aboutsummaryrefslogtreecommitdiff
path: root/externals
diff options
context:
space:
mode:
Diffstat (limited to 'externals')
-rw-r--r--externals/g3dlite/G3D.lib/include/G3D/AABSPTree.h1609
-rw-r--r--externals/g3dlite/G3D.lib/include/G3D/AABox.h281
-rw-r--r--externals/g3dlite/G3D.lib/include/G3D/AnyVal.h506
-rw-r--r--externals/g3dlite/G3D.lib/include/G3D/Array.h1180
-rw-r--r--externals/g3dlite/G3D.lib/include/G3D/AtomicInt32.h166
-rw-r--r--externals/g3dlite/G3D.lib/include/G3D/BinaryFormat.h140
-rw-r--r--externals/g3dlite/G3D.lib/include/G3D/BinaryInput.h441
-rw-r--r--externals/g3dlite/G3D.lib/include/G3D/BinaryOutput.h421
-rw-r--r--externals/g3dlite/G3D.lib/include/G3D/BoundsTrait.h20
-rw-r--r--externals/g3dlite/G3D.lib/include/G3D/Box.h193
-rw-r--r--externals/g3dlite/G3D.lib/include/G3D/Capsule.h90
-rw-r--r--externals/g3dlite/G3D.lib/include/G3D/CollisionDetection.h1178
-rw-r--r--externals/g3dlite/G3D.lib/include/G3D/Color1.h129
-rw-r--r--externals/g3dlite/G3D.lib/include/G3D/Color1uint8.h107
-rw-r--r--externals/g3dlite/G3D.lib/include/G3D/Color3.h390
-rw-r--r--externals/g3dlite/G3D.lib/include/G3D/Color3uint8.h127
-rw-r--r--externals/g3dlite/G3D.lib/include/G3D/Color4.h324
-rw-r--r--externals/g3dlite/G3D.lib/include/G3D/Color4uint8.h133
-rw-r--r--externals/g3dlite/G3D.lib/include/G3D/Cone.h68
-rw-r--r--externals/g3dlite/G3D.lib/include/G3D/ConvexPolyhedron.h179
-rw-r--r--externals/g3dlite/G3D.lib/include/G3D/CoordinateFrame.h315
-rw-r--r--externals/g3dlite/G3D.lib/include/G3D/Crypto.h96
-rw-r--r--externals/g3dlite/G3D.lib/include/G3D/Cylinder.h92
-rw-r--r--externals/g3dlite/G3D.lib/include/G3D/Discovery.h589
-rw-r--r--externals/g3dlite/G3D.lib/include/G3D/EqualsTrait.h26
-rw-r--r--externals/g3dlite/G3D.lib/include/G3D/G3D.h150
-rw-r--r--externals/g3dlite/G3D.lib/include/G3D/G3DAll.h26
-rw-r--r--externals/g3dlite/G3D.lib/include/G3D/G3DGameUnits.h44
-rw-r--r--externals/g3dlite/G3D.lib/include/G3D/GCamera.h294
-rw-r--r--externals/g3dlite/G3D.lib/include/G3D/GImage.h550
-rw-r--r--externals/g3dlite/G3D.lib/include/G3D/GLight.h72
-rw-r--r--externals/g3dlite/G3D.lib/include/G3D/GThread.h169
-rw-r--r--externals/g3dlite/G3D.lib/include/G3D/GUniqueID.h69
-rw-r--r--externals/g3dlite/G3D.lib/include/G3D/HashTrait.h63
-rw-r--r--externals/g3dlite/G3D.lib/include/G3D/Image1.h79
-rw-r--r--externals/g3dlite/G3D.lib/include/G3D/Image1uint8.h80
-rw-r--r--externals/g3dlite/G3D.lib/include/G3D/Image3.h79
-rw-r--r--externals/g3dlite/G3D.lib/include/G3D/Image3uint8.h85
-rw-r--r--externals/g3dlite/G3D.lib/include/G3D/Image4.h80
-rw-r--r--externals/g3dlite/G3D.lib/include/G3D/Image4uint8.h85
-rw-r--r--externals/g3dlite/G3D.lib/include/G3D/ImageFormat.h362
-rw-r--r--externals/g3dlite/G3D.lib/include/G3D/Line.h105
-rw-r--r--externals/g3dlite/G3D.lib/include/G3D/LineSegment.h115
-rw-r--r--externals/g3dlite/G3D.lib/include/G3D/Log.h109
-rw-r--r--externals/g3dlite/G3D.lib/include/G3D/Map2D.h665
-rw-r--r--externals/g3dlite/G3D.lib/include/G3D/Matrix.h634
-rw-r--r--externals/g3dlite/G3D.lib/include/G3D/Matrix2.h69
-rw-r--r--externals/g3dlite/G3D.lib/include/G3D/Matrix3.h323
-rw-r--r--externals/g3dlite/G3D.lib/include/G3D/Matrix4.h206
-rw-r--r--externals/g3dlite/G3D.lib/include/G3D/MeshAlg.h718
-rw-r--r--externals/g3dlite/G3D.lib/include/G3D/MeshBuilder.h82
-rw-r--r--externals/g3dlite/G3D.lib/include/G3D/NetAddress.h132
-rw-r--r--externals/g3dlite/G3D.lib/include/G3D/NetworkDevice.h738
-rw-r--r--externals/g3dlite/G3D.lib/include/G3D/PhysicsFrame.h74
-rw-r--r--externals/g3dlite/G3D.lib/include/G3D/Plane.h161
-rw-r--r--externals/g3dlite/G3D.lib/include/G3D/PointAABSPTree.h1207
-rw-r--r--externals/g3dlite/G3D.lib/include/G3D/PointHashGrid.h894
-rw-r--r--externals/g3dlite/G3D.lib/include/G3D/Pointer.h275
-rw-r--r--externals/g3dlite/G3D.lib/include/G3D/PositionTrait.h7
-rw-r--r--externals/g3dlite/G3D.lib/include/G3D/Quat.h725
-rw-r--r--externals/g3dlite/G3D.lib/include/G3D/Queue.h355
-rw-r--r--externals/g3dlite/G3D.lib/include/G3D/Ray.h329
-rw-r--r--externals/g3dlite/G3D.lib/include/G3D/Rect2D.h391
-rw-r--r--externals/g3dlite/G3D.lib/include/G3D/ReferenceCount.h597
-rw-r--r--externals/g3dlite/G3D.lib/include/G3D/RegistryUtil.h97
-rw-r--r--externals/g3dlite/G3D.lib/include/G3D/Set.h160
-rw-r--r--externals/g3dlite/G3D.lib/include/G3D/Sphere.h143
-rw-r--r--externals/g3dlite/G3D.lib/include/G3D/Spline.h367
-rw-r--r--externals/g3dlite/G3D.lib/include/G3D/Stopwatch.h108
-rw-r--r--externals/g3dlite/G3D.lib/include/G3D/System.h390
-rw-r--r--externals/g3dlite/G3D.lib/include/G3D/Table.h770
-rw-r--r--externals/g3dlite/G3D.lib/include/G3D/TextInput.h693
-rw-r--r--externals/g3dlite/G3D.lib/include/G3D/TextOutput.h249
-rw-r--r--externals/g3dlite/G3D.lib/include/G3D/ThreadSet.h79
-rw-r--r--externals/g3dlite/G3D.lib/include/G3D/Triangle.h143
-rw-r--r--externals/g3dlite/G3D.lib/include/G3D/UprightFrame.h83
-rw-r--r--externals/g3dlite/G3D.lib/include/G3D/Vector2.h457
-rw-r--r--externals/g3dlite/G3D.lib/include/G3D/Vector2int16.h137
-rw-r--r--externals/g3dlite/G3D.lib/include/G3D/Vector3.h761
-rw-r--r--externals/g3dlite/G3D.lib/include/G3D/Vector3int16.h130
-rw-r--r--externals/g3dlite/G3D.lib/include/G3D/Vector3int32.h138
-rw-r--r--externals/g3dlite/G3D.lib/include/G3D/Vector4.h717
-rw-r--r--externals/g3dlite/G3D.lib/include/G3D/Vector4int8.h113
-rw-r--r--externals/g3dlite/G3D.lib/include/G3D/WeakCache.h90
-rw-r--r--externals/g3dlite/G3D.lib/include/G3D/WrapMode.h79
-rw-r--r--externals/g3dlite/G3D.lib/include/G3D/debug.h66
-rw-r--r--externals/g3dlite/G3D.lib/include/G3D/debugAssert.h236
-rw-r--r--externals/g3dlite/G3D.lib/include/G3D/debugPrintf.h62
-rw-r--r--externals/g3dlite/G3D.lib/include/G3D/enumclass.h141
-rw-r--r--externals/g3dlite/G3D.lib/include/G3D/fileutils.h246
-rw-r--r--externals/g3dlite/G3D.lib/include/G3D/filter.h29
-rw-r--r--externals/g3dlite/G3D.lib/include/G3D/format.h44
-rw-r--r--externals/g3dlite/G3D.lib/include/G3D/g3dmath.h810
-rw-r--r--externals/g3dlite/G3D.lib/include/G3D/platform.h256
-rw-r--r--externals/g3dlite/G3D.lib/include/G3D/prompt.h67
-rw-r--r--externals/g3dlite/G3D.lib/include/G3D/serialize.h30
-rw-r--r--externals/g3dlite/G3D.lib/include/G3D/splinefunc.h118
-rw-r--r--externals/g3dlite/G3D.lib/include/G3D/stringutils.h130
-rw-r--r--externals/g3dlite/G3D.lib/include/G3D/uint128.h51
-rw-r--r--externals/g3dlite/G3D.lib/include/G3D/vectorMath.h235
-rw-r--r--externals/g3dlite/G3D.lib/source/AABox.cpp342
-rw-r--r--externals/g3dlite/G3D.lib/source/AnyVal.cpp1381
-rw-r--r--externals/g3dlite/G3D.lib/source/BinaryFormat.cpp81
-rw-r--r--externals/g3dlite/G3D.lib/source/BinaryInput.cpp568
-rw-r--r--externals/g3dlite/G3D.lib/source/BinaryOutput.cpp518
-rw-r--r--externals/g3dlite/G3D.lib/source/Box.cpp393
-rw-r--r--externals/g3dlite/G3D.lib/source/Capsule.cpp179
-rw-r--r--externals/g3dlite/G3D.lib/source/CollisionDetection.cpp2152
-rw-r--r--externals/g3dlite/G3D.lib/source/Color1.cpp40
-rw-r--r--externals/g3dlite/G3D.lib/source/Color1uint8.cpp38
-rw-r--r--externals/g3dlite/G3D.lib/source/Color3.cpp321
-rw-r--r--externals/g3dlite/G3D.lib/source/Color3uint8.cpp45
-rw-r--r--externals/g3dlite/G3D.lib/source/Color4.cpp140
-rw-r--r--externals/g3dlite/G3D.lib/source/Color4uint8.cpp47
-rw-r--r--externals/g3dlite/G3D.lib/source/Cone.cpp79
-rw-r--r--externals/g3dlite/G3D.lib/source/ConvexPolyhedron.cpp449
-rw-r--r--externals/g3dlite/G3D.lib/source/CoordinateFrame.cpp381
-rw-r--r--externals/g3dlite/G3D.lib/source/Crypto.cpp70
-rw-r--r--externals/g3dlite/G3D.lib/source/Crypto_md5.cpp471
-rw-r--r--externals/g3dlite/G3D.lib/source/Cylinder.cpp176
-rw-r--r--externals/g3dlite/G3D.lib/source/Discovery.cpp170
-rw-r--r--externals/g3dlite/G3D.lib/source/GCamera.cpp399
-rw-r--r--externals/g3dlite/G3D.lib/source/GImage.cpp1065
-rw-r--r--externals/g3dlite/G3D.lib/source/GImage_bayer.cpp298
-rw-r--r--externals/g3dlite/G3D.lib/source/GImage_bmp.cpp716
-rw-r--r--externals/g3dlite/G3D.lib/source/GImage_jpeg.cpp445
-rw-r--r--externals/g3dlite/G3D.lib/source/GImage_png.cpp245
-rw-r--r--externals/g3dlite/G3D.lib/source/GImage_ppm.cpp185
-rw-r--r--externals/g3dlite/G3D.lib/source/GImage_tga.cpp179
-rw-r--r--externals/g3dlite/G3D.lib/source/GLight.cpp143
-rw-r--r--externals/g3dlite/G3D.lib/source/GThread.cpp203
-rw-r--r--externals/g3dlite/G3D.lib/source/GUniqueID.cpp78
-rw-r--r--externals/g3dlite/G3D.lib/source/Image1.cpp224
-rw-r--r--externals/g3dlite/G3D.lib/source/Image1uint8.cpp212
-rw-r--r--externals/g3dlite/G3D.lib/source/Image3.cpp224
-rw-r--r--externals/g3dlite/G3D.lib/source/Image3uint8.cpp225
-rw-r--r--externals/g3dlite/G3D.lib/source/Image4.cpp226
-rw-r--r--externals/g3dlite/G3D.lib/source/Image4uint8.cpp222
-rw-r--r--externals/g3dlite/G3D.lib/source/ImageFormat.cpp440
-rw-r--r--externals/g3dlite/G3D.lib/source/ImageFormat_convert.cpp1305
-rw-r--r--externals/g3dlite/G3D.lib/source/Line.cpp89
-rw-r--r--externals/g3dlite/G3D.lib/source/LineSegment.cpp236
-rw-r--r--externals/g3dlite/G3D.lib/source/Log.cpp157
-rw-r--r--externals/g3dlite/G3D.lib/source/Matrix.cpp1801
-rw-r--r--externals/g3dlite/G3D.lib/source/Matrix3.cpp1725
-rw-r--r--externals/g3dlite/G3D.lib/source/Matrix4.cpp433
-rw-r--r--externals/g3dlite/G3D.lib/source/MeshAlg.cpp733
-rw-r--r--externals/g3dlite/G3D.lib/source/MeshAlgAdjacency.cpp729
-rw-r--r--externals/g3dlite/G3D.lib/source/MeshAlgWeld.cpp213
-rw-r--r--externals/g3dlite/G3D.lib/source/MeshAlgWeld2.cpp377
-rw-r--r--externals/g3dlite/G3D.lib/source/MeshBuilder.cpp113
-rw-r--r--externals/g3dlite/G3D.lib/source/NetAddress.cpp164
-rw-r--r--externals/g3dlite/G3D.lib/source/NetworkDevice.cpp1362
-rw-r--r--externals/g3dlite/G3D.lib/source/PhysicsFrame.cpp77
-rw-r--r--externals/g3dlite/G3D.lib/source/Plane.cpp149
-rw-r--r--externals/g3dlite/G3D.lib/source/Quat.cpp583
-rw-r--r--externals/g3dlite/G3D.lib/source/Ray.cpp112
-rw-r--r--externals/g3dlite/G3D.lib/source/RegistryUtil.cpp290
-rw-r--r--externals/g3dlite/G3D.lib/source/Sphere.cpp196
-rw-r--r--externals/g3dlite/G3D.lib/source/SplineBase.cpp162
-rw-r--r--externals/g3dlite/G3D.lib/source/Stopwatch.cpp96
-rw-r--r--externals/g3dlite/G3D.lib/source/System.cpp1864
-rw-r--r--externals/g3dlite/G3D.lib/source/TextInput.cpp988
-rw-r--r--externals/g3dlite/G3D.lib/source/TextOutput.cpp452
-rw-r--r--externals/g3dlite/G3D.lib/source/ThreadSet.cpp147
-rw-r--r--externals/g3dlite/G3D.lib/source/Triangle.cpp135
-rw-r--r--externals/g3dlite/G3D.lib/source/UprightFrame.cpp132
-rw-r--r--externals/g3dlite/G3D.lib/source/Vector2.cpp211
-rw-r--r--externals/g3dlite/G3D.lib/source/Vector2int16.cpp47
-rw-r--r--externals/g3dlite/G3D.lib/source/Vector3.cpp493
-rw-r--r--externals/g3dlite/G3D.lib/source/Vector3int16.cpp49
-rw-r--r--externals/g3dlite/G3D.lib/source/Vector3int32.cpp57
-rw-r--r--externals/g3dlite/G3D.lib/source/Vector4.cpp475
-rw-r--r--externals/g3dlite/G3D.lib/source/Vector4int8.cpp58
-rw-r--r--externals/g3dlite/G3D.lib/source/WinMain.cpp155
-rw-r--r--externals/g3dlite/G3D.lib/source/debugAssert.cpp392
-rw-r--r--externals/g3dlite/G3D.lib/source/fileutils.cpp1092
-rw-r--r--externals/g3dlite/G3D.lib/source/filter.cpp32
-rw-r--r--externals/g3dlite/G3D.lib/source/format.cpp164
-rw-r--r--externals/g3dlite/G3D.lib/source/g3dmath.cpp70
-rw-r--r--externals/g3dlite/G3D.lib/source/license.cpp70
-rw-r--r--externals/g3dlite/G3D.lib/source/prompt.cpp716
-rw-r--r--externals/g3dlite/G3D.lib/source/stringutils.cpp231
-rw-r--r--externals/g3dlite/G3D.lib/source/uint128.cpp155
-rw-r--r--externals/g3dlite/delme0
-rw-r--r--externals/g3dlite/doc-files/changelog.dox1872
-rw-r--r--externals/g3dlite/doc-files/contributors.dox85
-rw-r--r--externals/g3dlite/doc-files/license.dox120
-rw-r--r--externals/g3dlite/win/VC90/g3dlite.vcproj463
-rw-r--r--externals/g3dlite/win/delme0
-rw-r--r--externals/g3dlite/win/g3dlite.sln25
-rw-r--r--externals/g3dlite/zip.lib/include/zip/ioapi.h75
-rw-r--r--externals/g3dlite/zip.lib/include/zip/unzip.h354
-rw-r--r--externals/g3dlite/zip.lib/include/zip/zip.h235
-rw-r--r--externals/g3dlite/zip.lib/source/crypt.h132
-rw-r--r--externals/g3dlite/zip.lib/source/ioapi.c177
-rw-r--r--externals/g3dlite/zip.lib/source/iowin32.c272
-rw-r--r--externals/g3dlite/zip.lib/source/iowin32.h23
-rw-r--r--externals/g3dlite/zip.lib/source/unzip.c1604
-rw-r--r--externals/g3dlite/zip.lib/source/zip.c1221
-rw-r--r--externals/jemalloc/CMakeLists.txt27
-rw-r--r--externals/jemalloc/arena.c2446
-rw-r--r--externals/jemalloc/base.c106
-rw-r--r--externals/jemalloc/chunk.c150
-rw-r--r--externals/jemalloc/chunk_dss.c268
-rw-r--r--externals/jemalloc/chunk_mmap.c201
-rw-r--r--externals/jemalloc/chunk_swap.c383
-rw-r--r--externals/jemalloc/ckh.c601
-rw-r--r--externals/jemalloc/ctl.c1482
-rw-r--r--externals/jemalloc/delme0
-rw-r--r--externals/jemalloc/extent.c41
-rw-r--r--externals/jemalloc/hash.c2
-rw-r--r--externals/jemalloc/huge.c298
-rw-r--r--externals/jemalloc/include/internal/arena.h537
-rw-r--r--externals/jemalloc/include/internal/base.h24
-rw-r--r--externals/jemalloc/include/internal/chunk.h61
-rw-r--r--externals/jemalloc/include/internal/chunk_dss.h29
-rw-r--r--externals/jemalloc/include/internal/chunk_mmap.h20
-rw-r--r--externals/jemalloc/include/internal/chunk_swap.h33
-rw-r--r--externals/jemalloc/include/internal/ckh.h95
-rw-r--r--externals/jemalloc/include/internal/ctl.h117
-rw-r--r--externals/jemalloc/include/internal/extent.h49
-rw-r--r--externals/jemalloc/include/internal/hash.h70
-rw-r--r--externals/jemalloc/include/internal/huge.h38
-rw-r--r--externals/jemalloc/include/internal/jemalloc_internal.h561
-rw-r--r--externals/jemalloc/include/internal/jemalloc_internal.h.in561
-rw-r--r--externals/jemalloc/include/internal/mb.h108
-rw-r--r--externals/jemalloc/include/internal/mutex.h61
-rw-r--r--externals/jemalloc/include/internal/prof.h171
-rw-r--r--externals/jemalloc/include/internal/ql.h83
-rw-r--r--externals/jemalloc/include/internal/qr.h67
-rw-r--r--externals/jemalloc/include/internal/rb.h973
-rw-r--r--externals/jemalloc/include/internal/stats.h174
-rw-r--r--externals/jemalloc/include/internal/tcache.h380
-rw-r--r--externals/jemalloc/include/internal/totally_not_p_r_n.h60
-rw-r--r--externals/jemalloc/include/jemalloc.h42
-rw-r--r--externals/jemalloc/include/jemalloc.h.in42
-rw-r--r--externals/jemalloc/include/jemalloc_defs.h102
-rw-r--r--externals/jemalloc/include/jemalloc_defs.h.in101
-rw-r--r--externals/jemalloc/jemalloc.c1349
-rw-r--r--externals/jemalloc/mb.c2
-rw-r--r--externals/jemalloc/mutex.c70
-rw-r--r--externals/jemalloc/prof.c1328
-rw-r--r--externals/jemalloc/stats.c717
-rw-r--r--externals/jemalloc/tcache.c403
-rw-r--r--externals/sockets/Base64.cpp262
-rw-r--r--externals/sockets/CMakeLists.txt26
-rw-r--r--externals/sockets/Exception.cpp45
-rw-r--r--externals/sockets/Ipv4Address.cpp192
-rw-r--r--externals/sockets/Ipv6Address.cpp247
-rw-r--r--externals/sockets/Lock.cpp52
-rw-r--r--externals/sockets/Mutex.cpp77
-rw-r--r--externals/sockets/Parse.cpp318
-rw-r--r--externals/sockets/ResolvServer.cpp92
-rw-r--r--externals/sockets/ResolvSocket.cpp426
-rw-r--r--externals/sockets/Socket.cpp1726
-rw-r--r--externals/sockets/SocketHandler.cpp1377
-rw-r--r--externals/sockets/StdoutLog.cpp98
-rw-r--r--externals/sockets/StreamSocket.cpp145
-rw-r--r--externals/sockets/TcpSocket.cpp1681
-rw-r--r--externals/sockets/Thread.cpp154
-rw-r--r--externals/sockets/UdpSocket.cpp810
-rw-r--r--externals/sockets/Utility.cpp960
-rw-r--r--externals/sockets/delme0
-rw-r--r--externals/sockets/include/Base64.h77
-rw-r--r--externals/sockets/include/Exception.h55
-rw-r--r--externals/sockets/include/File.h82
-rw-r--r--externals/sockets/include/IFile.h71
-rw-r--r--externals/sockets/include/ISocketHandler.h231
-rw-r--r--externals/sockets/include/Ipv4Address.h95
-rw-r--r--externals/sockets/include/Ipv6Address.h105
-rw-r--r--externals/sockets/include/ListenSocket.h418
-rw-r--r--externals/sockets/include/Lock.h58
-rw-r--r--externals/sockets/include/Mutex.h68
-rw-r--r--externals/sockets/include/Parse.h100
-rw-r--r--externals/sockets/include/ResolvServer.h72
-rw-r--r--externals/sockets/include/ResolvSocket.h105
-rw-r--r--externals/sockets/include/SctpSocket.h108
-rw-r--r--externals/sockets/include/Socket.h735
-rw-r--r--externals/sockets/include/SocketAddress.h93
-rw-r--r--externals/sockets/include/SocketHandler.h265
-rw-r--r--externals/sockets/include/StdLog.h73
-rw-r--r--externals/sockets/include/StdoutLog.h55
-rw-r--r--externals/sockets/include/StreamSocket.h124
-rw-r--r--externals/sockets/include/TcpSocket.h356
-rw-r--r--externals/sockets/include/Thread.h100
-rw-r--r--externals/sockets/include/UdpSocket.h215
-rw-r--r--externals/sockets/include/Utility.h186
-rw-r--r--externals/sockets/include/socket_include.h290
-rw-r--r--externals/sockets/include/sockets-config.h90
-rw-r--r--externals/sockets/network_kist.txt20
-rw-r--r--externals/sockets/socket_include.cpp89
-rw-r--r--externals/utf8cpp/delme0
-rw-r--r--externals/utf8cpp/include/doc/ReleaseNotes9
-rw-r--r--externals/utf8cpp/include/doc/utf8cpp.html1574
-rw-r--r--externals/utf8cpp/include/utf8.h35
-rw-r--r--externals/utf8cpp/include/utf8/checked.h319
-rw-r--r--externals/utf8cpp/include/utf8/core.h269
-rw-r--r--externals/utf8cpp/include/utf8/unchecked.h229
-rw-r--r--externals/vld/delme0
-rw-r--r--externals/vld/vld.h105
-rw-r--r--externals/zlib/win/VC90/zlib.vcproj8
302 files changed, 98139 insertions, 4 deletions
diff --git a/externals/g3dlite/G3D.lib/include/G3D/AABSPTree.h b/externals/g3dlite/G3D.lib/include/G3D/AABSPTree.h
new file mode 100644
index 00000000000..1178fad93c3
--- /dev/null
+++ b/externals/g3dlite/G3D.lib/include/G3D/AABSPTree.h
@@ -0,0 +1,1609 @@
+/**
+ @file AABSPTree.h
+
+ @maintainer Morgan McGuire, matrix@graphics3d.com
+
+ @created 2004-01-11
+ @edited 2008-11-19
+
+ Copyright 2000-2008, Morgan McGuire.
+ All rights reserved.
+
+ */
+
+#ifndef G3D_KDTREE_H
+#define G3D_KDTREE_H
+
+#include "G3D/platform.h"
+#include "G3D/Array.h"
+#include "G3D/Table.h"
+#include "G3D/Vector2.h"
+#include "G3D/Vector3.h"
+#include "G3D/Vector4.h"
+#include "G3D/AABox.h"
+#include "G3D/Sphere.h"
+#include "G3D/Box.h"
+#include "G3D/Triangle.h"
+#include "G3D/Ray.h"
+#include "G3D/GCamera.h"
+#include "G3D/BinaryInput.h"
+#include "G3D/BinaryOutput.h"
+#include "G3D/CollisionDetection.h"
+#include "G3D/GCamera.h"
+#include "G3D/BoundsTrait.h"
+#include <algorithm>
+
+// If defined, in debug mode the tree is checked for consistency
+// as a way of detecting corruption due to implementation bugs
+// #define VERIFY_TREE
+
+template<> struct BoundsTrait<class G3D::Vector2> {
+ static void getBounds(const G3D::Vector2& v, G3D::AABox& out) { out = G3D::AABox(G3D::Vector3(v, 0)); }
+};
+
+template<> struct BoundsTrait<class G3D::Vector3> {
+ static void getBounds(const G3D::Vector3& v, G3D::AABox& out) { out = G3D::AABox(v); }
+};
+
+template<> struct BoundsTrait<class G3D::Vector4> {
+ static void getBounds(const G3D::Vector4& v, G3D::AABox& out) { out = G3D::AABox(v.xyz()); }
+};
+
+template<> struct BoundsTrait<class G3D::AABox> {
+ static void getBounds(const G3D::AABox& v, G3D::AABox& out) { out = v; }
+};
+
+template<> struct BoundsTrait<class G3D::Sphere> {
+ static void getBounds(const G3D::Sphere& s, G3D::AABox& out) { s.getBounds(out); }
+};
+
+template<> struct BoundsTrait<class G3D::Box> {
+ static void getBounds(const G3D::Box& b, G3D::AABox& out) { b.getBounds(out); }
+};
+
+template<> struct BoundsTrait<class G3D::Vector2*> {
+ static void getBounds(const G3D::Vector2*& v, G3D::AABox& out) { out = G3D::AABox(G3D::Vector3(*v, 0)); }
+};
+
+template<> struct BoundsTrait<class G3D::Vector3*> {
+ static void getBounds(const G3D::Vector3*& v, G3D::AABox& out) { out = G3D::AABox(*v); }
+};
+
+template<> struct BoundsTrait<class G3D::Vector4*> {
+ static void getBounds(const G3D::Vector4*& v, G3D::AABox& out) { out = G3D::AABox(v->xyz()); }
+};
+
+template<> struct BoundsTrait<class G3D::AABox*> {
+ static void getBounds(const G3D::AABox*& v, G3D::AABox& out) { out = *v; }
+};
+
+template<> struct BoundsTrait<class G3D::Sphere*> {
+ static void getBounds(const G3D::Sphere*& s, G3D::AABox& out) { s->getBounds(out); }
+};
+
+template<> struct BoundsTrait<class G3D::Box*> {
+ static void getBounds(const G3D::Box*& b, G3D::AABox& out) { b->getBounds(out); }
+};
+
+
+template<> struct BoundsTrait<class G3D::Triangle*> {
+ static void getBounds(const G3D::Triangle*& t, G3D::AABox& out) { t->getBounds(out); }
+};
+
+namespace G3D {
+ namespace _internal {
+
+ /**
+ Wraps a pointer value so that it can be treated as the instance itself;
+ convenient for inserting pointers into a Table but using the
+ object equality instead of pointer equality.
+ */
+ template<class Type>
+ class Indirector {
+ public:
+ Type* handle;
+
+ inline Indirector(Type* h) : handle(h) {}
+
+ inline Indirector() : handle(NULL) {}
+
+ /** Returns true iff the values referenced by the handles are equivalent. */
+ inline bool operator==(const Indirector& m) const {
+ return *handle == *(m.handle);
+ }
+
+ inline bool operator==(const Type& m) const {
+ return *handle == m;
+ }
+
+ inline size_t hashCode() const {
+ return handle->hashCode();
+ }
+ };
+ } // namespace internal
+} // namespace G3D
+
+template <class Handle> struct HashTrait<typename G3D::_internal::Indirector<Handle> > {
+ static size_t hashCode(const G3D::_internal::Indirector<Handle>& key) { return key.hashCode(); }
+};
+
+namespace G3D {
+
+/**
+ A set that supports spatial queries using a KD tree (axis-aligned
+ BSP tree) for speed.
+
+ KDTree allows you to quickly find objects in 3D that lie within
+ a box or along a ray. For large sets of objects it is much faster
+ than testing each object for a collision.
+
+ KDTree is as powerful as but more general than a Quad Tree, Oct
+ Tree, or regular KD tree that cycles through axes, but less general than an unconstrained BSP tree
+ (which is much slower to create).
+
+ Internally, objects
+ are arranged into a tree according to their
+ axis-aligned bounds. This increases the cost of insertion to
+ O(log n) but allows fast overlap queries.
+
+ <B>Template Parameters</B>
+ <DT>The template parameter <I>T</I> must be one for which
+ the following functions are all overloaded:
+
+ <pre>
+ T::T();</CODE> <I>(public constructor of no arguments)</I>
+ template <> struct HashTrait<T> { static size_t hashCode(int key); };
+ template<> struct BoundsTrait<T> { static void getBounds(const T& obj, G3D::AABox& out); };
+ </pre>
+
+ G3D provides these for common classes like G3D::Vector3 and G3D::Sphere.
+ If you use a custom class, or a pointer to a custom class, you will need
+ to define those functions.
+
+ <B>Moving %Set Members</B>
+ <DT>It is important that objects do not move without updating the
+ KDTree. If the axis-aligned bounds of an object are about
+ to change, KDTree::remove it before they change and
+ KDTree::insert it again afterward. For objects
+ where the hashCode and == operator are invariant with respect
+ to the 3D position,
+ you can use the KDTree::update method as a shortcut to
+ insert/remove an object in one step after it has moved.
+
+
+ Note: Do not mutate any value once it has been inserted into KDTree. Values
+ are copied interally. All KDTree iterators convert to pointers to constant
+ values to reinforce this.
+
+ If you want to mutate the objects you intend to store in a KDTree
+ simply insert <I>pointers</I> to your objects instead of the objects
+ themselves, and ensure that the above operations are defined. (And
+ actually, because values are copied, if your values are large you may
+ want to insert pointers anyway, to save space and make the balance
+ operation faster.)
+
+ <B>Dimensions</B>
+ Although designed as a 3D-data structure, you can use the KDTree
+ for data distributed along 2 or 1 axes by simply returning bounds
+ that are always zero along one or more dimensions.
+
+*/
+template< class T,
+ class BoundsFunc = BoundsTrait<T>,
+ class HashFunc = HashTrait<T>,
+ class EqualsFunc = EqualsTrait<T> >
+class KDTree {
+protected:
+#define TreeType KDTree<T, BoundsFunc, HashFunc, EqualsFunc>
+
+ /** Wrapper for a value that includes a cache of its bounds.
+ Except for the test value used in a set-query operation, there
+ is only ever one instance of the handle associated with any
+ value and the memberTable and Nodes maintain pointers to that
+ heap-allocated value.
+ */
+ class Handle {
+ public:
+ /** The bounds of each object are constrained to AABox::large */
+ AABox bounds;
+
+ /** Center of bounds. We cache this value to avoid recomputing it
+ during the median sort, and because MSVC 6 std::sort goes into
+ an infinite loop if we compute the midpoint on the fly (possibly
+ a floating point roundoff issue, where B<A and A<B both are true).*/
+ Vector3 center;
+
+ T value;
+
+ Handle() {}
+
+ inline Handle(const T& v) : value(v) {
+ BoundsFunc::getBounds(v, bounds);
+ bounds = bounds.intersect(AABox::large());
+ center = bounds.center();
+ }
+
+ inline bool operator==(const Handle& other) const {
+ return EqualsFunc::equals(value, other.value);
+ }
+
+ inline size_t hashCode() const {
+ return HashFunc::hashCode(value);
+ }
+ };
+
+ /** Returns the bounds of the sub array. Used by makeNode. */
+ static AABox computeBounds(
+ const Array<Handle*>& point,
+ int beginIndex,
+ int endIndex) {
+
+ Vector3 lo = Vector3::inf();
+ Vector3 hi = -lo;
+
+ debugAssertM(beginIndex <= endIndex, "No points");
+ for (int p = beginIndex; p <= endIndex; ++p) {
+ // This code is written with the vector min and max expanded
+ // because otherwise it compiles incorrectly with -O3 on
+ // gcc 3.4
+
+ const Vector3& pLo = point[p]->bounds.low();
+ const Vector3& pHi = point[p]->bounds.high();
+ for (int a = 0; a < 3; ++a) {
+ lo[a] = G3D::min(lo[a], pLo[a]);
+ hi[a] = G3D::max(hi[a], pHi[a]);
+ }
+ }
+
+ return AABox(lo, hi);
+ }
+
+ /** Compares centers */
+ class CenterComparator {
+ public:
+ Vector3::Axis sortAxis;
+
+ CenterComparator(Vector3::Axis a) : sortAxis(a) {}
+
+ inline int operator()(Handle* A, const Handle* B) const {
+ float a = A->center[sortAxis];
+ float b = B->center[sortAxis];
+
+ if (a < b) {
+ return 1;
+ } else if (a > b) {
+ return -1;
+ } else {
+ return 0;
+ }
+ }
+ };
+
+
+ /** Compares bounds for strict >, <, or overlap*/
+ class BoundsComparator {
+ public:
+ Vector3::Axis sortAxis;
+
+ BoundsComparator(Vector3::Axis a) : sortAxis(a) {}
+
+ inline int operator()(Handle* A, const Handle* B) const {
+ const AABox& a = A->bounds;
+ const AABox& b = B->bounds;
+
+ if (a.high()[sortAxis] < b.low()[sortAxis]) {
+ return 1;
+ } else if (a.low()[sortAxis] > b.high()[sortAxis]) {
+ return -1;
+ } else {
+ return 0;
+ }
+ }
+ };
+
+
+ /** Compares bounds to the sort location */
+ class Comparator {
+ public:
+ Vector3::Axis sortAxis;
+ float sortLocation;
+
+ Comparator(Vector3::Axis a, float l) : sortAxis(a), sortLocation(l) {}
+
+ inline int operator()(Handle* ignore, const Handle* handle) const {
+ const AABox& box = handle->bounds;
+ debugAssert(ignore == NULL);
+
+ if (box.high()[sortAxis] < sortLocation) {
+ // Box is strictly below the sort location
+ return -1;
+ } else if (box.low()[sortAxis] > sortLocation) {
+ // Box is strictly above the sort location
+ return 1;
+ } else {
+ // Box overlaps the sort location
+ return 0;
+ }
+ }
+ };
+
+ // Using System::malloc with this class provided no speed improvement.
+ class Node {
+ public:
+
+ /** Spatial bounds on all values at this node and its children, based purely on
+ the parent's splitting planes. May be infinite. */
+ AABox splitBounds;
+
+ Vector3::Axis splitAxis;
+
+ /** Location along the specified axis */
+ float splitLocation;
+
+ /** child[0] contains all values strictly
+ smaller than splitLocation along splitAxis.
+
+ child[1] contains all values strictly
+ larger.
+
+ Both may be NULL if there are not enough
+ values to bother recursing.
+ */
+ Node* child[2];
+
+ /** Array of values at this node (i.e., values
+ straddling the split plane + all values if
+ this is a leaf node).
+
+ This is an array of pointers because that minimizes
+ data movement during tree building, which accounts
+ for about 15% of the time cost of tree building.
+ */
+ Array<Handle*> valueArray;
+
+ /** For each object in the value array, a copy of its bounds.
+ Packing these into an array at the node level
+ instead putting them in the valueArray improves
+ cache coherence, which is about a 3x performance
+ increase when performing intersection computations.
+ */
+ Array<AABox> boundsArray;
+
+ /** Creates node with NULL children */
+ Node() {
+ splitAxis = Vector3::X_AXIS;
+ splitLocation = 0;
+ splitBounds = AABox(-Vector3::inf(), Vector3::inf());
+ for (int i = 0; i < 2; ++i) {
+ child[i] = NULL;
+ }
+ }
+
+ /**
+ Doesn't clone children.
+ */
+ Node(const Node& other) : valueArray(other.valueArray), boundsArray(other.boundsArray) {
+ splitAxis = other.splitAxis;
+ splitLocation = other.splitLocation;
+ splitBounds = other.splitBounds;
+ for (int i = 0; i < 2; ++i) {
+ child[i] = NULL;
+ }
+ }
+
+ /** Copies the specified subarray of pt into point, NULLs the children.
+ Assumes a second pass will set splitBounds. */
+ Node(const Array<Handle*>& pt) : valueArray(pt) {
+ splitAxis = Vector3::X_AXIS;
+ splitLocation = 0;
+ for (int i = 0; i < 2; ++i) {
+ child[i] = NULL;
+ }
+
+ boundsArray.resize(valueArray.size());
+ for (int i = 0; i < valueArray.size(); ++i) {
+ boundsArray[i] = valueArray[i]->bounds;
+ }
+ }
+
+ /** Deletes the children (but not the values) */
+ ~Node() {
+ for (int i = 0; i < 2; ++i) {
+ delete child[i];
+ }
+ }
+
+ /** Returns true if this node is a leaf (no children) */
+ inline bool isLeaf() const {
+ return (child[0] == NULL) && (child[1] == NULL);
+ }
+
+
+ /**
+ Recursively appends all handles and children's handles
+ to the array.
+ */
+ void getHandles(Array<Handle*>& handleArray) const {
+ handleArray.append(valueArray);
+ for (int i = 0; i < 2; ++i) {
+ if (child[i] != NULL) {
+ child[i]->getHandles(handleArray);
+ }
+ }
+ }
+
+ void verifyNode(const Vector3& lo, const Vector3& hi) {
+ // debugPrintf("Verifying: split %d @ %f [%f, %f, %f], [%f, %f, %f]\n",
+ // splitAxis, splitLocation, lo.x, lo.y, lo.z, hi.x, hi.y, hi.z);
+
+ debugAssertM(lo == splitBounds.low(),
+ format("lo = %s, splitBounds.lo = %s",
+ lo.toString().c_str(), splitBounds.low().toString().c_str()));
+ debugAssert(hi == splitBounds.high());
+
+ for (int i = 0; i < valueArray.length(); ++i) {
+ const AABox& b = valueArray[i]->bounds;
+ debugAssert(b == boundsArray[i]);
+
+ for(int axis = 0; axis < 3; ++axis) {
+ debugAssert(b.low()[axis] <= b.high()[axis]);
+ debugAssert(b.low()[axis] >= lo[axis]);
+ debugAssert(b.high()[axis] <= hi[axis]);
+ }
+ }
+
+ if (child[0] || child[1]) {
+ debugAssert(lo[splitAxis] < splitLocation);
+ debugAssert(hi[splitAxis] > splitLocation);
+ }
+
+ Vector3 newLo = lo;
+ newLo[splitAxis] = splitLocation;
+ Vector3 newHi = hi;
+ newHi[splitAxis] = splitLocation;
+
+ if (child[0] != NULL) {
+ child[0]->verifyNode(lo, newHi);
+ }
+
+ if (child[1] != NULL) {
+ child[1]->verifyNode(newLo, hi);
+ }
+ }
+
+
+ /**
+ Stores the locations of the splitting planes (the structure but not the content)
+ so that the tree can be quickly rebuilt from a previous configuration without
+ calling balance.
+ */
+ static void serializeStructure(const Node* n, BinaryOutput& bo) {
+ if (n == NULL) {
+ bo.writeUInt8(0);
+ } else {
+ bo.writeUInt8(1);
+ n->splitBounds.serialize(bo);
+ serialize(n->splitAxis, bo);
+ bo.writeFloat32(n->splitLocation);
+ for (int c = 0; c < 2; ++c) {
+ serializeStructure(n->child[c], bo);
+ }
+ }
+ }
+
+ /** Clears the member table */
+ static Node* deserializeStructure(BinaryInput& bi) {
+ if (bi.readUInt8() == 0) {
+ return NULL;
+ } else {
+ Node* n = new Node();
+ n->splitBounds.deserialize(bi);
+ deserialize(n->splitAxis, bi);
+ n->splitLocation = bi.readFloat32();
+ for (int c = 0; c < 2; ++c) {
+ n->child[c] = deserializeStructure(bi);
+ }
+ return n;
+ }
+ }
+
+ /** Returns the deepest node that completely contains bounds. */
+ Node* findDeepestContainingNode(const AABox& bounds) {
+
+ // See which side of the splitting plane the bounds are on
+ if (bounds.high()[splitAxis] < splitLocation) {
+ // Bounds are on the low side. Recurse into the child
+ // if it exists.
+ if (child[0] != NULL) {
+ return child[0]->findDeepestContainingNode(bounds);
+ }
+ } else if (bounds.low()[splitAxis] > splitLocation) {
+ // Bounds are on the high side, recurse into the child
+ // if it exists.
+ if (child[1] != NULL) {
+ return child[1]->findDeepestContainingNode(bounds);
+ }
+ }
+
+ // There was no containing child, so this node is the
+ // deepest containing node.
+ return this;
+ }
+
+
+ /** Appends all members that intersect the box.
+ If useSphere is true, members that pass the box test
+ face a second test against the sphere. */
+ void getIntersectingMembers(
+ const AABox& box,
+ const Sphere& sphere,
+ Array<T>& members,
+ bool useSphere) const {
+
+ // Test all values at this node
+ for (int v = 0; v < boundsArray.size(); ++v) {
+ const AABox& bounds = boundsArray[v];
+ if (bounds.intersects(box) &&
+ (! useSphere || bounds.intersects(sphere))) {
+ members.append(valueArray[v]->value);
+ }
+ }
+
+ // If the left child overlaps the box, recurse into it
+ if ((child[0] != NULL) && (box.low()[splitAxis] < splitLocation)) {
+ child[0]->getIntersectingMembers(box, sphere, members, useSphere);
+ }
+
+ // If the right child overlaps the box, recurse into it
+ if ((child[1] != NULL) && (box.high()[splitAxis] > splitLocation)) {
+ child[1]->getIntersectingMembers(box, sphere, members, useSphere);
+ }
+ }
+
+ /**
+ Recurse through the tree, assigning splitBounds fields.
+ */
+ void assignSplitBounds(const AABox& myBounds) {
+ splitBounds = myBounds;
+
+ AABox childBounds[2];
+ myBounds.split(splitAxis, splitLocation, childBounds[0], childBounds[1]);
+
+# if defined(G3D_DEBUG) && defined(VERIFY_TREE)
+ // Verify the split
+ for (int v = 0; v < boundsArray.size(); ++v) {
+ const AABox& bounds = boundsArray[v];
+ debugAssert(myBounds.contains(bounds));
+ }
+# endif
+
+ for (int c = 0; c < 2; ++c) {
+ if (child[c]) {
+ child[c]->assignSplitBounds(childBounds[c]);
+ }
+ }
+ }
+
+ /** Returns true if the ray intersects this node */
+ bool intersects(const Ray& ray, float distance) const {
+ // See if the ray will ever hit this node or its children
+ Vector3 location;
+ bool alreadyInsideBounds = false;
+ bool rayWillHitBounds =
+ CollisionDetection::collisionLocationForMovingPointFixedAABox(
+ ray.origin, ray.direction, splitBounds, location, alreadyInsideBounds);
+
+ bool canHitThisNode = (alreadyInsideBounds ||
+ (rayWillHitBounds && ((location - ray.origin).squaredLength() < square(distance))));
+
+ return canHitThisNode;
+ }
+
+ template<typename RayCallback>
+ void intersectRay(
+ const Ray& ray,
+ RayCallback& intersectCallback,
+ float& distance,
+ bool intersectCallbackIsFast) const {
+
+ if (! intersects(ray, distance)) {
+ // The ray doesn't hit this node, so it can't hit the children of the node.
+ return;
+ }
+
+ // Test for intersection against every object at this node.
+ for (int v = 0; v < valueArray.size(); ++v) {
+ bool canHitThisObject = true;
+
+ if (! intersectCallbackIsFast) {
+ // See if
+ Vector3 location;
+ const AABox& bounds = boundsArray[v];
+ bool alreadyInsideBounds = false;
+ bool rayWillHitBounds =
+ CollisionDetection::collisionLocationForMovingPointFixedAABox(
+ ray.origin, ray.direction, bounds, location, alreadyInsideBounds);
+
+ canHitThisObject = (alreadyInsideBounds ||
+ (rayWillHitBounds && ((location - ray.origin).squaredLength() < square(distance))));
+ }
+
+ if (canHitThisObject) {
+ // It is possible that this ray hits this object. Look for the intersection using the
+ // callback.
+ const T& value = valueArray[v]->value;
+ intersectCallback(ray, value, distance);
+ }
+ }
+
+ // There are three cases to consider next:
+ //
+ // 1. the ray can start on one side of the splitting plane and never enter the other,
+ // 2. the ray can start on one side and enter the other, and
+ // 3. the ray can travel exactly down the splitting plane
+
+ enum {NONE = -1};
+ int firstChild = NONE;
+ int secondChild = NONE;
+
+ if (ray.origin[splitAxis] < splitLocation) {
+
+ // The ray starts on the small side
+ firstChild = 0;
+
+ if (ray.direction[splitAxis] > 0) {
+ // The ray will eventually reach the other side
+ secondChild = 1;
+ }
+
+ } else if (ray.origin[splitAxis] > splitLocation) {
+
+ // The ray starts on the large side
+ firstChild = 1;
+
+ if (ray.direction[splitAxis] < 0) {
+ secondChild = 0;
+ }
+ } else {
+ // The ray starts on the splitting plane
+ if (ray.direction[splitAxis] < 0) {
+ // ...and goes to the small side
+ firstChild = 0;
+ } else if (ray.direction[splitAxis] > 0) {
+ // ...and goes to the large side
+ firstChild = 1;
+ }
+ }
+
+ // Test on the side closer to the ray origin.
+ if ((firstChild != NONE) && child[firstChild]) {
+ child[firstChild]->intersectRay(ray, intersectCallback, distance, intersectCallbackIsFast);
+ }
+
+ if (ray.direction[splitAxis] != 0) {
+ // See if there was an intersection before hitting the splitting plane.
+ // If so, there is no need to look on the far side and recursion terminates.
+ float distanceToSplittingPlane = (splitLocation - ray.origin[splitAxis]) / ray.direction[splitAxis];
+ if (distanceToSplittingPlane > distance) {
+ // We aren't going to hit anything else before hitting the splitting plane,
+ // so don't bother looking on the far side of the splitting plane at the other
+ // child.
+ return;
+ }
+ }
+
+ // Test on the side farther from the ray origin.
+ if ((secondChild != NONE) && child[secondChild]) {
+ child[secondChild]->intersectRay(ray, intersectCallback, distance, intersectCallbackIsFast);
+ }
+
+ }
+ };
+
+
+ /**
+ Recursively subdivides the subarray.
+
+ Clears the source array as soon as it is no longer needed.
+
+ Call assignSplitBounds() on the root node after making a tree.
+ */
+ Node* makeNode(
+ Array<Handle*>& source,
+ int valuesPerNode,
+ int numMeanSplits,
+ Array<Handle*>& temp) {
+
+ Node* node = NULL;
+
+ if (source.size() <= valuesPerNode) {
+ // Make a new leaf node
+ node = new Node(source);
+
+ // Set the pointers in the memberTable
+ for (int i = 0; i < source.size(); ++i) {
+ memberTable.set(Member(source[i]), node);
+ }
+ source.clear();
+
+ } else {
+ // Make a new internal node
+ node = new Node();
+
+ const AABox& bounds = computeBounds(source, 0, source.size() - 1);
+ const Vector3& extent = bounds.high() - bounds.low();
+
+ Vector3::Axis splitAxis = extent.primaryAxis();
+
+ float splitLocation;
+
+ // Arrays for holding the children
+ Array<Handle*> lt, gt;
+
+ if (numMeanSplits <= 0) {
+
+ source.medianPartition(lt, node->valueArray, gt, temp, CenterComparator(splitAxis));
+
+ // Choose the split location to be the center of whatever fell in the center
+ splitLocation = node->valueArray[0]->center[splitAxis];
+
+ // Some of the elements in the lt or gt array might really overlap the split location.
+ // Move them as needed.
+ for (int i = 0; i < lt.size(); ++i) {
+ const AABox& bounds = lt[i]->bounds;
+ if ((bounds.low()[splitAxis] <= splitLocation) && (bounds.high()[splitAxis] >= splitLocation)) {
+ node->valueArray.append(lt[i]);
+ // Remove this element and process the new one that
+ // is swapped in in its place.
+ lt.fastRemove(i); --i;
+ }
+ }
+
+ for (int i = 0; i < gt.size(); ++i) {
+ const AABox& bounds = gt[i]->bounds;
+ if ((bounds.low()[splitAxis] <= splitLocation) && (bounds.high()[splitAxis] >= splitLocation)) {
+ node->valueArray.append(gt[i]);
+ // Remove this element and process the new one that
+ // is swapped in in its place.
+ gt.fastRemove(i); --i;
+ }
+ }
+
+ if ((node->valueArray.size() > (source.size() / 2)) &&
+ (source.size() > 6)) {
+ // This was a bad partition; we ended up putting the splitting plane right in the middle of most of the
+ // objects. We could try to split on a different axis, or use a different partition (e.g., the extents mean,
+ // or geometric mean). This implementation falls back on the extents mean, since that case is already handled
+ // below.
+ numMeanSplits = 1;
+ }
+ }
+
+ // Note: numMeanSplits may have been increased by the code in the previous case above in order to
+ // force a re-partition.
+
+ if (numMeanSplits > 0) {
+ // Split along the mean
+ splitLocation =
+ bounds.high()[splitAxis] * 0.5f +
+ bounds.low()[splitAxis] * 0.5f;
+
+ debugAssertM(isFinite(splitLocation),
+ "Internal error: split location must be finite.");
+
+ source.partition(NULL, lt, node->valueArray, gt, Comparator(splitAxis, splitLocation));
+
+ // The Comparator ensures that elements are strictly on the correct side of the split
+ }
+
+
+# if defined(G3D_DEBUG) && defined(VERIFY_TREE)
+ debugAssert(lt.size() + node->valueArray.size() + gt.size() == source.size());
+ // Verify that all objects ended up on the correct side of the split.
+ // (i.e., make sure that the Array partition was correct)
+ for (int i = 0; i < lt.size(); ++i) {
+ const AABox& bounds = lt[i]->bounds;
+ debugAssert(bounds.high()[splitAxis] < splitLocation);
+ }
+
+ for (int i = 0; i < gt.size(); ++i) {
+ const AABox& bounds = gt[i]->bounds;
+ debugAssert(bounds.low()[splitAxis] > splitLocation);
+ }
+
+ for (int i = 0; i < node->valueArray.size(); ++i) {
+ const AABox& bounds = node->valueArray[i]->bounds;
+ debugAssert(bounds.high()[splitAxis] >= splitLocation);
+ debugAssert(bounds.low()[splitAxis] <= splitLocation);
+ }
+# endif
+
+ // The source array is no longer needed
+ source.clear();
+
+ node->splitAxis = splitAxis;
+ node->splitLocation = splitLocation;
+
+ // Update the bounds array and member table
+ node->boundsArray.resize(node->valueArray.size());
+ for (int i = 0; i < node->valueArray.size(); ++i) {
+ Handle* v = node->valueArray[i];
+ node->boundsArray[i] = v->bounds;
+ memberTable.set(Member(v), node);
+ }
+
+ if (lt.size() > 0) {
+ node->child[0] = makeNode(lt, valuesPerNode, numMeanSplits - 1, temp);
+ }
+
+ if (gt.size() > 0) {
+ node->child[1] = makeNode(gt, valuesPerNode, numMeanSplits - 1, temp);
+ }
+
+ }
+
+ return node;
+ }
+
+ /**
+ Recursively clone the passed in node tree, setting
+ pointers for members in the memberTable as appropriate.
+ called by the assignment operator.
+ */
+ Node* cloneTree(Node* src) {
+ Node* dst = new Node(*src);
+
+ // Make back pointers
+ for (int i = 0; i < dst->valueArray.size(); ++i) {
+ memberTable.set(Member(dst->valueArray[i]), dst);
+ }
+
+ // Clone children
+ for (int i = 0; i < 2; ++i) {
+ if (src->child[i] != NULL) {
+ dst->child[i] = cloneTree(src->child[i]);
+ }
+ }
+
+ return dst;
+ }
+
+ /**
+ Wrapper for a Handle; used to create a memberTable that acts like Table<Handle, Node*> but
+ stores only Handle* internally to avoid memory copies.
+ */
+ typedef _internal::Indirector<Handle> Member;
+
+ typedef Table<Member, Node*> MemberTable;
+
+ /** Maps members to the node containing them */
+ MemberTable memberTable;
+
+ Node* root;
+
+public:
+
+ /** To construct a balanced tree, insert the elements and then call
+ KDTree::balance(). */
+ KDTree() : root(NULL) {}
+
+
+ KDTree(const KDTree& src) : root(NULL) {
+ *this = src;
+ }
+
+
+ KDTree& operator=(const KDTree& src) {
+ delete root;
+ // Clone tree takes care of filling out the memberTable.
+ root = cloneTree(src.root);
+ return *this;
+ }
+
+
+ ~KDTree() {
+ clear();
+ }
+
+ /**
+ Throws out all elements of the set.
+ */
+ void clear() {
+ typedef typename Table<_internal::Indirector<Handle>, Node*>::Iterator It;
+
+ // Delete all handles stored in the member table
+ It cur = memberTable.begin();
+ It end = memberTable.end();
+ while (cur != end) {
+ delete cur->key.handle;
+ cur->key.handle = NULL;
+ ++cur;
+ }
+ memberTable.clear();
+
+ // Delete the tree structure itself
+ delete root;
+ root = NULL;
+ }
+
+ int size() const {
+ return memberTable.size();
+ }
+
+ /**
+ Inserts an object into the set if it is not
+ already present. O(log n) time. Does not
+ cause the tree to be balanced.
+ */
+ void insert(const T& value) {
+ if (contains(value)) {
+ // Already in the set
+ return;
+ }
+
+ Handle* h = new Handle(value);
+
+ if (root == NULL) {
+ // This is the first node; create a root node
+ root = new Node();
+ }
+
+ Node* node = root->findDeepestContainingNode(h->bounds);
+
+ // Insert into the node
+ node->valueArray.append(h);
+ node->boundsArray.append(h->bounds);
+
+ // Insert into the node table
+ Member m(h);
+ memberTable.set(m, node);
+ }
+
+ /** Inserts each elements in the array in turn. If the tree
+ begins empty (no structure and no elements), this is faster
+ than inserting each element in turn. You still need to balance
+ the tree at the end.*/
+ void insert(const Array<T>& valueArray) {
+ if (root == NULL) {
+ // Optimized case for an empty tree; don't bother
+ // searching or reallocating the root node's valueArray
+ // as we incrementally insert.
+ root = new Node();
+ root->valueArray.resize(valueArray.size());
+ root->boundsArray.resize(root->valueArray.size());
+ for (int i = 0; i < valueArray.size(); ++i) {
+ // Insert in opposite order so that we have the exact same
+ // data structure as if we inserted each (i.e., order is reversed
+ // from array).
+ Handle* h = new Handle(valueArray[i]);
+ int j = valueArray.size() - i - 1;
+ root->valueArray[j] = h;
+ root->boundsArray[j] = h->bounds;
+ memberTable.set(Member(h), root);
+ }
+
+ } else {
+ // Insert at appropriate tree depth.
+ for (int i = 0; i < valueArray.size(); ++i) {
+ insert(valueArray[i]);
+ }
+ }
+ }
+
+
+ /**
+ Returns true if this object is in the set, otherwise
+ returns false. O(1) time.
+ */
+ bool contains(const T& value) {
+ // Temporarily create a handle and member
+ Handle h(value);
+ return memberTable.containsKey(Member(&h));
+ }
+
+
+ /**
+ Removes an object from the set in O(1) time.
+ It is an error to remove members that are not already
+ present. May unbalance the tree.
+
+ Removing an element never causes a node (split plane) to be removed...
+ nodes are only changed when the tree is rebalanced. This behavior
+ is desirable because it allows the split planes to be serialized,
+ and then deserialized into an empty tree which can be repopulated.
+ */
+ void remove(const T& value) {
+ debugAssertM(contains(value),
+ "Tried to remove an element from a "
+ "KDTree that was not present");
+
+ // Get the list of elements at the node
+ Handle h(value);
+ Member m(&h);
+
+ Array<Handle*>& list = memberTable[m]->valueArray;
+
+ Handle* ptr = NULL;
+
+ // Find the element and remove it
+ for (int i = list.length() - 1; i >= 0; --i) {
+ if (list[i]->value == value) {
+ // This was the element. Grab the pointer so that
+ // we can delete it below
+ ptr = list[i];
+
+ // Remove the handle from the node
+ list.fastRemove(i);
+
+ // Remove the corresponding bounds
+ memberTable[m]->boundsArray.fastRemove(i);
+ break;
+ }
+ }
+
+ // Remove the member
+ memberTable.remove(m);
+
+ // Delete the handle data structure
+ delete ptr;
+ ptr = NULL;
+ }
+
+
+ /**
+ If the element is in the set, it is removed.
+ The element is then inserted.
+
+ This is useful when the == and hashCode methods
+ on <I>T</I> are independent of the bounds. In
+ that case, you may call update(v) to insert an
+ element for the first time and call update(v)
+ again every time it moves to keep the tree
+ up to date.
+ */
+ void update(const T& value) {
+ if (contains(value)) {
+ remove(value);
+ }
+ insert(value);
+ }
+
+
+ /**
+ Rebalances the tree (slow). Call when objects
+ have moved substantially from their original positions
+ (which unbalances the tree and causes the spatial
+ queries to be slow).
+
+ @param valuesPerNode Maximum number of elements to put at
+ a node.
+
+ @param numMeanSplits numMeanSplits = 0 gives a
+ fully axis aligned BSP-tree, where the balance operation attempts to balance
+ the tree so that every splitting plane has an equal number of left
+ and right children (i.e. it is a <B>median</B> split along that axis).
+ This tends to maximize average performance.
+
+ You can override this behavior by
+ setting a number of <B>mean</B> (average) splits. numMeanSplits = MAX_INT
+ creates a full oct-tree, which tends to optimize peak performance at the expense of
+ average performance. It tends to have better clustering behavior when
+ members are not uniformly distributed.
+ */
+ void balance(int valuesPerNode = 5, int numMeanSplits = 3) {
+ if (root == NULL) {
+ // Tree is empty
+ return;
+ }
+
+ // Get all handles and delete the old tree structure
+ Node* oldRoot = root;
+ for (int c = 0; c < 2; ++c) {
+ if (root->child[c] != NULL) {
+ root->child[c]->getHandles(root->valueArray);
+
+ // Delete the child; this will delete all structure below it
+ delete root->child[c];
+ root->child[c] = NULL;
+ }
+ }
+
+ Array<Handle*> temp;
+ // Make a new root. Work with a copy of the value array because
+ // makeNode clears the source array as it progresses
+ Array<Handle*> copy(oldRoot->valueArray);
+ root = makeNode(copy, valuesPerNode, numMeanSplits, temp);
+
+ // Throw away the old root node
+ delete oldRoot;
+ oldRoot = NULL;
+
+ // Walk the tree, assigning splitBounds. We start with unbounded
+ // space. This will override the current member table.
+ const AABox& LARGE = AABox::large();
+ root->assignSplitBounds(LARGE);
+
+# ifdef _DEBUG
+ {
+ // Ensure that the balanced tree is still correct
+ root->verifyNode(LARGE.low(), LARGE.high());
+ }
+# endif
+ }
+
+protected:
+
+ /**
+ @param parentMask The mask that this node returned from culledBy.
+ */
+ static void getIntersectingMembers(
+ const Array<Plane>& plane,
+ Array<T>& members,
+ Node* node,
+ uint32 parentMask) {
+
+ int dummy;
+
+ if (parentMask == 0) {
+ // None of these planes can cull anything
+ for (int v = node->valueArray.size() - 1; v >= 0; --v) {
+ members.append(node->valueArray[v]->value);
+ }
+
+ // Iterate through child nodes
+ for (int c = 0; c < 2; ++c) {
+ if (node->child[c]) {
+ getIntersectingMembers(plane, members, node->child[c], 0);
+ }
+ }
+ } else {
+
+ // Test values at this node against remaining planes
+ for (int v = node->boundsArray.size() - 1; v >= 0; --v) {
+ if (! node->boundsArray[v].culledBy(plane, dummy, parentMask)) {
+ members.append(node->valueArray[v]->value);
+ }
+ }
+
+ uint32 childMask = 0xFFFFFF;
+
+ // Iterate through child nodes
+ for (int c = 0; c < 2; ++c) {
+ if (node->child[c] &&
+ ! node->child[c]->splitBounds.culledBy(plane, dummy, parentMask, childMask)) {
+ // This node was not culled
+ getIntersectingMembers(plane, members, node->child[c], childMask);
+ }
+ }
+ }
+ }
+
+public:
+
+ /**
+ Returns all members inside the set of planes.
+ @param members The results are appended to this array.
+ */
+ void getIntersectingMembers(const Array<Plane>& plane, Array<T>& members) const {
+ if (root == NULL) {
+ return;
+ }
+
+ getIntersectingMembers(plane, members, root, 0xFFFFFF);
+ }
+
+ /**
+ Typically used to find all visible
+ objects inside the view frustum (see also GCamera::getClipPlanes)... i.e. all objects
+ <B>not<B> culled by frustum.
+
+ Example:
+ <PRE>
+ Array<Object*> visible;
+ tree.getIntersectingMembers(camera.frustum(), visible);
+ // ... Draw all objects in the visible array.
+ </PRE>
+ @param members The results are appended to this array.
+ */
+ void getIntersectingMembers(const GCamera::Frustum& frustum, Array<T>& members) const {
+ Array<Plane> plane;
+
+ for (int i = 0; i < frustum.faceArray.size(); ++i) {
+ plane.append(frustum.faceArray[i].plane);
+ }
+
+ getIntersectingMembers(plane, members);
+ }
+
+ /**
+ C++ STL style iterator variable. See beginBoxIntersection().
+ The iterator overloads the -> (dereference) operator, so this
+ acts like a pointer to the current member.
+ */
+ // This iterator turns Node::getIntersectingMembers into a
+ // coroutine. It first translates that method from recursive to
+ // stack based, then captures the system state (analogous to a Scheme
+ // continuation) after each element is appended to the member array,
+ // and allowing the computation to be restarted.
+ class BoxIntersectionIterator {
+ private:
+ friend class TreeType;
+
+ /** True if this is the "end" iterator instance */
+ bool isEnd;
+
+ /** The box that we're testing against. */
+ AABox box;
+
+ /** Node that we're currently looking at. Undefined if isEnd
+ is true. */
+ Node* node;
+
+ /** Nodes waiting to be processed */
+ // We could use backpointers within the tree and careful
+ // state management to avoid ever storing the stack-- but
+ // it is much easier this way and only inefficient if the
+ // caller uses post increment (which they shouldn't!).
+ Array<Node*> stack;
+
+ /** The next index of current->valueArray to return.
+ Undefined when isEnd is true.*/
+ int nextValueArrayIndex;
+
+ BoxIntersectionIterator() : isEnd(true) {}
+
+ BoxIntersectionIterator(const AABox& b, const Node* root) :
+ isEnd(root == NULL), box(b),
+ node(const_cast<Node*>(root)), nextValueArrayIndex(-1) {
+
+ // We intentionally start at the "-1" index of the current
+ // node so we can use the preincrement operator to move
+ // ourselves to element 0 instead of repeating all of the
+ // code from the preincrement method. Note that this might
+ // cause us to become the "end" instance.
+ ++(*this);
+ }
+
+ public:
+
+ inline bool operator!=(const BoxIntersectionIterator& other) const {
+ return ! (*this == other);
+ }
+
+ bool operator==(const BoxIntersectionIterator& other) const {
+ if (isEnd) {
+ return other.isEnd;
+ } else if (other.isEnd) {
+ return false;
+ } else {
+ // Two non-end iterators; see if they match. This is kind of
+ // silly; users shouldn't call == on iterators in general unless
+ // one of them is the end iterator.
+ if ((box != other.box) || (node != other.node) ||
+ (nextValueArrayIndex != other.nextValueArrayIndex) ||
+ (stack.length() != other.stack.length())) {
+ return false;
+ }
+
+ // See if the stacks are the same
+ for (int i = 0; i < stack.length(); ++i) {
+ if (stack[i] != other.stack[i]) {
+ return false;
+ }
+ }
+
+ // We failed to find a difference; they must be the same
+ return true;
+ }
+ }
+
+ /**
+ Pre increment.
+ */
+ BoxIntersectionIterator& operator++() {
+ ++nextValueArrayIndex;
+
+ bool foundIntersection = false;
+ while (! isEnd && ! foundIntersection) {
+
+ // Search for the next node if we've exhausted this one
+ while ((! isEnd) && (nextValueArrayIndex >= node->valueArray.length())) {
+ // If we entered this loop, then the iterator has exhausted the elements at
+ // node (possibly because it just switched to a child node with no members).
+ // This loop continues until it finds a node with members or reaches
+ // the end of the whole intersection search.
+
+ // If the right child overlaps the box, push it onto the stack for
+ // processing.
+ if ((node->child[1] != NULL) &&
+ (box.high()[node->splitAxis] > node->splitLocation)) {
+ stack.push(node->child[1]);
+ }
+
+ // If the left child overlaps the box, push it onto the stack for
+ // processing.
+ if ((node->child[0] != NULL) &&
+ (box.low()[node->splitAxis] < node->splitLocation)) {
+ stack.push(node->child[0]);
+ }
+
+ if (stack.length() > 0) {
+ // Go on to the next node (which may be either one of the ones we
+ // just pushed, or one from farther back the tree).
+ node = stack.pop();
+ nextValueArrayIndex = 0;
+ } else {
+ // That was the last node; we're done iterating
+ isEnd = true;
+ }
+ }
+
+ // Search for the next intersection at this node until we run out of children
+ while (! isEnd && ! foundIntersection && (nextValueArrayIndex < node->valueArray.length())) {
+ if (box.intersects(node->boundsArray[nextValueArrayIndex])) {
+ foundIntersection = true;
+ } else {
+ ++nextValueArrayIndex;
+ // If we exhaust this node, we'll loop around the master loop
+ // to find a new node.
+ }
+ }
+ }
+
+ return *this;
+ }
+
+ private:
+ /**
+ Post increment (much slower than preincrement!). Intentionally overloaded to preclude accidentally slow code.
+ */
+ BoxIntersectionIterator operator++(int);
+ /*{
+ BoxIntersectionIterator old = *this;
+ ++this;
+ return old;
+ }*/
+
+ public:
+
+ /** Overloaded dereference operator so the iterator can masquerade as a pointer
+ to a member */
+ const T& operator*() const {
+ alwaysAssertM(! isEnd, "Can't dereference the end element of an iterator");
+ return node->valueArray[nextValueArrayIndex]->value;
+ }
+
+ /** Overloaded dereference operator so the iterator can masquerade as a pointer
+ to a member */
+ T const * operator->() const {
+ alwaysAssertM(! isEnd, "Can't dereference the end element of an iterator");
+ return &(stack.last()->valueArray[nextValueArrayIndex]->value);
+ }
+
+ /** Overloaded cast operator so the iterator can masquerade as a pointer
+ to a member */
+ operator T*() const {
+ alwaysAssertM(! isEnd, "Can't dereference the end element of an iterator");
+ return &(stack.last()->valueArray[nextValueArrayIndex]->value);
+ }
+ };
+
+
+ /**
+ Iterates through the members that intersect the box
+ */
+ BoxIntersectionIterator beginBoxIntersection(const AABox& box) const {
+ return BoxIntersectionIterator(box, root);
+ }
+
+ BoxIntersectionIterator endBoxIntersection() const {
+ // The "end" iterator instance
+ return BoxIntersectionIterator();
+ }
+
+ /**
+ Appends all members whose bounds intersect the box.
+ See also KDTree::beginBoxIntersection.
+ */
+ void getIntersectingMembers(const AABox& box, Array<T>& members) const {
+ if (root == NULL) {
+ return;
+ }
+ root->getIntersectingMembers(box, Sphere(Vector3::zero(), 0), members, false);
+ }
+
+
+ /**
+ Invoke a callback for every member along a ray until the closest intersection is found.
+
+ @param callback either a function or an instance of a class with an overloaded operator() of the form:
+
+ <code>void callback(const Ray& ray, const T& object, float& distance)</code>. If the ray hits the object
+ before travelling distance <code>distance</code>, updates <code>distance</code> with the new distance to
+ the intersection, otherwise leaves it unmodified. A common example is:
+
+ <pre>
+ class Entity {
+ public:
+
+ void intersect(const Ray& ray, float& maxDist, Vector3& outLocation, Vector3& outNormal) {
+ float d = maxDist;
+
+ // ... search for intersection distance d
+
+ if ((d > 0) && (d < maxDist)) {
+ // Intersection occured
+ maxDist = d;
+ outLocation = ...;
+ outNormal = ...;
+ }
+ }
+ };
+
+ // Finds the surface normal and location of the first intersection with the scene
+ class Intersection {
+ public:
+ Entity* closestEntity;
+ Vector3 hitLocation;
+ Vector3 hitNormal;
+
+ void operator()(const Ray& ray, const Entity* entity, float& distance) {
+ entity->intersect(ray, distance, hitLocation, hitNormal);
+ }
+ };
+
+ KDTree<Entity*> scene;
+
+ Intersection intersection;
+ float distance = inf();
+ scene.intersectRay(camera.worldRay(x, y), intersection, distance);
+ </pre>
+
+
+ @param distance When the method is invoked, this is the maximum distance that the tree should search for an intersection.
+ On return, this is set to the distance to the first intersection encountered.
+
+ @param intersectCallbackIsFast If false, each object's bounds are tested before the intersectCallback is invoked.
+ If the intersect callback runs at the same speed or faster than AABox-ray intersection, set this to true.
+ */
+ template<typename RayCallback>
+ void intersectRay(
+ const Ray& ray,
+ RayCallback& intersectCallback,
+ float& distance,
+ bool intersectCallbackIsFast = false) const {
+
+ root->intersectRay(ray, intersectCallback, distance, intersectCallbackIsFast);
+
+ }
+
+
+ /**
+ @brief Finds all members whose bounding boxes intersect the sphere. The actual
+ elements may not intersect the sphere.
+
+ @param members The results are appended to this array.
+ */
+ void getIntersectingMembers(const Sphere& sphere, Array<T>& members) const {
+ if (root == NULL) {
+ return;
+ }
+
+ AABox box;
+ sphere.getBounds(box);
+ root->getIntersectingMembers(box, sphere, members, true);
+
+ }
+
+ /**
+ Stores the locations of the splitting planes (the structure but not the content)
+ so that the tree can be quickly rebuilt from a previous configuration without
+ calling balance.
+ */
+ void serializeStructure(BinaryOutput& bo) const {
+ Node::serializeStructure(root, bo);
+ }
+
+ /** Clears the member table */
+ void deserializeStructure(BinaryInput& bi) {
+ clear();
+ root = Node::deserializeStructure(bi);
+ }
+
+ /**
+ Returns an array of all members of the set. See also KDTree::begin.
+ */
+ void getMembers(Array<T>& members) const {
+ Array<Member> temp;
+ memberTable.getKeys(temp);
+ for (int i = 0; i < temp.size(); ++i) {
+ members.append(*(temp.handle));
+ }
+ }
+
+
+ /**
+ C++ STL style iterator variable. See begin().
+ Overloads the -> (dereference) operator, so this acts like a pointer
+ to the current member.
+ */
+ class Iterator {
+ private:
+ friend class TreeType;
+
+ // Note: this is a Table iterator, we are currently defining
+ // Set iterator
+ typename Table<Member, Node*>::Iterator it;
+
+ Iterator(const typename Table<Member, Node*>::Iterator& it) : it(it) {}
+
+ public:
+
+ inline bool operator!=(const Iterator& other) const {
+ return !(*this == other);
+ }
+
+ bool operator==(const Iterator& other) const {
+ return it == other.it;
+ }
+
+ /**
+ Pre increment.
+ */
+ Iterator& operator++() {
+ ++it;
+ return *this;
+ }
+
+ private:
+ /**
+ Post increment (slower than preincrement). Intentionally unimplemented to prevent slow code.
+ */
+ Iterator operator++(int);/* {
+ Iterator old = *this;
+ ++(*this);
+ return old;
+ }*/
+ public:
+
+ const T& operator*() const {
+ return it->key.handle->value;
+ }
+
+ T* operator->() const {
+ return &(it->key.handle->value);
+ }
+
+ operator T*() const {
+ return &(it->key.handle->value);
+ }
+ };
+
+
+ /**
+ C++ STL style iterator method. Returns the first member.
+ Use preincrement (++entry) to get to the next element (iteration
+ order is arbitrary).
+ Do not modify the set while iterating.
+ */
+ Iterator begin() const {
+ return Iterator(memberTable.begin());
+ }
+
+
+ /**
+ C++ STL style iterator method. Returns one after the last iterator
+ element.
+ */
+ Iterator end() const {
+ return Iterator(memberTable.end());
+ }
+#undef TreeType
+};
+
+/** @deprecated For backwards compatibility */
+#define AABSPTree KDTree
+
+}
+
+#endif
diff --git a/externals/g3dlite/G3D.lib/include/G3D/AABox.h b/externals/g3dlite/G3D.lib/include/G3D/AABox.h
new file mode 100644
index 00000000000..76c5d6d5195
--- /dev/null
+++ b/externals/g3dlite/G3D.lib/include/G3D/AABox.h
@@ -0,0 +1,281 @@
+/**
+ @file AABox.h
+
+ Axis-aligned box class
+
+ @maintainer Morgan McGuire, matrix@graphics3d.com
+
+ @created 2004-01-10
+ @edited 2006-02-10
+
+ Copyright 2000-2006, Morgan McGuire.
+ All rights reserved.
+ */
+
+#ifndef G3D_AABOX_H
+#define G3D_AABOX_H
+
+#include "G3D/platform.h"
+#include "G3D/Vector3.h"
+#include "G3D/debug.h"
+#include "G3D/Array.h"
+#include "G3D/Plane.h"
+
+namespace G3D {
+
+/**
+ An axis-aligned box.
+ */
+class AABox {
+private:
+
+ /** Optional argument placeholder */
+ static int dummy;
+
+ Vector3 lo;
+ Vector3 hi;
+
+public:
+
+ /** Does not initialize the fields */
+ inline AABox() {}
+
+ /**
+ Constructs a zero-area AABox at v.
+ */
+ inline explicit AABox(const Vector3& v) {
+ lo = hi = v;
+ }
+
+ /** Assumes that low is less than or equal to high along each dimension.
+ To have this automatically enforced, use
+ <code>AABox(low.min(high), low.max(high));</code>
+ */
+ inline AABox(const Vector3& low, const Vector3& high) {
+ set(low, high);
+ }
+
+ /** Assumes that low is less than or equal to high along each dimension.
+ */
+ inline void set(const Vector3& low, const Vector3& high) {
+ debugAssert(
+ (low.x <= high.x) &&
+ (low.y <= high.y) &&
+ (low.z <= high.z));
+ lo = low;
+ hi = high;
+ }
+
+ /**
+ Grows to include the bounds of a
+ */
+ inline void merge(const AABox& a) {
+ lo = lo.min(a.lo);
+ hi = hi.max(a.hi);
+ }
+
+ inline void merge(const Vector3& a) {
+ lo = lo.min(a);
+ hi = hi.max(a);
+ }
+
+ void serialize(class BinaryOutput& b) const;
+
+ void deserialize(class BinaryInput& b);
+
+ inline const Vector3& low() const {
+ return lo;
+ }
+
+ inline const Vector3& high() const {
+ return hi;
+ }
+
+ /**
+ The largest possible finite box.
+ */
+ static inline const AABox& maxFinite() {
+ static const AABox b = AABox(Vector3::minFinite(),
+ Vector3::maxFinite());
+ return b;
+ }
+
+ /** A large finite box. This is smaller than FLT_MAX
+ because it leaves room to add boxes together. */
+ static inline const AABox& large() {
+ static const AABox b = AABox(Vector3::minFinite() * 0.5f,
+ Vector3::maxFinite() * 0.5f);
+ return b;
+ }
+
+ static inline const AABox& inf() {
+ static const AABox b = AABox(-Vector3::inf(), Vector3::inf());
+ return b;
+ }
+
+ static inline const AABox& zero() {
+ static const AABox b = AABox(Vector3::zero(), Vector3::zero());
+ return b;
+ }
+
+ /**
+ Returns the centroid of the box.
+ */
+ inline Vector3 center() const {
+ return (lo + hi) * 0.5;
+ }
+
+ Vector3 corner(int index) const;
+
+ /**
+ Distance from corner(0) to the next corner along axis a.
+ */
+ inline float extent(int a) const {
+ debugAssert(a < 3);
+ return hi[a] - lo[a];
+ }
+
+
+ inline Vector3 extent() const {
+ return hi - lo;
+ }
+
+
+ /**
+ Splits the box into two AABoxes along the specified axis. low contains
+ the part that was closer to negative infinity along axis, high contains
+ the other part. Either may have zero volume.
+ */
+ void split(const Vector3::Axis& axis, float location, AABox& low, AABox& high) const;
+
+ /**
+ Conservative culling test for up to 32 planes.
+ Returns true if there exists a <CODE>plane[p]</CODE> for
+ which the entire object is in the negative half space
+ (opposite the plane normal).
+
+ <CODE>testMask</CODE> and <CODE>childMask</CODE>
+ are used for optimizing bounding volume hierarchies.
+ The version of this method that produces childMask
+ is slower than the version without; it should only
+ be used for parent nodes.
+
+ @param cullingPlaneIndex The index of the first plane for which
+ the entire object is in the negative half-space. The function
+ exits early when one plane is found. -1 when the function
+ returns false (i.e. when no plane culls the whole object).
+
+ @param testMask If bit <I>p</I> is 0, the
+ bounding volume automatically passes the culling test for
+ <CODE>plane[p]</CODE> (i.e. it is known that the volume
+ is entirely within the positive half space). The function
+ must return false if testMask is 0 and test all planes
+ when testMask is -1 (0xFFFFFFFF).
+
+ @param childMask Test mask for the children of this volume.
+
+ */
+ bool culledBy(
+ const Array<Plane>& plane,
+ int32& cullingPlaneIndex,
+ const uint32 testMask,
+ uint32& childMask) const;
+
+ /**
+ Conservative culling test that does not produce a mask for children.
+ */
+ bool culledBy(
+ const Array<Plane>& plane,
+ int32& cullingPlaneIndex = dummy,
+ const uint32 testMask = 0xFFFFFFFF) const;
+
+ /** less than or equal to containment */
+ inline bool contains(const AABox& other) const {
+ return
+ (other.hi.x <= hi.x) &&
+ (other.hi.y <= hi.y) &&
+ (other.hi.z <= hi.z) &&
+ (other.lo.x >= lo.x) &&
+ (other.lo.y >= lo.y) &&
+ (other.lo.z >= lo.z);
+ }
+
+ inline bool contains(
+ const Vector3& point) const {
+ return
+ (point.x >= lo.x) &&
+ (point.y >= lo.y) &&
+ (point.z >= lo.z) &&
+ (point.x <= hi.x) &&
+ (point.y <= hi.y) &&
+ (point.z <= hi.z);
+ }
+
+ inline float area() const {
+ Vector3 diag = hi - lo;
+ return 2.0f * (diag.x * diag.y + diag.y * diag.z + diag.x * diag.z);
+ }
+
+ inline float volume() const {
+ Vector3 diag = hi - lo;
+ return diag.x * diag.y * diag.z;
+ }
+
+ Vector3 randomInteriorPoint() const;
+
+ Vector3 randomSurfacePoint() const;
+
+ /** Returns true if there is any overlap */
+ bool intersects(const AABox& other) const;
+
+ /** Returns true if there is any overlap.
+ @cite Jim Arvo's algorithm from Graphics Gems II*/
+ bool intersects(const class Sphere& other) const;
+
+ /** Return the intersection of the two boxes */
+ AABox intersect(const AABox& other) const {
+ Vector3 H = hi.min(other.hi);
+ Vector3 L = lo.max(other.lo).min(H);
+ return AABox(L, H);
+ }
+
+ inline size_t hashCode() const {
+ return lo.hashCode() + hi.hashCode();
+ }
+
+ inline bool operator==(const AABox& b) const {
+ return (lo == b.lo) && (hi == b.hi);
+ }
+
+ inline bool operator!=(const AABox& b) const {
+ return !((lo == b.lo) && (hi == b.hi));
+ }
+
+ inline AABox operator+(const Vector3& v) const {
+ AABox out;
+ out.lo = lo + v;
+ out.hi = hi + v;
+ return out;
+ }
+
+ inline AABox operator-(const Vector3& v) const {
+ AABox out;
+ out.lo = lo - v;
+ out.hi = hi - v;
+ return out;
+ }
+
+ void getBounds(AABox& out) const {
+ out = *this;
+ }
+};
+
+}
+
+template <> struct HashTrait<G3D::AABox> {
+ static size_t hashCode(const G3D::AABox& key) { return key.hashCode(); }
+};
+
+
+
+#endif
diff --git a/externals/g3dlite/G3D.lib/include/G3D/AnyVal.h b/externals/g3dlite/G3D.lib/include/G3D/AnyVal.h
new file mode 100644
index 00000000000..8254dd73c93
--- /dev/null
+++ b/externals/g3dlite/G3D.lib/include/G3D/AnyVal.h
@@ -0,0 +1,506 @@
+/**
+ @file AnyVal.h
+ @author Morgan McGuire
+ @created 2006-06-11
+ @edited 2008-07-14
+ */
+
+#ifndef G3D_ANYVAL_H
+#define G3D_ANYVAL_H
+
+#include "G3D/platform.h"
+#include <string>
+#include "G3D/Array.h"
+#include "G3D/TextInput.h"
+
+namespace G3D {
+// Forward declarations for G3D types
+class Vector2;
+class Vector3;
+class Vector4;
+class Color1;
+class Color3;
+class Color4;
+class Quat;
+class Matrix2;
+class Matrix3;
+class Matrix4;
+class CoordinateFrame;
+class TextInput;
+class TextOutput;
+class BinaryInput;
+class BinaryOutput;
+class Rect2D;
+class AABox;
+
+/**
+ A generic value, useful for defining property trees that can
+ be loaded from and saved to disk. The values are intentionally
+ restricted to a small set.
+
+ When written to files, the syntax is as follows. Note that you can
+ nest arrays and tables in order to create full tree (i.e., XML-like)
+ structures as configuration files:
+
+ <table>
+ <tr><td>NULL</td><td><code>Nil</code></td></tr>
+ <tr><td>double</td><td><i>The number in printf double format</i></td></tr>
+ <tr><td>bool</td><td><code>true</code> <i>or</i> <code>false</code></td></tr>
+ <tr><td>std::string</td><td><i>The string in double-quotes (</i><code>"</code><i>)</i></td></tr>
+ <tr><td>Rect2D</td><td><code>R(</code><i>x<sub>0</sub></i><code>,</code><i>y<sub>0</sub></i><code>,</code><i>x<sub>1</sub></i><code>,</code><i>y<sub>1</sub></i><code>)</code></td></tr>
+ <tr><td>Color1</td><td><code>C1(</code><i>value</i><code>)</code></td></tr>
+ <tr><td>Color3</td><td><code>C3(</code><i>r</i><code>,</code><i>g</i><code>,</code><i>b</i><code>)</code></td></tr>
+ <tr><td>Color4</td><td><code>C4(</code><i>r</i><code>,</code><i>g</i><code>,</code><i>b</i><code>,</code><i>a</i><code>)</code></td></tr>
+ <tr><td>Vector2</td><td><code>V2(</code><i>x</i><code>,</code><i>y</i><code>)</code></td></tr>
+ <tr><td>Vector3</td><td><code>V3(</code><i>x</i><code>,</code><i>y</i><code>,</code><i>z</i><code>)</code></td></tr>
+ <tr><td>Vector4</td><td><code>V4(</code><i>x</i><code>,</code><i>y</i><code>,</code><i>z</i><code>,</code><i>w</i><code>)</code></td></tr>
+ <tr><td>Quat</td><td><code>V(</code>x<code>,</code>y<code>,</code>z<code>,</code>w<code>)</code></td></tr>
+ <tr><td>AABox</td><td><code>AAB(</code>low Vector3<code>, </code>high Vector3<code>)</code></td></tr>
+ <tr><td>Matrix2</td><td><code>M2(</code>r0c0<code>, </code>r0c1<code>,
+ <br>&nbsp;&nbsp;&nbsp;</code>r1c0<code>, </code>r1c1<code>)</code></td></tr>
+ <tr><td>Matrix3</td><td><code>M3(</code>r0c0<code>, </code>r0c1<code>, </code>r0c2<code>,
+ <br>&nbsp;&nbsp;&nbsp;</code>r1c0<code>, </code>r1c1<code>, </code>r1c2<code>,
+ <br>&nbsp;&nbsp;&nbsp;</code>r2c0<code>, </code>r2c1<code>, </code>r2c2<code>)</code></td></tr>
+ <tr><td>Matrix4</td><td><code>M4(</code>r0c0<code>, </code>r0c1<code>, </code>r0c2<code>, </code>r0c3<code>,
+ <br>&nbsp;&nbsp;&nbsp;</code>r1c0<code>, </code>r1c1<code>, </code>r1c2<code>, </code>r1c3<code>,
+ <br>&nbsp;&nbsp;&nbsp;</code>r2c0<code>, </code>r2c1<code>, </code>r2c2<code>, </code>r2c3<code>,
+ <br>&nbsp;&nbsp;&nbsp;</code>r3c0<code>, </code>r3c1<code>, </code>r3c2<code>, </code>r3c3<code>)</code></td></tr>
+ <tr><td>CoordinateFrame</td><td><code>CF(</code>r0c0<code>, </code>r0c1<code>, </code>r0c2<code>, </code>r0c3<code>,
+ <br>&nbsp;&nbsp;&nbsp;</code>r1c0<code>, </code>r1c1<code>, </code>r1c2<code>, </code>r1c3<code>,
+ <br>&nbsp;&nbsp;&nbsp;</code>r2c0<code>, </code>r2c1<code>, </code>r2c2<code>, </code>r2c3<code>)</code></td></tr>
+ <tr><td>CoordinateFrame</td><td><code>CF(V3(</code><i>x</i><code>, </code><i>y</i><code>, </code><i>z</i><code>), </code><i>yaw deg</i><code>, </code><i>pitch deg</i><code>, </code><i>optional roll deg</i><code>)</code></td></tr>
+
+ <tr><td>Array</td><td><code>[</code><i>element<sub>0</sub></i><code>, </code><i>element<sub>1</sub></i><code>, </code> ... <code>, </code><i>element<sub>n-1</sub></i><code>]</code></td></tr>
+ <tr><td>Table</td><td><code>{</code><i>symbol<sub>0</sub></i><code> = </code><i>value<sub>0</sub></i>
+ <br><code>&nbsp;</code><i>symbol<sub>1</sub></i><code> = </code><i>value<sub>1</sub></i>
+ <br><code>&nbsp;</code>...
+ <br><code>&nbsp;</code><i>symbol<sub>n-1</sub></i><code> = </code><i>value<sub>n-1</sub></i><code>}</code></td></tr>
+ </table>
+
+ See also boost::any for a more general purpose but slightly harder to use
+ "any" for C++.
+
+ The semantics of operator[] and the get() methods are slightly different;
+ operator[] acts more like a scripting language that automatically extends
+ arrays and tables instead of generating errors. get() has more strict semantics,
+ like a C++ class.
+
+ AnyVal uses copy-on-mutate, so that <code>AnyVal a = b</code> semantically copies <code>b</code> (like <code>int a = b</code> would), although in practice
+ it delays the copy until one is mutated so that it is still fast to "copy" large arrays and tables.
+
+ Reading example:
+ <pre>
+ AnyVal property = AnyVal::fromFile("c:/tmp/test.txt"));
+
+ Vector3 vel = property["angular velocity"]
+
+ <i>Using defaults to handle errors:
+ If there was no "enabled" value, this will return the default instead of failing</i>
+ bool enabled = property["enabled"].boolean(true);
+
+ </pre>
+
+ Writing to a file:
+ <pre>
+ AnyVal dict(AnyVal::TABLE);
+
+ dict["enabled"] = AnyVal(true);
+ dict["weight"] = 100;
+ dict["angular velocity"] = Vector3(1, -3, 4.5);
+
+ TextOutput t("c:/tmp/test.txt");
+ dict.serialize(t);
+ t.commit();
+ </pre>
+
+ Example of a data file:
+ <pre>
+ {
+ heights = [1, 17, 32]
+ model =
+ {
+ color = C3(1, 1, 1)
+ filename = "foo.md2"
+ }
+ position = V3(23, 14, 0)
+ name = "Elmer"
+ }
+ </pre>
+
+ <p>
+ <b>What's the difference from boost::any?</b>
+ <br>I think that AnyVal will be easier for novice C++ users. It addresses the problem that
+ even though G3D::TextInput makes reading configuration files extremely simple, many people
+ still don't use it. So AnyVal makes it ridiculously simple to read and write a tree of G3D
+ types to a file.
+
+ <i>AnyVal:</i>
+<pre>
+{
+AnyVal tree(TextInput("config.txt"));
+
+bool enabled = tree.get("enabled", false);
+Vector3 direction = tree.get("direction", Vector3::zero());
+...
+}
+</pre>
+
+<i>boost:</i>
+<pre>
+{
+bool enabled = false;
+Vector3 direction;
+Table<boost::any> tree;
+
+ ...write lots of file parsing code...
+
+ if (tree.containsKey("enabled")) {
+ const boost::any& val = tree["enabled"];
+ try {
+ enabled = any_cast<bool>(val);
+ } catch(const boost::bad_any_cast &) {
+ }
+ }
+
+ if (tree.containsKey("direction")) {
+ const boost::any& val = tree["direction"];
+ try {
+ direction = any_cast<Vector3>(val);
+ } catch(const boost::bad_any_cast &) {
+ }
+ }
+ ...
+}
+</pre>
+ */
+class AnyVal {
+public:
+
+ /** Array and table values are all Any.*/
+ enum Type {
+ NIL,
+ NUMBER,
+ BOOLEAN,
+ STRING,
+ VECTOR2,
+ VECTOR3,
+ VECTOR4,
+ MATRIX2,
+ MATRIX3,
+ MATRIX4,
+ QUAT,
+ COORDINATEFRAME,
+ COORDINATEFRAME2D,
+ CFRAME = COORDINATEFRAME,
+ CFRAME2D = COORDINATEFRAME2D,
+ COLOR1,
+ COLOR3,
+ COLOR4,
+ RECT2D,
+ AABOX2D = RECT2D,
+ AABOX,
+ ARRAY,
+ TABLE};
+
+ /** Base class for all AnyVal exceptions.*/
+ class Exception {
+ public:
+ virtual ~Exception() {}
+ };
+
+ /** Thrown when an inappropriate operation is performed (e.g., operator[] on a number) */
+ class WrongType : public Exception {
+ public:
+ Type expected;
+ Type actual;
+ WrongType() : expected(NIL), actual(NIL) {}
+ WrongType(Type e, Type a) : expected(e), actual(a) {}
+ };
+
+ /** Thrown by operator[] when a key is not present. */
+ class KeyNotFound : public Exception {
+ public:
+ std::string key;
+ KeyNotFound() {}
+ KeyNotFound(const std::string& k) : key(k) {}
+ };
+
+ class IndexOutOfBounds : public Exception {
+ public:
+ int index;
+ int size;
+ IndexOutOfBounds() : index(0), size(0) {}
+ IndexOutOfBounds(int i, int s) : index(i), size(s) {}
+ };
+
+ /** Thrown when deserialize() when the input is incorrectly formatted. */
+ class CorruptText : public Exception {
+ public:
+ std::string message;
+
+ /** Token where the problem occurred.*/
+ G3D::Token token;
+
+ CorruptText() {}
+ CorruptText(const std::string& s, const G3D::Token& t) : message(s), token(t) {}
+ };
+
+private:
+
+ Type m_type;
+ void* m_value;
+
+ /** For table and array types, *m_value is shared between multiple
+ instances. Mutation is allowed only if the reference count is
+ exactly 1, otherwise the mutating instance must copy the
+ value. This is not used for other types.
+ */
+ int* m_referenceCount;
+
+ /** Decrements the reference count (if there is one). If the
+ reference count is zero or does not exist. Calls delete on @a
+ m_value and sets it to NULL.
+ */
+ void deleteValue();
+
+ /** Returns a copy of the value. */
+ void* copyValue() const;
+
+ /** Assumes isSharedType. Ensures that this has a unique reference */
+ void makeMutable();
+
+ /** True if this is a shared value between multiple instances. */
+ inline bool isShared() const {
+ return m_referenceCount && (*m_referenceCount > 1);
+ }
+
+ /** True when m_value is a double pointer */
+ inline bool isSharedType() const {
+ return (m_type == TABLE) || (m_type == ARRAY);
+ }
+
+public:
+
+ AnyVal();
+
+ /** Deserialize */
+ explicit AnyVal(G3D::TextInput& t);
+
+ static AnyVal fromFile(const std::string& filename);
+
+ void load(const std::string& filename);
+
+ void save(const std::string& filename) const;
+
+ ///** Deserialize */
+ //explicit AnyVal(G3D::BinaryInput& t);
+
+ /** Construct a number */
+ AnyVal(double);
+ AnyVal(int);
+
+ // Explicit to avoid ambiguity with the 'double' constructor
+ // when an integer type is constructed
+ AnyVal(bool);
+ AnyVal(const G3D::Vector2&);
+ AnyVal(const G3D::Vector3&);
+ AnyVal(const G3D::Vector4&);
+
+ AnyVal(const G3D::Color1&);
+ AnyVal(const G3D::Color3&);
+ AnyVal(const G3D::Color4&);
+
+ AnyVal(const std::string&);
+ AnyVal(const char*);
+
+ AnyVal(const G3D::Quat&);
+
+ AnyVal(const G3D::Rect2D&);
+ AnyVal(const G3D::AABox&);
+
+ AnyVal(const G3D::CoordinateFrame&);
+ AnyVal(const G3D::Matrix2&);
+ AnyVal(const G3D::Matrix3&);
+ AnyVal(const G3D::Matrix4&);
+
+ AnyVal(const AnyVal&);
+
+ AnyVal(Type arrayOrTable);
+
+ AnyVal& operator=(const AnyVal&);
+
+ /** Frees the underlying storage */
+ ~AnyVal();
+
+ Type type() const;
+
+ bool isNil() const {
+ return type() == NIL;
+ }
+
+ void serialize(G3D::TextOutput& t) const;
+ //void serialize(G3D::BinaryOutput& t) const;
+ void deserialize(G3D::TextInput& t);
+ //void deserialize(G3D::BinaryInput& t);
+
+ /** Array dereference. If the index is out of bounds, IndexOutOfBounds is thrown */
+ const AnyVal& operator[](int) const;
+
+ /** Extend this array by one element. */
+ void append(const AnyVal&);
+
+ /** If the index is out of bounds, the array is resized. If the index is negative,
+ IndexOutOfBounds is thrown.*/
+ AnyVal& operator[](int);
+
+ /** If @a i is out of bounds or this is not an ARRAY, defaultVal is returned.*/
+ const AnyVal& get(int i, const AnyVal& defaultVal) const;
+
+ /** If out of bounds, IndexOutOfBounds is thrown. */
+ const AnyVal& get(int i) const;
+
+ /** Returns defaultVal if this is not a TABLE or the key is not found. */
+ const AnyVal& get(const std::string& key, const AnyVal& defaultVal) const;
+
+ /** Throws KeyNotFound exception if the key is not present.*/
+ const AnyVal& get(const std::string& key) const;
+
+ /** Table reference */
+ const AnyVal& operator[](const std::string&) const;
+
+ /** Table reference. If the element does not exist, it is created. */
+ AnyVal& operator[](const std::string&);
+
+ /** Table reference */
+ const AnyVal& operator[](const char*) const;
+
+ /** Table reference. If the element does not exist, it is created. */
+ AnyVal& operator[](const char*);
+
+ /** If this value is not a number throws a WrongType exception. */
+ double number() const;
+
+ /** If this value is not a number, returns defaultVal. */
+ double number(double defaultVal) const;
+
+ operator double () const {
+ return number();
+ }
+
+ operator float () const {
+ return (float)number();
+ }
+
+ bool boolean() const;
+ bool boolean(bool b) const;
+
+ operator bool() const {
+ return boolean();
+ }
+
+ const std::string& string() const;
+ const std::string& string(const std::string& defaultVal) const;
+
+ operator const std::string& () const {
+ return string();
+ }
+
+ const G3D::Rect2D& rect2D() const;
+ const G3D::Rect2D& rect2D(const G3D::Rect2D& defaultVal) const;
+
+ operator const Rect2D& () const {
+ return rect2D();
+ }
+
+ const G3D::AABox& aabox() const;
+ const G3D::AABox& aabox(const G3D::AABox& defaultVal) const;
+
+ operator const AABox& () const {
+ return aabox();
+ }
+
+ const G3D::Vector2& vector2() const;
+ const G3D::Vector2& vector2(const G3D::Vector2& defaultVal) const;
+
+ operator const Vector2& () const {
+ return vector2();
+ }
+
+ const G3D::Vector3& vector3() const;
+ const G3D::Vector3& vector3(const G3D::Vector3& defaultVal) const;
+
+ operator const Vector3& () {
+ return vector3();
+ }
+
+ const G3D::Vector4& vector4() const;
+ const G3D::Vector4& vector4(const G3D::Vector4& defaultVal) const;
+
+ operator const Vector4& () const {
+ return vector4();
+ }
+
+ const G3D::Color1& color1() const;
+ const G3D::Color1& color1(const G3D::Color1& defaultVal) const;
+
+ const G3D::Color3& color3() const;
+ const G3D::Color3& color3(const G3D::Color3& defaultVal) const;
+
+ operator const Color3& () const {
+ return color3();
+ }
+
+ const G3D::Color4& color4() const;
+ const G3D::Color4& color4(const G3D::Color4& defaultVal) const;
+
+ operator const Color4& () const {
+ return color4();
+ }
+
+ const G3D::CoordinateFrame& coordinateFrame() const;
+ const G3D::CoordinateFrame& coordinateFrame(const G3D::CoordinateFrame& defaultVal) const;
+
+ operator const CoordinateFrame& () const {
+ return coordinateFrame();
+ }
+
+ const G3D::Matrix2& matrix2() const;
+ const G3D::Matrix2& matrix2(const G3D::Matrix2& defaultVal) const;
+
+ operator const Matrix2& () const {
+ return matrix2();
+ }
+
+ const G3D::Matrix3& matrix3() const;
+ const G3D::Matrix3& matrix3(const G3D::Matrix3& defaultVal) const;
+
+ operator const Matrix3& () const {
+ return matrix3();
+ }
+
+ const G3D::Matrix4& matrix4() const;
+ const G3D::Matrix4& matrix4(const G3D::Matrix4& defaultVal) const;
+
+ operator const Matrix4& () const {
+ return matrix4();
+ }
+
+ const G3D::Quat& quat() const;
+ const G3D::Quat& quat(const G3D::Quat& defaultVal) const;
+
+ operator const Quat& () const {
+ return quat();
+ }
+
+ std::string toString() const;
+
+ /** Number of elements for an array or table.*/
+ int size() const;
+
+ /** For a table, returns the keys. */
+ void getKeys(G3D::Array<std::string>&) const;
+};
+
+}
+
+#endif
diff --git a/externals/g3dlite/G3D.lib/include/G3D/Array.h b/externals/g3dlite/G3D.lib/include/G3D/Array.h
new file mode 100644
index 00000000000..ae4eea8ab40
--- /dev/null
+++ b/externals/g3dlite/G3D.lib/include/G3D/Array.h
@@ -0,0 +1,1180 @@
+/**
+ @file Array.h
+
+ @maintainer Morgan McGuire, graphics3d.com
+ @cite Portions written by Aaron Orenstein, a@orenstein.name
+
+ @created 2001-03-11
+ @edited 2008-07-09
+
+ Copyright 2000-2008, Morgan McGuire, morgan@cs.williams.edu
+ All rights reserved.
+ */
+
+#ifndef G3D_ARRAY_H
+#define G3D_ARRAY_H
+
+#include "G3D/platform.h"
+#include "G3D/debug.h"
+#include "G3D/System.h"
+#ifdef G3D_DEBUG
+// For formatting error messages
+# include "G3D/format.h"
+#endif
+#include <vector>
+#include <algorithm>
+
+#ifdef _MSC_VER
+# include <new>
+
+# pragma warning (push)
+ // debug information too long
+# pragma warning( disable : 4312)
+# pragma warning( disable : 4786)
+#endif
+
+
+namespace G3D {
+
+/**
+ Constant for passing to Array::resize
+ */
+const bool DONT_SHRINK_UNDERLYING_ARRAY = false;
+
+/** Constant for Array::sort */
+const int SORT_INCREASING = 1;
+/** Constant for Array::sort */
+const int SORT_DECREASING = -1;
+
+/**
+ Dynamic 1D array.
+
+ Objects must have a default constructor (constructor that
+ takes no arguments) in order to be used with this template.
+ You will get the error "no appropriate default constructor found"
+ if they do not.
+
+ Do not use with objects that overload placement <code>operator new</code>,
+ since the speed of Array is partly due to pooled allocation.
+
+ If SSE is defined Arrays allocate the first element aligned to
+ 16 bytes.
+
+
+ Array is highly optimized compared to std::vector.
+ Array operations are less expensive than on std::vector and for large
+ amounts of data, Array consumes only 1.5x the total size of the
+ data, while std::vector consumes 2.0x. The default
+ array takes up zero heap space. The first resize (or append)
+ operation grows it to a reasonable internal size so it is efficient
+ to append to small arrays. Memory is allocated using
+ System::alignedMalloc, which produces pointers aligned to 16-byte
+ boundaries for use with SSE instructions and uses pooled storage for
+ fast allocation. When Array needs to copy
+ data internally on a resize operation it correctly invokes copy
+ constructors of the elements (the MSVC6 implementation of
+ std::vector uses realloc, which can create memory leaks for classes
+ containing references and pointers). Array provides a guaranteed
+ safe way to access the underlying data as a flat C array --
+ Array::getCArray. Although (T*)std::vector::begin() can be used for
+ this purpose, it is not guaranteed to succeed on all platforms.
+
+ To serialize an array, see G3D::serialize.
+
+ The template parameter MIN_ELEMENTS indicates the smallest number of
+ elements that will be allocated. The default of 10 is designed to avoid
+ the overhead of repeatedly allocating the array as it grows from 1, to 2, and so on.
+ If you are creating a lot of small Arrays, however, you may want to set this smaller
+ to reduce the memory cost. Once the array has been allocated, it will never
+ deallocate the underlying array unless MIN_ELEMENTS is set to 0, MIN_BYTES is 0, and the array
+ is empty.
+
+ Do not subclass an Array.
+ */
+template <class T, int MIN_ELEMENTS = 10, size_t MIN_BYTES = 32>
+class Array {
+private:
+ /** 0...num-1 are initialized elements, num...numAllocated-1 are not */
+ T* data;
+
+ int num;
+ int numAllocated;
+
+ void init(int n, int a) {
+ debugAssert(n <= a);
+ debugAssert(n >= 0);
+ this->num = 0;
+ this->numAllocated = 0;
+ data = NULL;
+ if (a > 0) {
+ resize(n);
+ } else {
+ data = NULL;
+ }
+ }
+
+ void _copy(const Array &other) {
+ init(other.num, other.num);
+ for (int i = 0; i < num; i++) {
+ data[i] = other.data[i];
+ }
+ }
+
+ /**
+ Returns true iff address points to an element of this array.
+ Used by append.
+ */
+ inline bool inArray(const T* address) {
+ return (address >= data) && (address < data + num);
+ }
+
+
+ /** Only compiled if you use the sort procedure. */
+ static bool __cdecl compareGT(const T& a, const T& b) {
+ return a > b;
+ }
+
+
+ /**
+ Allocates a new array of size numAllocated (not a parameter to the method)
+ and then copies at most oldNum elements from the old array to it. Destructors are
+ called for oldNum elements of the old array.
+ */
+ void realloc(int oldNum) {
+ T* oldData = data;
+
+ // The allocation is separate from the constructor invocation because we don't want
+ // to pay for the cost of constructors until the newly allocated
+ // elements are actually revealed to the application. They
+ // will be constructed in the resize() method.
+
+ data = (T*)System::alignedMalloc(sizeof(T) * numAllocated, 16);
+
+ // Call the copy constructors
+ {const int N = G3D::min(oldNum, numAllocated);
+ const T* end = data + N;
+ T* oldPtr = oldData;
+ for (T* ptr = data; ptr < end; ++ptr, ++oldPtr) {
+
+ // Use placement new to invoke the constructor at the location
+ // that we determined. Use the copy constructor to make the assignment.
+ const T* constructed = new (ptr) T(*oldPtr);
+
+ (void)constructed;
+ debugAssertM(constructed == ptr,
+ "new returned a different address than the one provided by Array.");
+ }}
+
+ // Call destructors on the old array (if there is no destructor, this will compile away)
+ {const T* end = oldData + oldNum;
+ for (T* ptr = oldData; ptr < end; ++ptr) {
+ ptr->~T();
+ }}
+
+
+ System::alignedFree(oldData);
+ }
+
+public:
+
+ /**
+ G3D C++ STL style iterator variable. Call begin() to get
+ the first iterator, pre-increment (++i) the iterator to get to
+ the next value. Use dereference (*i) to access the element.
+ */
+ typedef T* Iterator;
+ /** G3D C++ STL style const iterator in same style as Iterator. */
+ typedef const T* ConstIterator;
+
+ /** stl porting compatibility helper */
+ typedef Iterator iterator;
+ /** stl porting compatibility helper */
+ typedef ConstIterator const_iterator;
+ /** stl porting compatibility helper */
+ typedef T value_type;
+ /** stl porting compatibility helper */
+ typedef int size_type;
+ /** stl porting compatibility helper */
+ typedef int difference_type;
+
+ /**
+ C++ STL style iterator method. Returns the first iterator element.
+ Do not change the size of the array while iterating.
+ */
+ Iterator begin() {
+ return data;
+ }
+
+ ConstIterator begin() const {
+ return data;
+ }
+ /**
+ C++ STL style iterator method. Returns one after the last iterator
+ element.
+ */
+ ConstIterator end() const {
+ return data + num;
+ }
+
+ Iterator end() {
+ return data + num;
+ }
+
+ /**
+ The array returned is only valid until the next append() or resize call, or
+ the Array is deallocated.
+ */
+ T* getCArray() {
+ return data;
+ }
+
+ /**
+ The array returned is only valid until the next append() or resize call, or
+ the Array is deallocated.
+ */
+ const T* getCArray() const {
+ return data;
+ }
+
+ /** Creates a zero length array (no heap allocation occurs until resize). */
+ Array() {
+ init(0, 0);
+ }
+
+ /**
+ Creates an array of size.
+ */
+ Array(int size) {
+ init(size, size);
+ }
+
+ /**
+ Copy constructor
+ */
+ Array(const Array& other) {
+ _copy(other);
+ }
+
+ /**
+ Destructor does not delete() the objects if T is a pointer type
+ (e.g. T = int*) instead, it deletes the <B>pointers themselves</B> and
+ leaves the objects. Call deleteAll if you want to dealocate
+ the objects referenced. Do not call deleteAll if <CODE>T</CODE> is not a pointer
+ type (e.g. do call Array<Foo*>::deleteAll, do <B>not</B> call Array<Foo>::deleteAll).
+ */
+ ~Array() {
+ // Invoke the destructors on the elements
+ for (int i = 0; i < num; i++) {
+ (data + i)->~T();
+ }
+
+ System::alignedFree(data);
+ // Set to 0 in case this Array is global and gets referenced during app exit
+ data = NULL;
+ num = 0;
+ numAllocated = 0;
+ }
+
+
+ /**
+ Removes all elements. Use resize(0, false) or fastClear if you want to
+ remove all elements without deallocating the underlying array
+ so that future append() calls will be faster.
+ */
+ void clear(bool shrink = true) {
+ resize(0, shrink);
+ }
+
+ /** resize(0, false)
+ @deprecated*/
+ void fastClear() {
+ clear(false);
+ }
+
+ /**
+ Assignment operator.
+ */
+ Array& operator=(const Array& other) {
+ resize(other.num);
+ for (int i = 0; i < num; ++i) {
+ data[i] = other[i];
+ }
+ return *this;
+ }
+
+ Array& operator=(const std::vector<T>& other) {
+ resize((int)other.size());
+ for (int i = 0; i < num; ++i) {
+ data[i] = other[i];
+ }
+ return *this;
+ }
+
+ /**
+ Number of elements in the array.
+ */
+ inline int size() const {
+ return num;
+ }
+
+ /**
+ Number of elements in the array. (Same as size; this is just
+ here for convenience).
+ */
+ inline int length() const {
+ return size();
+ }
+
+ /**
+ Swaps element index with the last element in the array then
+ shrinks the array by one.
+ */
+ void fastRemove(int index, bool shrinkIfNecessary = false) {
+ debugAssert(index >= 0);
+ debugAssert(index < num);
+ data[index] = data[num - 1];
+ resize(size() - 1, shrinkIfNecessary);
+ }
+
+ /** Resizes without shrinking the underlying array. Same as resize(n, false).
+ @deprecated*/
+ void fastResize(int n) {
+ resize(n, false);
+ }
+
+
+ /**
+ Inserts at the specified index and shifts all other elements up by one.
+ */
+ void insert(int n, const T& value) {
+ // Add space for the extra element
+ resize(num + 1, false);
+
+ for (int i = num - 1; i > n; --i) {
+ data[i] = data[i - 1];
+ }
+ data[n] = value;
+ }
+
+ /** @param shrinkIfNecessary if false, memory will never be
+ reallocated when the array shrinks. This makes resizing much
+ faster but can waste memory. */
+ void resize(int n, bool shrinkIfNecessary = true) {
+ int oldNum = num;
+ num = n;
+
+ // Call the destructors on newly hidden elements if there are any
+ for (int i = num; i < oldNum; ++i) {
+ (data + i)->~T();
+ }
+
+ // Once allocated, always maintain MIN_ELEMENTS elements or 32 bytes, whichever is higher.
+ static const int minSize = G3D::max(MIN_ELEMENTS, (int)(MIN_BYTES / sizeof(T)));
+
+ if ((MIN_ELEMENTS == 0) && (MIN_BYTES == 0) && (n == 0) && shrinkIfNecessary) {
+ // Deallocate the array completely
+ numAllocated = 0;
+ System::alignedFree(data);
+ data = NULL;
+ return;
+ }
+
+ if (num > numAllocated) {
+ // Grow the underlying array
+
+ if (numAllocated == 0) {
+ // First allocation; grow to exactly the size requested to avoid wasting space.
+ numAllocated = n;
+ debugAssert(oldNum == 0);
+ realloc(oldNum);
+ } else {
+
+ if (num < minSize) {
+ // Grow to at least the minimum size
+ numAllocated = minSize;
+
+ } else {
+
+ // Increase the underlying size of the array. Grow aggressively
+ // up to 64k, less aggressively up to 400k, and then grow relatively
+ // slowly (1.5x per resize) to avoid excessive space consumption.
+ //
+ // These numbers are tweaked according to performance tests.
+
+ float growFactor = 3.0;
+
+ size_t oldSizeBytes = numAllocated * sizeof(T);
+ if (oldSizeBytes > 400000) {
+ // Avoid bloat
+ growFactor = 1.5;
+ } else if (oldSizeBytes > 64000) {
+ // This is what std:: uses at all times
+ growFactor = 2.0;
+ }
+
+ numAllocated = (num - numAllocated) + (int)(numAllocated * growFactor);
+
+ if (numAllocated < minSize) {
+ numAllocated = minSize;
+ }
+ }
+
+ realloc(oldNum);
+ }
+
+ } else if ((num <= numAllocated / 3) && shrinkIfNecessary && (num > minSize)) {
+ // Shrink the underlying array
+
+ // Only copy over old elements that still remain after resizing
+ // (destructors were called for others if we're shrinking)
+ realloc(iMin(num, oldNum));
+
+ }
+
+ // Call the constructors on newly revealed elements.
+ // Do not use parens because we don't want the intializer
+ // invoked for POD types.
+ for (int i = oldNum; i < num; ++i) {
+ new (data + i) T;
+ }
+ }
+
+ /**
+ Add an element to the end of the array. Will not shrink the underlying array
+ under any circumstances. It is safe to append an element that is already
+ in the array.
+ */
+ inline void append(const T& value) {
+
+ if (num < numAllocated) {
+ // This is a simple situation; just stick it in the next free slot using
+ // the copy constructor.
+ new (data + num) T(value);
+ ++num;
+ } else if (inArray(&value)) {
+ // The value was in the original array; resizing
+ // is dangerous because it may move the value
+ // we have a reference to.
+ T tmp = value;
+ append(tmp);
+ } else {
+ // Here we run the empty initializer where we don't have to, but
+ // this simplifies the computation.
+ resize(num + 1, DONT_SHRINK_UNDERLYING_ARRAY);
+ data[num - 1] = value;
+ }
+ }
+
+
+ inline void append(const T& v1, const T& v2) {
+ if (inArray(&v1) || inArray(&v2)) {
+ // Copy into temporaries so that the references won't break when
+ // the array resizes.
+ T t1 = v1;
+ T t2 = v2;
+ append(t1, t2);
+ } else if (num + 1 < numAllocated) {
+ // This is a simple situation; just stick it in the next free slot using
+ // the copy constructor.
+ new (data + num) T(v1);
+ new (data + num + 1) T(v2);
+ num += 2;
+ } else {
+ // Resize the array. Note that neither value is already in the array.
+ resize(num + 2, DONT_SHRINK_UNDERLYING_ARRAY);
+ data[num - 2] = v1;
+ data[num - 1] = v2;
+ }
+ }
+
+
+ inline void append(const T& v1, const T& v2, const T& v3) {
+ if (inArray(&v1) || inArray(&v2) || inArray(&v3)) {
+ T t1 = v1;
+ T t2 = v2;
+ T t3 = v3;
+ append(t1, t2, t3);
+ } else if (num + 2 < numAllocated) {
+ // This is a simple situation; just stick it in the next free slot using
+ // the copy constructor.
+ new (data + num) T(v1);
+ new (data + num + 1) T(v2);
+ new (data + num + 2) T(v3);
+ num += 3;
+ } else {
+ resize(num + 3, DONT_SHRINK_UNDERLYING_ARRAY);
+ data[num - 3] = v1;
+ data[num - 2] = v2;
+ data[num - 1] = v3;
+ }
+ }
+
+
+ inline void append(const T& v1, const T& v2, const T& v3, const T& v4) {
+ if (inArray(&v1) || inArray(&v2) || inArray(&v3) || inArray(&v4)) {
+ T t1 = v1;
+ T t2 = v2;
+ T t3 = v3;
+ T t4 = v4;
+ append(t1, t2, t3, t4);
+ } else if (num + 3 < numAllocated) {
+ // This is a simple situation; just stick it in the next free slot using
+ // the copy constructor.
+ new (data + num) T(v1);
+ new (data + num + 1) T(v2);
+ new (data + num + 2) T(v3);
+ new (data + num + 3) T(v4);
+ num += 4;
+ } else {
+ resize(num + 4, DONT_SHRINK_UNDERLYING_ARRAY);
+ data[num - 4] = v1;
+ data[num - 3] = v2;
+ data[num - 2] = v3;
+ data[num - 1] = v4;
+ }
+ }
+
+ /**
+ Returns true if the given element is in the array.
+ */
+ bool contains(const T& e) const {
+ for (int i = 0; i < size(); ++i) {
+ if ((*this)[i] == e) {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ /**
+ Append the elements of array. Cannot be called with this array
+ as an argument.
+ */
+ void append(const Array<T>& array) {
+ debugAssert(this != &array);
+ int oldNum = num;
+ int arrayLength = array.length();
+
+ resize(num + arrayLength, false);
+
+ for (int i = 0; i < arrayLength; i++) {
+ data[oldNum + i] = array.data[i];
+ }
+ }
+
+ /**
+ Pushes a new element onto the end and returns its address.
+ This is the same as A.resize(A.size() + 1, false); A.last()
+ */
+ inline T& next() {
+ resize(num + 1, false);
+ return last();
+ }
+
+ /**
+ Pushes an element onto the end (appends)
+ */
+ inline void push(const T& value) {
+ append(value);
+ }
+
+ inline void push(const Array<T>& array) {
+ append(array);
+ }
+
+ /** Alias to provide std::vector compatibility */
+ inline void push_back(const T& v) {
+ push(v);
+ }
+
+ /** "The member function removes the last element of the controlled sequence, which must be non-empty."
+ For compatibility with std::vector. */
+ inline void pop_back() {
+ pop();
+ }
+
+ /**
+ "The member function returns the storage currently allocated to hold the controlled
+ sequence, a value at least as large as size()"
+ For compatibility with std::vector.
+ */
+ int capacity() const {
+ return numAllocated;
+ }
+
+ /**
+ "The member function returns a reference to the first element of the controlled sequence,
+ which must be non-empty."
+ For compatibility with std::vector.
+ */
+ T& front() {
+ return (*this)[0];
+ }
+
+ /**
+ "The member function returns a reference to the first element of the controlled sequence,
+ which must be non-empty."
+ For compatibility with std::vector.
+ */
+ const T& front() const {
+ return (*this)[0];
+ }
+
+ /**
+ Removes the last element and returns it. By default, shrinks the underlying array.
+ */
+ inline T pop(bool shrinkUnderlyingArrayIfNecessary = true) {
+ debugAssert(num > 0);
+ T temp = data[num - 1];
+ resize(num - 1, shrinkUnderlyingArrayIfNecessary);
+ return temp;
+ }
+
+ /** Pops the last element and discards it without returning anything. Faster than pop.
+ By default, does not shrink the underlying array.*/
+ inline void popDiscard(bool shrinkUnderlyingArrayIfNecessary = false) {
+ debugAssert(num > 0);
+ resize(num - 1, shrinkUnderlyingArrayIfNecessary);
+ }
+
+
+ /**
+ "The member function swaps the controlled sequences between *this and str."
+ Note that this is slower than the optimal std implementation.
+
+ For compatibility with std::vector.
+ */
+ void swap(Array<T>& str) {
+ Array<T> temp = str;
+ str = *this;
+ *this = temp;
+ }
+
+
+ /**
+ Performs bounds checks in debug mode
+ */
+ inline T& operator[](int n) {
+ debugAssertM((n >= 0) && (n < num), format("Array index out of bounds. n = %d, size() = %d", n, num));
+ debugAssert(data!=NULL);
+ return data[n];
+ }
+
+ inline T& operator[](unsigned int n) {
+ debugAssertM(n < (unsigned int)num, format("Array index out of bounds. n = %d, size() = %d", n, num));
+ return data[n];
+ }
+
+ /**
+ Performs bounds checks in debug mode
+ */
+ inline const T& operator[](int n) const {
+ debugAssert((n >= 0) && (n < num));
+ debugAssert(data!=NULL);
+ return data[n];
+ }
+
+ inline const T& operator[](unsigned int n) const {
+ debugAssert((n < (unsigned int)num));
+ debugAssert(data!=NULL);
+ return data[n];
+ }
+
+ inline T& randomElement() {
+ debugAssert(num > 0);
+ debugAssert(data!=NULL);
+ return data[iRandom(0, num - 1)];
+ }
+
+ inline const T& randomElement() const {
+ debugAssert(num > 0);
+ debugAssert(data!=NULL);
+ return data[iRandom(0, num - 1)];
+ }
+
+ /**
+ Returns the last element, performing a check in
+ debug mode that there is at least one element.
+ */
+ inline const T& last() const {
+ debugAssert(num > 0);
+ debugAssert(data!=NULL);
+ return data[num - 1];
+ }
+
+ /** Returns element lastIndex() */
+ inline T& last() {
+ debugAssert(num > 0);
+ debugAssert(data!=NULL);
+ return data[num - 1];
+ }
+
+ /** Returns <i>size() - 1</i> */
+ inline int lastIndex() const {
+ debugAssertM(num > 0, "Array is empty");
+ return num - 1;
+ }
+
+ inline int firstIndex() const {
+ debugAssertM(num > 0, "Array is empty");
+ return 0;
+ }
+
+ /** Returns element firstIndex(), performing a check in debug mode to ensure that there is at least one */
+ inline T& first() {
+ debugAssertM(num > 0, "Array is empty");
+ return data[0];
+ }
+
+ inline const T& first() const {
+ debugAssertM(num > 0, "Array is empty");
+ return data[0];
+ }
+
+ /** Returns iFloor(size() / 2), throws an assertion in debug mode if the array is empty */
+ inline int middleIndex() const {
+ debugAssertM(num > 0, "Array is empty");
+ return num >> 1;
+ }
+
+ /** Returns element middleIndex() */
+ inline const T& middle() const {
+ debugAssertM(num > 0, "Array is empty");
+ return data[num >> 1];
+ }
+
+ /** Returns element middleIndex() */
+ inline T& middle() {
+ debugAssertM(num > 0, "Array is empty");
+ return data[num >> 1];
+ }
+
+ /**
+ Calls delete on all objects[0...size-1]
+ and sets the size to zero.
+ */
+ void deleteAll() {
+ for (int i = 0; i < num; i++) {
+ delete data[i];
+ }
+ resize(0);
+ }
+
+ /**
+ Returns the index of (the first occurance of) an index or -1 if
+ not found.
+ */
+ int findIndex(const T& value) const {
+ for (int i = 0; i < num; ++i) {
+ if (data[i] == value) {
+ return i;
+ }
+ }
+ return -1;
+ }
+
+ /**
+ Finds an element and returns the iterator to it. If the element
+ isn't found then returns end().
+ */
+ Iterator find(const T& value) {
+ for (int i = 0; i < num; ++i) {
+ if (data[i] == value) {
+ return data + i;
+ }
+ }
+ return end();
+ }
+
+ ConstIterator find(const T& value) const {
+ for (int i = 0; i < num; ++i) {
+ if (data[i] == value) {
+ return data + i;
+ }
+ }
+ return end();
+ }
+
+ /**
+ Removes count elements from the array
+ referenced either by index or Iterator.
+ */
+ void remove(Iterator element, int count = 1) {
+ debugAssert((element >= begin()) && (element < end()));
+ debugAssert((count > 0) && (element + count) <= end());
+ Iterator last = end() - count;
+
+ while(element < last) {
+ element[0] = element[count];
+ ++element;
+ }
+
+ resize(num - count);
+ }
+
+ void remove(int index, int count = 1) {
+ debugAssert((index >= 0) && (index < num));
+ debugAssert((count > 0) && (index + count <= num));
+
+ remove(begin() + index, count);
+ }
+
+ /**
+ Reverse the elements of the array in place.
+ */
+ void reverse() {
+ T temp;
+
+ int n2 = num / 2;
+ for (int i = 0; i < n2; ++i) {
+ temp = data[num - 1 - i];
+ data[num - 1 - i] = data[i];
+ data[i] = temp;
+ }
+ }
+
+ /**
+ Sort using a specific less-than function, e.g.:
+
+ <PRE>
+ bool __cdecl myLT(const MyClass& elem1, const MyClass& elem2) {
+ return elem1.x < elem2.x;
+ }
+ </PRE>
+
+ Note that for pointer arrays, the <CODE>const</CODE> must come
+ <I>after</I> the class name, e.g., <CODE>Array<MyClass*></CODE> uses:
+
+ <PRE>
+ bool __cdecl myLT(MyClass*const& elem1, MyClass*const& elem2) {
+ return elem1->x < elem2->x;
+ }
+ </PRE>
+ */
+ void sort(bool (__cdecl *lessThan)(const T& elem1, const T& elem2)) {
+ std::sort(data, data + num, lessThan);
+ }
+
+
+ /**
+ Sorts the array in increasing order using the > or < operator. To
+ invoke this method on Array<T>, T must override those operator.
+ You can overide these operators as follows:
+ <code>
+ bool T::operator>(const T& other) const {
+ return ...;
+ }
+ bool T::operator<(const T& other) const {
+ return ...;
+ }
+ </code>
+ */
+ void sort(int direction = SORT_INCREASING) {
+ if (direction == SORT_INCREASING) {
+ std::sort(data, data + num);
+ } else {
+ std::sort(data, data + num, compareGT);
+ }
+ }
+
+ /**
+ Sorts elements beginIndex through and including endIndex.
+ */
+ void sortSubArray(int beginIndex, int endIndex, int direction = SORT_INCREASING) {
+ if (direction == SORT_INCREASING) {
+ std::sort(data + beginIndex, data + endIndex + 1);
+ } else {
+ std::sort(data + beginIndex, data + endIndex + 1, compareGT);
+ }
+ }
+
+ void sortSubArray(int beginIndex, int endIndex, bool (__cdecl *lessThan)(const T& elem1, const T& elem2)) {
+ std::sort(data + beginIndex, data + endIndex + 1, lessThan);
+ }
+
+ /**
+ The StrictWeakOrdering can be either a class that overloads the function call operator() or
+ a function pointer of the form <code>bool (__cdecl *lessThan)(const T& elem1, const T& elem2)</code>
+ */
+ template<typename StrictWeakOrdering>
+ void sortSubArray(int beginIndex, int endIndex, StrictWeakOrdering& lessThan) {
+ std::sort(data + beginIndex, data + endIndex + 1, lessThan);
+ }
+
+ /** Uses < and == to evaluate operator(); this is the default comparator for Array::partition. */
+ class DefaultComparator {
+ public:
+ inline int operator()(const T& A, const T& B) const {
+ if (A < B) {
+ return 1;
+ } else if (A == B) {
+ return 0;
+ } else {
+ return -1;
+ }
+ }
+ };
+
+ /** The output arrays are resized with fastClear() so that if they are already of the same size
+ as this array no memory is allocated during partitioning.
+
+ @param comparator A function, or class instance with an overloaded operator() that compares
+ two elements of type <code>T</code> and returns 0 if they are equal, -1 if the second is smaller,
+ and 1 if the first is smaller (i.e., following the conventions of std::string::compare). For example:
+
+ <pre>
+ int compare(int A, int B) {
+ if (A < B) {
+ return 1;
+ } else if (A == B) {
+ return 0;
+ } else {
+ return -1;
+ }
+ }
+ </pre>
+ */
+ template<typename Comparator>
+ void partition(
+ const T& partitionElement,
+ Array<T>& ltArray,
+ Array<T>& eqArray,
+ Array<T>& gtArray,
+ const Comparator& comparator) const {
+
+ // Make sure all arrays are independent
+ debugAssert(&ltArray != this);
+ debugAssert(&eqArray != this);
+ debugAssert(&gtArray != this);
+ debugAssert(&ltArray != &eqArray);
+ debugAssert(&ltArray != &gtArray);
+ debugAssert(&eqArray != &gtArray);
+
+ // Clear the arrays
+ ltArray.fastClear();
+ eqArray.fastClear();
+ gtArray.fastClear();
+
+ // Form a table of buckets for lt, eq, and gt
+ Array<T>* bucket[3] = {&ltArray, &eqArray, &gtArray};
+
+ for (int i = 0; i < num; ++i) {
+ int c = comparator(partitionElement, data[i]);
+ debugAssertM(c >= -1 && c <= 1, "Comparator returned an illegal value.");
+
+ // Insert into the correct bucket, 0, 1, or 2
+ bucket[c + 1]->append(data[i]);
+ }
+ }
+
+ /**
+ Uses < and == on elements to perform a partition. See partition().
+ */
+ void partition(
+ const T& partitionElement,
+ Array<T>& ltArray,
+ Array<T>& eqArray,
+ Array<T>& gtArray) const {
+
+ partition(partitionElement, ltArray, eqArray, gtArray, typename Array<T>::DefaultComparator());
+ }
+
+ /**
+ Paritions the array into those below the median, those above the median, and those elements
+ equal to the median in expected O(n) time using quickselect. If the array has an even
+ number of different elements, the median for partition purposes is the largest value
+ less than the median.
+
+ @param tempArray used for working scratch space
+ @param comparator see parition() for a discussion.*/
+ template<typename Comparator>
+ void medianPartition(
+ Array<T>& ltMedian,
+ Array<T>& eqMedian,
+ Array<T>& gtMedian,
+ Array<T>& tempArray,
+ const Comparator& comparator) const {
+
+ ltMedian.fastClear();
+ eqMedian.fastClear();
+ gtMedian.fastClear();
+
+ // Handle trivial cases first
+ switch (size()) {
+ case 0:
+ // Array is empty; no parition is possible
+ return;
+
+ case 1:
+ // One element
+ eqMedian.append(first());
+ return;
+
+ case 2:
+ {
+ // Two element array; median is the smaller
+ int c = comparator(first(), last());
+
+ switch (c) {
+ case -1:
+ // first was bigger
+ eqMedian.append(last());
+ gtMedian.append(first());
+ break;
+
+ case 0:
+ // Both equal to the median
+ eqMedian.append(first(), last());
+ break;
+
+ case 1:
+ // Last was bigger
+ eqMedian.append(first());
+ gtMedian.append(last());
+ break;
+ }
+ }
+ return;
+ }
+
+ // All other cases use a recursive randomized median
+
+ // Number of values less than all in the current arrays
+ int ltBoost = 0;
+
+ // Number of values greater than all in the current arrays
+ int gtBoost = 0;
+
+ // For even length arrays, force the gt array to be one larger than the
+ // lt array:
+ // [1 2 3] size = 3, choose half = (s + 1) /2
+ //
+ int lowerHalfSize, upperHalfSize;
+ if (isEven(size())) {
+ lowerHalfSize = size() / 2;
+ upperHalfSize = lowerHalfSize + 1;
+ } else {
+ lowerHalfSize = upperHalfSize = (size() + 1) / 2;
+ }
+ const T* xPtr = NULL;
+
+ // Maintain pointers to the arrays; we'll switch these around during sorting
+ // to avoid copies.
+ const Array<T>* source = this;
+ Array<T>* lt = &ltMedian;
+ Array<T>* eq = &eqMedian;
+ Array<T>* gt = &gtMedian;
+ Array<T>* extra = &tempArray;
+
+ while (true) {
+ // Choose a random element -- choose the middle element; this is theoretically
+ // suboptimal, but for loosly sorted array is actually the best strategy
+
+ xPtr = &(source->middle());
+ if (source->size() == 1) {
+ // Done; there's only one element left
+ break;
+ }
+ const T& x = *xPtr;
+
+ // Note: partition (fast) clears the arrays for us
+ source->partition(x, *lt, *eq, *gt, comparator);
+
+ int L = lt->size() + ltBoost + eq->size();
+ int U = gt->size() + gtBoost + eq->size();
+ if ((L >= lowerHalfSize) &&
+ (U >= upperHalfSize)) {
+
+ // x must be the partition median
+ break;
+
+ } else if (L < lowerHalfSize) {
+
+ // x must be smaller than the median. Recurse into the 'gt' array.
+ ltBoost += lt->size() + eq->size();
+
+ // The new gt array will be the old source array, unless
+ // that was the this pointer (i.e., unless we are on the
+ // first iteration)
+ Array<T>* newGt = (source == this) ? extra : const_cast<Array<T>*>(source);
+
+ // Now set up the gt array as the new source
+ source = gt;
+ gt = newGt;
+
+ } else {
+
+ // x must be bigger than the median. Recurse into the 'lt' array.
+ gtBoost += gt->size() + eq->size();
+
+ // The new lt array will be the old source array, unless
+ // that was the this pointer (i.e., unless we are on the
+ // first iteration)
+ Array<T>* newLt = (source == this) ? extra : const_cast<Array<T>*>(source);
+
+ // Now set up the lt array as the new source
+ source = lt;
+ lt = newLt;
+ }
+ }
+
+ // Now that we know the median, make a copy of it (since we're about to destroy the array that it
+ // points into).
+ T median = *xPtr;
+ xPtr = NULL;
+
+ // Partition the original array (note that this fast clears for us)
+ partition(median, ltMedian, eqMedian, gtMedian, comparator);
+ }
+
+ /**
+ Computes a median partition using the default comparator and a dynamically allocated temporary
+ working array. If the median is not in the array, it is chosen to be the largest value smaller
+ than the true median.
+ */
+ void medianPartition(
+ Array<T>& ltMedian,
+ Array<T>& eqMedian,
+ Array<T>& gtMedian) const {
+
+ Array<T> temp;
+ medianPartition(ltMedian, eqMedian, gtMedian, temp, DefaultComparator());
+ }
+
+
+ /** Redistributes the elements so that the new order is statistically independent
+ of the original order. O(n) time.*/
+ void randomize() {
+ T temp;
+
+ for (int i = size() - 1; i >= 0; --i) {
+ int x = iRandom(0, i);
+
+ temp = data[i];
+ data[i] = data[x];
+ data[x] = temp;
+ }
+ }
+
+
+};
+
+
+/** Array::contains for C-arrays */
+template<class T> bool contains(const T* array, int len, const T& e) {
+ for (int i = len - 1; i >= 0; --i) {
+ if (array[i] == e) {
+ return true;
+ }
+ }
+ return false;
+}
+
+} // namespace
+
+#ifdef _MSC_VER
+# pragma warning (pop)
+#endif
+
+#endif
diff --git a/externals/g3dlite/G3D.lib/include/G3D/AtomicInt32.h b/externals/g3dlite/G3D.lib/include/G3D/AtomicInt32.h
new file mode 100644
index 00000000000..9d58e02efe5
--- /dev/null
+++ b/externals/g3dlite/G3D.lib/include/G3D/AtomicInt32.h
@@ -0,0 +1,166 @@
+/**
+ @file AtomicInt32.h
+
+ @maintainer Morgan McGuire, matrix@graphics3d.com
+
+ @created 2005-09-01
+ @edited 2006-06-21
+ */
+#ifndef G3D_ATOMICINT32_H
+#define G3D_ATOMICINT32_H
+
+#include "G3D/platform.h"
+#include "G3D/g3dmath.h"
+
+#if defined(G3D_OSX)
+ #include <libkern/OSAtomic.h>
+#endif
+
+namespace G3D {
+
+/**
+ An integer that may safely be used on different threads without
+ external locking.
+
+ On Win32, Linux, FreeBSD, and Mac OS X this is implemented without locks.
+
+ <B>BETA API</B> This is unsupported and may change
+ */
+class AtomicInt32 {
+private:
+# if defined(G3D_WIN32)
+ volatile long m_value;
+# elif defined(G3D_OSX)
+ int32_t m_value;
+# else
+ volatile int32 m_value;
+# endif
+
+
+public:
+
+ /** Initial value is undefined. */
+ AtomicInt32() {}
+
+ /** Atomic set */
+ explicit AtomicInt32(const int32 x) {
+ m_value = x;
+ }
+
+ /** Atomic set */
+ AtomicInt32(const AtomicInt32& x) {
+ m_value = x.m_value;
+ }
+
+ /** Atomic set */
+ const AtomicInt32& operator=(const int32 x) {
+ m_value = x;
+ return *this;
+ }
+
+ /** Atomic set */
+ void operator=(const AtomicInt32& x) {
+ m_value = x.m_value;
+ }
+
+ /** Returns the current value */
+ int32 value() const {
+ return m_value;
+ }
+
+ /** Returns the old value, before the add. */
+ int32 add(const int32 x) {
+# if defined(G3D_WIN32)
+
+ return InterlockedExchangeAdd(&m_value, x);
+
+# elif defined(G3D_LINUX) || defined(G3D_FREEBSD)
+
+ int32 old;
+ asm volatile ("lock; xaddl %0,%1"
+ : "=r"(old), "=m"(m_value) /* outputs */
+ : "0"(x), "m"(m_value) /* inputs */
+ : "memory", "cc");
+ return old;
+
+# elif defined(G3D_OSX)
+
+ int32 old = m_value;
+ OSAtomicAdd32(x, &m_value);
+ return old;
+
+# endif
+ }
+
+ /** Returns old value. */
+ int32 sub(const int32 x) {
+ return add(-x);
+ }
+
+ void increment() {
+# if defined(G3D_WIN32)
+ // Note: returns the newly incremented value
+ InterlockedIncrement(&m_value);
+# elif defined(G3D_LINUX) || defined(G3D_FREEBSD)
+ add(1);
+# elif defined(G3D_OSX)
+ // Note: returns the newly incremented value
+ OSAtomicIncrement32(&m_value);
+# endif
+ }
+
+ /** Returns zero if the result is zero after decrement, non-zero otherwise.*/
+ int32 decrement() {
+# if defined(G3D_WIN32)
+ // Note: returns the newly decremented value
+ return InterlockedDecrement(&m_value);
+# elif defined(G3D_LINUX) || defined(G3D_FREEBSD)
+ unsigned char nz;
+
+ asm volatile ("lock; decl %1;\n\t"
+ "setnz %%al"
+ : "=a" (nz)
+ : "m" (m_value)
+ : "memory", "cc");
+ return nz;
+# elif defined(G3D_OSX)
+ // Note: returns the newly decremented value
+ return OSAtomicDecrement32(&m_value);
+# endif
+ }
+
+
+ /** Atomic test-and-set: if <code>*this == comperand</code> then <code>*this := exchange</code> else do nothing.
+ In both cases, returns the old value of <code>*this</code>.
+
+ Performs an atomic comparison of this with the Comperand value.
+ If this is equal to the Comperand value, the Exchange value is stored in this.
+ Otherwise, no operation is performed.
+
+ Under VC6 the sign bit may be lost.
+
+ */
+ int32 compareAndSet(const int32 comperand, const int32 exchange) {
+# if defined(G3D_WIN32)
+ return InterlockedCompareExchange(&m_value, exchange, comperand);
+# elif defined(G3D_LINUX) || defined(G3D_FREEBSD)
+ int32 ret;
+ asm volatile ("lock; cmpxchgl %1, %2"
+ : "=a" (ret)
+ : "r" (exchange), "m" (m_value), "0"(comperand)
+ : "memory", "cc");
+ return ret;
+# elif defined(G3D_OSX)
+ int32 old = m_value;
+
+ OSAtomicCompareAndSwap32(comperand, exchange, &m_value);
+
+ return old;
+# endif
+ }
+
+};
+
+} // namespace
+
+#endif
diff --git a/externals/g3dlite/G3D.lib/include/G3D/BinaryFormat.h b/externals/g3dlite/G3D.lib/include/G3D/BinaryFormat.h
new file mode 100644
index 00000000000..53f0c144bf6
--- /dev/null
+++ b/externals/g3dlite/G3D.lib/include/G3D/BinaryFormat.h
@@ -0,0 +1,140 @@
+/**
+ @file BinaryFormat.h
+ @maintainer Morgan McGuire, matrix@graphics3d.com
+
+ @author 2005-06-03
+ @edited 2005-06-03
+
+ Copyright 2000-2005, Morgan McGuire.
+ All rights reserved.
+ */
+
+#ifndef G3D_BINARYFORMAT_H
+#define G3D_BINARYFORMAT_H
+
+#include "G3D/platform.h"
+#include "G3D/g3dmath.h"
+
+namespace G3D {
+
+class Vector2;
+class Vector2int16;
+class Vector3;
+class Vector3int16;
+class Vector4;
+class Vector4int16;
+class Color3;
+class Color3uint8;
+class Color4;
+class Color4uint8;
+
+/**
+ Some values like float16 and int128 have no current CPU data structure that implements them but are useful
+ for file formats and for GPUs.
+
+ CHUNK_BINFMT data follows the protocol.
+ */
+// Must be packed int 16 bits for the chunk reader
+// We can't name these just "INT8" etc. because some libraries #define names like that
+enum BinaryFormat {
+ FIRST_BINFMT = 1000,
+
+ BOOL8_BINFMT,
+ UINT8_BINFMT, INT8_BINFMT, UINT16_BINFMT, INT16_BINFMT, UINT32_BINFMT, INT32_BINFMT, UINT64_BINFMT, INT64_BINFMT, UINT128_BINFMT, INT128_BINFMT,
+ FLOAT16_BINFMT, FLOAT32_BINFMT, FLOAT64_BINFMT,
+ VECTOR2_BINFMT, VECTOR2INT16_BINFMT,
+ VECTOR3_BINFMT, VECTOR3INT16_BINFMT,
+ VECTOR4_BINFMT, VECTOR4INT16_BINFMT,
+ COLOR3_BINFMT, COLOR3UINT8_BINFMT, COLOR3INT16_BINFMT,
+ COLOR4_BINFMT, COLOR4UINT8_BINFMT, COLOR4INT16_BINFMT,
+ STRING_BINFMT, STRINGEVEN_BINFMT, STRING8_BINFMT, STRING16_BINFMT, STRING32_BINFMT,
+
+ CHUNK_BINFMT,
+
+ CUSTOM_BINFMT,
+
+ LAST_BINFMT
+};
+
+}
+
+/** A macro that maps G3D types to format constants.
+ (e.g. binaryFormatOf(Vector3) == VECTOR3_BINFMT).
+*/
+// This implementation is designed to meet the following constraints:
+// 1. Work around the many MSVC++ partial template bugs
+// 2. Work for primitive types (e.g. int)
+#define binaryFormatOf(T) (G3D::_internal::_BinaryFormat<T>::x())
+
+namespace G3D {
+namespace _internal {
+
+
+template<class T> class _BinaryFormat {
+public:
+ static BinaryFormat x() {
+ return CUSTOM_BINFMT;
+ }
+};
+}}
+
+
+/**
+ Macro to declare the underlying format (as will be returned by glFormatOf)
+ of a type. For example,
+
+ <PRE>
+ DECLARE_BINARYFORMATOF(Vector4, VECTOR4_BINFMT)
+ </PRE>
+
+ Use this so you can make vertex arrays of your own classes and not just
+ the standard ones.
+ */
+#define DECLARE_BINARYFORMATOF(CType, EnumType) \
+namespace G3D { \
+ namespace _internal { \
+ template<> class _BinaryFormat<CType> { \
+ public: \
+ static BinaryFormat x() { \
+ return EnumType; \
+ } \
+ }; \
+ } \
+}
+
+DECLARE_BINARYFORMATOF( bool, BOOL8_BINFMT )
+
+DECLARE_BINARYFORMATOF( uint8, UINT8_BINFMT )
+DECLARE_BINARYFORMATOF( int8, INT8_BINFMT )
+DECLARE_BINARYFORMATOF( uint16, UINT16_BINFMT )
+DECLARE_BINARYFORMATOF( int16, INT16_BINFMT )
+DECLARE_BINARYFORMATOF( uint32, UINT32_BINFMT )
+DECLARE_BINARYFORMATOF( int32, INT32_BINFMT )
+DECLARE_BINARYFORMATOF( uint64, UINT64_BINFMT )
+DECLARE_BINARYFORMATOF( int64, INT64_BINFMT )
+
+DECLARE_BINARYFORMATOF( float32, FLOAT32_BINFMT )
+DECLARE_BINARYFORMATOF( float64, FLOAT64_BINFMT )
+
+DECLARE_BINARYFORMATOF( Vector2, VECTOR2_BINFMT )
+DECLARE_BINARYFORMATOF( Vector2int16, VECTOR2INT16_BINFMT )
+DECLARE_BINARYFORMATOF( Vector3, VECTOR3_BINFMT )
+DECLARE_BINARYFORMATOF( Vector3int16, VECTOR3INT16_BINFMT )
+DECLARE_BINARYFORMATOF( Vector4, VECTOR4_BINFMT )
+DECLARE_BINARYFORMATOF( Vector4int16, VECTOR4INT16_BINFMT )
+
+DECLARE_BINARYFORMATOF( Color3, COLOR3_BINFMT )
+DECLARE_BINARYFORMATOF( Color3uint8, COLOR3UINT8_BINFMT )
+DECLARE_BINARYFORMATOF( Color4, COLOR4_BINFMT )
+DECLARE_BINARYFORMATOF( Color4uint8, COLOR4UINT8_BINFMT )
+
+namespace G3D {
+
+/** Returns -1 if the format is custom, otherwise the byte size
+ of a single element in this format.*/
+int32 byteSize(BinaryFormat f);
+
+
+} //G3D
+
+#endif
diff --git a/externals/g3dlite/G3D.lib/include/G3D/BinaryInput.h b/externals/g3dlite/G3D.lib/include/G3D/BinaryInput.h
new file mode 100644
index 00000000000..c0ab9052402
--- /dev/null
+++ b/externals/g3dlite/G3D.lib/include/G3D/BinaryInput.h
@@ -0,0 +1,441 @@
+/**
+ @file BinaryInput.h
+
+ @maintainer Morgan McGuire, graphics3d.com
+
+ @created 2001-08-09
+ @edited 2006-07-19
+
+ Copyright 2000-2007, Morgan McGuire.
+ All rights reserved.
+ */
+
+#ifndef G3D_BINARYINPUT_H
+#define G3D_BINARYINPUT_H
+
+#ifdef _MSC_VER
+// Disable conditional expression is constant, which occurs incorrectly on inlined functions
+# pragma warning(push)
+# pragma warning( disable : 4127 )
+#endif
+
+#include <assert.h>
+#include <string>
+#include <vector>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <stdio.h>
+#include "G3D/platform.h"
+#include "G3D/Array.h"
+#include "G3D/Color4.h"
+#include "G3D/Color3.h"
+#include "G3D/Vector4.h"
+#include "G3D/Vector3.h"
+#include "G3D/Vector2.h"
+#include "G3D/g3dmath.h"
+#include "G3D/debug.h"
+#include "G3D/System.h"
+
+
+namespace G3D {
+
+#if defined(G3D_WIN32) || defined(G3D_LINUX)
+ // Allow writing of integers to non-word aligned locations.
+ // This is legal on x86, but not on other platforms.
+ #define G3D_ALLOW_UNALIGNED_WRITES
+#endif
+
+/**
+ Sequential or random access byte-order independent binary file access.
+ Files compressed with zlib and beginning with an unsigned 32-bit int
+ size are transparently decompressed when the compressed = true flag is
+ specified to the constructor.
+
+ For every readX method there are also versions that operate on a whole
+ Array, std::vector, or C-array. e.g. readFloat32(Array<float32>& array, n)
+ These methods resize the array or std::vector to the appropriate size
+ before reading. For a C-array, they require the pointer to reference
+ a memory block at least large enough to hold <I>n</I> elements.
+
+ Most classes define serialize/deserialize methods that use BinaryInput,
+ BinaryOutput, TextInput, and TextOutput. There are text serializer
+ functions for primitive types (e.g. int, std::string, float, double) but not
+ binary serializers-- you <B>must</b> call the BinaryInput::readInt32 or
+ other appropriate function. This is because it would be very hard to
+ debug the error sequence: <CODE>serialize(1.0, bo); ... float f; deserialize(f, bi);</CODE>
+ in which a double is serialized and then deserialized as a float.
+ */
+class BinaryInput {
+private:
+
+ // The initial buffer will be no larger than this, but
+ // may grow if a large memory read occurs. 50 MB
+ enum {INITIAL_BUFFER_LENGTH = 50000000};
+
+ /**
+ is the file big or little endian
+ */
+ G3DEndian m_fileEndian;
+ std::string m_filename;
+
+ bool m_swapBytes;
+
+ /** Next position to read from in bitString during readBits. */
+ int m_bitPos;
+
+ /** Bits currently being read by readBits.
+ Contains at most 8 (low) bits. Note that
+ beginBits/readBits actually consumes one extra byte, which
+ will be restored by writeBits.*/
+ uint32 m_bitString;
+
+ /** 1 when between beginBits and endBits, 0 otherwise. */
+ int m_beginEndBits;
+
+ /** When operating on huge files, we cannot load the whole file into memory.
+ This is the file position to which buffer[0] corresponds.
+ */
+ int64 m_alreadyRead;
+
+ /**
+ Length of the entire file, in bytes.
+ For the length of the buffer, see bufferLength
+ */
+ int64 m_length;
+
+ /** Length of the array referenced by buffer. May go past the end of the file!*/
+ int64 m_bufferLength;
+ uint8* m_buffer;
+
+ /**
+ Next byte in file, relative to buffer.
+ */
+ int64 m_pos;
+
+ /**
+ When true, the buffer is freed in the destructor.
+ */
+ bool m_freeBuffer;
+
+ /** Ensures that we are able to read at least minLength from startPosition (relative
+ to start of file). */
+ void loadIntoMemory(int64 startPosition, int64 minLength = 0);
+
+ /** Verifies that at least this number of bytes can be read.*/
+ inline void prepareToRead(int64 nbytes) {
+ debugAssertM(m_length > 0, m_filename + " not found or corrupt.");
+ debugAssertM(m_pos + nbytes + m_alreadyRead <= m_length, "Read past end of file.");
+
+ if (m_pos + nbytes > m_bufferLength) {
+ loadIntoMemory(m_pos + m_alreadyRead, nbytes);
+ }
+ }
+
+ // Not implemented on purpose, don't use
+ BinaryInput(const BinaryInput&);
+ BinaryInput& operator=(const BinaryInput&);
+ bool operator==(const BinaryInput&);
+
+ /** Buffer is compressed; replace it with a decompressed version */
+ void decompress();
+public:
+
+ /** false, constant to use with the copyMemory option */
+ static const bool NO_COPY;
+
+ /**
+ If the file cannot be opened, a zero length buffer is presented.
+ Automatically opens files that are inside zipfiles.
+
+ @param compressed Set to true if and only if the file was
+ compressed using BinaryOutput's zlib compression. This has
+ nothing to do with whether the input is in a zipfile.
+ */
+ BinaryInput(
+ const std::string& filename,
+ G3DEndian fileEndian,
+ bool compressed = false);
+
+ /**
+ Creates input stream from an in memory source.
+ Unless you specify copyMemory = false, the data is copied
+ from the pointer, so you may deallocate it as soon as the
+ object is constructed. It is an error to specify copyMemory = false
+ and compressed = true.
+
+ To decompress part of a file, you can follow the following paradigm:
+
+ <PRE>
+ BinaryInput master(...);
+
+ // read from master to point where compressed data exists.
+
+ BinaryInput subset(master.getCArray() + master.getPosition(),
+ master.length() - master.getPosition(),
+ master.endian(), true, true);
+
+ // Now read from subset (it is ok for master to go out of scope)
+ </PRE>
+ */
+ BinaryInput(
+ const uint8* data,
+ int64 dataLen,
+ G3DEndian dataEndian,
+ bool compressed = false,
+ bool copyMemory = true);
+
+ virtual ~BinaryInput();
+
+ /** Change the endian-ness of the file. This only changes the
+ interpretation of the file for future read calls; the
+ underlying data is unmodified.*/
+ void setEndian(G3DEndian endian);
+
+ G3DEndian endian() const {
+ return m_fileEndian;
+ }
+
+ std::string getFilename() const {
+ return m_filename;
+ }
+
+ /**
+ Returns a pointer to the internal memory buffer.
+ May throw an exception for huge files.
+ */
+ const uint8* getCArray() const {
+ if (m_alreadyRead > 0) {
+ throw "Cannot getCArray for a huge file";
+ }
+ return m_buffer;
+ }
+
+ /**
+ Performs bounds checks in debug mode. [] are relative to
+ the start of the file, not the current position.
+ Seeks to the new position before reading (and leaves
+ that as the current position)
+ */
+ inline uint8 operator[](int64 n) {
+ setPosition(n);
+ return readUInt8();
+ }
+
+ /**
+ Returns the length of the file in bytes.
+ */
+ inline int64 getLength() const {
+ return m_length;
+ }
+
+ inline int64 size() const {
+ return getLength();
+ }
+
+ /**
+ Returns the current byte position in the file,
+ where 0 is the beginning and getLength() - 1 is the end.
+ */
+ inline int64 getPosition() const {
+ return m_pos + m_alreadyRead;
+ }
+
+ /**
+ Sets the position. Cannot set past length.
+ May throw a char* when seeking backwards more than 10 MB on a huge file.
+ */
+ inline void setPosition(int64 p) {
+ debugAssertM(p <= m_length, "Read past end of file");
+ m_pos = p - m_alreadyRead;
+ if ((m_pos < 0) || (m_pos > m_bufferLength)) {
+ loadIntoMemory(m_pos + m_alreadyRead);
+ }
+ }
+
+ /**
+ Goes back to the beginning of the file.
+ */
+ inline void reset() {
+ setPosition(0);
+ }
+
+ inline int8 readInt8() {
+ prepareToRead(1);
+ return m_buffer[m_pos++];
+ }
+
+ inline bool readBool8() {
+ return (readInt8() != 0);
+ }
+
+ inline uint8 readUInt8() {
+ prepareToRead(1);
+ return ((uint8*)m_buffer)[m_pos++];
+ }
+
+ uint16 inline readUInt16() {
+ prepareToRead(2);
+
+ m_pos += 2;
+ if (m_swapBytes) {
+ uint8 out[2];
+ out[0] = m_buffer[m_pos - 1];
+ out[1] = m_buffer[m_pos - 2];
+ return *(uint16*)out;
+ } else {
+ #ifdef G3D_ALLOW_UNALIGNED_WRITES
+ return *(uint16*)(&m_buffer[m_pos - 2]);
+ #else
+ uint8 out[2];
+ out[0] = m_buffer[m_pos - 2];
+ out[1] = m_buffer[m_pos - 1];
+ return *(uint16*)out;
+ #endif
+ }
+
+ }
+
+ inline int16 readInt16() {
+ uint16 a = readUInt16();
+ return *(int16*)&a;
+ }
+
+ inline uint32 readUInt32() {
+ prepareToRead(4);
+
+ m_pos += 4;
+ if (m_swapBytes) {
+ uint8 out[4];
+ out[0] = m_buffer[m_pos - 1];
+ out[1] = m_buffer[m_pos - 2];
+ out[2] = m_buffer[m_pos - 3];
+ out[3] = m_buffer[m_pos - 4];
+ return *(uint32*)out;
+ } else {
+ #ifdef G3D_ALLOW_UNALIGNED_WRITES
+ return *(uint32*)(&m_buffer[m_pos - 4]);
+ #else
+ uint8 out[4];
+ out[0] = m_buffer[m_pos - 4];
+ out[1] = m_buffer[m_pos - 3];
+ out[2] = m_buffer[m_pos - 2];
+ out[3] = m_buffer[m_pos - 1];
+ return *(uint32*)out;
+ #endif
+ }
+ }
+
+
+ inline int32 readInt32() {
+ uint32 a = readUInt32();
+ return *(int32*)&a;
+ }
+
+ uint64 readUInt64();
+
+ inline int64 readInt64() {
+ uint64 a = readUInt64();
+ return *(int64*)&a;
+ }
+
+ inline float32 readFloat32() {
+ union {
+ uint32 a;
+ float32 b;
+ };
+ a = readUInt32();
+ return b;
+ }
+
+ inline float64 readFloat64() {
+ union {
+ uint64 a;
+ float64 b;
+ };
+ a = readUInt64();
+ return b;
+ }
+
+ void readBytes(void* bytes, int64 n);
+
+ /**
+ Reads an n character string. The string is not
+ required to end in NULL in the file but will
+ always be a proper std::string when returned.
+ */
+ std::string readString(int64 n);
+
+ /**
+ Reads until NULL or the end of the file is encountered.
+ */
+ std::string readString();
+
+ /**
+ Reads until NULL or the end of the file is encountered.
+ If the string has odd length (including NULL), reads
+ another byte.
+ */
+ std::string readStringEven();
+
+
+ std::string readString32();
+
+ Vector4 readVector4();
+ Vector3 readVector3();
+ Vector2 readVector2();
+
+ Color4 readColor4();
+ Color3 readColor3();
+
+ /**
+ Skips ahead n bytes.
+ */
+ inline void skip(int64 n) {
+ setPosition(m_pos + m_alreadyRead + n);
+ }
+
+ /**
+ Returns true if the position is not at the end of the file
+ */
+ inline bool hasMore() const {
+ return m_pos + m_alreadyRead < m_length;
+ }
+
+ /** Prepares for bit reading via readBits. Only readBits can be
+ called between beginBits and endBits without corrupting the
+ data stream. */
+ void beginBits();
+
+ /** Can only be called between beginBits and endBits */
+ uint32 readBits(int numBits);
+
+ /** Ends bit-reading. */
+ void endBits();
+
+# define DECLARE_READER(ucase, lcase)\
+ void read##ucase(lcase* out, int64 n);\
+ void read##ucase(std::vector<lcase>& out, int64 n);\
+ void read##ucase(Array<lcase>& out, int64 n);
+
+ DECLARE_READER(Bool8, bool)
+ DECLARE_READER(UInt8, uint8)
+ DECLARE_READER(Int8, int8)
+ DECLARE_READER(UInt16, uint16)
+ DECLARE_READER(Int16, int16)
+ DECLARE_READER(UInt32, uint32)
+ DECLARE_READER(Int32, int32)
+ DECLARE_READER(UInt64, uint64)
+ DECLARE_READER(Int64, int64)
+ DECLARE_READER(Float32, float32)
+ DECLARE_READER(Float64, float64)
+# undef DECLARE_READER
+};
+
+
+}
+
+#ifdef _MSC_VER
+# pragma warning(pop)
+#endif
+
+#endif
diff --git a/externals/g3dlite/G3D.lib/include/G3D/BinaryOutput.h b/externals/g3dlite/G3D.lib/include/G3D/BinaryOutput.h
new file mode 100644
index 00000000000..d81ec56a67b
--- /dev/null
+++ b/externals/g3dlite/G3D.lib/include/G3D/BinaryOutput.h
@@ -0,0 +1,421 @@
+/**
+ @file BinaryOutput.h
+
+ @maintainer Morgan McGuire, graphics3d.com
+
+ @created 2001-08-09
+ @edited 2008-01-24
+
+ Copyright 2000-2006, Morgan McGuire.
+ All rights reserved.
+ */
+
+#ifndef G3D_BINARYOUTPUT_H
+#define G3D_BINARYOUTPUT_H
+
+#include "G3D/platform.h"
+#include <assert.h>
+#include <string>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <stdio.h>
+#include "G3D/Color4.h"
+#include "G3D/Color3.h"
+#include "G3D/Vector4.h"
+#include "G3D/Vector3.h"
+#include "G3D/Vector2.h"
+#include "G3D/g3dmath.h"
+#include "G3D/debug.h"
+#include "G3D/BinaryInput.h"
+#include "G3D/System.h"
+
+#ifdef _MSC_VER
+# pragma warning (push)
+// Conditional is constant (wrong in inline)
+# pragma warning (disable : 4127)
+#endif
+namespace G3D {
+
+/**
+ Sequential or random access byte-order independent binary file access.
+
+ The compress() call can be used to compress with zlib.
+
+ Any method call can trigger an out of memory error (thrown as char*)
+ when writing to "<memory>" instead of a file.
+
+ Compressed writing and seeking backwards is not supported for huge files
+ (i.e., BinaryOutput may have to dump the contents to disk if they
+ exceed available RAM).
+ */
+class BinaryOutput {
+private:
+ std::string m_filename;
+
+ bool m_committed;
+
+ /** 0 outside of beginBits...endBits, 1 inside */
+ int m_beginEndBits;
+
+ /** The current string of bits being built up by beginBits...endBits.
+ This string is treated semantically, as if the lowest bit was
+ on the left and the highest was on the right.*/
+ int8 m_bitString;
+
+ /** Position (from the lowest bit) currently used in bitString.*/
+ int m_bitPos;
+
+ // True if the file endianess does not match the machine endian
+ bool m_swapBytes;
+
+ G3DEndian m_fileEndian;
+
+ uint8* m_buffer;
+
+ /** Size of the elements used */
+ int m_bufferLen;
+
+ /** Underlying size of memory allocaded */
+ int m_maxBufferLen;
+
+ /** Next byte in file */
+ int m_pos;
+
+ /** is this initialized? */
+ bool m_init;
+
+ /** Number of bytes already written to the file.*/
+ size_t m_alreadyWritten;
+
+ bool m_ok;
+
+ void reserveBytesWhenOutOfMemory(size_t bytes);
+
+ void reallocBuffer(size_t bytes, size_t oldBufferLen);
+
+ /**
+ Make sure at least bytes can be written, resizing if
+ necessary.
+ */
+ inline void reserveBytes(int bytes) {
+ debugAssert(bytes > 0);
+ size_t oldBufferLen = (size_t)m_bufferLen;
+
+ m_bufferLen = iMax(m_bufferLen, (m_pos + bytes));
+ if (m_bufferLen > m_maxBufferLen) {
+ reallocBuffer(bytes, oldBufferLen);
+ }
+ }
+
+ // Not implemented on purpose, don't use
+ BinaryOutput(const BinaryOutput&);
+ BinaryOutput& operator=(const BinaryOutput&);
+ bool operator==(const BinaryOutput&);
+
+public:
+
+ /**
+ You must call setEndian() if you use this (memory) constructor.
+ */
+ BinaryOutput();
+
+ /**
+ Doesn't actually open the file; commit() does that.
+ Use "<memory>" as the filename if you're going to commit
+ to memory.
+ */
+ BinaryOutput(
+ const std::string& filename,
+ G3DEndian fileEndian);
+
+ ~BinaryOutput();
+
+ /** Compresses the data in the buffer in place,
+ preceeding it with a little-endian uint32 indicating
+ the uncompressed size.
+
+ Call immediately before commit().
+
+ Cannot be used for huge files (ones where the data
+ was already written to disk)-- will throw char*.
+ */
+ void compress();
+
+ /** True if no errors have been encountered.*/
+ bool ok() const;
+
+ /**
+ Returns a pointer to the internal memory buffer.
+ */
+ inline const uint8* getCArray() const {
+ return m_buffer;
+ }
+
+ void setEndian(G3DEndian fileEndian);
+
+ G3DEndian endian() const {
+ return m_fileEndian;
+ }
+
+ std::string getFilename() const {
+ return m_filename;
+ }
+
+ /**
+ Write the bytes to disk. It is ok to call this
+ multiple times; it will just overwrite the previous file.
+
+ Parent directories are created as needed if they do
+ not exist.
+
+ <B>Not</B> called from the destructor; you must call
+ it yourself.
+
+ @param flush If true (default) the file is ready for reading when the method returns, otherwise
+ the method returns immediately and writes the file in the background.
+ */
+ void commit(bool flush = true);
+
+ /**
+ Write the bytes to memory (which must be of
+ at least size() bytes).
+ */
+ void commit(uint8*);
+
+ /**
+ A memory BinaryOutput may be reset so that it can be written to again
+ without allocating new memory. The underlying array will not be deallocated,
+ but the reset structure will act like a newly intialized one.
+ */
+ void reset();
+
+
+ inline int length() const {
+ return (int)m_bufferLen + (int)m_alreadyWritten;
+ }
+
+ inline int size() const {
+ return length();
+ }
+
+ /**
+ Sets the length of the file to n, padding
+ with 0's past the current end. Does not
+ change the position of the next byte to be
+ written unless n < size().
+
+ Throws char* when resetting a huge file to be shorter
+ than its current length.
+ */
+ inline void setLength(int n) {
+ n = n - (int)m_alreadyWritten;
+
+ if (n < 0) {
+ throw "Cannot resize huge files to be shorter.";
+ }
+
+ if (n < m_bufferLen) {
+ m_pos = n;
+ }
+ if (n > m_bufferLen) {
+ reserveBytes(n - m_bufferLen);
+ }
+ }
+
+ /**
+ Returns the current byte position in the file,
+ where 0 is the beginning and getLength() - 1 is the end.
+ */
+ inline int64 position() const {
+ return (int64)m_pos + (int64)m_alreadyWritten;
+ }
+
+
+ /**
+ Sets the position. Can set past length, in which case
+ the file is padded with zeros up to one byte before the
+ next to be written.
+
+ May throw a char* exception when seeking backwards on a huge file.
+ */
+ inline void setPosition(int64 p) {
+ p = p - (int64)m_alreadyWritten;
+
+ if (p > m_bufferLen) {
+ setLength((int)(p + (int64)m_alreadyWritten));
+ }
+
+ if (p < 0) {
+ throw "Cannot seek more than 10 MB backwards on huge files.";
+ }
+
+ m_pos = (int)p;
+ }
+
+
+ void writeBytes(
+ const void* b,
+ int count) {
+
+ reserveBytes(count);
+ debugAssert(m_pos >= 0);
+ debugAssert(m_bufferLen >= count);
+ System::memcpy(m_buffer + m_pos, b, count);
+ m_pos += count;
+ }
+
+ /**
+ Writes a signed 8-bit integer to the current position.
+ */
+ inline void writeInt8(int8 i) {
+ reserveBytes(1);
+ m_buffer[m_pos] = *(uint8*)&i;
+ m_pos++;
+ }
+
+ inline void writeBool8(bool b) {
+ writeInt8(b ? 1 : 0);
+ }
+
+ inline void writeUInt8(uint8 i) {
+ reserveBytes(1);
+ m_buffer[m_pos] = i;
+ m_pos++;
+ }
+
+ void writeUInt16(uint16 u);
+
+ inline void writeInt16(int16 i) {
+ writeUInt16(*(uint16*)&i);
+ }
+
+ void writeUInt32(uint32 u);
+
+ inline void writeInt32(int32 i) {
+ debugAssert(m_beginEndBits == 0);
+ writeUInt32(*(uint32*)&i);
+ }
+
+ void writeUInt64(uint64 u);
+
+ inline void writeInt64(int64 i) {
+ writeUInt64(*(uint64*)&i);
+ }
+
+ inline void writeFloat32(float32 f) {
+ debugAssert(m_beginEndBits == 0);
+ union {
+ float32 a;
+ uint32 b;
+ };
+ a = f;
+ writeUInt32(b);
+ }
+
+ inline void writeFloat64(float64 f) {
+ union {
+ float64 a;
+ uint64 b;
+ };
+ a = f;
+ writeUInt64(b);
+ }
+
+ /**
+ Write a string with NULL termination.
+ */
+ inline void writeString(const std::string& s) {
+ writeString(s.c_str());
+ }
+
+ void writeString(const char* s);
+
+ /**
+ Write a string, ensuring that the total length
+ including NULL is even.
+ */
+ void writeStringEven(const std::string& s) {
+ writeStringEven(s.c_str());
+ }
+
+ void writeStringEven(const char* s);
+
+
+ void writeString32(const char* s);
+
+ /**
+ Write a string with a 32-bit length field in front
+ of it.
+ */
+ void writeString32(const std::string& s) {
+ writeString32(s.c_str());
+ }
+
+ void writeVector4(const Vector4& v);
+
+ void writeVector3(const Vector3& v);
+
+ void writeVector2(const Vector2& v);
+
+ void writeColor4(const Color4& v);
+
+ void writeColor3(const Color3& v);
+
+ /**
+ Skips ahead n bytes.
+ */
+ inline void skip(int n) {
+ if (m_pos + n > m_bufferLen) {
+ setLength((int)m_pos + (int)m_alreadyWritten + n);
+ }
+ m_pos += n;
+ }
+
+ /** Call before a series of BinaryOutput::writeBits calls. Only writeBits
+ can be called between beginBits and endBits without corrupting the stream.*/
+ void beginBits();
+
+ /** Write numBits from bitString to the output stream. Bits are numbered from
+ low to high.
+
+ Can only be
+ called between beginBits and endBits. Bits written are semantically
+ little-endian, regardless of the actual endian-ness of the system. That is,
+ <CODE>writeBits(0xABCD, 16)</CODE> writes 0xCD to the first byte and
+ 0xAB to the second byte. However, if used with BinaryInput::readBits, the ordering
+ is transparent to the caller.
+ */
+ void writeBits(uint32 bitString, int numBits);
+
+ /** Call after a series of BinaryOutput::writeBits calls. This will
+ finish out with zeros the last byte into which bits were written.*/
+ void endBits();
+
+
+# define DECLARE_WRITER(ucase, lcase)\
+ void write##ucase(const lcase* out, int n);\
+ void write##ucase(const std::vector<lcase>& out, int n);\
+ void write##ucase(const Array<lcase>& out, int n);
+
+ DECLARE_WRITER(Bool8, bool)
+ DECLARE_WRITER(UInt8, uint8)
+ DECLARE_WRITER(Int8, int8)
+ DECLARE_WRITER(UInt16, uint16)
+ DECLARE_WRITER(Int16, int16)
+ DECLARE_WRITER(UInt32, uint32)
+ DECLARE_WRITER(Int32, int32)
+ DECLARE_WRITER(UInt64, uint64)
+ DECLARE_WRITER(Int64, int64)
+ DECLARE_WRITER(Float32, float32)
+ DECLARE_WRITER(Float64, float64)
+# undef DECLARE_WRITER
+
+};
+
+}
+
+#ifdef _MSC_VER
+# pragma warning (pop)
+#endif
+
+#endif
diff --git a/externals/g3dlite/G3D.lib/include/G3D/BoundsTrait.h b/externals/g3dlite/G3D.lib/include/G3D/BoundsTrait.h
new file mode 100644
index 00000000000..31525dab73b
--- /dev/null
+++ b/externals/g3dlite/G3D.lib/include/G3D/BoundsTrait.h
@@ -0,0 +1,20 @@
+/**
+ @file BoundsTrait.h
+
+ @maintainer Morgan McGuire, morgan@cs.williams.edu
+ @created 2008-10-01
+ @edited 2008-10-01
+ Copyright 2000-2008, Morgan McGuire.
+ All rights reserved.
+ */
+
+#ifndef G3D_BOUNDSTRAIT_H
+#define G3D_BOUNDSTRAIT_H
+
+#include "G3D/platform.h"
+
+template<typename Value>
+struct BoundsTrait{};
+
+#endif
+
diff --git a/externals/g3dlite/G3D.lib/include/G3D/Box.h b/externals/g3dlite/G3D.lib/include/G3D/Box.h
new file mode 100644
index 00000000000..83e06a2f069
--- /dev/null
+++ b/externals/g3dlite/G3D.lib/include/G3D/Box.h
@@ -0,0 +1,193 @@
+/**
+ @file Box.h
+
+ Box class
+
+ @maintainer Morgan McGuire, matrix@graphics3d.com
+
+ @cite Portions based on Dave Eberly's Magic Software Library at <A HREF="http://www.magic-software.com">http://www.magic-software.com</A>
+ @created 2001-06-02
+ @edited 2007-06-05
+
+ Copyright 2000-2006, Morgan McGuire.
+ All rights reserved.
+ */
+
+#ifndef G3D_BOX_H
+#define G3D_BOX_H
+
+#include "G3D/platform.h"
+#include "G3D/Vector3.h"
+#include "G3D/Array.h"
+#include "G3D/Plane.h"
+
+namespace G3D {
+
+class CoordinateFrame;
+
+/**
+ An arbitrary 3D box, useful as a bounding box.
+
+
+ To construct a box from a coordinate frame, center and extent, use the idiom:
+
+ <CODE>Box box = cframe.toObjectSpace(Box(center - extent/2, center + extent/2));</CODE>
+ */
+class Box {
+private:
+
+ static int32 dummy;
+
+ friend class CoordinateFrame;
+
+ /**
+ <PRE>
+ 3 2 7 6
+
+ 0 1 4 5
+
+ front back (seen through front)
+ </PRE>
+ */
+ Vector3 _corner[8];
+
+ /**
+ Unit axes.
+ */
+ Vector3 _axis[3];
+
+ Vector3 _center;
+
+ /**
+ Extent along each axis.
+ */
+ Vector3 _extent;
+
+ float _area;
+ float _volume;
+
+ void init(
+ const Vector3& min,
+ const Vector3& max);
+
+public:
+
+ /**
+ Does not initialize the fields.
+ */
+ Box();
+
+ /**
+ Constructs a box from two opposite corners.
+ */
+ Box(
+ const Vector3& min,
+ const Vector3& max);
+
+ static Box inf();
+
+ Box(class BinaryInput& b);
+
+ Box(const class AABox& b);
+
+ void serialize(class BinaryOutput& b) const;
+ void deserialize(class BinaryInput& b);
+
+ /**
+ Returns the object to world transformation for
+ this box. localFrame().worldToObject(...) takes
+ objects into the space where the box axes are
+ (1,0,0), (0,1,0), (0,0,1). Note that there
+ is no scaling in this transformation.
+ */
+ CoordinateFrame localFrame() const;
+
+ void getLocalFrame(CoordinateFrame& frame) const;
+
+ /**
+ Returns the centroid of the box.
+ */
+ inline Vector3 center() const {
+ return _center;
+ }
+
+
+ inline Vector3 corner(int i) const {
+ debugAssert(i < 8);
+ return _corner[i];
+ }
+
+ /**
+ Unit length.
+ */
+ inline Vector3 axis(int a) const {
+ debugAssert(a < 3);
+ return _axis[a];
+ }
+
+ /**
+ Distance from corner(0) to the next corner
+ along the box's local axis a.
+ */
+ inline float extent(int a) const {
+ debugAssert(a < 3);
+ return (float)_extent[a];
+ }
+
+ inline Vector3 extent() const {
+ return _extent;
+ }
+
+ /**
+ Returns the four corners of a face (0 <= f < 6).
+ The corners are returned to form a counter clockwise quad facing outwards.
+ */
+ void getFaceCorners(
+ int f,
+ Vector3& v0,
+ Vector3& v1,
+ Vector3& v2,
+ Vector3& v3) const;
+
+
+ /**
+ See AABox::culledBy
+ */
+ bool culledBy(
+ const Array<Plane>& plane,
+ int32& cullingPlaneIndex,
+ const uint32 testMask,
+ uint32& childMask) const;
+
+ /**
+ Conservative culling test that does not produce a mask for children.
+ */
+ bool culledBy(
+ const Array<Plane>& plane,
+ int32& cullingPlaneIndex = dummy,
+ const uint32 testMask = -1) const;
+
+ bool contains(
+ const Vector3& point) const;
+
+ float area() const;
+
+ float volume() const;
+
+ void getRandomSurfacePoint(Vector3& P, Vector3& N = Vector3::dummy) const;
+
+ /**
+ Uniformly distributed on the interior (includes surface)
+ */
+ Vector3 randomInteriorPoint() const;
+
+ void getBounds(class AABox&) const;
+
+ bool isFinite() const {
+ return G3D::isFinite(_volume);
+ }
+};
+
+}
+
+#endif
diff --git a/externals/g3dlite/G3D.lib/include/G3D/Capsule.h b/externals/g3dlite/G3D.lib/include/G3D/Capsule.h
new file mode 100644
index 00000000000..237dcdab69d
--- /dev/null
+++ b/externals/g3dlite/G3D.lib/include/G3D/Capsule.h
@@ -0,0 +1,90 @@
+/**
+ @file Capsule.h
+
+ @maintainer Morgan McGuire, matrix@graphics3d.com
+
+ @created 2003-02-07
+ @edited 2005-08-20
+
+ Copyright 2000-2006, Morgan McGuire.
+ All rights reserved.
+ */
+
+#ifndef G3D_CAPSULE_H
+#define G3D_CAPSULE_H
+
+#include "G3D/platform.h"
+#include "G3D/g3dmath.h"
+#include "G3D/Vector3.h"
+
+namespace G3D {
+
+class Line;
+class AABox;
+/**
+ A shape formed by extruding a sphere along a line segment.
+ */
+class Capsule {
+private:
+ Vector3 p1;
+ Vector3 p2;
+
+ float _radius;
+public:
+
+
+ /** Uninitialized */
+ Capsule();
+ Capsule(class BinaryInput& b);
+ Capsule(const Vector3& _p1, const Vector3& _p2, float _r);
+ void serialize(class BinaryOutput& b) const;
+ void deserialize(class BinaryInput& b);
+
+ /** The line down the center of the capsule */
+ Line axis() const;
+
+ inline float radius() const {
+ return _radius;
+ }
+
+ /** Argument may be 0 or 1 */
+ inline Vector3 point(int i) const {
+ debugAssert(i == 0 || i == 1);
+ return (i == 0) ? p1 : p2;
+ }
+
+ /** Distance between the sphere centers. The total extent of the cylinder is
+ 2r + h. */
+ inline float height() const {
+ return (p1 - p2).magnitude();
+ }
+
+ inline Vector3 center() const {
+ return (p1 + p2) / 2.0;
+ }
+
+ /** Get a reference frame in which the center of mass is the origin and Y is the axis of the capsule.*/
+ void getReferenceFrame(class CoordinateFrame& cframe) const;
+
+ /**
+ Returns true if the point is inside the capsule or on its surface.
+ */
+ bool contains(const Vector3& p) const;
+
+ float volume() const;
+
+ float area() const;
+
+ /** Get axis aligned bounding box */
+ void getBounds(AABox& out) const;
+
+ /** Random world space point with outward facing normal. */
+ void getRandomSurfacePoint(Vector3& P, Vector3& N) const;
+
+ /** Point selected uniformly at random over the volume. */
+ Vector3 randomInteriorPoint() const;
+};
+
+} // namespace
+
+#endif
diff --git a/externals/g3dlite/G3D.lib/include/G3D/CollisionDetection.h b/externals/g3dlite/G3D.lib/include/G3D/CollisionDetection.h
new file mode 100644
index 00000000000..62f92c18d33
--- /dev/null
+++ b/externals/g3dlite/G3D.lib/include/G3D/CollisionDetection.h
@@ -0,0 +1,1178 @@
+/**
+ @file CollisionDetection.h
+
+
+ Moving collision detection for simple primitives.
+
+ @author Morgan McGuire, matrix@graphics3d.com
+ @cite Spherical collision 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 intersection by Eric Haines.
+ Box-Box intersection written by Kevin Egan.
+ Thanks to Max McGuire of Iron Lore for various bug fixes.
+
+ @created 2001-11-19
+ @edited 2006-01-10
+
+ Copyright 2000-2006, Morgan McGuire.
+ All rights reserved.
+ */
+
+#ifndef G3D_COLLISIONDETECTION_H
+#define G3D_COLLISIONDETECTION_H
+
+#include "G3D/platform.h"
+#include "G3D/Vector3.h"
+#include "G3D/Plane.h"
+#include "G3D/Box.h"
+#include "G3D/Triangle.h"
+#include "G3D/Array.h"
+#include "G3D/Ray.h"
+#include "G3D/Line.h"
+
+namespace G3D {
+
+
+/**
+ Collision detection primitives and tools for building
+ higher order collision detection schemes.
+
+ These routines provide <I>moving</I> and static collision detection.
+ Moving collision detection allows the calculation of collisions that
+ occur during a period of time -- as opposed to the intersection of
+ two static bodies.
+
+ Moving collision detection routines detect collisions between
+ <I>only</I> static primitives and moving spheres or points. Since the
+ reference frame can be user defined, these functions can be used to
+ detect the collision between two moving bodies by subtracting
+ the velocity vector of one object from the velocity vector of the
+ sphere or point the detection is to occur with. This unified
+ velocity vector will act as if both objects are moving simultaneously.
+
+ Collisions are detected for single-sided objects only. That is,
+ no collision is detected when <I>leaving</I> a primitive or passing
+ through a plane or triangle opposite the normal... except for the
+ point-sphere calculation or when otherwise noted.
+
+ For a sphere, the collision location returned is the point in world
+ space where the surface of the sphere and the fixed object meet.
+ It is <B>not</B> the position of the center of the sphere at
+ the time of the collision.
+
+ The collision normal returned is the surface normal to the fixed
+ object at the collision location.
+
+ <p>
+ <b>Static Collision Detection:</b> (Neither object is moving)
+
+ <table>
+ <tr><td></td><td><b>Vector3</b></td><td><b>LineSegment</b></td><td><b>Ray *</b></td><td><b>Line</b></td><td><b>Plane</b></td><td><b>Triangle</b></td><td><b>Sphere</b></td><td><b>Cylinder</b></td><td><b>Capsule</b></td><td><b>AABox</b></td><td><b>Box</b></td></tr>
+ <tr><td><b>Vector3</b></td><td>Vector3::operator== Vector3::fuzzyEq G3D::distance</td><td bgcolor=#C0C0C0 colspan=10 ></td></tr>
+ <tr><td><b>LineSegment</b></td><td>LineSegment::closestPoint LineSegment::distance CollisionDetection::closestPointOnLineSegment</td><td></td><td bgcolor=#C0C0C0 colspan=9 ></td></tr>
+ <tr><td><b>Ray *</b></td><td>Ray::closestPoint Ray::distance</td><td></td><td></td><td bgcolor=#C0C0C0 colspan=8 ></td></tr>
+ <tr><td><b>Line</b></td><td>Line::closestPoint Line::distance</td><td></td><td>CollisionDetection::closestPointsBetweenLineAndLine</td><td></td><td bgcolor=#C0C0C0 colspan=7 ></td></tr>
+ <tr><td><b>Plane</b></td><td></td><td></td><td></td><td></td><td></td><td bgcolor=#C0C0C0 colspan=6 ></td></tr>
+ <tr><td><b>Triangle</b></td><td></td><td></td><td></td><td></td><td></td><td></td><td bgcolor=#C0C0C0 colspan=5 ></td></tr>
+ <tr><td><b>Sphere</b></td><td>Sphere::contains</td><td></td><td></td><td></td><td></td><td></td><td></td><td bgcolor=#C0C0C0 colspan=4 ></td></tr>
+ <tr><td><b>Cylinder</b></td><td>Cylinder::contains</td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td bgcolor=#C0C0C0 colspan=3 ></td></tr>
+ <tr><td><b>Capsule</b></td><td>Capsule::contains</td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td bgcolor=#C0C0C0 colspan=2 ></td></tr>
+ <tr><td><b>AABox</b></td><td>AABox::contains</td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td bgcolor=#C0C0C0 colspan=1 ></td></tr>
+ <tr><td><b>Box</b></td><td>Box::contains</td><td>(treat as Ray)</td><td>CollisionDetection::collisionTimeForMovingPointFixedBox</td><td>(treat as Ray)</td><td>CollisionDetection::penetrationDepthForFixedBoxFixedPlane</td><td>CollisionDetection::penetrationDepthForFixedBoxFixedPlane</td><td>CollisionDetection::penetrationDepthForFixedSphereFixedBox</td><td>None (use OPCODE)</td><td>CollisionDetection::movingSpherePassesThroughFixedBox</td><td>CollisionDetection::penetrationDepthForFixedBoxFixedBox</td><td>CollisionDetection::penetrationDepthForFixedBoxFixedBox</td></tr>
+ </table>
+
+ <p>
+ <b>Moving Collision Detection:</b>
+
+ <i>* Note: Moving collision detection against certain primitives is equivalent to static collision
+ detection against a bigger primitive. Ray, Line Segment == ``moving Point''; Capsule ==``moving Sphere''; Plane == ``moving Line''</i>
+ */
+class CollisionDetection {
+private:
+
+ /**
+ Default parameter if value passed to a function as reference is
+ not to be calculated. Must be explicitly supported by function.
+ */
+ static Vector3 ignore;
+
+ /**
+ Default parameter if value passed to a function as reference is
+ not to be calculated. Must be explicitly supported by function.
+ */
+ static bool ignoreBool;
+
+ /**
+ Default parameter if value passed to a function as reference is
+ not to be calculated. Must be explicitly supported by function.
+ */
+ static Array<Vector3> ignoreArray;
+
+
+ // Static class!
+ CollisionDetection() {}
+ virtual ~CollisionDetection() {}
+
+public:
+
+ /**
+ Converts an index [0, 15] to the corresponding separating axis.
+ Does not return normalized vector in the edge-edge case
+ (indices 6 through 15).
+
+ @param separatingAxisIndex Separating axis.
+ @param box1 Box 1.
+ @param box2 Box 2.
+
+ @return Axis that separates the two boxes.
+ */
+ static Vector3 separatingAxisForSolidBoxSolidBox(
+ const int separatingAxisIndex,
+ const Box & box1,
+ const Box & box2);
+
+ /**
+ Tests whether two boxes have axes that are parallel to
+ each other. If they are, axis1 and axis2 are set to be
+ the parallel axes for both box1 and box2 respectively.
+
+ @param ca Dot products of each of the boxes axes
+ @param epsilon Fudge factor (small unit by which the dot
+ products may vary and still be considered
+ zero).
+ @param axis1 Parallel Axis 1. [Post Condition]
+ @param axis2 Parallel Axis 2. [Post Condition]
+
+ @return true - If boxes have a parallel axis
+ @return false - otherwise.
+ */
+ static bool parallelAxisForSolidBoxSolidBox(
+ const double* ca,
+ const double epsilon,
+ int & axis1,
+ int & axis2);
+
+ /**
+ Calculates the projected distance between the two boxes along
+ the specified separating axis, negative distances correspond
+ to an overlap along that separating axis. The distance is not
+ divided by denominator dot(L, L), see
+ penetrationDepthForFixedSphereFixedBox() for more details
+
+ @param separatingAxisIndex
+ @param a Box 1's bounding sphere vector
+ @param b Box 2's bounding sphere vector
+ @param D Vector between Box 1 and Box 2's center points
+ @param c Pointer to array of dot products of the axes of Box 1
+ and Box 2.
+ @param ca Pointer to array of unsigned dot products of the axes
+ of Box 1 and Box 2.
+ @param ad Pointer to array of dot products of Box 1 axes and D.
+ @param bd Pointer to array of dot products of Box 2 axes and D.
+
+ @return Projected distance between the two boxes along the
+ specified separating axis.
+ */
+ static float 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);
+
+
+ /**
+ Creates a set of standard information about two boxes in order to
+ solve for their collision. This information includes a vector to
+ the radius of the bounding sphere for each box, the vector between
+ each boxes' center and a series of dot products between differing
+ important vectors. These dot products include those between the axes
+ of both boxes (signed and unsigned values), and the dot products
+ between all the axes of box1 and the boxes' center vector and box2
+ and the boxes' center vector.
+
+ @pre The following space requirements must be met:
+ - c[] 9 elements
+ - ca[] 9 elements
+ - ad[] 3 elements
+ - bd[] 3 elements
+
+ @cite dobted from David Eberly's papers, variables used in this function
+ correspond to variables used in pages 6 and 7 in the pdf
+ http://www.magic-software.com/Intersection.html
+ http://www.magic-software.com/Documentation/DynamicCollisionDetection.pdf
+
+ @note Links are out-dated. (Kept to preserve origin and authorship)
+
+ @param box1 Box 1
+ @param box2 Box 2
+ @param a Box 1's bounding sphere vector
+ @param b Box 2's bounding sphere vector
+ @param D Vector between Box 1 and Box 2's center points
+ @param c Pointer to array of dot products of the axes of Box 1
+ and Box 2.
+ @param ca Pointer to array of unsigned dot products of the axes
+ of Box 1 and Box 2.
+ @param ad Pointer to array of dot products of Box 1 axes and D.
+ @param bd Pointer to array of dot products of Box 2 axes and D.
+ */
+ static void fillSolidBoxSolidBoxInfo(
+ const Box & box1,
+ const Box & box2,
+ Vector3 & a,
+ Vector3 & b,
+ Vector3 & D,
+ double* c,
+ double* ca,
+ double* ad,
+ double* bd);
+
+ /**
+ Performs a simple bounding sphere check between two boxes to determine
+ whether these boxes could <i>possibly</i> intersect. This is a very
+ cheap operation (three dot products, two sqrts and a few others). If
+ it returns true, an intersection is possible, but not necessarily
+ guaranteed.
+
+ @param a Vector from box A's center to an outer vertex
+ @param b Vector from box B's center to an outer vertex
+ @param D Distance between the centers of the two boxes
+
+ @return true - if possible intersection
+ @return false - otherwise (This does not guarantee an intersection)
+ */
+ static bool conservativeBoxBoxTest(
+ const Vector3 & a,
+ const Vector3 & b,
+ const Vector3 & D);
+
+ /**
+ Determines whether two fixed solid boxes intersect.
+
+ @note To speed up collision detection, the lastSeparatingAxis from
+ the previous time step can be passed in and that plane can be
+ checked first. If the separating axis was not saved, or if the
+ two boxes intersected then lastSeparatingAxis should equal -1.
+
+ @cite Adobted from David Eberly's papers, variables used in this function
+ correspond to variables used in pages 6 and 7 in the pdf
+ http://www.magic-software.com/Intersection.html
+ http://www.magic-software.com/Documentation/DynamicCollisionDetection.pdf
+
+ @param box1 Box 1.
+ @param box2 Box 2.
+ @param lastSeparatingAxis Last separating axis.
+ (optimization - see note)
+
+ @return true - Intersection.
+ @return false - otherwise.
+ */
+ static bool fixedSolidBoxIntersectsFixedSolidBox(
+ const Box& box1,
+ const Box& box2,
+ const int lastSeparatingAxis = -1);
+
+ /**
+ Calculates the closest points on two lines with each other. If the
+ lines are parallel then using the starting point, else calculate the
+ closest point on each line to the other.
+
+ @note This is very similiar to calculating the intersection of two lines.
+ Logically then, the two points calculated would be identical if calculated
+ with inifinite precision, but with the finite precision of floating point
+ calculations, these values could (will) differ as the line slope approaches
+ zero or inifinity.
+
+ @cite variables and algorithm based on derivation at the following website:
+ http://softsurfer.com/Archive/algorithm_0106/algorithm_0106.htm
+
+ @param line1 Line 1.
+ @param line2 Line 2.
+ @param closest1 Closest point on line 1.
+ @param closest2 Closest point on line 2.
+ */
+ static void closestPointsBetweenLineAndLine(
+ const Line & line1,
+ const Line & line2,
+ Vector3 & closest1,
+ Vector3 & closest2);
+
+ /**
+ Calculates the depth of penetration between two fixed boxes.
+ Contact normal faces away from box1 and into box2. If there is
+ contact, only one contact point is returned. The minimally
+ violated separating plane is computed
+ - if the separating axis corresponds to a face
+ the contact point is half way between the deepest vertex
+ and the face
+ - if the separating axis corresponds to two edges
+ the contact point is the midpoint of the smallest line
+ segment between the two edge lines
+
+ @note This is very similiar to calculating the intersection of two lines.
+ Logically then, the two points calculated would be identical if calculated
+ with inifinite precision, but with the finite precision of floating point
+ calculations, these values could (will) differ as the line slope approaches
+ zero or inifinity.
+
+ @cite adobted from David Eberly's papers, variables used in this function
+ correspond to variables used in pages 6 and 7 in the pdf
+ http://www.magic-software.com/Intersection.html
+ http://www.magic-software.com/Documentation/DynamicCollisionDetection.pdf
+
+ @param box1 Box 1
+ @param box2 Box 2
+ @param contactPoints Contact point between boxes. [Post Condition]
+ @param contactNormals Surface normal at contact point. [Post Condition]
+ @param lastSeparatingAxis Last separating axis. (Used for optimization)
+
+ @return Depth of penetration between the two boxes. If there is no
+ intersection between the boxes, then a negative value is returned.
+ */
+ static float penetrationDepthForFixedBoxFixedBox(
+ const Box& box1,
+ const Box& box2,
+ Array<Vector3>& contactPoints,
+ Array<Vector3>& contactNormals,
+ const int lastSeparatingAxis = -1);
+
+ /**
+ Calculates the depth of penetration between two fixed spheres as well
+ as the deepest point of Sphere A that penetrates Sphere B. The normal
+ returned points <B>away</B> from the object A, although it may
+ represent a perpendicular to either the faces of object B or object A
+ depending on their relative orientations.
+
+ @param sphereA Fixed Sphere A.
+ @param sphereB Fixed Sphere B.
+ @param contactPoints Sphere A's deepest point that penetrates Sphere B.
+ [Post Condition]
+ @param contactNormals Normal at penetration point. [Post Condition]
+
+ @return Depth of penetration. If there is no intersection between the
+ objects then the depth will be a negative value.
+ */
+ static float penetrationDepthForFixedSphereFixedSphere(
+ const class Sphere& sphereA,
+ const Sphere& sphereB,
+ Array<Vector3>& contactPoints,
+ Array<Vector3>& contactNormals = ignoreArray);
+
+ /**
+ Calculates the depth of penetration between a fixed sphere and a fixed
+ box as well as the deepest point of the sphere that penetrates the box
+ and the normal at that intersection.
+
+ @note There are three possible intersections between a sphere and box.
+ - Sphere completely contained in the box
+ - Sphere intersects one edge
+ - Sphere intersects one vertex
+
+ The contact point and contact normal vary for each of these situations.
+ - Sphere contained in Box:
+ - Normal is based on side of least penetration (as is the depth calculation).
+ - Point is based on center of sphere
+ - Sphere intersects one edge
+ - Normal is based on vector from the box center to the point of depth.
+ - Point is closest point to the sphere on the line
+ - Sphere intersects one vertex
+ - Normal is based on vector from the box center to the vertex of penetration.
+ - Point is vertex of penetration.
+
+ @cite Adapted from Jim Arvo's method in Graphics Gems
+ See also http://www.win.tue.nl/~gino/solid/gdc2001depth.pdf
+
+ @param sphere Fixed Sphere.
+ @param box Fixed Box.
+ @param contactPoints Sphere point that penetrates the box. [Post Condition]
+ @param contactNormals Normal at the penetration point. [Post Condition]
+
+ @return Depth of penetration. If there is no intersection between the
+ objects then the depth will be a negative value.
+ */
+ static float penetrationDepthForFixedSphereFixedBox(
+ const Sphere& sphere,
+ const Box& box,
+ Array<Vector3>& contactPoints,
+ Array<Vector3>& contactNormals = ignoreArray);
+
+ /**
+ Calculates the depth of penetration between a Fixed Sphere and a Fixed
+ Plane as well as the deepest point of the sphere that penetrates the plane
+ and the plane normal at that intersection.
+
+ @param sphere Fixed Sphere.
+ @param plane Fixed Plane.
+ @param contactPoints Sphere point that penetrates the plane.
+ [Post Condition]
+ @param contactNormals Normal at penetration point. [Post Condition]
+
+ @return Depth of penetration. If there is no intersection between the
+ objects then the depth will be a negative value.
+ */
+ static float penetrationDepthForFixedSphereFixedPlane(
+ const Sphere& sphereA,
+ const class Plane& planeB,
+ Array<Vector3>& contactPoints,
+ Array<Vector3>& contactNormals = ignoreArray);
+
+ /**
+ Calculates the depth of penetration between a fixed box and a fixed
+ plane as well as the vertexes of the box that penetrate the plane
+ and the plane normals at those intersections.
+
+ @param box Fixed Box.
+ @param plane Fixed Plane.
+ @param contactPoints Box points that penetrate the plane.
+ [Post Condition]
+ @param contactNormals Normals at penetration points [Post Condition]
+
+ @return Depth of penetration. If there is no intersection between the
+ objects then the depth will be a negative value.
+ */
+ static float penetrationDepthForFixedBoxFixedPlane(
+ const Box& box,
+ const Plane& plane,
+ Array<Vector3>& contactPoints,
+ Array<Vector3>& contactNormals = ignoreArray);
+
+ /**
+ Calculates time between the intersection of a moving point and a fixed
+ plane.
+
+ @note This is only a one sided collision test. The side defined by
+ the plane's surface normal is the only one tested. For a two sided
+ collision, call the function once for each side's surface normal.
+
+ @param point Moving point.
+ @param velocity Point's velocity.
+ @param plane Fixed plane.
+ @param location Location of collision. [Post Condition]
+ (Infinite vector on no collision)
+ @param outNormal Plane's surface normal. [Post Condition]
+
+ @return Time til collision. If there is no collision then the return
+ value will be inf().
+ */
+ static float collisionTimeForMovingPointFixedPlane(
+ const Vector3& point,
+ const Vector3& velocity,
+ const class Plane& plane,
+ Vector3& outLocation,
+ Vector3& outNormal = ignore);
+
+ /**
+ Calculates time between the intersection of a moving point and a fixed
+ triangle.
+
+ @note This is only a one sided collision test. The side defined by
+ the triangle's surface normal is the only one tested. For a two sided
+ collision, call the function once for each side's surface normal.
+
+ @param orig Moving point.
+ @param dir Point's velocity.
+ @param v0 Triangle vertex 1.
+ @param v1 Triangle vertex 2.
+ @param v2 Triangle vertex 3
+ @param location Location of collision. [Post Condition]
+ (Infinite vector on no collision)
+
+ @return Time til collision. If there is no collision then the return
+ value will be inf().
+ */
+ inline static float collisionTimeForMovingPointFixedTriangle(
+ const Vector3& orig,
+ const Vector3& dir,
+ const Vector3& v0,
+ const Vector3& v1,
+ const Vector3& v2) {
+ return Ray::fromOriginAndDirection(orig, dir).intersectionTime(v0, v1, v2);
+ }
+
+ /**
+ Calculates time between the intersection of a moving point and a fixed
+ triangle.
+
+ @note This is only a one sided collision test. The side defined by
+ the triangle's surface normal is the only one tested. For a two sided
+ collision, call the function once for each side's surface normal.
+
+ @param orig Moving point.
+ @param dir Point's velocity.
+ @param v0 Triangle vertex 1.
+ @param v1 Triangle vertex 2.
+ @param v2 Triangle vertex 3
+ @param location Location of collision. [Post Condition]
+ (Infinite vector on no collision)
+
+ @return Time til collision. If there is no collision then the return
+ value will be inf().
+ */
+ inline static float collisionTimeForMovingPointFixedTriangle(
+ const Vector3& orig,
+ const Vector3& dir,
+ const Vector3& v0,
+ const Vector3& v1,
+ const Vector3& v2,
+ Vector3& location) {
+ float t = collisionTimeForMovingPointFixedTriangle(orig, dir, v0, v1, v2);
+ if (t < inf()) {
+ location = orig + dir * t;
+ }
+ return t;
+ }
+
+ /**
+ Calculates time between the intersection of a moving point and a fixed
+ triangle.
+
+ @note This is only a one sided collision test. The side defined by
+ the triangle's surface normal is the only one tested. For a two sided
+ collision, call the function once for each side's surface normal.
+
+ @param orig Moving point.
+ @param dir Point's velocity.
+ @param tri Fixed triangle.
+ @param location Location of collision. [Post Condition]
+ (Infinite vector on no collision)
+ @param normal Triangle's surface normal. [Post Condition]
+
+ @return Time til collision. If there is no collision then the return
+ value will be inf().
+ */
+ inline static float collisionTimeForMovingPointFixedTriangle(
+ const Vector3& orig,
+ const Vector3& dir,
+ const Triangle& tri,
+ Vector3& location = ignore,
+ Vector3& normal = ignore) {
+
+ float t = collisionTimeForMovingPointFixedTriangle(
+ orig, dir, tri.vertex(0), tri.vertex(1), tri.vertex(2));
+
+ if ((t < inf()) && (&location != &ignore)) {
+ location = orig + dir * t;
+ normal = tri.normal();
+ }
+ return t;
+ }
+
+ /**
+ Calculates time between the intersection of a moving point and a fixed
+ triangle.
+
+ @note This is only a one sided collision test. The side defined by
+ the triangle's surface normal is the only one tested. For a two sided
+ collision, call the function once for each side's surface normal.
+
+ @param orig Moving point.
+ @param dir Point's velocity.
+ @param v0 Triangle vertex 1.
+ @param v1 Triangle vertex 2.
+ @param v2 Triangle vertex 3
+ @param location Location of collision. [Post Condition]
+ (Infinite vector on no collision)
+ @param normal Triangle's surface normal. [Post Condition]
+
+ @return Time til collision. If there is no collision then the return
+ value will be inf().
+ */
+ inline static float collisionTimeForMovingPointFixedTriangle(
+ const Vector3& orig,
+ const Vector3& dir,
+ const Vector3& v0,
+ const Vector3& v1,
+ const Vector3& v2,
+ Vector3& location,
+ Vector3& normal) {
+ float t = collisionTimeForMovingPointFixedTriangle(orig, dir, v0, v1, v2);
+ if (t < inf()) {
+ location = orig + dir * t;
+ normal = (v2 - v0).cross(v1 - v0).direction();
+ }
+ return t;
+ }
+
+ /**
+ Unlike other methods, does not support an output normal.
+ If the ray origin is inside the box, returns inf() but inside
+ is set to true.
+ <B>Beta API</B>
+
+ @cite Andrew Woo, from "Graphics Gems", Academic Press, 1990
+ @cite Optimized code by Pierre Terdiman, 2000 (~20-30% faster on my Celeron 500)
+ @cite Epsilon value added by Klaus Hartmann
+ @cite http://www.codercorner.com/RayAABB.cpp
+ */
+ static float collisionTimeForMovingPointFixedAABox(
+ const Vector3& point,
+ const Vector3& velocity,
+ const class AABox& box,
+ Vector3& outLocation,
+ bool& inside = ignoreBool,
+ Vector3& outNormal = ignore);
+
+ /**
+ Calculates time between the intersection of a moving point and a fixed
+ Axis-Aligned Box (AABox).
+
+ @note Avoids the sqrt from collisionTimeForMovingPointFixedAABox.
+
+ @param point Moving point.
+ @param velocity Sphere's velocity.
+ @param box Fixed AAbox.
+ @param location Location of collision. [Post Condition]
+ @param Inside Does the ray originate inside the box? [Post Condition]
+ @param normal Box's surface normal to collision [Post Condition]
+
+ @return Time til collision. If there is no collision then the return
+ value will be inf().
+ */
+ static bool collisionLocationForMovingPointFixedAABox(
+ const Vector3& point,
+ const Vector3& velocity,
+ const class AABox& box,
+ Vector3& outLocation,
+ bool& inside = ignoreBool,
+ Vector3& normal = ignore);
+
+ /**
+ Calculates time between the intersection of a moving point and a fixed
+ sphere.
+
+ @note When ray is starts inside the rectangle, the exiting intersection
+ is detected.
+
+ @param point Moving point.
+ @param velocity Point's velocity.
+ @param Sphere Fixed Sphere.
+ @param location Location of collision. [Post Condition]
+ @param outNormal Sphere's surface normal to collision [Post Condition]
+
+ @return Time til collision. If there is no collision then the return
+ value will be inf().
+ */
+ static float collisionTimeForMovingPointFixedSphere(
+ const Vector3& point,
+ const Vector3& velocity,
+ const class Sphere& sphere,
+ Vector3& outLocation,
+ Vector3& outNormal = ignore);
+
+ /**
+ Calculates time between the intersection of a moving point and a fixed
+ box.
+
+ @note If the point is already inside the box, no collision: inf is returned.
+
+ @param point Moving point.
+ @param velocity Sphere's velocity.
+ @param box Fixed box.
+ @param location Position of collision. [Post Condition]
+ @param outNormal Box's surface normal to collision [Post Condition]
+
+ @return Time til collision. If there is no collision then the return
+ value will be inf().
+ */
+ static float collisionTimeForMovingPointFixedBox(
+ const Vector3& point,
+ const Vector3& velocity,
+ const class Box& box,
+ Vector3& outLocation,
+ Vector3& outNormal = ignore);
+
+ /**
+ Calculates time between the intersection of a moving point and a fixed
+ rectangle defined by the points v0, v1, v2, & v3.
+
+ @note This is only a one sided collision test. The side defined by
+ the rectangle's surface normal is the only one tested. For a two sided
+ collision, call the function once for each side's surface normal.
+
+ @param point Moving point.
+ @param velocity Sphere's velocity.
+ @param v0 Rectangle vertex 1.
+ @param v1 Rectangle vertex 2.
+ @param v2 Rectangle vertex 3
+ @param v3 Rectangle vertex 4.
+ @param location Location of collision [Post Condition]
+ @param outNormal Rectangle's surface normal. [Post Condition]
+
+ @return Time til collision. If there is no collision then the return
+ value will be inf().
+ */
+ static float collisionTimeForMovingPointFixedRectangle(
+ const Vector3& point,
+ const Vector3& velocity,
+ const Vector3& v0,
+ const Vector3& v1,
+ const Vector3& v2,
+ const Vector3& v3,
+ Vector3& outLocation,
+ Vector3& outNormal = ignore);
+
+ /**
+ Calculates time between the intersection of a moving point and a fixed
+ capsule.
+
+ @param point Moving point.
+ @param velocity Point's velocity.
+ @param capsule Fixed capsule.
+ @param location Location of collision. [Post Condition]
+ @param outNormal Capsule's surface normal to collision [Post Condition]
+
+ @return Time til collision. If there is no collision then the return
+ value will be inf().
+ */
+ static float collisionTimeForMovingPointFixedCapsule(
+ const Vector3& point,
+ const Vector3& velocity,
+ const class Capsule& capsule,
+ Vector3& outLocation,
+ Vector3& outNormal = ignore);
+
+ /**
+ Calculates time between the intersection of a moving sphere and a fixed
+ triangle.
+
+ @param sphere Moving sphere.
+ @param velocity Sphere's velocity.
+ @param plane Fixed Plane.
+ @param location Location of collision -- not center position of sphere
+ at the collision time. [Post Condition]
+ @param outNormal Box's surface normal to collision [Post Condition]
+
+ @return Time til collision. If there is no collision then the return
+ value will be inf().
+ */
+ static float collisionTimeForMovingSphereFixedPlane(
+ const class Sphere& sphere,
+ const Vector3& velocity,
+ const class Plane& plane,
+ Vector3& outLocation,
+ Vector3& outNormal = ignore);
+
+ /**
+ Calculates time between the intersection of a moving sphere and a fixed
+ triangle.
+
+ @param sphere Moving sphere.
+ @param velocity Sphere's velocity.
+ @param triangle Fixed Triangle. (collisions can happen on the back side of the triangle)
+ @param outLocation Location of collision, if collision occurs -- not center position of sphere
+ at the collision time. If there is interpenetration at the start, this point may be inside
+ the sphere.
+ @param b Barycentric coordinates. These are not valid unless collision occurs.
+
+ @return Time til collision. If there is no collision then the return
+ value will be inf().
+ */
+ static float collisionTimeForMovingSphereFixedTriangle(
+ const class Sphere& sphere,
+ const Vector3& velocity,
+ const Triangle& triangle,
+ Vector3& outLocation,
+ float b[3] = (float*)&ignore);
+
+ /**
+ Calculates time between the intersection of a moving sphere and a fixed
+ rectangle defined by the points v0, v1, v2, & v3.
+
+ @param sphere Moving sphere.
+ @param velocity Sphere's velocity.
+ @param v0 Rectangle vertex 1.
+ @param v1 Rectangle vertex 2.
+ @param v2 Rectangle vertex 3
+ @param v3 Rectangle vertex 4.
+ @param location Location of collision -- not center position of sphere
+ at the collision time. [Post Condition]
+ @param outNormal Box's surface normal to collision [Post Condition]
+
+ @return Time til collision. If there is no collision then the return
+ value will be inf().
+ */
+ static float collisionTimeForMovingSphereFixedRectangle(
+ const class Sphere& sphere,
+ const Vector3& velocity,
+ const Vector3& v0,
+ const Vector3& v1,
+ const Vector3& v2,
+ const Vector3& v3,
+ Vector3& outLocation,
+ Vector3& outNormal = ignore);
+
+ /**
+ Calculates time between the intersection of a moving sphere and a fixed
+ box.
+
+ @note This function will not detect an intersection between a moving object
+ that is already interpenetrating the fixed object.
+
+ @param sphere Moving sphere.
+ @param velocity Sphere's velocity.
+ @param box Fixed box.
+ @param location Location of collision -- not center position of sphere
+ at the collision time. [Post Condition]
+ @param outNormal Box's surface normal to collision [Post Condition]
+
+ @return Time til collision. If there is no collision then the return
+ value will be inf().
+ */
+ static float collisionTimeForMovingSphereFixedBox(
+ const class Sphere& sphere,
+ const Vector3& velocity,
+ const class Box& box,
+ Vector3& outLocation,
+ Vector3& outNormal = ignore);
+
+ /**
+ Calculates time between the intersection of a moving sphere and a fixed
+ sphere.
+
+ @note This won't detect a collision if the sphere is already interpenetrating
+ the fixed sphere.
+
+ @param movingSphere Moving sphere.
+ @param velocity Sphere's velocity.
+ @param fixedSphere Fixed Sphere.
+ @param location Location of collision -- not center position of sphere
+ at the collision time. [Post Condition]
+ @param outNormal Sphere's surface normal to collision [Post Condition]
+
+ @return Time til collision. If there is no collision then the return
+ value will be inf().
+ */
+ static float collisionTimeForMovingSphereFixedSphere(
+ const class Sphere& sphere,
+ const Vector3& velocity,
+ const class Sphere& fixedSphere,
+ Vector3& outLocation,
+ Vector3& outNormal = ignore);
+
+ /**
+ Calculates time between the intersection of a moving sphere and a fixed
+ capsule.
+
+ @note This won't detect a collision if the sphere is already
+ interpenetrating the capsule.
+
+ @param sphere Moving sphere.
+ @param velocity Sphere's velocity.
+ @param capsule Fixed capsule.
+ @param location Location of collision -- not center position of sphere
+ at the collision time. [Post Condition]
+ @param outNormal Capsule's surface normal to the collision [Post Condition]
+
+ @return Time til collision. If there is no collision then the return
+ value will be inf().
+ */
+ static float collisionTimeForMovingSphereFixedCapsule(
+ const class Sphere& sphere,
+ const Vector3& velocity,
+ const class Capsule& capsule,
+ Vector3& outLocation,
+ Vector3& outNormal = ignore);
+
+ /**
+ Finds the direction of bounce that a sphere would have when it
+ intersects an object with the given time of collision, the
+ collision location and the collision normal.
+
+ @note This function works like a pong style ball bounce.
+
+ @param sphere Moving sphere.
+ @param velocity Sphere's velocity.
+ @param collisionTime Time of collision.
+ @param collisionLocation Collision location.
+ @param collisionNormal Surface collision normal.
+
+ @return Direction of bounce.
+ */
+ static Vector3 bounceDirection(
+ const class Sphere& sphere,
+ const Vector3& velocity,
+ const float collisionTime,
+ const Vector3& collisionLocation,
+ const Vector3& collisionNormal);
+
+ /**
+ Finds the direction of slide given a moving sphere, its velocity, the
+ time of collision and the collision location. This function works as
+ if the sphere intersects the surface and continues to hug it.
+
+ @note The result will work well for calculating the movement of a player
+ who collides with an object and continues moving along the object instead
+ of just bouncing off it.
+
+ @param sphere Moving sphere.
+ @param velocity Sphere's velocity.
+ @param collisionTime Time of collision
+ @param collisionLocation Collision location.
+
+ @return Direction of slide.
+ */
+ static Vector3 slideDirection(
+ const class Sphere& sphere,
+ const Vector3& velocity,
+ const float collisionTime,
+ const Vector3& collisionLocation);
+
+ /**
+ Finds the closest point on a line segment to a given point.
+
+ @param v0 line vertex 1.
+ @param v1 line vertex 2.
+ @param point External point.
+
+ @return Closests point to <code>point</code> on the line segment.
+ */
+ static Vector3 closestPointOnLineSegment(
+ const Vector3& v0,
+ const Vector3& v1,
+ const Vector3& point);
+
+ /**
+ Finds the closest point on a line segment to a given point.
+
+ @note This is an optimization to closestPointOnLineSegment. Edge length
+ and direction can be used in this function if already pre-calculated. This
+ prevents doing the same work twice.
+
+ @param v0 line vertex 0.
+ @param v1 line vertex 1.
+ @param edgeDirection The direction of the segment (unit length).
+ @param edgeLength The length of the segment.
+ @param point External point.
+
+ @return Closests point to <code>point</code> on the line segment.
+ */
+ static Vector3 closestPointOnLineSegment(
+ const Vector3& v0,
+ const Vector3& v1,
+ const Vector3& edgeDirection,
+ float edgeLength,
+ const Vector3& point);
+
+ /**
+ Finds the closest point on the perimeter of the triangle to an external point;
+ given a triangle defined by three points v0, v1, & v2, and the external point.
+
+ @param v0 Triangle vertex 0.
+ @param v1 Triangle vertex 1.
+ @param v2 Triangle vertex 2.
+ @param point External point.
+
+ @return Closests point to <code>point</code> on the perimeter of the
+ triangle.
+ */
+ static Vector3 closestPointOnTrianglePerimeter(
+ const Vector3& v0,
+ const Vector3& v1,
+ const Vector3& v2,
+ const Vector3& point);
+
+ /**
+ Finds the closest point on the perimeter of the triangle to an external point;
+ given a triangle defined by the array of points v, its edge directions and
+ their lengths, as well as the external point.
+
+ @note This is an optimization to closestPointToTrianglePerimeter. Edge length
+ and direction can be used in this function if already pre-calculated. This
+ prevents doing the same work twice.
+
+ @param v0 Triangle vertex 0.
+ @param v1 Triangle vertex 1.
+ @param v2 Triangle vertex 2.
+ @param point External point.
+ @param edgeIndex The point lies on the edge between v[edgeIndex] and v[(edgeIndex + 1) % 3]
+
+ @return Closests point to <code>point</code> on the perimeter of the
+ triangle.
+ */
+ static Vector3 closestPointOnTrianglePerimeter(
+ const Vector3 v[3],
+ const Vector3 edgeDirection[3],
+ const float edgeLength[3],
+ const Vector3& point,
+ int& edgeIndex);
+
+ /**
+ Tests whether a point is contained within the triangle defined by
+ v0, v1, and v2 and its plane's normal.
+
+ @param v0 Triangle vertex 0.
+ @param v1 Triangle vertex 1.
+ @param v2 Triangle vertex 2.
+ @param normal Normal to triangle's plane.
+ @param point The point in question.
+ @param primaryAxis Primary axis of triangle. This will be detected
+ if not given. This parameter is provided as an optimization.
+ @param b Barycentric coordinates; b[i] is the weight on v[i]
+
+ @return true - if point is inside the triangle.
+ @return false - otherwise
+ */
+ static bool isPointInsideTriangle(
+ const Vector3& v0,
+ const Vector3& v1,
+ const Vector3& v2,
+ const Vector3& normal,
+ const Vector3& point,
+ float b[3],
+ Vector3::Axis primaryAxis = Vector3::DETECT_AXIS);
+
+ inline static bool isPointInsideTriangle(
+ const Vector3& v0,
+ const Vector3& v1,
+ const Vector3& v2,
+ const Vector3& normal,
+ const Vector3& point,
+ Vector3::Axis primaryAxis = Vector3::DETECT_AXIS) {
+
+ float b[3];
+ return isPointInsideTriangle(v0, v1, v2, normal, point, b, primaryAxis);
+ }
+
+ /**
+ Tests for the intersection of a moving sphere and a fixed box in a
+ given time limit.
+
+ @note Returns true if any part of the sphere is inside the box
+ during the time period (inf means "ever"). Useful for
+ performing bounding-box collision detection.
+
+ @param sphere Moving sphere.
+ @param velocity Velocity of moving sphere.
+ @param box Fixed box.
+ @param timeLimit Time limit for intersection test.
+
+ @return true - if the two objects will touch.
+ @return false - if there is no intersection.
+ */
+ static bool movingSpherePassesThroughFixedBox(
+ const Sphere& sphere,
+ const Vector3& velocity,
+ const Box& box,
+ double timeLimit = inf());
+
+ /**
+ Tests for the intersection of a moving sphere and a fixed sphere in a
+ given time limit.
+
+ @note This function will not detect an intersection between a moving object
+ that is already interpenetrating the fixed object.
+
+ @param sphere Moving sphere.
+ @param velocity Velocity of moving sphere.
+ @param fixedSphere Fixed sphere.
+ @param timeLimit Time limit for intersection test.
+
+ @return true - if the two spheres will touch.
+ @return false - if there is no intersection.
+ */
+ static bool movingSpherePassesThroughFixedSphere(
+ const Sphere& sphere,
+ const Vector3& velocity,
+ const Sphere& fixedSphere,
+ double timeLimit = inf());
+
+ /**
+ Tests for the intersection of two fixed spheres.
+
+ @param sphere1 Fixed sphere 1.
+ @param sphere2 Fixed sphere 2.
+
+ @return true - if the two spheres touch.
+ @return false - if there is no intersection.
+ */
+ static bool fixedSolidSphereIntersectsFixedSolidSphere(
+ const Sphere& sphere1,
+ const Sphere& sphere2);
+
+ /**
+ Tests for the intersection of a fixed sphere and a fixed box.
+
+ @param sphere Fixed sphere.
+ @param box Fixed box.
+
+ @return true - if the two objects touch.
+ @return false - if there is no intersection.
+ */
+ static bool fixedSolidSphereIntersectsFixedSolidBox(
+ const Sphere& sphere,
+ const Box& box);
+
+ static bool fixedSolidSphereIntersectsFixedTriangle(
+ const Sphere& sphere,
+ const Triangle& triangle);
+
+ /**
+ Tests whether a point is inside a rectangle defined by the vertexes
+ v0, v1, v2, & v3, and the rectangle's plane normal.
+
+ @param v0 Rectangle vertex 1.
+ @param v1 Rectangle vertex 2.
+ @param v2 Rectangle vertex 3.
+ @param v3 Rectangle vertex 4.
+ @param normal Normal to rectangle's plane.
+ @param point The point in question.
+
+ @return true - if point is inside the rectangle.
+ @return false - otherwise
+ */
+ static bool isPointInsideRectangle(
+ const Vector3& v0,
+ const Vector3& v1,
+ const Vector3& v2,
+ const Vector3& v3,
+ const Vector3& normal,
+ const Vector3& point);
+
+ /**
+ Finds the closest point on the perimeter of the rectangle to an
+ external point; given a rectangle defined by four points v0, v1,
+ v2, & v3, and the external point.
+
+ @param v0 Rectangle vertex 1.
+ @param v1 Rectangle vertex 2.
+ @param v2 Rectangle vertex 3.
+ @param v3 Rectangle vertex 4.
+ @param point External point.
+
+ @return Closests point to <code>point</code> on the perimeter of the
+ rectangle.
+ */
+ static Vector3 closestPointToRectanglePerimeter(
+ const Vector3& v0,
+ const Vector3& v1,
+ const Vector3& v2,
+ const Vector3& v3,
+ const Vector3& point);
+
+ /**
+ Finds the closest point in the rectangle to an external point; Given
+ a rectangle defined by four points v0, v1, v2, & v3, and the external
+ point.
+
+ @param v0 Rectangle vertex 1.
+ @param v1 Rectangle vertex 2.
+ @param v2 Rectangle vertex 3
+ @param v3 Rectangle vertex 4.
+ @param point External point.
+
+ @return Closet point in the rectangle to the external point.
+ */
+ static Vector3 closestPointToRectangle(
+ const Vector3& v0,
+ const Vector3& v1,
+ const Vector3& v2,
+ const Vector3& v3,
+ const Vector3& point);
+};
+
+} // namespace
+
+#endif // G3D_COLLISIONDETECTION_H
diff --git a/externals/g3dlite/G3D.lib/include/G3D/Color1.h b/externals/g3dlite/G3D.lib/include/G3D/Color1.h
new file mode 100644
index 00000000000..1958da2b6cd
--- /dev/null
+++ b/externals/g3dlite/G3D.lib/include/G3D/Color1.h
@@ -0,0 +1,129 @@
+/**
+ @file Color1.h
+
+ Monochrome Color class
+
+ @maintainer Morgan McGuire, morgan@cs.williams.edu
+ @created 2007-01-31
+ @edited 2008-10-02
+
+ Copyright 2000-2008, Morgan McGuire.
+ All rights reserved.
+ */
+
+#ifndef G3D_COLOR1_H
+#define G3D_COLOR1_H
+
+#include "G3D/platform.h"
+#include "G3D/g3dmath.h"
+#include "G3D/HashTrait.h"
+#include <string>
+
+namespace G3D {
+
+/**
+ Monochrome color. This is just a float, but it has nice semantics
+ because a scaling by 255 automatically occurs when switching between
+ fixed point (Color1uint8) and floating point (Color1) formats.
+ */
+class Color1 {
+private:
+ // Hidden operators
+ bool operator<(const Color1&) const;
+ bool operator>(const Color1&) const;
+ bool operator<=(const Color1&) const;
+ bool operator>=(const Color1&) const;
+
+public:
+ float value;
+
+ /**
+ Initializes to 0
+ */
+ inline Color1() : value(0) {}
+
+ Color1(class BinaryInput& bi);
+
+ inline explicit Color1(float v) : value(v) {
+ }
+
+ Color1 (const class Color1uint8& other);
+
+ void serialize(class BinaryOutput& bo) const;
+ void deserialize(class BinaryInput& bi);
+
+ Color1 operator+ (const Color1& other) const {
+ return Color1(value + other.value);
+ }
+
+ Color1 operator+ (const float other) const {
+ return Color1(value + other);
+ }
+
+ Color1& operator+= (const Color1 other) {
+ value += other.value;
+ return *this;
+ }
+
+ Color1& operator-= (const Color1 other) {
+ value -= other.value;
+ return *this;
+ }
+
+ Color1 operator- (const Color1& other) const {
+ return Color1(value - other.value);
+ }
+
+ Color1 operator- (const float other) const {
+ return Color1(value - other);
+ }
+
+ Color1 operator- () const {
+ return Color1(-value);
+ }
+
+ Color1 operator* (const Color1& other) const {
+ return Color1(value * other.value);
+ }
+
+ Color1 operator* (const float other) const {
+ return Color1(value * other);
+ }
+
+ Color1 operator/ (const Color1& other) const {
+ return Color1(value / other.value);
+ }
+
+ Color1 operator/ (const float other) const {
+ return Color1(value / other);
+ }
+
+ inline Color1 max(const Color1& other) const {
+ return Color1(G3D::max(value, other.value));
+ }
+
+ inline Color1 min(const Color1& other) const {
+ return Color1(G3D::min(value, other.value));
+ }
+
+ inline Color1 lerp(const Color1& other, float a) const {
+ return Color1(value + (other.value - value) * a);
+
+ }
+
+ inline size_t hashCode() const {
+ return (size_t)(value * 0xFFFFFF);
+ }
+};
+
+}
+
+template <>
+struct HashTrait<G3D::Color1> {
+ static size_t hashCode(const G3D::Color1& key) {
+ return key.hashCode();
+ }
+};
+
+
+#endif
diff --git a/externals/g3dlite/G3D.lib/include/G3D/Color1uint8.h b/externals/g3dlite/G3D.lib/include/G3D/Color1uint8.h
new file mode 100644
index 00000000000..ffaa2d43ac4
--- /dev/null
+++ b/externals/g3dlite/G3D.lib/include/G3D/Color1uint8.h
@@ -0,0 +1,107 @@
+/**
+ @file Color1uint8.h
+
+ @maintainer Morgan McGuire, graphics3d.com
+
+ @created 2007-01-30
+ @edited 2007-01-30
+
+ Copyright 2000-2007, Morgan McGuire.
+ All rights reserved.
+ */
+
+#ifndef G3D_COLOR1UINT8_H
+#define G3D_COLOR1UINT8_H
+
+#include "G3D/platform.h"
+#include "G3D/g3dmath.h"
+
+namespace G3D {
+
+#if defined(G3D_WIN32)
+ // Switch to tight alignment
+ #pragma pack(push, 1)
+#endif
+
+
+/**
+ Represents a Color1 as a packed integer. Convenient
+ for creating unsigned int vertex arrays.
+
+ <B>WARNING</B>: Integer color formats are different than
+ integer vertex formats. The color channels are automatically
+ scaled by 255 (because OpenGL automatically scales integer
+ colors back by this factor). So Color3(1,1,1) == Color3uint8(255,255,255)
+ but Vector3(1,1,1) == Vector3int16(1,1,1).
+
+ <B>Note</B>:
+ Conversion of a float32 to uint8 is accomplished by min(iFloor(f * 256)) and
+ back to float32 by u / 255.0f. This gives equal size intervals.
+Consider a number line from 0 to 1 and a corresponding one from 0 to 255. If we use iRound(x * 255), then the mapping for three critical intervals are:
+
+<pre>
+let s = 0.5/255
+ float int size
+[0, s) -> 0 s
+[s, s * 3) -> 1 2*s
+(1 - s, 1] -> 255 s
+</pre>
+
+If we use max(floor(x * 256), 255), then we get:
+
+<pre>
+let s = 1/256
+ float int size
+[0, s) -> 0 s
+[s, 2 * s) -> 1 s
+(1 - s, 1] -> 255 s
+</PRE>
+and the intervals are all the same size, thus giving equal precision to all values.
+ */
+class Color1uint8 {
+private:
+ // Hidden operators
+ bool operator<(const Color1uint8&) const;
+ bool operator>(const Color1uint8&) const;
+ bool operator<=(const Color1uint8&) const;
+ bool operator>=(const Color1uint8&) const;
+
+public:
+
+ uint8 value;
+
+ Color1uint8() : value(0) {}
+
+ explicit Color1uint8(const uint8 _v) : value(_v) {}
+
+ Color1uint8(const class Color1& c);
+
+ Color1uint8(class BinaryInput& bi);
+
+ void serialize(class BinaryOutput& bo) const;
+
+ void deserialize(class BinaryInput& bi);
+
+ inline bool operator==(const Color1uint8& other) const {
+ return value == other.value;
+ }
+
+ inline bool operator!=(const Color1uint8& other) const {
+ return value != other.value;
+ }
+
+}
+
+#if defined(G3D_LINUX) || defined(G3D_OSX)
+ __attribute((aligned(1)))
+#endif
+
+;
+
+#ifdef G3D_WIN32
+ #pragma pack(pop)
+#endif
+
+}
+
+#endif
diff --git a/externals/g3dlite/G3D.lib/include/G3D/Color3.h b/externals/g3dlite/G3D.lib/include/G3D/Color3.h
new file mode 100644
index 00000000000..b8d0f94829a
--- /dev/null
+++ b/externals/g3dlite/G3D.lib/include/G3D/Color3.h
@@ -0,0 +1,390 @@
+/**
+ @file Color3.h
+
+ Color class
+
+ @maintainer Morgan McGuire, matrix@graphics3d.com
+ @cite Portions based on Dave Eberly's Magic Software Library
+ at <A HREF="http://www.magic-software.com">http://www.magic-software.com</A>
+
+ @created 2001-06-02
+ @edited 2008-07-17
+
+ Copyright 2000-2008, Morgan McGuire.
+ All rights reserved.
+ */
+
+#ifndef G3D_COLOR3_H
+#define G3D_COLOR3_H
+
+#include "G3D/platform.h"
+#include "G3D/g3dmath.h"
+#include "G3D/HashTrait.h"
+#include "G3D/Color1.h"
+#include <string>
+
+namespace G3D {
+
+/**
+ Do not subclass-- this implementation makes assumptions about the
+ memory layout.
+ */
+class Color3 {
+private:
+ // Hidden operators
+ bool operator<(const Color3&) const;
+ bool operator>(const Color3&) const;
+ bool operator<=(const Color3&) const;
+ bool operator>=(const Color3&) const;
+
+public:
+ /**
+ Does not initialize fields.
+ */
+ Color3();
+
+ explicit Color3(class BinaryInput& bi);
+
+ Color3(float r, float g, float b);
+ Color3(float v) : r(v), g(v), b(v) {}
+
+ explicit Color3(const class Vector3& v);
+
+ explicit Color3(const float value[3]);
+
+ /**
+ Initialize from another color.
+ */
+ Color3 (const Color3& other);
+
+ Color3 (const class Color3uint8& other);
+
+ /**
+ Initialize from an HTML-style color (e.g. 0xFF0000 == RED)
+ */
+ static Color3 fromARGB(uint32);
+
+ /** Returns one of the color wheel colors (e.g. RED, GREEN, CYAN).
+ Does not include white, black, or gray. */
+ static const Color3& wheelRandom();
+
+ /**
+ * Channel value.
+ */
+ float r, g, b;
+
+ void serialize(class BinaryOutput& bo) const;
+ void deserialize(class BinaryInput& bi);
+
+ // access vector V as V[0] = V.r, V[1] = V.g, V[2] = V.b
+ //
+ // WARNING. These member functions rely on
+ // (1) Color3 not having virtual functions
+ // (2) the data packed in a 3*sizeof(float) memory block
+ const float& operator[] (int i) const;
+ float& operator[] (int i);
+
+ // assignment and comparison
+ Color3& operator= (const Color3& rkVector);
+ bool operator== (const Color3& rkVector) const;
+ bool operator!= (const Color3& rkVector) const;
+ size_t hashCode() const;
+
+ // arithmetic operations
+ Color3 operator+ (const Color3& rkVector) const;
+ Color3 operator- (const Color3& rkVector) const;
+ Color3 operator* (float fScalar) const;
+ Color3 operator* (const Color3& rkVector) const;
+ Color3 operator/ (float fScalar) const;
+ Color3 operator- () const;
+
+ // arithmetic updates
+ Color3& operator+= (const Color3& rkVector);
+ Color3& operator-= (const Color3& rkVector);
+ Color3& operator*= (const Color3& rkVector);
+ Color3& operator*= (float fScalar);
+ Color3& operator/= (float fScalar);
+
+ bool fuzzyEq(const Color3& other) const;
+ bool fuzzyNe(const Color3& other) const;
+
+ inline operator float* () {
+ return (float*)this;
+ }
+
+ operator const float* () const {
+ return (float*)this;
+ }
+
+ // vector operations
+ float length () const;
+ Color3 direction() const;
+ float squaredLength () const;
+ float dot (const Color3& rkVector) const;
+ float unitize (float fTolerance = 1e-06);
+ Color3 cross (const Color3& rkVector) const;
+ Color3 unitCross (const Color3& rkVector) const;
+
+ inline Color3 pow(const Color3& other) const {
+ return Color3(::pow(r, other.r), ::pow(g, other.g), ::pow(b, other.b));
+ }
+
+ inline Color3 pow(float other) const {
+ return Color3(::pow(r, other), ::pow(g, other), ::pow(b, other));
+ }
+
+ /**@return the largest component */
+ inline float max() const {
+ return G3D::max(G3D::max(r, g), b);
+ }
+
+ inline Color3 max(const Color3& other) const {
+ return Color3(G3D::max(r, other.r), G3D::max(g, other.g), G3D::max(b, other.b));
+ }
+
+ inline Color3 min(const Color3& other) const {
+ return Color3(G3D::min(r, other.r), G3D::min(g, other.g), G3D::min(b, other.b));
+ }
+
+ inline Color3 lerp(const Color3& other, float a) const {
+ return (*this) + (other - *this) * a;
+
+ }
+
+ inline float sum() const {
+ return r + g + b;
+ }
+
+ inline float average() const {
+ return sum() / 3.0f;
+ }
+
+
+ /**
+ * Converts from HSV to RGB , note: toHSV(fromHSV(_hsv)) may not be _hsv, if it is at a grey point or black point.
+ * The components of _hsv should lie in the unit interval.
+ * @cite Alvy Ray Smith SIGGRAPH 1978 "Color Gamut Transform Pairs"
+ **/
+ static Color3 fromHSV(const Vector3& _hsv);
+ static Vector3 toHSV(const Color3& _rgb);
+
+ /** Duplicates the matlab jet colormap maps [0,1] --> (r,g,b) where blue is close to 0 and red is close to 1. */
+ static Color3 jetColorMap(const float& val);
+
+ /** Returns colors with maximum saturation and value @param hue [0, 1]*/
+ static Color3 rainbowColorMap(float hue);
+
+ std::string toString() const;
+
+ /** Random unit vector */
+ static Color3 random();
+
+ // Special values.
+ // Intentionally not inlined: see Matrix3::identity() for details.
+ static const Color3& red();
+ static const Color3& green();
+ static const Color3& blue();
+ static const Color3& purple();
+ static const Color3& cyan();
+ static const Color3& yellow();
+ static const Color3& brown();
+ static const Color3& orange();
+ static const Color3& black();
+ static const Color3& gray();
+ static const Color3& white();
+
+ static const Color3& zero();
+ static const Color3& one();
+
+ inline Color3 bgr() const {
+ return Color3(b, g, r);
+ }
+};
+
+inline G3D::Color3 operator* (float s, const G3D::Color3& c) {
+ return c * s;
+}
+
+inline G3D::Color3 operator* (G3D::Color1& s, const G3D::Color3& c) {
+ return c * s.value;
+}
+
+inline G3D::Color3 operator* (const G3D::Color3& c, G3D::Color1& s) {
+ return c * s.value;
+}
+
+
+//----------------------------------------------------------------------------
+inline Color3::Color3 () {
+}
+
+//----------------------------------------------------------------------------
+
+inline Color3::Color3(float fX, float fY, float fZ) {
+ r = fX;
+ g = fY;
+ b = fZ;
+}
+
+//----------------------------------------------------------------------------
+inline Color3::Color3(const float afCoordinate[3]) {
+ r = afCoordinate[0];
+ g = afCoordinate[1];
+ b = afCoordinate[2];
+}
+
+//----------------------------------------------------------------------------
+inline Color3::Color3 (const Color3& rkVector) {
+ r = rkVector.r;
+ g = rkVector.g;
+ b = rkVector.b;
+}
+
+//----------------------------------------------------------------------------
+inline float& Color3::operator[] (int i) {
+ return ((float*)this)[i];
+}
+
+//----------------------------------------------------------------------------
+
+inline const float& Color3::operator[] (int i) const {
+ return ((float*)this)[i];
+}
+
+//----------------------------------------------------------------------------
+
+inline bool Color3::fuzzyEq(const Color3& other) const {
+ return G3D::fuzzyEq((*this - other).squaredLength(), 0);
+}
+
+//----------------------------------------------------------------------------
+
+inline bool Color3::fuzzyNe(const Color3& other) const {
+ return G3D::fuzzyNe((*this - other).squaredLength(), 0);
+}
+
+
+//----------------------------------------------------------------------------
+inline Color3& Color3::operator= (const Color3& rkVector) {
+ r = rkVector.r;
+ g = rkVector.g;
+ b = rkVector.b;
+ return *this;
+}
+
+//----------------------------------------------------------------------------
+inline bool Color3::operator== (const Color3& rkVector) const {
+ return ( r == rkVector.r && g == rkVector.g && b == rkVector.b );
+}
+
+//----------------------------------------------------------------------------
+inline bool Color3::operator!= (const Color3& rkVector) const {
+ return ( r != rkVector.r || g != rkVector.g || b != rkVector.b );
+}
+
+//----------------------------------------------------------------------------
+inline Color3 Color3::operator+ (const Color3& rkVector) const {
+ return Color3(r + rkVector.r, g + rkVector.g, b + rkVector.b);
+}
+
+//----------------------------------------------------------------------------
+inline Color3 Color3::operator- (const Color3& rkVector) const {
+ return Color3(r -rkVector.r, g - rkVector.g, b - rkVector.b);
+}
+
+//----------------------------------------------------------------------------
+inline Color3 Color3::operator* (float fScalar) const {
+ return Color3(fScalar*r, fScalar*g, fScalar*b);
+}
+
+//----------------------------------------------------------------------------
+inline Color3 Color3::operator* (const Color3& rkVector) const {
+ return Color3(r * rkVector.r, g * rkVector.g, b * rkVector.b);
+}
+
+//----------------------------------------------------------------------------
+inline Color3 Color3::operator- () const {
+ return Color3( -r, -g, -b);
+}
+
+//----------------------------------------------------------------------------
+inline Color3& Color3::operator+= (const Color3& rkVector) {
+ r += rkVector.r;
+ g += rkVector.g;
+ b += rkVector.b;
+ return *this;
+}
+
+//----------------------------------------------------------------------------
+inline Color3& Color3::operator-= (const Color3& rkVector) {
+ r -= rkVector.r;
+ g -= rkVector.g;
+ b -= rkVector.b;
+ return *this;
+}
+
+//----------------------------------------------------------------------------
+inline Color3& Color3::operator*= (float fScalar) {
+ r *= fScalar;
+ g *= fScalar;
+ b *= fScalar;
+ return *this;
+}
+
+//----------------------------------------------------------------------------
+inline Color3& Color3::operator*= (const Color3& rkVector) {
+ r *= rkVector.r;
+ g *= rkVector.g;
+ b *= rkVector.b;
+ return *this;
+}
+//----------------------------------------------------------------------------
+inline float Color3::squaredLength () const {
+ return r*r + g*g + b*b;
+}
+
+//----------------------------------------------------------------------------
+inline float Color3::length () const {
+ return sqrtf(r*r + g*g + b*b);
+}
+
+//----------------------------------------------------------------------------
+inline Color3 Color3::direction () const {
+ float lenSquared = r * r + g * g + b * b;
+
+ if (lenSquared != 1.0f) {
+ return *this / sqrtf(lenSquared);
+ } else {
+ return *this;
+ }
+}
+
+//----------------------------------------------------------------------------
+inline float Color3::dot (const Color3& rkVector) const {
+ return r*rkVector.r + g*rkVector.g + b*rkVector.b;
+}
+
+//----------------------------------------------------------------------------
+inline Color3 Color3::cross (const Color3& rkVector) const {
+ return Color3(g*rkVector.b - b*rkVector.g, b*rkVector.r - r*rkVector.b,
+ r*rkVector.g - g*rkVector.r);
+}
+
+//----------------------------------------------------------------------------
+inline Color3 Color3::unitCross (const Color3& rkVector) const {
+ Color3 kCross(g*rkVector.b - b*rkVector.g, b*rkVector.r - r*rkVector.b,
+ r*rkVector.g - g*rkVector.r);
+ kCross.unitize();
+ return kCross;
+}
+} // namespace
+
+
+template <> struct HashTrait<G3D::Color3> {
+ static size_t hashCode(const G3D::Color3& key) {
+ return key.hashCode();
+ }
+};
+
+
+#endif
diff --git a/externals/g3dlite/G3D.lib/include/G3D/Color3uint8.h b/externals/g3dlite/G3D.lib/include/G3D/Color3uint8.h
new file mode 100644
index 00000000000..20e225a0734
--- /dev/null
+++ b/externals/g3dlite/G3D.lib/include/G3D/Color3uint8.h
@@ -0,0 +1,127 @@
+/**
+ @file Color3uint8.h
+
+ @maintainer Morgan McGuire, graphics3d.com
+
+ @created 2003-04-07
+ @edited 2006-06-24
+
+ Copyright 2000-2006, Morgan McGuire.
+ All rights reserved.
+ */
+
+#ifndef G3D_COLOR3UINT8_H
+#define G3D_COLOR3UINT8_H
+
+#include "G3D/platform.h"
+#include "G3D/g3dmath.h"
+
+namespace G3D {
+
+/**
+ Represents a Color3 as a packed integer. Convenient
+ for creating unsigned int vertex arrays. Used by
+ G3D::GImage as the underlying format.
+
+ <B>WARNING</B>: Integer color formats are different than
+ integer vertex formats. The color channels are automatically
+ scaled by 255 (because OpenGL automatically scales integer
+ colors back by this factor). So Color3(1,1,1) == Color3uint8(255,255,255)
+ but Vector3(1,1,1) == Vector3int16(1,1,1).
+ */
+
+#if defined(G3D_WIN32)
+ // Switch to tight alignment
+ #pragma pack(push, 1)
+#endif
+
+class Color3uint8 {
+private:
+ // Hidden operators
+ bool operator<(const Color3uint8&) const;
+ bool operator>(const Color3uint8&) const;
+ bool operator<=(const Color3uint8&) const;
+ bool operator>=(const Color3uint8&) const;
+
+public:
+ uint8 r;
+ uint8 g;
+ uint8 b;
+
+ Color3uint8() : r(0), g(0), b(0) {}
+
+ Color3uint8(const uint8 _r, const uint8 _g, const uint8 _b) : r(_r), g(_g), b(_b) {}
+
+ Color3uint8(const class Color3& c);
+
+ Color3uint8(class BinaryInput& bi);
+
+ inline static Color3uint8 fromARGB(uint32 i) {
+ Color3uint8 c;
+ c.r = (i >> 16) & 0xFF;
+ c.g = (i >> 8) & 0xFF;
+ c.b = i & 0xFF;
+ return c;
+ }
+
+ inline Color3uint8 bgr() const {
+ return Color3uint8(b, g, r);
+ }
+
+ /**
+ Returns the color packed into a uint32
+ (the upper byte is 0xFF)
+ */
+ inline uint32 asUInt32() const {
+ return (0xFF << 24) + ((uint32)r << 16) + ((uint32)g << 8) + b;
+ }
+
+ void serialize(class BinaryOutput& bo) const;
+
+ void deserialize(class BinaryInput& bi);
+
+ // access vector V as V[0] = V.r, V[1] = V.g, V[2] = V.b
+ //
+ // WARNING. These member functions rely on
+ // (1) Color3 not having virtual functions
+ // (2) the data packed in a 3*sizeof(uint8) memory block
+ G3D::uint8& operator[] (int i) const;
+ operator G3D::uint8* ();
+ operator const G3D::uint8* () const;
+
+ bool operator==(const Color3uint8& other) const {
+ return (other.r == r) && (other.g == g) && (other.b == b);
+ }
+
+ bool operator!=(const Color3uint8& other) const {
+ return (other.r != r) && (other.g != g) && (other.b != b);
+ }
+}
+
+#if defined(G3D_LINUX) || defined(G3D_OSX)
+ __attribute((aligned(1)))
+#endif
+
+;
+
+#ifdef G3D_WIN32
+ #pragma pack(pop)
+#endif
+
+
+inline G3D::uint8& Color3uint8::operator[] (int i) const {
+ debugAssert((unsigned int)i < 3);
+ return ((G3D::uint8*)this)[i];
+}
+
+//----------------------------------------------------------------------------
+inline Color3uint8::operator G3D::uint8* () {
+ return (G3D::uint8*)this;
+}
+
+inline Color3uint8::operator const G3D::uint8* () const {
+ return (G3D::uint8*)this;
+}
+}
+
+#endif
diff --git a/externals/g3dlite/G3D.lib/include/G3D/Color4.h b/externals/g3dlite/G3D.lib/include/G3D/Color4.h
new file mode 100644
index 00000000000..6b1b3b4c4bb
--- /dev/null
+++ b/externals/g3dlite/G3D.lib/include/G3D/Color4.h
@@ -0,0 +1,324 @@
+/**
+ @file Color4.h
+
+ Color class
+
+ @maintainer Morgan McGuire, matrix@graphics3d.com
+ @cite Portions based on Dave Eberly's Magic Software Library
+ at <A HREF="http://www.magic-software.com">http://www.magic-software.com</A>
+
+ @created 2002-06-25
+ @edited 2008-07-16
+
+ Copyright 2000-2008, Morgan McGuire.
+ All rights reserved.
+ */
+
+#ifndef G3D_COLOR4_H
+#define G3D_COLOR4_H
+
+#include "G3D/platform.h"
+#include "G3D/g3dmath.h"
+#include "G3D/Color3.h"
+#include <string>
+
+namespace G3D {
+
+/**
+ Do not subclass-- this implementation makes assumptions about the
+ memory layout.
+ */
+class Color4 {
+private:
+ // Hidden operators
+ bool operator<(const Color4&) const;
+ bool operator>(const Color4&) const;
+ bool operator<=(const Color4&) const;
+ bool operator>=(const Color4&) const;
+
+public:
+
+ /**
+ * Does not initialize fields.
+ */
+ Color4 ();
+
+ Color4(const Color3& c3, float a = 1.0);
+
+ Color4(const class Color4uint8& c);
+
+ Color4(class BinaryInput& bi);
+
+ Color4(const class Vector4& v);
+
+ /**
+ * Initialize from G3D::Reals.
+ */
+ Color4(float r, float g, float b, float a = 1.0);
+
+ /**
+ * Initialize from array of G3D::Reals.
+ */
+ Color4(float value[4]);
+
+ /**
+ * Initialize from another color.
+ */
+ Color4(const Color4& other);
+
+ void serialize(class BinaryOutput& bo) const;
+ void deserialize(class BinaryInput& bi);
+
+ /**
+ Initialize from an HTML-style color (e.g. 0xFFFF0000 == RED)
+ */
+ static Color4 fromARGB(uint32);
+
+ /**
+ * Channel values.
+ */
+ float r, g, b, a;
+
+ inline Color3 rgb() const {
+ return Color3(r, g, b);
+ }
+
+ // access vector V as V[0] = V.r, V[1] = V.g, V[2] = V.b, v[3] = V.a
+ //
+ // WARNING. These member functions rely on
+ // (1) Color4 not having virtual functions
+ // (2) the data packed in a 3*sizeof(float) memory block
+ float& operator[] (int i) const;
+ operator float* ();
+ operator const float* () const;
+
+ // assignment and comparison
+ Color4& operator= (const Color4& rkVector);
+ bool operator== (const Color4& rkVector) const;
+ bool operator!= (const Color4& rkVector) const;
+ size_t hashCode() const;
+
+ // arithmetic operations
+ Color4 operator+ (const Color4& rkVector) const;
+ Color4 operator- (const Color4& rkVector) const;
+ Color4 operator* (float fScalar) const;
+ Color4 operator/ (float fScalar) const;
+ Color4 operator- () const;
+ friend Color4 operator* (double fScalar, const Color4& rkVector);
+
+ // arithmetic updates
+ Color4& operator+= (const Color4& rkVector);
+ Color4& operator-= (const Color4& rkVector);
+ Color4& operator*= (float fScalar);
+ Color4& operator/= (float fScalar);
+
+ bool fuzzyEq(const Color4& other) const;
+ bool fuzzyNe(const Color4& other) const;
+
+ std::string toString() const;
+
+ inline Color4 max(const Color4& other) const {
+ return Color4(G3D::max(r, other.r), G3D::max(g, other.g), G3D::max(b, other.b), G3D::max(a, other.a));
+ }
+
+ inline Color4 min(const Color4& other) const {
+ return Color4(G3D::min(r, other.r), G3D::min(g, other.g), G3D::min(b, other.b), G3D::min(a, other.a));
+ }
+
+ /** r + g + b + a */
+ inline float sum() const {
+ return r + g + b + a;
+ }
+
+ inline Color4 lerp(const Color4& other, float a) const {
+ return (*this) + (other - *this) * a;
+
+ }
+
+ // Special values.
+ // Intentionally not inlined: see Matrix3::identity() for details.
+ static const Color4& zero();
+ static const Color4& clear();
+
+ static const Color4& inf();
+
+ inline Color3 bgr() const {
+ return Color3(b, g, r);
+ }
+};
+
+/**
+ Extends the c3 with alpha = 1.0
+ */
+Color4 operator*(const Color3& c3, const Color4& c4);
+
+
+inline Color4 operator*(const Color3& c3, const Color4& c4) {
+ return Color4(c3.r * c4.r, c3.g * c4.g, c3.b * c4.b, c4.a);
+}
+
+//----------------------------------------------------------------------------
+
+inline Color4::Color4 () {
+ // For efficiency in construction of large arrays of vectors, the
+ // default constructor does not initialize the vector.
+}
+
+//----------------------------------------------------------------------------
+
+inline Color4::Color4(const Color3& c3, float a) {
+ r = c3.r;
+ g = c3.g;
+ b = c3.b;
+ this->a = a;
+}
+
+//----------------------------------------------------------------------------
+
+inline Color4::Color4(
+ float r,
+ float g,
+ float b,
+ float a) :
+ r(r), g(g), b(b), a(a) {
+}
+
+//----------------------------------------------------------------------------
+inline Color4::Color4 (float afCoordinate[4]) {
+ r = afCoordinate[0];
+ g = afCoordinate[1];
+ b = afCoordinate[2];
+ a = afCoordinate[3];
+}
+
+//----------------------------------------------------------------------------
+
+inline Color4::Color4(
+ const Color4& other) {
+
+ r = other.r;
+ g = other.g;
+ b = other.b;
+ a = other.a;
+}
+
+//----------------------------------------------------------------------------
+
+inline float& Color4::operator[] (int i) const {
+ return ((float*)this)[i];
+}
+
+//----------------------------------------------------------------------------
+inline Color4::operator float* () {
+ return (float*)this;
+}
+
+inline Color4::operator const float* () const {
+ return (float*)this;
+}
+
+//----------------------------------------------------------------------------
+
+inline bool Color4::fuzzyEq(const Color4& other) const {
+ Color4 dif = (*this - other);
+ return G3D::fuzzyEq(dif.r * dif.r + dif.g * dif.g + dif.b * dif.b + dif.a * dif.a, 0);
+}
+
+//----------------------------------------------------------------------------
+
+inline bool Color4::fuzzyNe(const Color4& other) const {
+ Color4 dif = (*this - other);
+ return G3D::fuzzyNe(dif.r * dif.r + dif.g * dif.g + dif.b * dif.b + dif.a * dif.a, 0);
+}
+
+
+//----------------------------------------------------------------------------
+inline Color4& Color4::operator= (const Color4& other) {
+ r = other.r;
+ g = other.g;
+ b = other.b;
+ a = other.a;
+ return *this;
+}
+
+//----------------------------------------------------------------------------
+
+inline bool Color4::operator== (const Color4& other) const {
+ return ( r == other.r && g == other.g && b == other.b && a == other.a);
+}
+
+//----------------------------------------------------------------------------
+
+inline bool Color4::operator!= (const Color4& other) const {
+ return ( r != other.r || g != other.g || b != other.b || a != other.a);
+}
+
+//----------------------------------------------------------------------------
+inline Color4 Color4::operator+ (const Color4& other) const {
+ return Color4(r + other.r, g + other.g, b + other.b, a + other.a);
+}
+
+//----------------------------------------------------------------------------
+inline Color4 Color4::operator- (const Color4& other) const {
+ return Color4(r - other.r, g - other.g, b - other.b, a - other.a);
+}
+
+//----------------------------------------------------------------------------
+
+inline Color4 Color4::operator* (float fScalar) const {
+ return Color4(fScalar * r, fScalar * g, fScalar * b, fScalar * a);
+}
+
+//----------------------------------------------------------------------------
+
+inline Color4 Color4::operator- () const {
+ return Color4(-r, -g, -b, -a);
+}
+
+//----------------------------------------------------------------------------
+
+inline Color4 operator* (float fScalar, const Color4& other) {
+ return Color4(fScalar * other.r, fScalar * other.g,
+ fScalar * other.b, fScalar * other.a);
+}
+
+//----------------------------------------------------------------------------
+
+inline Color4& Color4::operator+= (const Color4& other) {
+ r += other.r;
+ g += other.g;
+ b += other.b;
+ a += other.a;
+ return *this;
+}
+
+//----------------------------------------------------------------------------
+
+inline Color4& Color4::operator-= (const Color4& other) {
+ r -= other.r;
+ g -= other.g;
+ b -= other.b;
+ a -= other.a;
+ return *this;
+}
+
+//----------------------------------------------------------------------------
+
+inline Color4& Color4::operator*= (float fScalar) {
+ r *= fScalar;
+ g *= fScalar;
+ b *= fScalar;
+ a *= fScalar;
+ return *this;
+}
+
+} // namespace
+
+template <>
+struct HashTrait<G3D::Color4> {
+ static size_t hashCode(const G3D::Color4& key) {
+ return key.hashCode();
+ }
+};
+
+#endif
diff --git a/externals/g3dlite/G3D.lib/include/G3D/Color4uint8.h b/externals/g3dlite/G3D.lib/include/G3D/Color4uint8.h
new file mode 100644
index 00000000000..7804a6e1e51
--- /dev/null
+++ b/externals/g3dlite/G3D.lib/include/G3D/Color4uint8.h
@@ -0,0 +1,133 @@
+/**
+ @file Color4uint8.h
+
+ @maintainer Morgan McGuire, graphics3d.com
+
+ @created 2003-04-07
+ @edited 2006-03-24
+
+ Copyright 2000-2006, Morgan McGuire.
+ All rights reserved.
+ */
+
+#ifndef COLOR4UINT8_H
+#define COLOR4UINT8_H
+
+#include "G3D/g3dmath.h"
+#include "G3D/platform.h"
+#include "G3D/Color3uint8.h"
+
+namespace G3D {
+
+/**
+ Represents a Color4 as a packed integer. Convenient
+ for creating unsigned int vertex arrays. Used by
+ G3D::GImage as the underlying format.
+
+ <B>WARNING</B>: Integer color formats are different than
+ integer vertex formats. The color channels are automatically
+ scaled by 255 (because OpenGL automatically scales integer
+ colors back by this factor). So Color4(1,1,1) == Color4uint8(255,255,255)
+ but Vector3(1,1,1) == Vector3int16(1,1,1).
+
+ */
+
+#ifdef G3D_WIN32
+ // Switch to tight alignment
+ #pragma pack(push, 1)
+#endif
+
+class Color4uint8 {
+private:
+ // Hidden operators
+ bool operator<(const Color4uint8&) const;
+ bool operator>(const Color4uint8&) const;
+ bool operator<=(const Color4uint8&) const;
+ bool operator>=(const Color4uint8&) const;
+
+public:
+ uint8 r;
+ uint8 g;
+ uint8 b;
+ uint8 a;
+
+ Color4uint8() : r(0), g(0), b(0), a(0) {}
+
+ Color4uint8(const class Color4& c);
+
+ Color4uint8(const uint8 _r, const uint8 _g, const uint8 _b, const uint8 _a) : r(_r), g(_g), b(_b), a(_a) {}
+
+ Color4uint8(const Color3uint8& c, const uint8 _a) : r(c.r), g(c.g), b(c.b), a(_a) {}
+
+ Color4uint8(class BinaryInput& bi);
+
+ inline static Color4uint8 fromARGB(uint32 i) {
+ Color4uint8 c;
+ c.a = (i >> 24) & 0xFF;
+ c.r = (i >> 16) & 0xFF;
+ c.g = (i >> 8) & 0xFF;
+ c.b = i & 0xFF;
+ return c;
+ }
+
+ inline uint32 asUInt32() const {
+ return ((uint32)a << 24) + ((uint32)r << 16) + ((uint32)g << 8) + b;
+ }
+
+ // access vector V as V[0] = V.r, V[1] = V.g, V[2] = V.b
+ //
+ // WARNING. These member functions rely on
+ // (1) Color4uint8 not having virtual functions
+ // (2) the data packed in a 3*sizeof(uint8) memory block
+ G3D::uint8& operator[] (int i) const;
+ operator G3D::uint8* ();
+ operator const G3D::uint8* () const;
+
+
+ inline Color3uint8 bgr() const {
+ return Color3uint8(b, g, r);
+ }
+
+ void serialize(class BinaryOutput& bo) const;
+
+ void deserialize(class BinaryInput& bi);
+
+ inline Color3uint8 rgb() const {
+ return Color3uint8(r, g, b);
+ }
+
+ bool operator==(const Color4uint8& other) const {
+ return *reinterpret_cast<const uint32*>(this) == *reinterpret_cast<const uint32*>(&other);
+ }
+
+ bool operator!=(const Color4uint8& other) const {
+ return *reinterpret_cast<const uint32*>(this) != *reinterpret_cast<const uint32*>(&other);
+ }
+
+}
+#if defined(G3D_LINUX) || defined(G3D_OSX)
+ __attribute((aligned(1)))
+#endif
+;
+
+#ifdef G3D_WIN32
+ #pragma pack(pop)
+#endif
+
+
+inline G3D::uint8& Color4uint8::operator[] (int i) const {
+ return ((G3D::uint8*)this)[i];
+}
+
+//----------------------------------------------------------------------------
+inline Color4uint8::operator G3D::uint8* () {
+ return (G3D::uint8*)this;
+}
+
+inline Color4uint8::operator const G3D::uint8* () const {
+ return (G3D::uint8*)this;
+}
+
+}
+
+#endif
diff --git a/externals/g3dlite/G3D.lib/include/G3D/Cone.h b/externals/g3dlite/G3D.lib/include/G3D/Cone.h
new file mode 100644
index 00000000000..c09b9b6846c
--- /dev/null
+++ b/externals/g3dlite/G3D.lib/include/G3D/Cone.h
@@ -0,0 +1,68 @@
+/**
+ @file Cone.h
+
+ Cone class
+
+ @maintainer Morgan McGuire, matrix@graphics3d.com
+ @cite Portions based on Dave Eberly's Magic Software Library at <A HREF="http://www.magic-software.com">http://www.magic-software.com</A>
+
+ @created 2001-06-02
+ @edited 2006-02-23
+
+ Copyright 2000-2006, Morgan McGuire.
+ All rights reserved.
+ */
+
+#ifndef G3D_CONE_H
+#define G3D_CONE_H
+
+#include "G3D/platform.h"
+#include "G3D/g3dmath.h"
+#include "G3D/Vector3.h"
+
+namespace G3D {
+
+/**
+ An infinite cone.
+ */
+class Cone {
+
+private:
+ Vector3 tip;
+ Vector3 direction;
+
+ /** Angle from the center line to the edge. */
+ float angle;
+
+public:
+
+ /**
+ @param angle Angle from the center line to the edge, in radians
+ */
+ Cone(const Vector3& tip, const Vector3& direction, float angle);
+
+ /**
+ Forms the smallest cone that contains the box. Undefined if
+ the tip is inside or on the box.
+ */
+ Cone(const Vector3& tip, const class Box& box);
+
+ virtual ~Cone() {}
+
+ /**
+ Returns true if the cone touches, intersects, or contains b.
+
+ If c.intersects(s) and c.intersects(Sphere(s.center, s.radius * 2)
+ then the sphere s is entirely within cone c.
+ */
+ bool intersects(const class Sphere& s) const;
+
+ /**
+ True if v is a point inside the cone.
+ */
+ bool contains(const class Vector3& v) const;
+};
+
+} // namespace
+
+#endif
diff --git a/externals/g3dlite/G3D.lib/include/G3D/ConvexPolyhedron.h b/externals/g3dlite/G3D.lib/include/G3D/ConvexPolyhedron.h
new file mode 100644
index 00000000000..6ae9ba136ff
--- /dev/null
+++ b/externals/g3dlite/G3D.lib/include/G3D/ConvexPolyhedron.h
@@ -0,0 +1,179 @@
+/**
+ @file ConvexPolyhedron.h
+
+ @maintainer Morgan McGuire, matrix@graphics3d.com
+
+ @created 2001-11-11
+ @edited 2006-04-10
+
+ Copyright 2000-2006, Morgan McGuire.
+ All rights reserved.
+ */
+
+#ifndef G3D_CONVEXPOLYHEDRON_H
+#define G3D_CONVEXPOLYHEDRON_H
+
+#include "G3D/platform.h"
+#include "G3D/Vector3.h"
+#include "G3D/Vector2.h"
+#include "G3D/CoordinateFrame.h"
+#include "G3D/Plane.h"
+#include "G3D/Line.h"
+#include "G3D/Array.h"
+
+namespace G3D {
+
+class DirectedEdge {
+public:
+ Vector3 start;
+ Vector3 stop;
+};
+
+class ConvexPolygon {
+private:
+
+ friend class ConvexPolyhedron;
+
+ Array<Vector3> _vertex;
+
+public:
+
+ ConvexPolygon() {}
+ ConvexPolygon(const Array<Vector3>& __vertex);
+ virtual ~ConvexPolygon() {}
+
+ /**
+ Counter clockwise winding order.
+ */
+ inline const Vector3& vertex(int i) const {
+ return _vertex[i];
+ }
+
+ inline void setVertex(int i, const Vector3& v) {
+ _vertex[i] = v;
+ }
+
+ /**
+ Zero vertices indicates an empty polygon (zero area).
+ */
+ inline int numVertices() const {
+ return _vertex.size();
+ }
+
+ inline void setNumVertices(int n) {
+ _vertex.resize(n);
+ }
+
+ /**
+ O(n) in the number of edges
+ */
+ bool isEmpty() const;
+
+ /**
+ Cuts the polygon at the plane. If the polygon is entirely above or below
+ the plane, one of the returned polygons will be empty.
+
+ @param above The part of the polygon above (on the side the
+ normal points to or in the plane) the plane
+ @param below The part of the polygon below the plane.
+ @param newEdge If a new edge was introduced, this is that edge (on the above portion; the below portion is the opposite winding.
+ */
+ void cut(const Plane& plane, ConvexPolygon &above, ConvexPolygon &below, DirectedEdge& newEdge);
+ void cut(const Plane& plane, ConvexPolygon &above, ConvexPolygon &below);
+
+ /**
+ When a cut plane grazes a vertex in the polygon, two near-identical vertices may be created.
+ The closeness of these two points can cause a number of problems, such as ConvexPolygon::normal()
+ returning an infinite vector. It should be noted, however, that not all applications are
+ sensitive to near-identical vertices.
+
+ removeDuplicateVertices() detects and eliminates redundant vertices.
+ */
+ void removeDuplicateVertices();
+
+ /**
+ O(n) in the number of edges
+ */
+ float getArea() const;
+
+ inline Vector3 normal() const {
+ debugAssert(_vertex.length() >= 3);
+ return (_vertex[1] - _vertex[0]).cross(_vertex[2] - _vertex[0]).direction();
+ }
+
+ /**
+ Returns the same polygon with inverse winding.
+ */
+ ConvexPolygon inverse() const;
+};
+
+
+
+class ConvexPolyhedron {
+public:
+ /**
+ Zero faces indicates an empty polyhedron
+ */
+ Array<ConvexPolygon> face;
+
+ ConvexPolyhedron() {}
+ ConvexPolyhedron(const Array<ConvexPolygon>& _face);
+
+ /**
+ O(n) in the number of edges
+ */
+ bool isEmpty() const;
+
+ /**
+ O(n) in the number of edges
+ */
+ float getVolume() const;
+
+ /**
+ Cuts the polyhedron at the plane. If the polyhedron is entirely above or below
+ the plane, one of the returned polyhedra will be empty.
+
+ @param above The part of the polyhedron above (on the side the
+ normal points to or in the plane) the plane
+ @param below The part of the polyhedron below the plane.
+ */
+ void cut(const Plane& plane, ConvexPolyhedron &above, ConvexPolyhedron &below);
+};
+
+/**
+
+ */
+class ConvexPolygon2D {
+private:
+
+ Array<Vector2> m_vertex;
+
+public:
+
+ ConvexPolygon2D() {}
+
+ /**
+ Points are counter-clockwise in a Y = down, X = right coordinate
+ system.
+
+ @param reverse If true, the points are reversed (i.e. winding direction is changed)
+ before the polygon is created.
+ */
+ ConvexPolygon2D(const Array<Vector2>& pts, bool reverse = false);
+
+ inline int numVertices() const {
+ return m_vertex.size();
+ }
+
+ inline const Vector2& vertex(int index) const {
+ debugAssert((index >= 0) && (index <= m_vertex.size()));
+ return m_vertex[index];
+ }
+
+ /** @param reverseWinding If true, the winding direction of the polygon is reversed for this test.*/
+ bool contains(const Vector2& p, bool reverseWinding = false) const;
+};
+
+
+} // namespace
+#endif
diff --git a/externals/g3dlite/G3D.lib/include/G3D/CoordinateFrame.h b/externals/g3dlite/G3D.lib/include/G3D/CoordinateFrame.h
new file mode 100644
index 00000000000..705c6c93ae2
--- /dev/null
+++ b/externals/g3dlite/G3D.lib/include/G3D/CoordinateFrame.h
@@ -0,0 +1,315 @@
+/**
+ @file CoordinateFrame.h
+
+ @maintainer Morgan McGuire, morgan@cs.williams.edu
+
+ @created 2001-03-04
+ @edited 2008-07-14
+
+ Copyright 2000-2008, Morgan McGuire.
+ All rights reserved.
+*/
+
+#ifndef G3D_COORDINATEFRAME_H
+#define G3D_COORDINATEFRAME_H
+
+#include "G3D/platform.h"
+#include "G3D/Vector3.h"
+#include "G3D/Vector4.h"
+#include "G3D/Matrix3.h"
+#include "G3D/Array.h"
+#include <math.h>
+#include <string>
+#include <stdio.h>
+#include <cstdarg>
+#include <assert.h>
+
+namespace G3D {
+
+/**
+ A rigid body RT (rotation-translation) transformation.
+
+CoordinateFrame abstracts a 4x4 matrix that maps object space to world space:
+
+ v_world = C * v_object
+
+CoordinateFrame::rotation is the upper 3x3 submatrix, CoordinateFrame::translation
+is the right 3x1 column. The 4th row is always [0 0 0 1], so it isn't stored.
+So you don't have to remember which way the multiplication and transformation work,
+it provides explicit toWorldSpace and toObjectSpace methods. Also, points, vectors
+(directions), and surface normals transform differently, so they have separate methods.
+
+Some helper functions transform whole primitives like boxes in and out of object space.
+
+Convert to Matrix4 using CoordinateFrame::toMatrix4. You <I>can</I> construct a CoordinateFrame
+from a Matrix4 using Matrix4::approxCoordinateFrame, however, because a Matrix4 is more
+general than a CoordinateFrame, some information may be lost.
+
+@sa G3D::UprightFrame, G3D::PhysicsFrame, G3D::Matrix4, G3D::Quat
+*/
+class CoordinateFrame {
+public:
+
+ /** Takes object space points to world space. */
+ Matrix3 rotation;
+
+ /** Takes object space points to world space. */
+ Vector3 translation;
+
+ inline bool operator==(const CoordinateFrame& other) const {
+ return (translation == other.translation) && (rotation == other.rotation);
+ }
+
+ inline bool operator!=(const CoordinateFrame& other) const {
+ return !(*this == other);
+ }
+
+ bool fuzzyEq(const CoordinateFrame& other) const;
+
+ bool fuzzyIsIdentity() const;
+
+ bool isIdentity() const;
+
+ /**
+ Initializes to the identity coordinate frame.
+ */
+ inline CoordinateFrame() :
+ rotation(Matrix3::identity()), translation(Vector3::zero()) {
+ }
+
+ CoordinateFrame(const Vector3& _translation) :
+ rotation(Matrix3::identity()), translation(_translation) {
+ }
+
+ CoordinateFrame(const Matrix3 &rotation, const Vector3 &translation) :
+ rotation(rotation), translation(translation) {
+ }
+
+ CoordinateFrame(const Matrix3 &rotation) :
+ rotation(rotation), translation(Vector3::zero()) {
+ }
+
+ CoordinateFrame(const class UprightFrame& f);
+
+ static CoordinateFrame fromXYZYPRRadians(float x, float y, float z, float yaw = 0.0f, float pitch = 0.0f, float roll = 0.0f);
+
+ /** Construct a coordinate frame from translation = (x,y,z) and
+ rotations (in that order) about Y, object space X, object space
+ Z. Note that because object-space axes are used, these are not
+ equivalent to Euler angles; they are known as Tait-Bryan
+ rotations and are more convenient for intuitive positioning.*/
+ static CoordinateFrame fromXYZYPRDegrees(float x, float y, float z, float yaw = 0.0f, float pitch = 0.0f, float roll = 0.0f);
+
+ CoordinateFrame(class BinaryInput& b);
+
+ void deserialize(class BinaryInput& b);
+
+ void serialize(class BinaryOutput& b) const;
+
+ CoordinateFrame(const CoordinateFrame &other) :
+ rotation(other.rotation), translation(other.translation) {}
+
+ /**
+ Computes the inverse of this coordinate frame.
+ */
+ inline CoordinateFrame inverse() const {
+ CoordinateFrame out;
+ out.rotation = rotation.transpose();
+ out.translation = -out.rotation * translation;
+ return out;
+ }
+
+ inline ~CoordinateFrame() {}
+
+ /** See also Matrix4::approxCoordinateFrame */
+ class Matrix4 toMatrix4() const;
+
+ void getXYZYPRRadians(float& x, float& y, float& z, float& yaw, float& pitch, float& roll) const;
+ void getXYZYPRDegrees(float& x, float& y, float& z, float& yaw, float& pitch, float& roll) const;
+
+
+ /**
+ Produces an XML serialization of this coordinate frame.
+ @deprecated
+ */
+ std::string toXML() const;
+
+ /**
+ Returns the heading of the lookVector as an angle in radians relative to
+ the world -z axis. That is, a counter-clockwise heading where north (-z)
+ is 0 and west (-x) is PI/2.
+
+ Note that the heading ignores the Y axis, so an inverted
+ object has an inverted heading.
+ */
+ inline float getHeading() const {
+ Vector3 look = rotation.getColumn(2);
+ float angle = -(float) atan2(-look.x, look.z);
+ return angle;
+ }
+
+ /**
+ Takes the coordinate frame into object space.
+ this->inverse() * c
+ */
+ inline CoordinateFrame toObjectSpace(const CoordinateFrame& c) const {
+ return this->inverse() * c;
+ }
+
+ inline Vector4 toObjectSpace(const Vector4& v) const {
+ return this->inverse().toWorldSpace(v);
+ }
+
+ inline Vector4 toWorldSpace(const Vector4& v) const {
+ return Vector4(rotation * Vector3(v.x, v.y, v.z) + translation * v.w, v.w);
+ }
+
+ /**
+ Transforms the point into world space.
+ */
+ inline Vector3 pointToWorldSpace(const Vector3& v) const {
+ return Vector3(
+ rotation[0][0] * v[0] + rotation[0][1] * v[1] + rotation[0][2] * v[2] + translation[0],
+ rotation[1][0] * v[0] + rotation[1][1] * v[1] + rotation[1][2] * v[2] + translation[1],
+ rotation[2][0] * v[0] + rotation[2][1] * v[1] + rotation[2][2] * v[2] + translation[2]);
+ }
+
+ /**
+ Transforms the point into object space. Assumes that the rotation matrix is orthonormal.
+ */
+ inline Vector3 pointToObjectSpace(const Vector3& v) const {
+ float p[3];
+ p[0] = v[0] - translation[0];
+ p[1] = v[1] - translation[1];
+ p[2] = v[2] - translation[2];
+ debugAssert(G3D::fuzzyEq(rotation.determinant(), 1.0f));
+ return Vector3(
+ rotation[0][0] * p[0] + rotation[1][0] * p[1] + rotation[2][0] * p[2],
+ rotation[0][1] * p[0] + rotation[1][1] * p[1] + rotation[2][1] * p[2],
+ rotation[0][2] * p[0] + rotation[1][2] * p[1] + rotation[2][2] * p[2]);
+ }
+
+ /**
+ Transforms the vector into world space (no translation).
+ */
+ inline Vector3 vectorToWorldSpace(const Vector3& v) const {
+ return rotation * v;
+ }
+
+ inline Vector3 normalToWorldSpace(const Vector3& v) const {
+ return rotation * v;
+ }
+
+ class Ray toObjectSpace(const Ray& r) const;
+
+ Ray toWorldSpace(const Ray& r) const;
+
+ /**
+ Transforms the vector into object space (no translation).
+ */
+ inline Vector3 vectorToObjectSpace(const Vector3 &v) const {
+ // Multiply on the left (same as rotation.transpose() * v)
+ return v * rotation;
+ }
+
+ inline Vector3 normalToObjectSpace(const Vector3 &v) const {
+ // Multiply on the left (same as rotation.transpose() * v)
+ return v * rotation;
+ }
+
+ void pointToWorldSpace(const Array<Vector3>& v, Array<Vector3>& vout) const;
+
+ void normalToWorldSpace(const Array<Vector3>& v, Array<Vector3>& vout) const;
+
+ void vectorToWorldSpace(const Array<Vector3>& v, Array<Vector3>& vout) const;
+
+ void pointToObjectSpace(const Array<Vector3>& v, Array<Vector3>& vout) const;
+
+ void normalToObjectSpace(const Array<Vector3>& v, Array<Vector3>& vout) const;
+
+ void vectorToObjectSpace(const Array<Vector3>& v, Array<Vector3>& vout) const;
+
+ class Box toWorldSpace(const class AABox& b) const;
+
+ class Box toWorldSpace(const class Box& b) const;
+
+ class Cylinder toWorldSpace(const class Cylinder& b) const;
+
+ class Capsule toWorldSpace(const class Capsule& b) const;
+
+ class Plane toWorldSpace(const class Plane& p) const;
+
+ class Sphere toWorldSpace(const class Sphere& b) const;
+
+ class Triangle toWorldSpace(const class Triangle& t) const;
+
+ class Box toObjectSpace(const AABox& b) const;
+
+ class Box toObjectSpace(const Box& b) const;
+
+ class Plane toObjectSpace(const Plane& p) const;
+
+ class Sphere toObjectSpace(const Sphere& b) const;
+
+ Triangle toObjectSpace(const Triangle& t) const;
+
+ /** Compose: create the transformation that is <I>other</I> followed by <I>this</I>.*/
+ CoordinateFrame operator*(const CoordinateFrame &other) const {
+ return CoordinateFrame(rotation * other.rotation,
+ pointToWorldSpace(other.translation));
+ }
+
+ CoordinateFrame operator+(const Vector3& v) const {
+ return CoordinateFrame(rotation, translation + v);
+ }
+
+ CoordinateFrame operator-(const Vector3& v) const {
+ return CoordinateFrame(rotation, translation - v);
+ }
+
+ void lookAt(const Vector3& target);
+
+ void lookAt(
+ const Vector3& target,
+ Vector3 up);
+
+ /** The direction this camera is looking (its negative z axis)*/
+ inline Vector3 lookVector() const {
+ return -rotation.getColumn(2);
+ }
+
+ /** Returns the ray starting at the camera origin travelling in direction CoordinateFrame::lookVector. */
+ class Ray lookRay() const;
+
+ /** Up direction for this camera (its y axis). */
+ inline Vector3 upVector() const {
+ return rotation.getColumn(1);
+ }
+
+ inline Vector3 rightVector() const {
+ return rotation.getColumn(0);
+ }
+
+ /**
+ If a viewer looks along the look vector, this is the viewer's "left".
+ Useful for strafing motions and building alternative coordinate frames.
+ */
+ inline Vector3 leftVector() const {
+ return -rotation.getColumn(0);
+ }
+
+ /**
+ Linearly interpolates between two coordinate frames, using
+ Quat::slerp for the rotations.
+ */
+ CoordinateFrame lerp(
+ const CoordinateFrame& other,
+ float alpha) const;
+
+};
+
+typedef CoordinateFrame CFrame;
+
+} // namespace
+
+#endif
diff --git a/externals/g3dlite/G3D.lib/include/G3D/Crypto.h b/externals/g3dlite/G3D.lib/include/G3D/Crypto.h
new file mode 100644
index 00000000000..f8266b8721b
--- /dev/null
+++ b/externals/g3dlite/G3D.lib/include/G3D/Crypto.h
@@ -0,0 +1,96 @@
+/**
+ @file Crypto.h
+
+ @maintainer Morgan McGuire, matrix@graphics3d.com
+
+
+ @created 2006-03-29
+ @edited 2006-04-06
+ */
+
+#ifndef G3D_CRYPTO_H
+#define G3D_CRYPTO_H
+
+#include "G3D/platform.h"
+#include "G3D/g3dmath.h"
+#include <string>
+
+namespace G3D {
+
+/** See G3D::Crypto::md5 */
+class MD5Hash {
+private:
+
+ uint8 value[16];
+
+public:
+
+ MD5Hash() {
+ for (int i = 0; i < 16; ++i) {
+ value[i] = 0;
+ }
+ }
+
+ explicit MD5Hash(class BinaryInput& b);
+
+ uint8& operator[](int i) {
+ return value[i];
+ }
+
+ const uint8& operator[](int i) const {
+ return value[i];
+ }
+
+ bool operator==(const MD5Hash& other) const {
+ bool match = true;
+ for (int i = 0; i < 16; ++i) {
+ match = match && (other.value[i] == value[i]);
+ }
+ return match;
+ }
+
+ inline bool operator!=(const MD5Hash& other) const {
+ return !(*this == other);
+ }
+
+ void deserialize(class BinaryInput& b);
+
+ void serialize(class BinaryOutput& b) const;
+};
+
+
+/** Cryptography and hashing helper functions */
+class Crypto {
+public:
+
+ /**
+ Computes the CRC32 value of a byte array. CRC32 is designed to be a hash
+ function that produces different values for similar strings.
+
+ This implementation is compatible with PKZIP and GZIP.
+
+ Based on http://www.gamedev.net/reference/programming/features/crc32/
+ */
+ static uint32 crc32(const void* bytes, size_t numBytes);
+
+ /**
+ Computes the MD5 hash (message digest) of a byte stream, as defined by
+ http://www.ietf.org/rfc/rfc1321.txt.
+
+ @cite Based on implementation by L. Peter Deutsch, ghost@aladdin.com
+ */
+ MD5Hash md5(const void* bytes, size_t numBytes);
+
+ /**
+ Returns the nth prime less than 2000 in constant time. The first prime has index
+ 0 and is the number 2.
+ */
+ static int smallPrime(int n);
+
+ /** Returns 1 + the largest value that can be passed to smallPrime. */
+ static int numSmallPrimes();
+};
+
+}
+
+#endif
diff --git a/externals/g3dlite/G3D.lib/include/G3D/Cylinder.h b/externals/g3dlite/G3D.lib/include/G3D/Cylinder.h
new file mode 100644
index 00000000000..c341d29a2b9
--- /dev/null
+++ b/externals/g3dlite/G3D.lib/include/G3D/Cylinder.h
@@ -0,0 +1,92 @@
+/**
+ @file Cylinder.h
+
+ @maintainer Morgan McGuire, matrix@graphics3d.com
+
+ @created 2003-02-07
+ @edited 2005-09-26
+
+ Copyright 2000-2005, Morgan McGuire.
+ All rights reserved.
+ */
+
+#ifndef G3D_Cylinder_H
+#define G3D_Cylinder_H
+
+#include "G3D/platform.h"
+#include "G3D/g3dmath.h"
+#include "G3D/Vector3.h"
+
+namespace G3D {
+
+class Line;
+class AABox;
+/**
+ Right cylinder
+ */
+class Cylinder {
+private:
+ Vector3 p1;
+ Vector3 p2;
+
+ float mRadius;
+
+public:
+
+ /** Uninitialized */
+ Cylinder();
+ Cylinder(class BinaryInput& b);
+ Cylinder(const Vector3& _p1, const Vector3& _p2, float _r);
+ void serialize(class BinaryOutput& b) const;
+ void deserialize(class BinaryInput& b);
+
+ /** The line down the center of the Cylinder */
+ Line axis() const;
+
+ /**
+ A reference frame in which the center of mass is at the origin and
+ the Y-axis is the cylinder's axis. If the cylinder is transformed, this reference frame
+ may freely rotate around its axis.*/
+ void getReferenceFrame(class CoordinateFrame& cframe) const;
+
+ /** Returns point 0 or 1 */
+ inline const Vector3& point(int i) const {
+ debugAssert(i >= 0 && i <= 1);
+ return (i == 0) ? p1 : p2;
+ }
+
+ /**
+ Returns true if the point is inside the Cylinder or on its surface.
+ */
+ bool contains(const Vector3& p) const;
+
+ float area() const;
+
+ float volume() const;
+
+ float radius() const;
+
+ /** Center of mass */
+ inline Vector3 center() const {
+ return (p1 + p2) / 2.0f;
+ }
+
+ inline float height() const {
+ return (p1 - p2).magnitude();
+ }
+
+ /**
+ Get close axis aligned bounding box.
+ With vertical world orientation, the top and bottom might not be very tight. */
+ void getBounds(AABox& out) const;
+
+ /** Random world space point with outward facing normal. */
+ void getRandomSurfacePoint(Vector3& P, Vector3& N) const;
+
+ /** Point selected uniformly at random over the volume. */
+ Vector3 randomInteriorPoint() const;
+};
+
+} // namespace
+
+#endif
diff --git a/externals/g3dlite/G3D.lib/include/G3D/Discovery.h b/externals/g3dlite/G3D.lib/include/G3D/Discovery.h
new file mode 100644
index 00000000000..acd1c2c01d0
--- /dev/null
+++ b/externals/g3dlite/G3D.lib/include/G3D/Discovery.h
@@ -0,0 +1,589 @@
+/**
+ @file Discovery.h
+
+ <H2>Discovery</H2>
+ Discovery is the process by which computers on a Local Area Network (LAN) find
+ one another. The Discovery API allows clients to make a list of servers running
+ the same application. The application typically presents this list to the user
+ so he can choose which server to connect to.
+ <P>
+ Features of the Discovery API:
+ <P>
+ Low network traffic
+ <BR> - Broadcasts mainly occur only when a new machine enters/leaves the network
+ <BR>Responsive
+ <BR> - Servers appear immediately after launching
+ <BR> - Servers disappear immedatiately after they shut down
+ <BR>Extensible
+ <BR> - Add your own game information (e.g. score, num players, map name)
+ <BR>Versioned
+ <BR> - Tracks incompatible servers so end-users know to upgrade their client/server
+
+ <H2>Using the Discovery API</H2>
+
+ Subclass DiscoveryAdvertisement to add fields describing a server running your
+ application. For a game, these might be the current map name and the number
+ of players.
+ <P>
+ On the client, create an instance of DiscoveryClient<your advertisement subclass>.
+ On the server, create an instance of DiscoveryServer and initialize it with an
+ instance of your advertisement subclass. From your main loop, call doNetwork()
+ on the client and server instances. When your server shuts down, invoke cleanup()
+ on it.
+
+ @maintainer Morgan McGuire, matrix@graphics3d.com
+
+ @created 2003-06-26
+ @edited 2008-11-24
+ */
+
+#ifndef G3D_DISCOVERY_H
+#define G3D_DISCOVERY_H
+
+#include "G3D/platform.h"
+#include "G3D/G3DGameUnits.h"
+#include "G3D/NetworkDevice.h"
+#include "G3D/Log.h"
+#include <time.h>
+
+/**
+ Different versions of G3D discovery protocols can't communicate with each other.
+ */
+#define G3D_DISCOVERY_PROTOCOL_NAME "G3D DISC"
+#define G3D_DISCOVERY_PROTOCOL_VERSION 1
+
+namespace G3D {
+
+/**
+ If a machine is running two different programs using discovery they
+ must have different ports. However different programs can share the
+ same ports if they run on the same LAN with different servers.
+
+ @deprecated See Discovery2::Settings
+ */
+class DiscoverySettings {
+public:
+
+ /**
+ Name of the program using discovery; used so that mutliple
+ programs can use the same discovery ports on the same network.
+ */
+ const char* appProtocolName;
+
+ /**
+ Version of the network protocol of the program using discovery.
+ Used so that discovery can identify incompatible versions of
+ the server.
+ */
+ int appProtocolVersion;
+
+ /**
+ Port on which the server broadcasts its identity. The client
+ and server must agree on this value.
+ */
+ uint16 serverBroadcastPort;
+
+ /**
+ Port on which the client broadcasts a server request. The client
+ and server must agree on this value.
+ */
+ uint16 clientBroadcastPort;
+
+ /**
+ Clients connect into this port using a reliable conduit
+ to receive the advertisement from a server. The client
+ doesn't look at this value; it uses whatever the server
+ sends it.
+ */
+ uint16 serverAdvertisementPort;
+
+ /**
+ You can use the default G3D discovery ports as long as no other
+ program with the same protocol name is using this port. You
+ <B>can</B> run two different G3D discovery programs on the same
+ two ports as long as they have different application protocol
+ strings.
+ */
+ DiscoverySettings(
+ const char* _appProtocolName,
+ int _appProtocolVersion,
+ uint16 _serverBroadcast = 6173,
+ uint16 _clientBroadcast = 6174,
+ uint16 _serverAdvertisementPort = 6175) :
+ appProtocolName(_appProtocolName),
+ appProtocolVersion(_appProtocolVersion),
+ serverBroadcastPort(_serverBroadcast),
+ clientBroadcastPort(_clientBroadcast),
+ serverAdvertisementPort(_serverAdvertisementPort) {}
+};
+
+/**
+ Make your own subclass of this advertisement. Add fields
+ (e.g. numPlayers, currentScore) to increase the amount
+ of information advertised.
+
+ Overrides must provide a default constructor.
+ @deprecated See Discovery2::Settings
+ */
+class DiscoveryAdvertisement {
+public:
+
+ /**
+ Address to connect to on the server for the actual game.
+ The IP portion is ignored (the client figures out the IP
+ address from the packet itself) but the port is essential.
+ Note that this port must not be the discovery port.
+ */
+ NetAddress address;
+
+ /**
+ (Only used on the client)
+ Time since this advertisement was updated.
+ */
+ RealTime lastUpdateTime;
+
+ /**
+ Overrides must call DiscoveryAdvertisement::serialize(b) first.
+ */
+ virtual void serialize(BinaryOutput& b) const;
+
+ /**
+ Overrides must call DiscoveryAdvertisement::deserialize(b) first.
+ */
+ virtual void deserialize(BinaryInput& b);
+
+ /**
+ An empty virtual destructor for virtual methods.
+ */
+ virtual ~DiscoveryAdvertisement() {}
+};
+
+
+/**
+ Sent by servers to describe their location.
+ */
+class DiscoveryServerAddressMessage {
+public:
+
+ /**
+ Not part of the message; these settings are used to determine
+ if the correct protocol is being used.
+ */
+ const DiscoverySettings* settings;
+
+
+ /**
+ Set to true if this server is running the correct protocol.
+ */
+ bool correctProtocol;
+
+ /**
+ This is set during the serialize process from the server's settings.
+ If different from the client's settings the discovery system will
+ classify this server as incompatible.
+ */
+ int serverProtocolVersion[2];
+
+ Array<NetAddress> address;
+
+ DiscoveryServerAddressMessage() {}
+ DiscoveryServerAddressMessage(const DiscoverySettings* s) : settings(s) {}
+
+ void serialize(BinaryOutput& b) const;
+
+ void deserialize(BinaryInput& b);
+};
+
+
+/**
+ Base class for DiscoveryClient and DiscoveryServer.
+ */
+class Discovery {
+public:
+
+ const DiscoverySettings* settings;
+
+ enum {
+ SERVER_SHUTDOWN_MESSAGE = 2,
+ SERVER_BROADCAST_MESSAGE = 3,
+ CLIENT_BROADCAST_MESSAGE = 4};
+
+ /**
+ Only called from subclasses.
+ */
+ virtual void init(
+ const DiscoverySettings* _settings) {
+ settings = _settings;
+ }
+
+ /**
+ An empty virtual destructor for virtual methods.
+ */
+ virtual ~Discovery() {}
+};
+
+/** @deprecated See Discovery2::Server*/
+class DiscoveryServer : private Discovery {
+private:
+
+ class ShutdownMessage {
+ public:
+ void serialize(BinaryOutput& b) const { (void)b; }
+
+ void deserialize(BinaryInput& b) { (void)b; }
+ };
+
+ /**
+ For broadcast.
+ */
+ LightweightConduitRef net;
+
+ /**
+ Listen for clients wanting to hear the advertisement over
+ a reliable connection.
+ */
+ NetListenerRef listener;
+
+ DiscoveryAdvertisement* advertisement;
+
+ /**
+ Broadcast across the lightweight conduit.
+ */
+ DiscoveryServerAddressMessage addressMessage;
+
+ /**
+ Servers periodically broadcast (unsolicited) in case
+ anyone missed the previous message.
+ */
+ RealTime lastBroadcast;
+
+ void sendAnnouncement() const;
+
+ void sendShutDown() const;
+
+public:
+
+ /**
+ You may update the advertisement (synchronously with calling doNetwork)
+ after it has been passed in. This allows a server to change the advertised
+ number of players or score for a game, for example.
+
+ You must set the port of the @a _advertisement G3D::DiscoveryAdvertisement::address
+ to the port which the G3D::NetListener for the actual program protocol (not discovery)
+ is running. That field how the client knows what address to connect to using
+ G3D::ReliableConduit or G3D::LightweightConduit to actually initiate communication.
+ */
+ virtual void init(
+ const DiscoverySettings* _settings,
+ DiscoveryAdvertisement* _advertisement);
+
+ /** Returns the broadcast address in use.*/
+ NetAddress broadcastAddress() const;
+
+ /**
+ Returns true if this discovery server has been initialized
+ and is functioning properly.
+ */
+ bool ok() const;
+
+ /**
+ Call periodically to let the server do its job.
+ */
+ void doNetwork();
+
+ /**
+ Broadcast a shutdown message.
+ */
+ void cleanup();
+};
+
+
+/**
+ Used by DiscoveryClient to report servers running a different version
+ of this application's protocol.
+ */
+class IncompatibleServerDescription {
+public:
+ NetAddress address;
+ int protocolVersion[2];
+ RealTime lastUpdateTime;
+
+ std::string toString() const;
+};
+
+
+/**
+ Only one DiscoveryClient can be active on a given port at a time on
+ a single computer.
+
+ AdType must be a subclass of DiscoveryAdvertisement.
+
+ @deprecated See Discovery2::Client
+ */
+template<class AdType>
+class DiscoveryClient : private Discovery {
+public:
+
+ /**
+ List of servers. Do not access on a second thread while in
+ doNetwork.
+ */
+ Array<AdType> serverList;
+
+ /**
+ List of servers running the same application but a different protocol.
+ It is useful to show these to users to help them recognize version
+ conflicts between client and server.
+ Do not access on a second thread while in doNetwork.
+ */
+ Array<IncompatibleServerDescription> incompatibleServerList;
+
+private:
+
+ class BroadcastMessage {
+ public:
+ void serialize(BinaryOutput& b) const {}
+
+ void deserialize(BinaryInput& b) {}
+ };
+
+ /**
+ The client periodically checks servers to make sure they are still up
+ and to update its information about them.
+ */
+ RealTime lastServerCheck;
+
+ LightweightConduitRef net;
+
+ /**
+ Returns an index in serverList of the server with the given address.
+ Returns -1 if there is none. Only checks IP addresses.
+ */
+ int findServerListIndex(const NetAddress& addr) const {
+ for (int i = 0; i < serverList.size(); ++i) {
+ if (addr.ip() == serverList[i].address.ip()) {
+ return i;
+ }
+ }
+
+ return -1;
+ }
+
+ /**
+ Returns true if this discovery client has been initialized
+ and is functioning properly.
+ */
+ bool ok() const {
+ return net->ok();
+ }
+
+ /**
+ Adds a server to the incompatible list if it is not already there.
+ */
+ void addToIncompatibleList(const NetAddress& addr, uint32 p0, uint32 p1) {
+ const RealTime now = System::time();
+
+ bool alreadyHere = false;
+
+ // Incorrect protocol; add to the incompatible list
+ for (int i = 0; i < incompatibleServerList.size(); ++i) {
+ IncompatibleServerDescription& server = incompatibleServerList[i];
+
+ if (server.address == addr) {
+ server.lastUpdateTime = now;
+ alreadyHere = true;
+ break;
+ }
+ }
+
+ if (! alreadyHere) {
+ IncompatibleServerDescription server;
+
+ server.lastUpdateTime = now;
+ server.address = addr;
+ server.protocolVersion[0] = p0;
+ server.protocolVersion[1] = p1;
+
+ incompatibleServerList.append(server);
+ }
+ }
+
+ /**
+ Connects to the specified server, reads its advertisement,
+ and adds it to the active server list. Returns true if the server
+ can be reached.
+ */
+ bool readAdvertisement(const NetAddress& address) {
+ std::string hostname = address.toString();
+
+ RealTime TIMEOUT = 2.0;
+
+ ReliableConduitRef server = ReliableConduit::create(address);
+
+ if (! server->ok()) {
+ return false;
+ }
+
+ AdType advertisement;
+
+ // Read the advertisement
+ RealTime stopWaiting = System::time() + TIMEOUT;
+ bool timedOut = false;
+
+ while (! server->messageWaiting() && ! timedOut && server->ok()) {
+ System::sleep(0.1);
+ timedOut = (System::time() > stopWaiting);
+ }
+
+ if (timedOut) {
+ Log::common()->printf("Discovery: Timed out while reading advertisment from %s\n",
+ hostname.c_str());
+ return false;
+ }
+
+
+ if (! server->ok()) {
+ Log::common()->printf("Discovery: Server %s dropped connection\n", hostname.c_str());
+ return false;
+ }
+
+ // Read the advertisement
+ debugAssert(server->messageWaiting());
+ if (! server->receive(advertisement)) {
+ Log::common()->printf("Discovery: Server %s failed to send advertisement\n", hostname.c_str());
+ return false;
+ }
+
+ // Update existing server info or create a new entry
+ int index = findServerListIndex(address);
+ if (index == -1) {
+ index = serverList.size();
+ serverList.resize(index + 1);
+ }
+
+ // Update element index
+ advertisement.address = address;
+ serverList[index] = advertisement;
+
+ return true;
+ }
+
+ /**
+ Remove this address from our list if we previously
+ had a server there.
+ */
+ void removeServer(const NetAddress& address) {
+ int index = findServerListIndex(address);
+ if (index > -1) {
+ serverList.fastRemove(index);
+ }
+ }
+
+ /**
+ Tries to connect to the server through the addresses in the array.
+ */
+ void addToServerList(const Array<NetAddress>& addressArray) {
+ // Try to connect to each address listed
+ for (int a = addressArray.size() - 1; a >= 0; --a) {
+ const NetAddress& address = addressArray[a];
+
+ if (readAdvertisement(address)) {
+ // We've connected to the server
+ break;
+ } else {
+ removeServer(address);
+ }
+ }
+ }
+
+ void checkRandomServer() {
+ if (serverList.size() >= 1) {
+ int index = iRandom(0, serverList.size() - 1);
+
+ Array<NetAddress> address;
+ address.append(serverList[index].address);
+
+ // Remove this server
+ serverList.fastRemove(index);
+
+ // Add it back with new info (or leave it removed if no response)
+ addToServerList(address);
+ }
+ }
+
+public:
+
+ void init(
+ const DiscoverySettings* _settings) {
+
+ Discovery::init(_settings);
+
+ lastServerCheck = System::time();
+
+ net = LightweightConduit::create(settings->serverBroadcastPort, true, true);
+
+ // Send announcement
+ NetAddress broadcast(NetworkDevice::instance()->broadcastAddressArray()[0],
+ settings->clientBroadcastPort);
+ BroadcastMessage tmp;
+ net->send(broadcast, CLIENT_BROADCAST_MESSAGE, tmp);
+ }
+
+ /** Shut down the discovery client. */
+ void cleanup() {
+ net = NULL;
+ }
+
+ /**
+ Call this regularly (several times per second) to
+ update the server list. Not threadsafe-- you must not touch
+ the server list while this is running. This will not block.
+ */
+ void doNetwork() {
+ if (net->messageWaiting()) {
+ NetAddress sender;
+
+ switch (net->waitingMessageType()) {
+ case SERVER_SHUTDOWN_MESSAGE:
+ // Remove the server
+ net->receive(sender);
+ removeServer(sender);
+ break;
+
+ case SERVER_BROADCAST_MESSAGE:
+ // Check the G3D protocol and the network protocol, then read the ad
+ DiscoveryServerAddressMessage msg(settings);
+ net->receive(sender, msg);
+
+ if (msg.correctProtocol && (msg.address.size() > 0)) {
+ // Add the actual return address as the first one to be tried.
+ msg.address.append(NetAddress(sender.ip(), msg.address[0].port()));
+
+ addToServerList(msg.address);
+
+ } else {
+
+ addToIncompatibleList(
+ sender,
+ msg.serverProtocolVersion[0],
+ msg.serverProtocolVersion[1]);
+ }
+ break;
+ }
+ }
+
+ // Periodically re-check servers in the list to see if they crashed
+ // (if they shut down, they should have broadcast a shut down message).
+ RealTime now = System::time();
+ const RealTime UPDATE_TIME_INTERVAL = 30;
+
+ if (now > lastServerCheck + UPDATE_TIME_INTERVAL) {
+ lastServerCheck = now;
+ checkRandomServer();
+ }
+ }
+};
+
+}
+
+#endif
diff --git a/externals/g3dlite/G3D.lib/include/G3D/EqualsTrait.h b/externals/g3dlite/G3D.lib/include/G3D/EqualsTrait.h
new file mode 100644
index 00000000000..acf17615b45
--- /dev/null
+++ b/externals/g3dlite/G3D.lib/include/G3D/EqualsTrait.h
@@ -0,0 +1,26 @@
+/**
+ @file EqualsTrait.h
+
+ @maintainer Morgan McGuire, morgan@cs.williams.edu
+ @created 2008-10-01
+ @edited 2008-10-01
+ Copyright 2000-2008, Morgan McGuire.
+ All rights reserved.
+ */
+
+#ifndef G3D_EQUALSTRAIT_H
+#define G3D_EQUALSTRAIT_H
+
+#include "G3D/platform.h"
+
+/** Default implementation of EqualsTrait.
+ @see G3D::Table for specialization requirements.
+*/
+template<typename Key> struct EqualsTrait {
+ static bool equals(const Key& a, const Key& b) {
+ return a == b;
+ }
+};
+
+#endif
+
diff --git a/externals/g3dlite/G3D.lib/include/G3D/G3D.h b/externals/g3dlite/G3D.lib/include/G3D/G3D.h
new file mode 100644
index 00000000000..42ab18e2c24
--- /dev/null
+++ b/externals/g3dlite/G3D.lib/include/G3D/G3D.h
@@ -0,0 +1,150 @@
+/**
+ @file graphics3D.h
+
+ This header includes all of the graphics3D libraries in
+ appropriate namespaces.
+
+ @maintainer Morgan McGuire, matrix@graphics3d.com
+
+ @created 2001-08-25
+ @edited 2008-11-01
+
+ Copyright 2000-2008, Morgan McGuire.
+ All rights reserved.
+*/
+
+#ifndef G3D_GRAPHICS3D_H
+#define G3D_GRAPHICS3D_H
+
+#define NOMINMAX 1
+#ifdef min
+ #undef min
+#endif
+#ifdef max
+ #undef max
+#endif
+
+#include "G3D/platform.h"
+#include "G3D/Array.h"
+#include "G3D/Queue.h"
+#include "G3D/Crypto.h"
+#include "G3D/format.h"
+#include "G3D/Vector2.h"
+#include "G3D/Vector3.h"
+#include "G3D/Vector4.h"
+#include "G3D/Color1.h"
+#include "G3D/Color3.h"
+#include "G3D/Color4.h"
+#include "G3D/Matrix2.h"
+#include "G3D/Matrix3.h"
+#include "G3D/Matrix4.h"
+#include "G3D/CoordinateFrame.h"
+#include "G3D/PhysicsFrame.h"
+#include "G3D/Plane.h"
+#include "G3D/Line.h"
+#include "G3D/Ray.h"
+#include "G3D/Sphere.h"
+#include "G3D/Box.h"
+#include "G3D/AABox.h"
+#include "G3D/WrapMode.h"
+#include "G3D/Cone.h"
+#include "G3D/Quat.h"
+#include "G3D/stringutils.h"
+#include "G3D/prompt.h"
+#include "G3D/Table.h"
+#include "G3D/Set.h"
+#include "G3D/GUniqueID.h"
+#include "G3D/BinaryFormat.h"
+#include "G3D/BinaryInput.h"
+#include "G3D/BinaryOutput.h"
+#include "G3D/debug.h"
+#include "G3D/G3DGameUnits.h"
+#include "G3D/g3dmath.h"
+#include "G3D/uint128.h"
+#include "G3D/fileutils.h"
+#include "G3D/ReferenceCount.h"
+
+template<class T> struct HashTrait< G3D::ReferenceCountedPointer<T> > {
+ static size_t hashCode(G3D::ReferenceCountedPointer<T> key) { return reinterpret_cast<size_t>( key.pointer() ); }
+};
+
+#include "G3D/GImage.h"
+#include "G3D/CollisionDetection.h"
+#include "G3D/Log.h"
+#include "G3D/serialize.h"
+#include "G3D/TextInput.h"
+#include "G3D/NetAddress.h"
+#include "G3D/NetworkDevice.h"
+#include "G3D/System.h"
+#include "G3D/splinefunc.h"
+#include "G3D/Spline.h"
+#include "G3D/UprightFrame.h"
+#include "G3D/LineSegment.h"
+#include "G3D/Capsule.h"
+#include "G3D/Cylinder.h"
+#include "G3D/Triangle.h"
+#include "G3D/Color3uint8.h"
+#include "G3D/Color4uint8.h"
+#include "G3D/Vector2int16.h"
+#include "G3D/Vector3int16.h"
+#include "G3D/Vector3int32.h"
+#include "G3D/Vector4int8.h"
+#include "G3D/ConvexPolyhedron.h"
+#include "G3D/Discovery.h"
+#include "G3D/MeshAlg.h"
+#include "G3D/vectorMath.h"
+#include "G3D/Rect2D.h"
+#include "G3D/GCamera.h"
+#include "G3D/GLight.h"
+#include "G3D/AABSPTree.h"
+#include "G3D/PointAABSPTree.h"
+#include "G3D/TextOutput.h"
+#include "G3D/MeshBuilder.h"
+#include "G3D/Stopwatch.h"
+#include "G3D/AtomicInt32.h"
+#include "G3D/GThread.h"
+#include "G3D/ThreadSet.h"
+#include "G3D/RegistryUtil.h"
+#include "G3D/AnyVal.h"
+#include "G3D/PointHashGrid.h"
+#include "G3D/Map2D.h"
+#include "G3D/Image1.h"
+#include "G3D/Image1uint8.h"
+#include "G3D/Image3.h"
+#include "G3D/Image3uint8.h"
+#include "G3D/Image4.h"
+#include "G3D/Image4uint8.h"
+#include "G3D/filter.h"
+#include "G3D/WeakCache.h"
+#include "G3D/Pointer.h"
+#include "G3D/Matrix.h"
+#include "G3D/ImageFormat.h"
+
+#ifdef _MSC_VER
+# pragma comment(lib, "zlib")
+# pragma comment(lib, "ws2_32")
+# pragma comment(lib, "winmm")
+# pragma comment(lib, "imagehlp")
+# pragma comment(lib, "gdi32")
+# pragma comment(lib, "user32")
+# pragma comment(lib, "kernel32")
+# pragma comment(lib, "version")
+# pragma comment(lib, "advapi32")
+# pragma comment(lib, "png")
+# pragma comment(lib, "jpeg")
+# pragma comment(lib, "zip")
+# ifdef _DEBUG
+ // Don't link against G3D when building G3D itself.
+# ifndef G3D_BUILDING_LIBRARY_DLL
+# pragma comment(lib, "G3Dd.lib")
+# endif
+# else
+ // Don't link against G3D when building G3D itself.
+# ifndef G3D_BUILDING_LIBRARY_DLL
+# pragma comment(lib, "G3D.lib")
+# endif
+# endif
+#endif
+
+#endif
+
diff --git a/externals/g3dlite/G3D.lib/include/G3D/G3DAll.h b/externals/g3dlite/G3D.lib/include/G3D/G3DAll.h
new file mode 100644
index 00000000000..feba3d6d390
--- /dev/null
+++ b/externals/g3dlite/G3D.lib/include/G3D/G3DAll.h
@@ -0,0 +1,26 @@
+/**
+ @file G3DAll.h
+
+ Includes all G3D and GLG3D files and uses the G3D namespace.
+
+ This requires OpenGL and SDL headers. If you don't want all of this,
+ #include <G3D.h> separately.
+
+ @maintainer Morgan McGuire, matrix@graphics3d.com
+
+ @created 2002-01-01
+ @edited 2006-08-13
+
+ Copyright 2000-2006, Morgan McGuire.
+ All rights reserved.
+ */
+
+#ifndef G3D_G3DALL_H
+#define G3D_G3DALL_H
+
+#include "G3D/G3D.h"
+#include "GLG3D/GLG3D.h"
+
+using namespace G3D;
+
+#endif
diff --git a/externals/g3dlite/G3D.lib/include/G3D/G3DGameUnits.h b/externals/g3dlite/G3D.lib/include/G3D/G3DGameUnits.h
new file mode 100644
index 00000000000..bc4c873f290
--- /dev/null
+++ b/externals/g3dlite/G3D.lib/include/G3D/G3DGameUnits.h
@@ -0,0 +1,44 @@
+/**
+ @file G3DGameUnits.h
+
+ @maintainer Morgan McGuire, matrix@graphics3d.com
+ @created 2002-10-05
+ @edited 2006-11-10
+ */
+
+#ifndef G3D_GAMEUNITS_H
+#define G3D_GAMEUNITS_H
+
+#include "G3D/platform.h"
+
+namespace G3D {
+/**
+ Time, in seconds.
+ */
+typedef double GameTime;
+typedef double SimTime;
+
+/**
+ Actual wall clock time in seconds.
+ */
+typedef double RealTime;
+
+enum AMPM {AM, PM};
+
+enum {SECOND=1, MINUTE=60, HOUR = 60*60, DAY=24*60*60, SUNRISE=24*60*60/4, SUNSET=24*60*60*3/4, MIDNIGHT=0, METER=1, KILOMETER=1000};
+
+#define CENTIMETER (0.01)
+#define DECIMETER (0.1)
+
+/**
+ Converts a 12 hour clock time into the number of seconds since
+ midnight. Note that 12:00 PM is noon and 12:00 AM is midnight.
+
+ Example: <CODE>toSeconds(10, 00, AM)</CODE>
+ */
+SimTime toSeconds(int hour, int minute, double seconds, AMPM ap);
+SimTime toSeconds(int hour, int minute, AMPM ap);
+
+}
+
+#endif
diff --git a/externals/g3dlite/G3D.lib/include/G3D/GCamera.h b/externals/g3dlite/G3D.lib/include/G3D/GCamera.h
new file mode 100644
index 00000000000..4dfa500883b
--- /dev/null
+++ b/externals/g3dlite/G3D.lib/include/G3D/GCamera.h
@@ -0,0 +1,294 @@
+/**
+ @file GCamera.h
+
+ @maintainer Morgan McGuire, matrix@graphics3d.com
+
+ @created 2005-07-20
+ @edited 2007-07-24
+*/
+
+#ifndef G3D_GCamera_H
+#define G3D_GCamera_H
+
+#include "G3D/platform.h"
+#include "G3D/CoordinateFrame.h"
+#include "G3D/Vector3.h"
+#include "G3D/Plane.h"
+#include "G3D/debugAssert.h"
+
+namespace G3D {
+
+class Matrix4;
+class Rect2D;
+
+/**
+ Abstraction of a pinhole camera.
+
+ The area a camera sees is called a frustum. It is bounded by the near plane, the far plane, and the sides
+ of the view frame projected into the scene. It has the shape of a pyramid with the top cut off.
+
+ Cameras can project points from 3D to 2D. The "unit" projection matches OpenGL. It maps the entire view frustum
+ to a cube of unit radius (i.e., edges of length 2) centered at the origin. The non-unit projection then maps
+ that cube to the specified pixel viewport in X and Y and the range [0, 1] in Z. The projection is reversable
+ as long as the projected Z value is known.
+
+ All viewport arguments are the pixel bounds of the viewport-- e.g.,
+ RenderDevice::viewport().
+ */
+class GCamera {
+
+public:
+ /**
+ Stores the direction of the field of view
+ */
+ enum FOVDirection {HORIZONTAL, VERTICAL};
+
+private:
+
+
+ /** field of view (in radians) */
+ float m_fieldOfView;
+
+ /** Clipping plane, *not* imaging plane. Negative numbers. */
+ float m_nearPlaneZ;
+
+ /** Negative */
+ float m_farPlaneZ;
+
+ /** Stores the camera's location and orientation */
+ CoordinateFrame m_cframe;
+
+ /** Horizontal or Vertical */
+ FOVDirection m_direction;
+
+public:
+
+ class Frustum {
+ public:
+ class Face {
+ public:
+ /** Counter clockwise indices into vertexPos */
+ int vertexIndex[4];
+
+ /** The plane containing the face. */
+ Plane plane;
+ };
+
+ /** The vertices, in homogeneous space. If w == 0,
+ a vertex is at infinity. */
+ Array<Vector4> vertexPos;
+
+ /** The faces in the frustum. When the
+ far plane is at infinity, there are 5 faces,
+ otherwise there are 6. The faces are in the order
+ N,R,L,B,T,[F].
+ */
+ Array<Face> faceArray;
+ };
+
+ GCamera();
+
+ virtual ~GCamera();
+
+ /** Returns the current coordinate frame */
+ const CoordinateFrame& coordinateFrame() const {
+ return m_cframe;
+ }
+
+ /** Sets c to the camera's coordinate frame */
+ void getCoordinateFrame(CoordinateFrame& c) const;
+
+ /** Sets a new coordinate frame for the camera */
+ void setCoordinateFrame(const CoordinateFrame& c);
+
+ /** Sets P equal to the camera's projection matrix */
+ void getProjectUnitMatrix(const Rect2D& viewport, Matrix4& P) const;
+
+ /** Converts projected points from OpenGL standards
+ (-1, 1) to normal 3D coordinate standards (0, 1) */
+ Vector3 convertFromUnitToNormal(const Vector3& in, const Rect2D& viewport) const;
+
+ /**
+ Sets the vertical field of view, in radians. The
+ initial angle is toRadians(55). Must specify the direction of the angle
+ */
+ void setFieldOfView(float angle, FOVDirection direction);
+
+ /** Returns the current field of view angle and direction */
+ inline void getFieldOfView(float& angle, FOVDirection& direction) const {
+ angle = m_fieldOfView;
+ direction = m_direction;
+ }
+
+ /**
+ Projects a world space point onto a width x height screen. The
+ returned coordinate uses pixmap addressing: x = right and y =
+ down. The resulting z value is 0 at the near plane, 1 at the far plane,
+ and is a linear compression of unit cube projection.
+
+ If the point is behind the camera, Vector3::inf() is returned.
+ */
+ Vector3 project(const G3D::Vector3& point,
+ const class Rect2D& viewport) const;
+
+ /**
+ Projects a world space point onto a unit cube. The resulting
+ x,y,z values range between -1 and 1, where z is -1
+ at the near plane and 1 at the far plane and varies hyperbolically in between.
+
+ If the point is behind the camera, Vector3::inf() is returned.
+ */
+ Vector3 projectUnit(const G3D::Vector3& point,
+ const class Rect2D& viewport) const;
+
+ /**
+ Gives the world-space coordinates of screen space point v, where
+ v.x is in pixels from the left, v.y is in pixels from
+ the top, and v.z is on the range 0 (near plane) to 1 (far plane).
+ */
+ Vector3 unproject(const Vector3& v, const Rect2D& viewport) const;
+
+ /**
+ Gives the world-space coordinates of unit cube point v, where
+ v varies from -1 to 1 on all axes. The unproject first
+ transforms the point into a pixel location for the viewport, then calls unproject
+ */
+ Vector3 unprojectUnit(const Vector3& v, const Rect2D& viewport) const;
+
+ /**
+ Returns the pixel area covered by a shape of the given
+ world space area at the given z value (z must be negative).
+ */
+ float worldToScreenSpaceArea(float area, float z, const class Rect2D& viewport) const;
+
+ /**
+ Returns the world space 3D viewport corners. These
+ are at the near clipping plane. The corners are constructed
+ from the nearPlaneZ, viewportWidth, and viewportHeight.
+ "left" and "right" are from the GCamera's perspective.
+ */
+ void getNearViewportCorners(const class Rect2D& viewport,
+ Vector3& outUR, Vector3& outUL,
+ Vector3& outLL, Vector3& outLR) const;
+
+ /**
+ Returns the world space 3D viewport corners. These
+ are at the Far clipping plane. The corners are constructed
+ from the nearPlaneZ, farPlaneZ, viewportWidth, and viewportHeight.
+ "left" and "right" are from the GCamera's perspective.
+ */
+ void getFarViewportCorners(const class Rect2D& viewport,
+ Vector3& outUR, Vector3& outUL,
+ Vector3& outLL, Vector3& outLR) const;
+
+ /**
+ Returns the image plane depth, assumes imagePlane
+ is the same as the near clipping plane.
+ returns a positive number.
+ */
+ float imagePlaneDepth() const;
+
+ /**
+ Returns the world space ray passing through the center of pixel
+ (x, y) on the image plane. The pixel x and y axes are opposite
+ the 3D object space axes: (0,0) is the upper left corner of the screen.
+ They are in viewport coordinates, not screen coordinates.
+
+ The ray origin is at the origin. To start it at the image plane,
+ move it forward by imagePlaneDepth/ray.direction.z
+
+ Integer (x, y) values correspond to
+ the upper left corners of pixels. If you want to cast rays
+ through pixel centers, add 0.5 to x and y.
+ */
+ Ray worldRay(
+ float x,
+ float y,
+ const class Rect2D& viewport) const;
+
+ /**
+ Returns a negative z-value.
+ */
+ inline float nearPlaneZ() const {
+ return m_nearPlaneZ;
+ }
+
+ /**
+ Returns a negative z-value.
+ */
+ inline float farPlaneZ() const {
+ return m_farPlaneZ;
+ }
+
+ /**
+ Sets a new value for the far clipping plane
+ Expects a negative value
+ */
+ inline void setFarPlaneZ(float z) {
+ debugAssert(z < 0);
+ m_farPlaneZ = z;
+ }
+
+ /**
+ Sets a new value for the near clipping plane
+ Expects a negative value
+ */
+ inline void setNearPlaneZ(float z) {
+ debugAssert(z < 0);
+ m_nearPlaneZ = z;
+ }
+
+ /**
+ Returns the camera space width of the viewport at the near plane.
+ */
+ float viewportWidth(const class Rect2D& viewport) const;
+
+ /**
+ Returns the camera space height of the viewport at the near plane.
+ */
+ float viewportHeight(const class Rect2D& viewport) const;
+
+ void setPosition(const Vector3& t);
+
+ /** Rotate the camera in place to look at the target. Does not
+ persistently look at that location when the camera moves;
+ i.e., if you move the camera and still want it to look at the
+ old target, you must call lookAt again after moving the
+ camera.)*/
+ void lookAt(const Vector3& position, const Vector3& up = Vector3::unitY());
+
+ /**
+ Returns the clipping planes of the frustum, in world space.
+ The planes have normals facing <B>into</B> the view frustum.
+
+ The plane order is guaranteed to be:
+ Near, Right, Left, Top, Bottom, [Far]
+
+ If the far plane is at infinity, the resulting array will have
+ 5 planes, otherwise there will be 6.
+
+ The viewport is used only to determine the aspect ratio of the screen; the
+ absolute dimensions and xy values don't matter.
+ */
+ void getClipPlanes
+ (
+ const Rect2D& viewport,
+ Array<Plane>& outClip) const;
+
+ /**
+ Returns the world space view frustum, which is a truncated pyramid describing
+ the volume of space seen by this camera.
+ */
+ void frustum(const Rect2D& viewport, GCamera::Frustum& f) const;
+
+ GCamera::Frustum frustum(const Rect2D& viewport) const;
+
+ /** Read and Write camera parameters */
+ void serialize(class BinaryOutput& bo) const;
+ void deserialize(class BinaryInput& bi);
+
+};
+
+} // namespace G3D
+
+#endif
diff --git a/externals/g3dlite/G3D.lib/include/G3D/GImage.h b/externals/g3dlite/G3D.lib/include/G3D/GImage.h
new file mode 100644
index 00000000000..12003514d6a
--- /dev/null
+++ b/externals/g3dlite/G3D.lib/include/G3D/GImage.h
@@ -0,0 +1,550 @@
+/**
+ @file GImage.h
+
+ See G3D::GImage for details.
+
+ @cite JPEG compress/decompressor is the <A HREF="http://www.ijg.org/files/">IJG library</A>, used in accordance with their license.
+ @cite JPG code by John Chisholm, using the IJG Library
+ @cite TGA code by Morgan McGuire
+ @cite BMP code by John Chisholm, based on code by Edward "CGameProgrammer" Resnick <A HREF="mailto:cgp@gdnmail.net">mailto:cgp@gdnmail.net</A> at <A HREF="ftp://ftp.flipcode.com/cotd/LoadPicture.txt">ftp://ftp.flipcode.com/cotd/LoadPicture.txt</A>
+ @cite PCX format described in the ZSOFT PCX manual http://www.nist.fss.ru/hr/doc/spec/pcx.htm#2
+ @cite PNG compress/decompressor is the <A HREF="http://www.libpng.org/pub/png/libpng.html">libpng library</A>, used in accordance with their license.
+ @cite PPM code by Morgan McGuire based on http://netpbm.sourceforge.net/doc/ppm.html
+
+ @maintainer Morgan McGuire, morgan@graphics3d.com
+ @created 2002-05-27
+ @edited 2007-01-31
+
+ Copyright 2000-2007, Morgan McGuire.
+ All rights reserved.
+
+ */
+
+#ifndef G3D_GIMAGE_H
+#define G3D_GIMAGE_H
+
+#include "G3D/platform.h"
+#include <string>
+#include "G3D/Array.h"
+#include "G3D/g3dmath.h"
+#include "G3D/stringutils.h"
+#include "G3D/Color1uint8.h"
+#include "G3D/Color3uint8.h"
+#include "G3D/Color4uint8.h"
+
+namespace G3D {
+class BinaryInput;
+class BinaryOutput;
+/**
+ Interface to image compression & file formats.
+
+ Supported formats (decode and encode): Color JPEG, PNG, (Uncompressed)TGA 24, (Uncompressed)TGA 32, BMP 1, BMP 4, BMP 8, BMP 24, PPM (P6), and PPM ASCII (P1, P2, P3).
+ 8-bit paletted PCX, 24-bit PCX, and ICO are supported for decoding only.
+
+ Sample usage:
+
+ <PRE>
+ #include "graphics3D.h"
+
+ // Loading from disk:
+ G3D::GImage im1 = G3D::GImage("test.jpg");
+
+ // Loading from memory:
+ G3D::GImage im2 = G3D::GImage(data, length);
+
+ // im.pixel is a pointer to RGB color data. If you want
+ // an alpha channel, call RGBtoRGBA or RGBtoARGB for
+ // conversion.
+
+ // Saving to memory:
+ G3D::GImage im3 = G3D::GImage(width, height);
+ // (Set the pixels of im3...)
+ uint8* data2;
+ int len2;
+ im3.encode(G3D::GImage::JPEG, data2, len2);
+
+ // Saving to disk
+ im3.save("out.jpg");
+ </PRE>
+
+ The free Image Magick Magick Wand API
+ (http://www.imagemagick.org/www/api/magick_wand.html) provides a more powerful
+ API for image manipulation and wider set of image load/save formats. It is
+ recommended over GImage (we don't include it directly in G3D because their license
+ is more restrictive than the BSD one).
+
+ */
+class GImage {
+private:
+ uint8* _byte;
+
+public:
+
+ class Error {
+ public:
+ Error(
+ const std::string& reason,
+ const std::string& filename = "") :
+ reason(reason), filename(filename) {}
+
+ std::string reason;
+ std::string filename;
+ };
+
+ enum Format {JPEG, BMP, TGA, PCX, ICO, PNG, PPM_ASCII, PPM, AUTODETECT, UNKNOWN};
+
+ int width;
+ int height;
+
+ /**
+ The number of channels; either 3 (RGB) or 4 (RGBA)
+ */
+ int channels;
+
+ inline const uint8* byte() const {
+ return _byte;
+ }
+
+ /** Returns a pointer to the upper left pixel
+ as Color3uint8.
+ */
+ inline const Color3uint8* pixel3() const {
+ debugAssertM(channels == 3, format("Tried to call GImage::pixel3 on an image with %d channels", channels));
+ return (Color3uint8*)_byte;
+ }
+
+ /** Returns a pointer to the upper left pixel
+ as Color4uint8.
+ */
+ inline const Color4uint8* pixel4() const {
+ debugAssertM(channels == 4, format("Tried to call GImage::pixel4 on an image with %d channels", channels));
+ return (Color4uint8*)_byte;
+ }
+
+ inline const Color1uint8* pixel1() const {
+ debugAssertM(channels == 1, format("Tried to call GImage::pixel1 on an image with %d channels", channels));
+ return (Color1uint8*)_byte;
+ }
+
+ inline Color1uint8* pixel1() {
+ debugAssertM(channels == 1, format("Tried to call GImage::pixel1 on an image with %d channels", channels));
+ return (Color1uint8*)_byte;
+ }
+
+ /** Returns the pixel at (x, y), where (0,0) is the upper left. */
+ inline const Color1uint8& pixel1(int x, int y) const {
+ debugAssert(x >= 0 && x < width);
+ debugAssert(y >= 0 && y < height);
+ return pixel1()[x + y * width];
+ }
+
+ /** Returns the pixel at (x, y), where (0,0) is the upper left. */
+ inline Color1uint8& pixel1(int x, int y) {
+ debugAssert(x >= 0 && x < width);
+ debugAssert(y >= 0 && y < height);
+ return pixel1()[x + y * width];
+ }
+
+ /** Returns the pixel at (x, y), where (0,0) is the upper left. */
+ inline const Color3uint8& pixel3(int x, int y) const {
+ debugAssert(x >= 0 && x < width);
+ debugAssert(y >= 0 && y < height);
+ return pixel3()[x + y * width];
+ }
+
+ inline Color3uint8& pixel3(int x, int y) {
+ debugAssert(x >= 0 && x < width);
+ debugAssert(y >= 0 && y < height);
+ return pixel3()[x + y * width];
+ }
+
+ /** Returns the pixel at (x, y), where (0,0) is the upper left. */
+ inline const Color4uint8& pixel4(int x, int y) const {
+ debugAssert(x >= 0 && x < width);
+ debugAssert(y >= 0 && y < height);
+ return pixel4()[x + y * width];
+ }
+
+ inline Color4uint8& pixel4(int x, int y) {
+ debugAssert(x >= 0 && x < width);
+ debugAssert(y >= 0 && y < height);
+ return pixel4()[x + y * width];
+ }
+
+ inline uint8* byte() {
+ return _byte;
+ }
+
+ inline Color3uint8* pixel3() {
+ debugAssert(channels == 3);
+ return (Color3uint8*)_byte;
+ }
+
+ inline Color4uint8* pixel4() {
+ debugAssert(channels == 4);
+ return (Color4uint8*)_byte;
+ }
+
+private:
+
+ void encodeBMP(
+ BinaryOutput& out) const;
+
+ /**
+ The TGA file will be either 24- or 32-bit depending
+ on the number of channels.
+ */
+ void encodeTGA(
+ BinaryOutput& out) const;
+
+ /**
+ Converts this image into a JPEG
+ */
+ void encodeJPEG(
+ BinaryOutput& out) const;
+
+ /**
+ Converts this image into a JPEG
+ */
+ void encodePNG(
+ BinaryOutput& out) const;
+
+ void encodePPM(
+ BinaryOutput& out) const;
+
+ void encodePPMASCII(
+ BinaryOutput& out) const;
+
+ void decodeTGA(
+ BinaryInput& input);
+
+ void decodeBMP(
+ BinaryInput& input);
+
+ void decodeJPEG(
+ BinaryInput& input);
+
+ void decodePCX(
+ BinaryInput& input);
+
+ void decodeICO(
+ BinaryInput& input);
+
+ void decodePNG(
+ BinaryInput& input);
+
+ void decodePPM(
+ BinaryInput& input);
+
+ void decodePPMASCII(
+ BinaryInput& input);
+
+ /**
+ Given [maybe] a filename, memory buffer, and [maybe] a format,
+ returns the most likely format of this file.
+ */
+ static Format resolveFormat(
+ const std::string& filename,
+ const uint8* data,
+ int dataLen,
+ Format maybeFormat);
+
+ void _copy(
+ const GImage& other);
+
+public:
+ static Format resolveFormat(const std::string& filename);
+
+ GImage() {
+ width = height = channels = 0;
+ _byte = NULL;
+ }
+
+ /**
+ Load an encoded image from disk and decode it.
+ Throws GImage::Error if something goes wrong.
+ */
+ GImage(
+ const std::string& filename,
+ Format format = AUTODETECT);
+
+ /**
+ Decodes an image stored in a buffer.
+ */
+ GImage(
+ const unsigned char*data,
+ int length,
+ Format format = AUTODETECT);
+
+ /**
+ Create an empty image of the given size.
+ */
+ GImage(
+ int width,
+ int height,
+ int channels = 3);
+
+ GImage(
+ const GImage& other);
+
+ GImage& operator=(const GImage& other);
+
+ /**
+ Returns a new GImage that has 4 channels. RGB is
+ taken from this GImage and the alpha from the red
+ channel of the supplied image. The new GImage is passed
+ as a reference parameter for speed.
+ */
+ void insertRedAsAlpha(const GImage& alpha, GImage& output) const;
+
+ GImage G3D_DEPRECATED insertRedAsAlpha(const GImage& alpha) const;
+
+ /**
+ Returns a new GImage with 3 channels, removing
+ the alpha channel if there is one. The new GImage
+ is passed as a reference parameter for speed.
+ */
+ void stripAlpha(GImage& output) const;
+
+ GImage G3D_DEPRECATED stripAlpha() const;
+
+ /**
+ Loads an image from disk (clearing the old one first).
+ */
+ void load(
+ const std::string& filename,
+ Format format = AUTODETECT);
+
+ /**
+ Frees memory and resets to a 0x0 image.
+ */
+ void clear();
+
+ /**
+ Deallocates the pixels.
+ */
+ virtual ~GImage();
+
+ /**
+ Resizes the internal buffer to (width x height) with the
+ number of channels specified. All data is set to 0 (black).
+ */
+ void resize(int width, int height, int channels);
+
+
+ /**
+ Copies src sub-image data into dest at a certain offset.
+ The dest variable must already contain an image that is large
+ enough to contain the src sub-image at the specified offset.
+ Returns true on success and false if the src sub-image cannot
+ completely fit within dest at the specified offset. Both
+ src and dest must have the same number of channels.
+ */
+ static bool pasteSubImage(GImage & dest, const GImage & src,
+ int destX, int destY, int srcX, int srcY, int srcWidth, int srcHeight);
+
+ /**
+ creates dest from src sub-image data.
+ Returns true on success and false if the src sub-image
+ is not within src.
+ */
+ static bool copySubImage(GImage & dest, const GImage & src,
+ int srcX, int srcY, int srcWidth, int srcHeight);
+
+ void convertToRGBA();
+
+ void convertToRGB();
+
+ /** Averages color channels if they exist */
+ void convertToL8();
+
+ /**
+ Returns true if format is supported. Format
+ should be an extension string (e.g. "BMP").
+ */
+ static bool supportedFormat(
+ const std::string& format);
+
+ /**
+ Converts a string to an enum, returns UNKNOWN if not recognized.
+ */
+ static Format stringToFormat(
+ const std::string& format);
+
+ /**
+ Encode and save to disk.
+ */
+ void save(
+ const std::string& filename,
+ Format format = AUTODETECT) const;
+
+ /**
+ The caller must delete the returned buffer.
+ */
+ void encode(
+ Format format,
+ uint8*& outData,
+ int& outLength) const;
+
+ /**
+ Does not commit the BinaryOutput when done.
+ */
+ void encode(
+ Format format,
+ BinaryOutput& out) const;
+
+ /**
+ Decodes the buffer into this image.
+ @format Must be the correct format.
+ */
+ void decode(
+ BinaryInput& input,
+ Format format);
+
+ /** Returns the size of this object in bytes */
+ int sizeInMemory() const;
+
+
+ /** Ok for in == out */
+ static void R8G8B8_to_Y8U8V8(int width, int height, const uint8* in, uint8* out);
+
+ /** Ok for in == out */
+ static void Y8U8V8_to_R8G8B8(int width, int height, const uint8* in, uint8* out);
+
+ /**
+ @param in RGB buffer of numPixels * 3 bytes
+ @param out Buffer of numPixels * 4 bytes
+ @param numPixels Number of RGB pixels to convert
+ */
+ static void RGBtoRGBA(
+ const uint8* in,
+ uint8* out,
+ int numPixels);
+
+ static void RGBtoARGB(
+ const uint8* in,
+ uint8* out,
+ int numPixels);
+
+ /** Safe for in == out */
+ static void RGBtoBGR(
+ const uint8* in,
+ uint8* out,
+ int numPixels);
+
+ /**
+ Win32 32-bit HDC format.
+ */
+ static void RGBtoBGRA(
+ const uint8* in,
+ uint8* out,
+ int numPixels);
+
+ static void RGBAtoRGB(
+ const uint8* in,
+ uint8* out,
+ int numPixels);
+ /**
+ Uses the red channel of the second image as an alpha channel.
+ */
+ static void RGBxRGBtoRGBA(
+ const uint8* colorRGB,
+ const uint8* alphaRGB,
+ uint8* out,
+ int numPixels);
+
+ /**
+ Flips the image along the vertical axis.
+ Safe for in == out.
+ */
+ static void flipRGBVertical(
+ const uint8* in,
+ uint8* out,
+ int width,
+ int height);
+
+ static void flipRGBAVertical(
+ const uint8* in,
+ uint8* out,
+ int width,
+ int height);
+
+ /**
+ Given a tangent space bump map, computes a new image where the
+ RGB channels are a tangent space normal map and the alpha channel
+ is the original bump map. Assumes the input image is tileable.
+
+ In the resulting image, x = red = tangent, y = green = binormal, and z = blue = normal.
+
+ Particularly useful as part of the idiom:
+ <PRE>
+ GImage normal;
+ computeNormalMap(GImage(filename), normal);
+ return Texture::fromGImage(filename, normal);
+ </PRE>
+
+ @param lowPassBump If true, a 9x9 filter of 1's is used to low-pass filter the elevations,
+ which produces better results for parallax mapping.
+
+ @param scaleHeightByNz After computing normals, scale the height by |N.z|, a trick that
+ reduces texture swim in steep areas for parallax mapping.
+
+ @param whiteHeightInPixels How high should the brightest input value be considered for purposes
+ of normal computation, compared to the horizontal and vertical size of a pixel.
+ A value of 255 means that a 255 x 255 bump image with a full black-to-white gradient will
+ produce a 45-degree ramp (this also results in "cubic" voxels).
+ A special (default) value of -1 means scale the effective white height so that it is equal
+ to the larger spatial dimension.
+ */
+ static void computeNormalMap(
+ const class GImage& bump,
+ class GImage& normal,
+ float whiteHeightInPixels = -1.0f,
+ bool lowPassBump = false,
+ bool scaleHeightByNz = false);
+
+ static void computeNormalMap(
+ int width,
+ int height,
+ int channels,
+ const uint8* src,
+ GImage& normal,
+ float whiteHeightInPixels,
+ bool lowPassBump,
+ bool scaleHeightByNz);
+
+ /**
+ Bayer demosaicing using the filter proposed in
+
+ HIGH-QUALITY LINEAR INTERPOLATION FOR DEMOSAICING OF BAYER-PATTERNED COLOR IMAGES
+ Henrique S. Malvar, Li-wei He, and Ross Cutler
+
+ The filter wraps at the image boundaries.
+
+ Assumes in != out.
+ */
+ static void BAYER_G8B8_R8G8_to_R8G8B8_MHC(int w, int h, const uint8* in, uint8* _out);
+ static void BAYER_G8R8_B8G8_to_R8G8B8_MHC(int w, int h, const uint8* in, uint8* _out);
+ static void BAYER_R8G8_G8B8_to_R8G8B8_MHC(int w, int h, const uint8* in, uint8* _out);
+ static void BAYER_B8G8_G8R8_to_R8G8B8_MHC(int w, int h, const uint8* in, uint8* _out);
+
+ /** Fast conversion; the output has 1/2 the size of the input in each direction. Assumes in != out.
+ See G3D::BAYER_G8B8_R8G8_to_R8G8B8_MHC for a much better result. */
+ static void BAYER_G8B8_R8G8_to_Quarter_R8G8B8(int inWidth, int inHeight, const uint8* in, uint8* out);
+
+ /** Attempt to undo fast conversion of G3D::BAYER_G8B8_R8G8_to_Quarter_R8G8B8;
+ the green channel will lose data. Assumes in != out
+ The input should have size 3 * inWidth * inHeight. The output should have size
+ 2 * inWidth * 2 * inHeight.
+ */
+ static void Quarter_R8G8B8_to_BAYER_G8B8_R8G8(int inWidth, int inHeight, const uint8* in, uint8* out);
+
+ /** Overwrites every pixel with one of the two colors in a checkerboard pattern.
+ The fields used from the two colors depend on the current number of channels in @a im.
+ */
+ static void makeCheckerboard(GImage& im, int checkerSize = 1, const Color4uint8& color1 = Color4uint8(255,255,255,255), const Color4uint8& color2 = Color4uint8(0,0,0,255));
+};
+
+}
+
+#endif
diff --git a/externals/g3dlite/G3D.lib/include/G3D/GLight.h b/externals/g3dlite/G3D.lib/include/G3D/GLight.h
new file mode 100644
index 00000000000..b8fa7b54261
--- /dev/null
+++ b/externals/g3dlite/G3D.lib/include/G3D/GLight.h
@@ -0,0 +1,72 @@
+/**
+ @file GLight.h
+
+ @maintainer Morgan McGuire, matrix@graphics3d.com
+
+ @created 2003-11-12
+ @edited 2006-02-08
+*/
+
+#ifndef G3D_GLIGHT_H
+#define G3D_GLIGHT_H
+
+#include "G3D/platform.h"
+#include "G3D/Vector4.h"
+#include "G3D/Vector3.h"
+#include "G3D/Color4.h"
+
+namespace G3D {
+
+/**
+ A light representation that closely follows the OpenGL light format.
+ */
+class GLight {
+public:
+ /** World space position (for a directional light, w = 0 */
+ Vector4 position;
+
+ /** Direction in which the light faces, if a spot light. This is the "look vector" of the light source. */
+ Vector3 spotDirection;
+
+ /** In <B>degrees</B>. 180 = no cutoff (point/dir) >90 = spot light */
+ float spotCutoff;
+
+ /** Constant, linear, quadratic */
+ float attenuation[3];
+
+ /** May be outside the range [0, 1] */
+ Color3 color;
+
+ /** If false, this light is ignored */
+ bool enabled;
+
+ /** If false, this light does not create specular highlights (useful when using negative lights). */
+ bool specular;
+
+ /** If false, this light does not create diffuse illumination (useful when rendering a specular-only pass). */
+ bool diffuse;
+
+ GLight();
+
+ /** @param toLight will be normalized */
+ static GLight directional(const Vector3& toLight, const Color3& color, bool specular = true, bool diffuse = true);
+
+ static GLight point(const Vector3& pos, const Color3& color, float constAtt = 1, float linAtt = 0, float quadAtt = 0.5f, bool specular = true, bool diffuse = true);
+
+ /** @param pointDirection Will be normalized. Points in the direction that light propagates.
+ @param cutOffAngleDegrees Must be on the range [0, 90]. This is the angle from the point direction
+ to the edge of the light cone.
+ */
+ static GLight spot(const Vector3& pos, const Vector3& pointDirection, float cutOffAngleDegrees, const Color3& color, float constAtt = 1, float linAtt = 0, float quadAtt = 0, bool specular = true, bool diffuse = true);
+
+ /** Returns the sphere within which this light has some noticable effect. May be infinite.
+ @param cutoff The value at which the light intensity is considered negligible. */
+ class Sphere effectSphere(float cutoff = 30.0f / 255) const;
+
+ bool operator==(const GLight& other) const;
+ bool operator!=(const GLight& other) const;
+};
+
+} // namespace
+#endif
+
diff --git a/externals/g3dlite/G3D.lib/include/G3D/GThread.h b/externals/g3dlite/G3D.lib/include/G3D/GThread.h
new file mode 100644
index 00000000000..63f1c235bda
--- /dev/null
+++ b/externals/g3dlite/G3D.lib/include/G3D/GThread.h
@@ -0,0 +1,169 @@
+/**
+ @file GThread.h
+
+ @created 2005-09-22
+ @edited 2007-01-31
+
+ */
+
+#ifndef G3D_GTHREAD_H
+#define G3D_GTHREAD_H
+
+#include "G3D/platform.h"
+#include "G3D/ReferenceCount.h"
+#include <string>
+
+#ifndef G3D_WIN32
+# include <pthread.h>
+# include <signal.h>
+#endif
+
+
+namespace G3D {
+
+typedef ReferenceCountedPointer<class GThread> GThreadRef;
+
+/**
+ Platform independent thread implementation. You can either subclass and
+ override GThread::threadMain or call the create method with a method.
+
+ Beware of reference counting and threads. If circular references exist between
+ GThread subclasses then neither class will ever be deallocated. Also,
+ dropping all pointers (and causing deallocation) of a GThread does NOT
+ stop the underlying process.
+
+ @sa G3D::GMutex, G3D::AtomicInt32
+*/
+class GThread : public ReferenceCountedObject {
+private:
+ // "Status" is a reserved work on FreeBSD
+ enum GStatus {STATUS_CREATED, STATUS_STARTED, STATUS_RUNNING, STATUS_COMPLETED};
+
+ // Not implemented on purpose, don't use
+ GThread(const GThread &);
+ GThread& operator=(const GThread&);
+ bool operator==(const GThread&);
+
+#ifdef G3D_WIN32
+ static DWORD WINAPI internalThreadProc(LPVOID param);
+#else
+ static void* internalThreadProc(void* param);
+#endif //G3D_WIN32
+
+ volatile GStatus m_status;
+
+ // Thread handle to hold HANDLE and pthread_t
+#ifdef G3D_WIN32
+ HANDLE m_handle;
+ HANDLE m_event;
+#else
+ pthread_t m_handle;
+#endif //G3D_WIN32
+
+ std::string m_name;
+
+public:
+
+ GThread(const std::string& name);
+
+ virtual ~GThread();
+
+ /** Constructs a basic GThread without requiring a subclass.
+
+ @param proc The global or static function for the threadMain() */
+ static GThreadRef create(const std::string& name, void (*proc)(void*), void* param);
+
+ /** @deprecated use overload that accepts void* param */
+ static GThreadRef create(const std::string& name, void (*proc)());
+
+ /** Starts the thread and executes threadMain(). Returns false if
+ the thread failed to start (either because it was already started
+ or because the OS refused).*/
+ bool start();
+
+ /** Terminates the thread without notifying or
+ waiting for a cancelation point. */
+ void terminate();
+
+ /**
+ Returns true if threadMain is currently executing. This will
+ only be set when the thread is actually running and might not
+ be set when start() returns. */
+ bool running() const;
+
+ /** True after start() has been called, even through the thread
+ may have already completed(), or be currently running().*/
+ bool started() const;
+
+ /** Returns true if the thread has exited. */
+ bool completed() const;
+
+ /** Waits for the thread to finish executing. */
+ void waitForCompletion();
+
+ /** Returns thread name */
+ inline const std::string& name() {
+ return m_name;
+ }
+
+ /** Overriden by the thread implementor */
+ virtual void threadMain() = 0;
+};
+
+
+/**
+ Mutual exclusion lock used for synchronization.
+ @sa G3D::GThread, G3D::AtomicInt32
+*/
+class GMutex {
+private:
+# ifdef G3D_WIN32
+ CRITICAL_SECTION m_handle;
+# else
+ pthread_mutex_t m_handle;
+# endif
+
+ // Not implemented on purpose, don't use
+ GMutex(const GMutex &mlock);
+ GMutex &operator=(const GMutex &);
+ bool operator==(const GMutex&);
+
+public:
+ GMutex();
+ ~GMutex();
+
+ /** Locks the mutex or blocks until available. */
+ void lock();
+
+ /** Unlocks the mutex. */
+ void unlock();
+};
+
+
+/**
+ Automatically locks while in scope.
+*/
+class GMutexLock {
+private:
+ GMutex* m;
+
+ // Not implemented on purpose, don't use
+ GMutexLock(const GMutexLock &mlock);
+ GMutexLock &operator=(const GMutexLock &);
+ bool operator==(const GMutexLock&);
+
+public:
+ GMutexLock(GMutex* mutex) {
+ m = mutex;
+ m->lock();
+ }
+
+ ~GMutexLock() {
+ m->unlock();
+ }
+};
+
+
+} // namespace G3D
+
+#endif //G3D_GTHREAD_H
diff --git a/externals/g3dlite/G3D.lib/include/G3D/GUniqueID.h b/externals/g3dlite/G3D.lib/include/G3D/GUniqueID.h
new file mode 100644
index 00000000000..44ec6e03405
--- /dev/null
+++ b/externals/g3dlite/G3D.lib/include/G3D/GUniqueID.h
@@ -0,0 +1,69 @@
+/**
+ @file GUniqueID.h
+ @author Morgan McGuire, morgan@cs.williams.edu
+ */
+#ifndef G3D_GUNIQUEID_H
+#define G3D_GUNIQUEID_H
+
+#include "G3D/platform.h"
+#include "G3D/g3dmath.h"
+#include "G3D/Table.h"
+
+namespace G3D {
+
+/** Globally unique identifiers. The probability of two different
+ programs generating the same value from UniqueID::create is
+ vanishingly small.
+
+ UniqueIDs optionally contain a 10-bit application specific tag
+ that distinguishes their type.
+*/
+class GUniqueID {
+private:
+
+ uint64 id;
+
+public:
+
+ GUniqueID() : id(0) {}
+
+ bool uninitialized() const {
+ return id == 0;
+ }
+
+ uint16 tag() const {
+ return id >> 54;
+ }
+
+ operator uint64() const {
+ return id;
+ }
+
+ bool operator==(const GUniqueID& other) const {
+ return id == other.id;
+ }
+
+ bool operator!=(const GUniqueID& other) const {
+ return id != other.id;
+ }
+
+ void serialize(class BinaryOutput& b) const;
+
+ void deserialize(class BinaryInput& b);
+
+ void serialize(class TextOutput& t) const;
+
+ void deserialize(class TextInput& t);
+
+ /** Create a new ID */
+ static GUniqueID create(uint16 tag = 0);
+};
+
+} // G3D
+
+/** For Table and Set */
+template<> struct HashTrait<class G3D::GUniqueID> {
+ static size_t hashCode(G3D::GUniqueID id) { return (size_t)(G3D::uint64)id; }
+};
+
+#endif
diff --git a/externals/g3dlite/G3D.lib/include/G3D/HashTrait.h b/externals/g3dlite/G3D.lib/include/G3D/HashTrait.h
new file mode 100644
index 00000000000..702903ff09b
--- /dev/null
+++ b/externals/g3dlite/G3D.lib/include/G3D/HashTrait.h
@@ -0,0 +1,63 @@
+/**
+ @file HashTrait.h
+
+ @maintainer Morgan McGuire, morgan@cs.williams.edu
+ @created 2008-10-01
+ @edited 2008-10-01
+ Copyright 2000-2008, Morgan McGuire.
+ All rights reserved.
+ */
+
+#ifndef G3D_HASHTRAIT_H
+#define G3D_HASHTRAIT_H
+
+#include "G3D/platform.h"
+#include "G3D/Crypto.h"
+#include "G3D/g3dmath.h"
+#include "G3D/uint128.h"
+
+/** Must be specialized for custom types.
+ @see G3D::Table for specialization requirements.
+*/
+template <typename T> struct HashTrait{};
+
+template <typename T> struct HashTrait<T*> {
+ static size_t hashCode(const void* k) { return reinterpret_cast<size_t>(k); }
+};
+
+template <> struct HashTrait <int> {
+ static size_t hashCode(int k) { return static_cast<size_t>(k); }
+};
+
+template <> struct HashTrait <G3D::uint32> {
+ static size_t hashCode(G3D::uint32 k) { return static_cast<size_t>(k); }
+};
+
+template <> struct HashTrait <G3D::uint64> {
+ static size_t hashCode(G3D::uint64 k) { return static_cast<size_t>(k); }
+};
+
+template <> struct HashTrait <std::string> {
+ static size_t hashCode(const std::string& k) { return static_cast<size_t>(G3D::Crypto::crc32(k.c_str(), k.size())); }
+};
+
+template <> struct HashTrait<G3D::uint128> {
+ // Use the FNV-1 hash (http://isthe.com/chongo/tech/comp/fnv/#FNV-1).
+ static size_t hashCode(G3D::uint128 key) {
+ static const G3D::uint128 FNV_PRIME_128(1 << 24, 0x159);
+ static const G3D::uint128 FNV_OFFSET_128(0xCF470AAC6CB293D2LL, 0xF52F88BF32307F8FLL);
+
+ G3D::uint128 hash = FNV_OFFSET_128;
+ G3D::uint128 mask(0, 0xFF);
+ for (int i = 0; i < 16; ++i) {
+ hash *= FNV_PRIME_128;
+ hash ^= (mask & key);
+ key >>= 8;
+ }
+
+ G3D::uint64 foldedHash = hash.hi ^ hash.lo;
+ return static_cast<size_t>((foldedHash >> 32) ^ (foldedHash & 0xFFFFFFFF));
+ }
+};
+
+#endif
diff --git a/externals/g3dlite/G3D.lib/include/G3D/Image1.h b/externals/g3dlite/G3D.lib/include/G3D/Image1.h
new file mode 100644
index 00000000000..f0850710d2c
--- /dev/null
+++ b/externals/g3dlite/G3D.lib/include/G3D/Image1.h
@@ -0,0 +1,79 @@
+/**
+ @file Image1.h
+
+ @maintainer Morgan McGuire, matrix@graphics3d.com
+
+ @created 2007-01-31
+ @edited 2007-01-31
+*/
+
+
+#ifndef G3D_IMAGE1_H
+#define G3D_IMAGE1_H
+
+#include "G3D/platform.h"
+#include "G3D/Map2D.h"
+#include "G3D/Color1.h"
+#include "G3D/GImage.h"
+
+namespace G3D {
+
+typedef ReferenceCountedPointer<class Image1> Image1Ref;
+
+/**
+ Luminance image with 32-bit floating point storage.
+
+ See also G3D::Image1uint8, G3D::GImage.
+ */
+class Image1 : public Map2D<Color1, Color1> {
+public:
+
+ typedef Image1 Type;
+ typedef Image1Ref Ref;
+
+protected:
+
+ Image1(int w, int h, WrapMode wrap);
+
+ void copyGImage(const class GImage& im);
+ void copyArray(const Color1* src, int w, int h);
+ void copyArray(const Color3* src, int w, int h);
+ void copyArray(const Color4* src, int w, int h);
+ void copyArray(const Color1uint8* src, int w, int h);
+ void copyArray(const Color3uint8* src, int w, int h);
+ void copyArray(const Color4uint8* src, int w, int h);
+
+public:
+
+ const class ImageFormat* format() const;
+
+ /** Creates an all-zero width x height image. */
+ static Ref createEmpty(int width, int height, WrapMode wrap = WrapMode::ERROR);
+
+ /** Creates a 0 x 0 image. */
+ static Ref createEmpty(WrapMode wrap = WrapMode::ERROR);
+
+ static Ref fromFile(const std::string& filename, WrapMode wrap = WrapMode::ERROR, GImage::Format fmt = GImage::AUTODETECT);
+
+ static Ref fromArray(const class Color1uint8* ptr, int width, int height, WrapMode wrap = WrapMode::ERROR);
+ static Ref fromArray(const class Color3uint8* ptr, int width, int height, WrapMode wrap = WrapMode::ERROR);
+ static Ref fromArray(const class Color4uint8* ptr, int width, int height, WrapMode wrap = WrapMode::ERROR);
+ static Ref fromArray(const class Color1* ptr, int width, int height, WrapMode wrap = WrapMode::ERROR);
+ static Ref fromArray(const class Color3* ptr, int width, int height, WrapMode wrap = WrapMode::ERROR);
+ static Ref fromArray(const class Color4* ptr, int width, int height, WrapMode wrap = WrapMode::ERROR);
+
+ static Ref fromImage1uint8(const ReferenceCountedPointer<class Image1uint8>& im);
+
+ static Ref fromGImage(const class GImage& im, WrapMode wrap = WrapMode::ERROR);
+
+ /** Loads from any of the file formats supported by G3D::GImage. If there is an alpha channel on the input,
+ it is stripped. */
+ void load(const std::string& filename, GImage::Format fmt = GImage::AUTODETECT);
+
+ /** Saves in any of the formats supported by G3D::GImage. */
+ void save(const std::string& filename, GImage::Format fmt = GImage::AUTODETECT);
+};
+
+} // G3D
+
+#endif
diff --git a/externals/g3dlite/G3D.lib/include/G3D/Image1uint8.h b/externals/g3dlite/G3D.lib/include/G3D/Image1uint8.h
new file mode 100644
index 00000000000..7225ca35db8
--- /dev/null
+++ b/externals/g3dlite/G3D.lib/include/G3D/Image1uint8.h
@@ -0,0 +1,80 @@
+/**
+ @file Image1uint8.h
+
+ @maintainer Morgan McGuire, matrix@graphics3d.com
+
+ @created 2007-01-31
+ @edited 2007-01-31
+*/
+
+#ifndef G3D_IMAGE1UINT8_H
+#define G3D_IMAGE1UINT8_H
+
+#include "G3D/platform.h"
+#include "G3D/Map2D.h"
+#include "G3D/Color1uint8.h"
+#include "G3D/Color1.h"
+#include "G3D/GImage.h"
+
+namespace G3D {
+
+typedef ReferenceCountedPointer<class Image1uint8> Image1uint8Ref;
+
+/**
+ Compact storage for luminance 8-bit images.
+
+ See also G3D::Image3, G3D::GImage
+ */
+class Image1uint8 : public Map2D<Color1uint8, Color1> {
+public:
+
+ typedef Image1uint8 Type;
+ typedef Image1uint8Ref Ref;
+
+protected:
+
+ Image1uint8(int w, int h, WrapMode wrap);
+
+ void copyGImage(const class GImage& im);
+ void copyArray(const Color1* src, int w, int h);
+ void copyArray(const Color3* src, int w, int h);
+ void copyArray(const Color4* src, int w, int h);
+ void copyArray(const Color1uint8* src, int w, int h);
+ void copyArray(const Color3uint8* src, int w, int h);
+ void copyArray(const Color4uint8* src, int w, int h);
+
+public:
+
+ const class ImageFormat* format() const;
+
+ /** Creates an all-zero width x height image. */
+ static Ref createEmpty(int width, int height, WrapMode wrap = WrapMode::ERROR);
+
+ /** Creates a 0 x 0 image. */
+ static Ref createEmpty(WrapMode wrap = WrapMode::ERROR);
+
+ static Ref fromFile(const std::string& filename, WrapMode wrap = WrapMode::ERROR, GImage::Format fmt = GImage::AUTODETECT);
+
+ static Ref fromGImage(const class GImage& im, WrapMode wrap = WrapMode::ERROR);
+
+ static Ref fromArray(const class Color1uint8* ptr, int width, int height, WrapMode wrap = WrapMode::ERROR);
+ static Ref fromArray(const class Color3uint8* ptr, int width, int height, WrapMode wrap = WrapMode::ERROR);
+ static Ref fromArray(const class Color4uint8* ptr, int width, int height, WrapMode wrap = WrapMode::ERROR);
+ static Ref fromArray(const class Color1* ptr, int width, int height, WrapMode wrap = WrapMode::ERROR);
+ static Ref fromArray(const class Color3* ptr, int width, int height, WrapMode wrap = WrapMode::ERROR);
+ static Ref fromArray(const class Color4* ptr, int width, int height, WrapMode wrap = WrapMode::ERROR);
+
+ static Ref fromImage1(const ReferenceCountedPointer<class Image1>& im);
+ static Ref fromImage3uint8(const ReferenceCountedPointer<class Image3uint8>& im);
+
+ /** Loads from any of the file formats supported by G3D::GImage. If there is an alpha channel on the input,
+ it is stripped. */
+ void load(const std::string& filename, GImage::Format fmt = GImage::AUTODETECT);
+
+ /** Saves in any of the formats supported by G3D::GImage. */
+ void save(const std::string& filename, GImage::Format fmt = GImage::AUTODETECT);
+};
+
+} // G3D
+
+#endif
diff --git a/externals/g3dlite/G3D.lib/include/G3D/Image3.h b/externals/g3dlite/G3D.lib/include/G3D/Image3.h
new file mode 100644
index 00000000000..c67669c1c5e
--- /dev/null
+++ b/externals/g3dlite/G3D.lib/include/G3D/Image3.h
@@ -0,0 +1,79 @@
+/**
+ @file Image3.h
+
+ @maintainer Morgan McGuire, matrix@graphics3d.com
+
+ @created 2007-01-31
+ @edited 2007-01-31
+*/
+
+
+#ifndef G3D_IMAGE3_H
+#define G3D_IMAGE3_H
+
+#include "G3D/platform.h"
+#include "G3D/Map2D.h"
+#include "G3D/Color3.h"
+#include "G3D/GImage.h"
+
+namespace G3D {
+
+typedef ReferenceCountedPointer<class Image3> Image3Ref;
+
+/**
+ RGB image with 32-bit floating point storage for each channel.
+
+ See also G3D::Image3uint8, G3D::GImage.
+ */
+class Image3 : public Map2D<Color3, Color3> {
+public:
+
+ typedef Image3 Type;
+ typedef Image3Ref Ref;
+
+protected:
+
+ Image3(int w, int h, WrapMode wrap);
+
+ void copyGImage(const class GImage& im);
+ void copyArray(const Color1* src, int w, int h);
+ void copyArray(const Color3* src, int w, int h);
+ void copyArray(const Color4* src, int w, int h);
+ void copyArray(const Color1uint8* src, int w, int h);
+ void copyArray(const Color3uint8* src, int w, int h);
+ void copyArray(const Color4uint8* src, int w, int h);
+
+public:
+
+ const class ImageFormat* format() const;
+
+ /** Creates an all-zero width x height image. */
+ static Ref createEmpty(int width, int height, WrapMode wrap = WrapMode::ERROR);
+
+ /** Creates a 0 x 0 image. */
+ static Ref createEmpty(WrapMode wrap = WrapMode::ERROR);
+
+ static Ref fromFile(const std::string& filename, WrapMode wrap = WrapMode::ERROR, GImage::Format fmt = GImage::AUTODETECT);
+
+ static Ref fromArray(const class Color1uint8* ptr, int width, int height, WrapMode wrap = WrapMode::ERROR);
+ static Ref fromArray(const class Color3uint8* ptr, int width, int height, WrapMode wrap = WrapMode::ERROR);
+ static Ref fromArray(const class Color4uint8* ptr, int width, int height, WrapMode wrap = WrapMode::ERROR);
+ static Ref fromArray(const class Color1* ptr, int width, int height, WrapMode wrap = WrapMode::ERROR);
+ static Ref fromArray(const class Color3* ptr, int width, int height, WrapMode wrap = WrapMode::ERROR);
+ static Ref fromArray(const class Color4* ptr, int width, int height, WrapMode wrap = WrapMode::ERROR);
+
+ static Ref fromImage3uint8(const ReferenceCountedPointer<class Image3uint8>& im);
+
+ static Ref fromGImage(const class GImage& im, WrapMode wrap = WrapMode::ERROR);
+
+ /** Loads from any of the file formats supported by G3D::GImage. If there is an alpha channel on the input,
+ it is stripped. */
+ void load(const std::string& filename, GImage::Format fmt = GImage::AUTODETECT);
+
+ /** Saves in any of the formats supported by G3D::GImage. */
+ void save(const std::string& filename, GImage::Format fmt = GImage::AUTODETECT);
+};
+
+} // G3D
+
+#endif
diff --git a/externals/g3dlite/G3D.lib/include/G3D/Image3uint8.h b/externals/g3dlite/G3D.lib/include/G3D/Image3uint8.h
new file mode 100644
index 00000000000..9ee1ef6678b
--- /dev/null
+++ b/externals/g3dlite/G3D.lib/include/G3D/Image3uint8.h
@@ -0,0 +1,85 @@
+/**
+ @file Image3uint8.h
+
+ @maintainer Morgan McGuire, matrix@graphics3d.com
+
+ @created 2007-01-31
+ @edited 2007-01-31
+*/
+
+#ifndef G3D_IMAGE3UINT8_H
+#define G3D_IMAGE3UINT8_H
+
+#include "G3D/platform.h"
+#include "G3D/Map2D.h"
+#include "G3D/Color3uint8.h"
+#include "G3D/Color3.h"
+#include "G3D/GImage.h"
+
+namespace G3D {
+
+typedef ReferenceCountedPointer<class Image3uint8> Image3uint8Ref;
+
+/**
+ Compact storage for RGB 8-bit per channel images.
+
+ See also G3D::Image3, G3D::GImage
+ */
+class Image3uint8 : public Map2D<Color3uint8, Color3> {
+public:
+
+ typedef Image3uint8 Type;
+ typedef Image3uint8Ref Ref;
+
+protected:
+
+ Image3uint8(int w, int h, WrapMode wrap);
+
+ void copyGImage(const class GImage& im);
+ void copyArray(const Color1* src, int w, int h);
+ void copyArray(const Color3* src, int w, int h);
+ void copyArray(const Color4* src, int w, int h);
+ void copyArray(const Color1uint8* src, int w, int h);
+ void copyArray(const Color3uint8* src, int w, int h);
+ void copyArray(const Color4uint8* src, int w, int h);
+
+public:
+
+ const class ImageFormat* format() const;
+
+ /** Creates an all-zero width x height image. */
+ static Ref createEmpty(int width, int height, WrapMode wrap = WrapMode::ERROR);
+
+
+ /** Creates a 0 x 0 image. */
+ static Ref createEmpty(WrapMode wrap = WrapMode::ERROR);
+
+
+ static Ref fromFile(const std::string& filename, WrapMode wrap = WrapMode::ERROR, GImage::Format fmt = GImage::AUTODETECT);
+
+ static Ref fromGImage(const class GImage& im, WrapMode wrap = WrapMode::ERROR);
+
+ static Ref fromArray(const class Color1uint8* ptr, int width, int height, WrapMode wrap = WrapMode::ERROR);
+ static Ref fromArray(const class Color3uint8* ptr, int width, int height, WrapMode wrap = WrapMode::ERROR);
+ static Ref fromArray(const class Color4uint8* ptr, int width, int height, WrapMode wrap = WrapMode::ERROR);
+ static Ref fromArray(const class Color1* ptr, int width, int height, WrapMode wrap = WrapMode::ERROR);
+ static Ref fromArray(const class Color3* ptr, int width, int height, WrapMode wrap = WrapMode::ERROR);
+ static Ref fromArray(const class Color4* ptr, int width, int height, WrapMode wrap = WrapMode::ERROR);
+
+ static Ref fromImage3(const ReferenceCountedPointer<class Image3>& im);
+ static Ref fromImage1uint8(const ReferenceCountedPointer<class Image1uint8>& im);
+
+ /** Loads from any of the file formats supported by G3D::GImage. If there is an alpha channel on the input,
+ it is stripped. */
+ void load(const std::string& filename, GImage::Format fmt = GImage::AUTODETECT);
+
+ /** Saves in any of the formats supported by G3D::GImage. */
+ void save(const std::string& filename, GImage::Format fmt = GImage::AUTODETECT);
+
+ /** Extracts color channel 0 <= c <= 2 and returns it as a new monochrome image. */
+ ReferenceCountedPointer<class Image1uint8> getChannel(int c) const;
+};
+
+} // G3D
+
+#endif
diff --git a/externals/g3dlite/G3D.lib/include/G3D/Image4.h b/externals/g3dlite/G3D.lib/include/G3D/Image4.h
new file mode 100644
index 00000000000..04df43f9527
--- /dev/null
+++ b/externals/g3dlite/G3D.lib/include/G3D/Image4.h
@@ -0,0 +1,80 @@
+/**
+ @file Image4.h
+
+ @maintainer Morgan McGuire, matrix@graphics3d.com
+
+ @created 2007-01-31
+ @edited 2007-01-31
+*/
+
+
+#ifndef G3D_IMAGE4_H
+#define G3D_IMAGE4_H
+
+#include "G3D/platform.h"
+#include "G3D/Map2D.h"
+#include "G3D/Color4.h"
+#include "G3D/GImage.h"
+
+namespace G3D {
+
+typedef ReferenceCountedPointer<class Image4> Image4Ref;
+
+/**
+ RGBA image with 32-bit floating point storage for each channel.
+
+ Whenever a method needs to convert from RGB to ARGB, A=1 is assumed.
+
+ See also G3D::Image4uint8, G3D::GImage.
+ */
+class Image4 : public Map2D<Color4, Color4> {
+public:
+
+ typedef Image4 Type;
+ typedef Image4Ref Ref;
+
+protected:
+
+ Image4(int w, int h, WrapMode wrap);
+
+ void copyGImage(const class GImage& im);
+ void copyArray(const Color1* src, int w, int h);
+ void copyArray(const Color3* src, int w, int h);
+ void copyArray(const Color4* src, int w, int h);
+ void copyArray(const Color1uint8* src, int w, int h);
+ void copyArray(const Color3uint8* src, int w, int h);
+ void copyArray(const Color4uint8* src, int w, int h);
+
+public:
+
+ const class ImageFormat* format() const;
+
+ /** Creates an all-zero width x height image. */
+ static Ref createEmpty(int width, int height, WrapMode wrap = WrapMode::ERROR);
+
+ /** Creates a 0 x 0 image. */
+ static Ref createEmpty(WrapMode wrap = WrapMode::ERROR);
+
+ static Ref fromFile(const std::string& filename, WrapMode wrap = WrapMode::ERROR, GImage::Format fmt = GImage::AUTODETECT);
+
+ static Ref fromArray(const class Color1uint8* ptr, int width, int height, WrapMode wrap = WrapMode::ERROR);
+ static Ref fromArray(const class Color3uint8* ptr, int width, int height, WrapMode wrap = WrapMode::ERROR);
+ static Ref fromArray(const class Color4uint8* ptr, int width, int height, WrapMode wrap = WrapMode::ERROR);
+ static Ref fromArray(const class Color1* ptr, int width, int height, WrapMode wrap = WrapMode::ERROR);
+ static Ref fromArray(const class Color3* ptr, int width, int height, WrapMode wrap = WrapMode::ERROR);
+ static Ref fromArray(const class Color4* ptr, int width, int height, WrapMode wrap = WrapMode::ERROR);
+
+ static Ref fromImage4uint8(const ReferenceCountedPointer<class Image4uint8>& im);
+
+ static Ref fromGImage(const class GImage& im, WrapMode wrap = WrapMode::ERROR);
+
+ /** Loads from any of the file formats supported by G3D::GImage. */
+ void load(const std::string& filename, GImage::Format fmt = GImage::AUTODETECT);
+
+ /** Saves in any of the formats supported by G3D::GImage. */
+ void save(const std::string& filename, GImage::Format fmt = GImage::AUTODETECT);
+};
+
+} // G3D
+
+#endif
diff --git a/externals/g3dlite/G3D.lib/include/G3D/Image4uint8.h b/externals/g3dlite/G3D.lib/include/G3D/Image4uint8.h
new file mode 100644
index 00000000000..11daf97c83c
--- /dev/null
+++ b/externals/g3dlite/G3D.lib/include/G3D/Image4uint8.h
@@ -0,0 +1,85 @@
+/**
+ @file Image4uint8.h
+
+ @maintainer Morgan McGuire, matrix@graphics3d.com
+
+ @created 2007-01-31
+ @edited 2007-01-31
+*/
+
+#ifndef G3D_IMAGE4UINT8_H
+#define G3D_IMAGE4UINT8_H
+
+#include "G3D/platform.h"
+#include "G3D/Map2D.h"
+#include "G3D/Color4uint8.h"
+#include "G3D/Color4.h"
+#include "G3D/GImage.h"
+#include "G3D/Image1uint8.h"
+
+namespace G3D {
+
+typedef ReferenceCountedPointer<class Image4uint8> Image4uint8Ref;
+
+/**
+ Compact storage for RGBA 8-bit per channel images.
+
+ See also G3D::Image4, G3D::GImage
+ */
+class Image4uint8 : public Map2D<Color4uint8, Color4> {
+public:
+
+ typedef Image4uint8 Type;
+ typedef Image4uint8Ref Ref;
+
+protected:
+
+ Image4uint8(int w, int h, WrapMode wrap);
+
+ void copyGImage(const class GImage& im);
+ void copyArray(const Color1* src, int w, int h);
+ void copyArray(const Color3* src, int w, int h);
+ void copyArray(const Color4* src, int w, int h);
+ void copyArray(const Color1uint8* src, int w, int h);
+ void copyArray(const Color3uint8* src, int w, int h);
+ void copyArray(const Color4uint8* src, int w, int h);
+
+public:
+
+ const class ImageFormat* format() const;
+
+ /** Creates an all-zero width x height image. */
+ static Ref createEmpty(int width, int height, WrapMode wrap = WrapMode::ERROR);
+
+
+ /** Creates a 0 x 0 image. */
+ static Ref createEmpty(WrapMode wrap = WrapMode::ERROR);
+
+
+ static Ref fromFile(const std::string& filename, WrapMode wrap = WrapMode::ERROR, GImage::Format fmt = GImage::AUTODETECT);
+
+ static Ref fromGImage(const class GImage& im, WrapMode wrap = WrapMode::ERROR);
+
+ static Ref fromArray(const class Color1uint8* ptr, int width, int height, WrapMode wrap = WrapMode::ERROR);
+ static Ref fromArray(const class Color3uint8* ptr, int width, int height, WrapMode wrap = WrapMode::ERROR);
+ static Ref fromArray(const class Color4uint8* ptr, int width, int height, WrapMode wrap = WrapMode::ERROR);
+ static Ref fromArray(const class Color1* ptr, int width, int height, WrapMode wrap = WrapMode::ERROR);
+ static Ref fromArray(const class Color3* ptr, int width, int height, WrapMode wrap = WrapMode::ERROR);
+ static Ref fromArray(const class Color4* ptr, int width, int height, WrapMode wrap = WrapMode::ERROR);
+
+ static Ref fromImage4(const ReferenceCountedPointer<class Image4>& im);
+
+ /** Loads from any of the file formats supported by G3D::GImage. If there is an alpha channel on the input,
+ it is stripped. */
+ void load(const std::string& filename, GImage::Format fmt = GImage::AUTODETECT);
+
+ /** Saves in any of the formats supported by G3D::GImage. */
+ void save(const std::string& filename, GImage::Format fmt = GImage::AUTODETECT);
+
+ /** Extracts color channel 0 <= c <= 3 and returns it as a new monochrome image. */
+ ReferenceCountedPointer<class Image1uint8> getChannel(int c) const;
+};
+
+} // G3D
+
+#endif
diff --git a/externals/g3dlite/G3D.lib/include/G3D/ImageFormat.h b/externals/g3dlite/G3D.lib/include/G3D/ImageFormat.h
new file mode 100644
index 00000000000..a1334fb1a5a
--- /dev/null
+++ b/externals/g3dlite/G3D.lib/include/G3D/ImageFormat.h
@@ -0,0 +1,362 @@
+/**
+ @file ImageFormat.h
+
+ @maintainer Morgan McGuire, matrix@graphics3d.com
+
+ @created 2003-05-23
+ @edited 2008-07-17
+*/
+
+#ifndef GLG3D_ImageFormat_H
+#define GLG3D_ImageFormat_H
+
+#include "G3D/platform.h"
+#include "G3D/Table.h"
+#include "G3D/enumclass.h"
+
+namespace G3D {
+
+/** Information about common image formats.
+ Don't construct these; use the methods provided.
+
+ For most formats, the number indicates the number of bits per channel and a suffix of "F" indicates
+ floating point. This does not hold for the YUV and DXT formats.*/
+class ImageFormat {
+public:
+
+ // Must update ImageFormat::name() when this enum changes.
+ enum Code {
+ CODE_NONE = -1,
+ CODE_L8,
+ CODE_L16,
+ CODE_L16F,
+ CODE_L32F,
+
+ CODE_A8,
+ CODE_A16,
+ CODE_A16F,
+ CODE_A32F,
+
+ CODE_LA4,
+ CODE_LA8,
+ CODE_LA16,
+ CODE_LA16F,
+ CODE_LA32F,
+
+ CODE_RGB5,
+ CODE_RGB5A1,
+ CODE_RGB8,
+ CODE_RGB10,
+ CODE_RGB10A2,
+ CODE_RGB16,
+ CODE_RGB16F,
+ CODE_RGB32F,
+
+ CODE_ARGB8,
+ CODE_BGR8,
+
+ CODE_RGBA8,
+ CODE_RGBA16,
+ CODE_RGBA16F,
+ CODE_RGBA32F,
+
+ CODE_BAYER_RGGB8,
+ CODE_BAYER_GRBG8,
+ CODE_BAYER_GBRG8,
+ CODE_BAYER_BGGR8,
+ CODE_BAYER_RGGB32F,
+ CODE_BAYER_GRBG32F,
+ CODE_BAYER_GBRG32F,
+ CODE_BAYER_BGGR32F,
+
+ CODE_HSV8,
+ CODE_HSV32F,
+
+ CODE_YUV420_PLANAR,
+ CODE_YUV422,
+ CODE_YUV444,
+
+ CODE_RGB_DXT1,
+ CODE_RGBA_DXT1,
+ CODE_RGBA_DXT3,
+ CODE_RGBA_DXT5,
+
+ CODE_DEPTH16,
+ CODE_DEPTH24,
+ CODE_DEPTH32,
+ CODE_DEPTH32F,
+
+ CODE_STENCIL1,
+ CODE_STENCIL4,
+ CODE_STENCIL8,
+ CODE_STENCIL16,
+
+ CODE_DEPTH24_STENCIL8,
+
+ CODE_NUM
+ };
+
+ enum ColorSpace {
+ COLOR_SPACE_NONE,
+ COLOR_SPACE_RGB,
+ COLOR_SPACE_HSV,
+ COLOR_SPACE_YUV
+ };
+
+ enum BayerPattern {
+ BAYER_PATTERN_NONE,
+ BAYER_PATTERN_RGGB,
+ BAYER_PATTERN_GRBG,
+ BAYER_PATTERN_GBRG,
+ BAYER_PATTERN_BGGR
+ };
+
+ /** Number of channels (1 for a depth texture). */
+ int numComponents;
+ bool compressed;
+
+ /** Useful for serializing. */
+ Code code;
+
+ ColorSpace colorSpace;
+
+ /** If this is a Bayer format, what is the pattern. */
+ BayerPattern bayerPattern;
+
+ /** The OpenGL format equivalent to this one, e.g, GL_RGB8 Zero if there is no equivalent. This is actually a GLenum */
+ int openGLFormat;
+
+ /** The OpenGL base format equivalent to this one (e.g., GL_RGB, GL_ALPHA). Zero if there is no equivalent. */
+ int openGLBaseFormat;
+
+ int luminanceBits;
+
+ /** Number of bits per pixel storage for alpha values; Zero for compressed textures and non-RGB. */
+ int alphaBits;
+
+ /** Number of bits per pixel storage for red values; Zero for compressed textures and non-RGB. */
+ int redBits;
+
+ /** Number of bits per pixel storage for green values; Zero for compressed textures and non-RGB. */
+ int greenBits;
+
+ /** Number of bits per pixel storage for blue values; Zero for compressed textures and non-RGB. */
+ int blueBits;
+
+ /** Number of bits per pixel */
+ int stencilBits;
+
+ /** Number of depth bits (for depth textures; e.g. shadow maps) */
+ int depthBits;
+
+ /** Amount of CPU memory per pixel when packed into an array, discounting any end-of-row padding. */
+ int cpuBitsPerPixel;
+
+ /** Amount of CPU memory per pixel when packed into an array, discounting any end-of-row padding. @deprecated Use cpuBitsPerPixel*/
+ int packedBitsPerTexel;
+
+ /**
+ Amount of GPU memory per pixel on most graphics cards, for formats supported by OpenGL. This is
+ only an estimate--the actual amount of memory may be different on your actual card.
+
+ This may be greater than the sum of the per-channel bits
+ because graphics cards need to pad to the nearest 1, 2, or
+ 4 bytes.
+ */
+ int openGLBitsPerPixel;
+
+ /** @deprecated Use openGLBitsPerPixel */
+ int hardwareBitsPerTexel;
+
+ /** The OpenGL bytes format of the data buffer used with this texture format, e.g., GL_UNSIGNED_BYTE */
+ int openGLDataFormat;
+
+ /** True if there is no alpha channel for this texture. */
+ bool opaque;
+
+ /** True if the bit depths specified are for float formats. */
+ bool floatingPoint;
+
+ /** Human readable name of this texture.*/
+ std::string name() const;
+
+private:
+
+ ImageFormat(
+ int numComponents,
+ bool compressed,
+ int glFormat,
+ int glBaseFormat,
+ int luminanceBits,
+ int alphaBits,
+ int redBits,
+ int greenBits,
+ int blueBits,
+ int depthBits,
+ int stencilBits,
+ int hardwareBitsPerTexel,
+ int packedBitsPerTexel,
+ int glDataFormat,
+ bool opaque,
+ bool floatingPoint,
+ Code code,
+ ColorSpace colorSpace,
+ BayerPattern bayerPattern = BAYER_PATTERN_NONE);
+
+public:
+
+ static const ImageFormat* L8();
+
+ static const ImageFormat* L16();
+
+ static const ImageFormat* L16F();
+
+ static const ImageFormat* L32F();
+
+ static const ImageFormat* A8();
+
+ static const ImageFormat* A16();
+
+ static const ImageFormat* A16F();
+
+ static const ImageFormat* A32F();
+
+ static const ImageFormat* LA4();
+
+ static const ImageFormat* LA8();
+
+ static const ImageFormat* LA16();
+
+ static const ImageFormat* LA16F();
+
+ static const ImageFormat* LA32F();
+
+ static const ImageFormat* BGR8();
+
+ static const ImageFormat* RGB5();
+
+ static const ImageFormat* RGB5A1();
+
+ static const ImageFormat* RGB8();
+
+ static const ImageFormat* RGB10();
+
+ static const ImageFormat* RGB10A2();
+
+ static const ImageFormat* RGB16();
+
+ static const ImageFormat* RGB16F();
+
+ static const ImageFormat* RGB32F();
+
+ static const ImageFormat* RGBA8();
+
+ static const ImageFormat* RGBA16();
+
+ static const ImageFormat* RGBA16F();
+
+ static const ImageFormat* RGBA32F();
+
+ static const ImageFormat* RGB_DXT1();
+
+ static const ImageFormat* RGBA_DXT1();
+
+ static const ImageFormat* RGBA_DXT3();
+
+ static const ImageFormat* RGBA_DXT5();
+
+ static const ImageFormat* DEPTH16();
+
+ static const ImageFormat* DEPTH24();
+
+ static const ImageFormat* DEPTH32();
+
+ static const ImageFormat* DEPTH32F();
+
+ static const ImageFormat* STENCIL1();
+
+ static const ImageFormat* STENCIL4();
+
+ static const ImageFormat* STENCIL8();
+
+ static const ImageFormat* STENCIL16();
+
+ static const ImageFormat* DEPTH24_STENCIL8();
+
+ static const ImageFormat* YUV420_PLANAR();
+
+ static const ImageFormat* YUV422();
+
+ static const ImageFormat* YUV444();
+
+ /**
+ NULL pointer; indicates that the G3D::Texture class should choose
+ either RGBA8 or RGB8 depending on the presence of an alpha channel
+ in the input.
+ */
+ static const ImageFormat* AUTO() { return NULL; }
+
+ /** Returns DEPTH16, DEPTH24, or DEPTH32 according to the bits
+ specified. You can use "glGetInteger(GL_DEPTH_BITS)" to match
+ the screen's format.*/
+ static const ImageFormat* depth(int depthBits = 24);
+
+ /** Returns STENCIL1, STENCIL4, STENCIL8 or STENCIL16 according to the bits
+ specified. You can use "glGetInteger(GL_STENCIL_BITS)" to match
+ the screen's format.*/
+ static const ImageFormat* stencil(int bits = 8);
+
+ /** Returns the matching ImageFormat* identified by the Code. May return NULL
+ if this format's code is reserved but not yet implemented by G3D. */
+ static const ImageFormat* fromCode(ImageFormat::Code code);
+
+
+
+ /** For use with ImageFormat::convert. */
+ class BayerAlgorithm {
+ public:
+ enum Value {
+ NEAREST,
+ BILINEAR,
+ mhc,
+ HIGH_QUALITY = mhc
+ };
+ private:
+
+ Value value;
+
+ public:
+
+ G3D_DECLARE_ENUM_CLASS_METHODS(BayerAlgorithm);
+ };
+
+ /** Converts between arbitrary formats on the CPU. Not all format conversions are supported or directly supported.
+ Formats without direct conversions will attempt to convert through RGBA first.
+
+ A conversion routine might only support source or destination padding or y inversion or none.
+ If support is needed and not available in any of the direct conversion routines, then no conversion is done.
+
+ YUV422 expects data in YUY2 format (Y, U, Y2, v). Most YUV formats require width and heights that are multiples of 2.
+
+ Returns true if a conversion was available, false if none occurred.
+ */
+ static bool convert(const Array<const void*>& srcBytes, int srcWidth, int srcHeight, const ImageFormat* srcFormat, int srcRowPadBits,
+ const Array<void*>& dstBytes, const ImageFormat* dstFormat, int dstRowPadBits,
+ bool invertY = false, BayerAlgorithm bayerAlg = BayerAlgorithm::HIGH_QUALITY);
+
+ /* Checks if a conversion between two formats is available. */
+ static bool conversionAvailable(const ImageFormat* srcFormat, int srcRowPadBits, const ImageFormat* dstFormat, int dstRowPadBits, bool invertY = false);
+};
+
+typedef ImageFormat TextureFormat;
+
+}
+
+template <>
+struct HashTrait<const G3D::ImageFormat*> {
+ static size_t hashCode(const G3D::ImageFormat* key) { return reinterpret_cast<size_t>(key); }
+};
+
+
+
+#endif
diff --git a/externals/g3dlite/G3D.lib/include/G3D/Line.h b/externals/g3dlite/G3D.lib/include/G3D/Line.h
new file mode 100644
index 00000000000..ff6dc8d08e7
--- /dev/null
+++ b/externals/g3dlite/G3D.lib/include/G3D/Line.h
@@ -0,0 +1,105 @@
+/**
+ @file Line.h
+
+ Line class
+
+ @maintainer Morgan McGuire, matrix@graphics3d.com
+
+ @created 2001-06-02
+ @edited 2006-02-28
+ */
+
+#ifndef G3D_LINE_H
+#define G3D_LINE_H
+
+#include "G3D/platform.h"
+#include "G3D/Vector3.h"
+
+namespace G3D {
+
+class Plane;
+
+/**
+ An infinite 3D line.
+ */
+class Line {
+protected:
+
+ Vector3 _point;
+ Vector3 _direction;
+
+ Line(const Vector3& point, const Vector3& direction) {
+ _point = point;
+ _direction = direction.direction();
+ }
+
+public:
+
+ /** Undefined (provided for creating Array<Line> only) */
+ inline Line() {}
+
+ Line(class BinaryInput& b);
+
+ void serialize(class BinaryOutput& b) const;
+
+ void deserialize(class BinaryInput& b);
+
+ virtual ~Line() {}
+
+ /**
+ Constructs a line from two (not equal) points.
+ */
+ static Line fromTwoPoints(const Vector3 &point1, const Vector3 &point2) {
+ return Line(point1, point2 - point1);
+ }
+
+ /**
+ Creates a line from a point and a (nonzero) direction.
+ */
+ static Line fromPointAndDirection(const Vector3& point, const Vector3& direction) {
+ return Line(point, direction);
+ }
+
+ /**
+ Returns the closest point on the line to point.
+ */
+ Vector3 closestPoint(const Vector3& pt) const;
+
+ /**
+ Returns the distance between point and the line
+ */
+ double distance(const Vector3& point) const {
+ return (closestPoint(point) - point).magnitude();
+ }
+
+ /** Returns a point on the line */
+ Vector3 point() const;
+
+ /** Returns the direction (or negative direction) of the line */
+ Vector3 direction() const;
+
+ /**
+ Returns the point where the line and plane intersect. If there
+ is no intersection, returns a point at infinity.
+ */
+ Vector3 intersection(const Plane &plane) const;
+
+
+ /** Finds the closest point to the two lines.
+
+ @param minDist Returns the minimum distance between the lines.
+
+ @cite http://objectmix.com/graphics/133793-coordinates-closest-points-pair-skew-lines.html
+ */
+ Vector3 closestPoint(const Line& B, float& minDist) const;
+
+ inline Vector3 closestPoint(const Line& B) const {
+ float m;
+ return closestPoint(B, m);
+ }
+};
+
+};// namespace
+
+
+#endif
diff --git a/externals/g3dlite/G3D.lib/include/G3D/LineSegment.h b/externals/g3dlite/G3D.lib/include/G3D/LineSegment.h
new file mode 100644
index 00000000000..092b0c9ca6d
--- /dev/null
+++ b/externals/g3dlite/G3D.lib/include/G3D/LineSegment.h
@@ -0,0 +1,115 @@
+/**
+ @file LineSegment.h
+
+ @maintainer Morgan McGuire, matrix@graphics3d.com
+
+ @created 2003-02-08
+ @edited 2008-02-02
+ */
+
+#ifndef G3D_LINESEGMENT_H
+#define G3D_LINESEGMENT_H
+
+#include "G3D/platform.h"
+#include "G3D/Vector3.h"
+
+namespace G3D {
+
+/**
+ An finite segment of an infinite 3D line.
+ */
+class LineSegment {
+protected:
+
+ Vector3 _point;
+
+ /** Not normalized */
+ Vector3 direction;
+
+ LineSegment(const Vector3& __point, const Vector3& _direction) : _point(__point), direction(_direction) {
+ }
+
+public:
+
+ inline LineSegment() : _point(Vector3::zero()), direction(Vector3::zero()) {}
+
+ LineSegment(class BinaryInput& b);
+
+ void serialize(class BinaryOutput& b) const;
+
+ void deserialize(class BinaryInput& b);
+
+ virtual ~LineSegment() {}
+
+ /**
+ * Constructs a line from two (not equal) points.
+ */
+ static LineSegment fromTwoPoints(const Vector3 &point1, const Vector3 &point2) {
+ return LineSegment(point1, point2 - point1);
+ }
+
+ /** Call with 0 or 1 */
+ Vector3 point(int i) const;
+
+ inline float length() const {
+ return direction.magnitude();
+ }
+
+ /**
+ * Returns the closest point on the line segment to point.
+ */
+ Vector3 closestPoint(const Vector3 &point) const;
+
+ /**
+ Returns the distance between point and the line
+ */
+ double distance(const Vector3& p) const {
+ return (closestPoint(p) - p).magnitude();
+ }
+
+ double distanceSquared(const Vector3& p) const {
+ return (closestPoint(p) - p).squaredMagnitude();
+ }
+
+ /** Returns true if some part of this segment is inside the sphere */
+ bool intersectsSolidSphere(const class Sphere& s) const;
+
+ Vector3 randomPoint() const;
+
+};
+
+
+class LineSegment2D {
+private:
+
+ Vector2 m_origin;
+
+ /** Not normalized */
+ Vector2 m_direction;
+
+ /** Length of m_direction */
+ float m_length;
+
+public:
+
+ LineSegment2D() {}
+
+ static LineSegment2D fromTwoPoints(const Vector2& p0, const Vector2& p1);
+
+ /** Returns the intersection of these segements (including
+ testing endpoints), or Vector2::inf() if they do not intersect. */
+ Vector2 intersection(const LineSegment2D& other) const;
+
+ Vector2 point(int i) const;
+
+ Vector2 closestPoint(const Vector2& Q) const;
+
+ float distance(const Vector2& p) const;
+
+ float length() const;
+};
+
+} // namespace
+
+
+#endif
diff --git a/externals/g3dlite/G3D.lib/include/G3D/Log.h b/externals/g3dlite/G3D.lib/include/G3D/Log.h
new file mode 100644
index 00000000000..19b4b65c82d
--- /dev/null
+++ b/externals/g3dlite/G3D.lib/include/G3D/Log.h
@@ -0,0 +1,109 @@
+/**
+ @file Log.h
+
+ @maintainer Morgan McGuire, morgan@graphics3d.com
+ @cite Backtrace by Aaron Orenstein
+ @created 2001-08-04
+ @edited 2005-11-04
+ */
+
+#ifndef G3D_LOG_H
+#define G3D_LOG_H
+
+#include <stdio.h>
+#include <string>
+#include "G3D/platform.h"
+
+#ifndef G3D_WIN32
+ #include <stdarg.h>
+#endif
+
+namespace G3D {
+
+/** Prints to the common system log, log.txt, which is usually
+ in the working directory of the program. If your disk is
+ not writable or is slow, it will attempt to write to "c:/tmp/log.txt" or
+ "c:/temp/log.txt" on Windows systems instead.
+
+ Unlike printf or debugPrintf,
+ this function guarantees that all output is committed before it returns.
+ This is very useful for debugging a crash, which might hide the last few
+ buffered print statements otherwise.
+
+ Many G3D routines write useful warnings and debugging information to the
+ system log, which makes it a good first place to go when tracking down
+ a problem.
+ */
+void logPrintf(const char* fmt, ...);
+
+/**
+ System log for debugging purposes. The first log opened
+ is the "common log" and can be accessed with the static
+ method common(). If you access common() and a common log
+ does not yet exist, one is created for you.
+ */
+class Log {
+private:
+
+ /**
+ Log messages go here.
+ */
+ FILE* logFile;
+
+ std::string filename;
+
+ static Log* commonLog;
+
+ int stripFromStackBottom;
+
+ /**
+ Prints the time & stack trace.
+ */
+ void printHeader();
+
+public:
+
+ /**
+ @param stripFromStackBottom Number of call stacks to strip from the
+ bottom of the stack when printing a trace. Useful for hiding
+ routines like "main" and "WinMain". If the specified file cannot
+ be opened for some reason, tries to open "c:/tmp/log.txt" or
+ "c:/temp/log.txt" instead.
+ */
+ Log(const std::string& filename = "log.txt",
+ int stripFromStackBottom = 0);
+
+ virtual ~Log();
+
+ /**
+ Returns the handle to the file log.
+ */
+ FILE* getFile() const;
+
+ /**
+ Marks the beginning of a logfile section.
+ */
+ void section(const std::string& s);
+
+ /**
+ Given arguments like printf, writes characters to the debug text overlay.
+ */
+ // We want G3D_CHECK_PRINTF_ARGS here, but that conflicts with the
+ // overload.
+ void __cdecl printf(const char* fmt, ...) G3D_CHECK_PRINTF_METHOD_ARGS;
+
+ void __cdecl vprintf(const char*, va_list argPtr) G3D_CHECK_VPRINTF_METHOD_ARGS;
+
+ static Log* common();
+
+ static std::string getCommonLogFilename();
+
+ void print(const std::string& s);
+
+
+ void println(const std::string& s);
+};
+
+}
+
+#endif
diff --git a/externals/g3dlite/G3D.lib/include/G3D/Map2D.h b/externals/g3dlite/G3D.lib/include/G3D/Map2D.h
new file mode 100644
index 00000000000..48e2e957a5f
--- /dev/null
+++ b/externals/g3dlite/G3D.lib/include/G3D/Map2D.h
@@ -0,0 +1,665 @@
+/**
+ @file Map2D.h
+
+ More flexible support than provided by G3D::GImage.
+
+ @maintainer Morgan McGuire, morgan@cs.brown.edu
+ @created 2004-10-10
+ @edited 2007-07-18
+ */
+#ifndef G3D_MAP2D_H
+#define G3D_MAP2D_H
+
+#include "G3D/platform.h"
+#include "G3D/g3dmath.h"
+#include "G3D/Array.h"
+#include "G3D/Vector2int16.h"
+#include "G3D/ReferenceCount.h"
+#include "G3D/AtomicInt32.h"
+#include "G3D/GThread.h"
+#include "G3D/Rect2D.h"
+#include "G3D/WrapMode.h"
+
+#include <string>
+
+namespace G3D {
+namespace _internal {
+
+/** The default compute type for a type is the type itself. */
+template<typename Storage> class _GetComputeType {
+public:
+ typedef Storage Type;
+};
+
+} // _internal
+} // G3D
+
+// This weird syntax is needed to support VC6, which doesn't
+// properly implement template overloading.
+#define DECLARE_COMPUTE_TYPE(StorageType, ComputeType) \
+namespace G3D { \
+ namespace _internal { \
+ template<> class _GetComputeType < StorageType > { \
+ public: \
+ typedef ComputeType Type; \
+ }; \
+ } \
+}
+
+DECLARE_COMPUTE_TYPE( float32, float64)
+DECLARE_COMPUTE_TYPE( float64, float64)
+
+DECLARE_COMPUTE_TYPE( int8, float32)
+DECLARE_COMPUTE_TYPE( int16, float32)
+DECLARE_COMPUTE_TYPE( int32, float64)
+DECLARE_COMPUTE_TYPE( int64, float64)
+
+DECLARE_COMPUTE_TYPE( uint8, float32)
+DECLARE_COMPUTE_TYPE( uint16, float32)
+DECLARE_COMPUTE_TYPE( uint32, float64)
+DECLARE_COMPUTE_TYPE( uint64, float64)
+
+DECLARE_COMPUTE_TYPE( Vector2, Vector2)
+DECLARE_COMPUTE_TYPE( Vector2int16, Vector2)
+
+DECLARE_COMPUTE_TYPE( Vector3, Vector3)
+DECLARE_COMPUTE_TYPE( Vector3int16, Vector3)
+
+DECLARE_COMPUTE_TYPE( Vector4, Vector4)
+
+DECLARE_COMPUTE_TYPE( Color3, Color3)
+DECLARE_COMPUTE_TYPE( Color3uint8, Color3)
+
+DECLARE_COMPUTE_TYPE( Color4, Color4)
+DECLARE_COMPUTE_TYPE( Color4uint8, Color4)
+#undef DECLARE_COMPUTE_TYPE
+
+namespace G3D {
+
+/**
+ Map of values across a discrete 2D plane. Can be thought of as a generic class for 2D images,
+ allowing flexibility as to pixel format and convenient methods.
+ In fact, the "pixels" can be any values
+ on a grid that can be sensibly interpolated--RGB colors, scalars, 4D vectors, and so on.
+
+ Other "image" classes in G3D:
+
+ G3D::GImage - Supports file formats, fast, Color3uint8 and Color4uint8 formats. No interpolation.
+
+ G3D::Texture::Ref - Represents image on the graphics card (not directly readable on the CPU). Supports 2D, 3D, and a variety of interpolation methods, loads file formats.
+
+ G3D::Image3 - A subclass of Map2D<Color3> that supports image loading and saving and conversion to Texture.
+
+ G3D::Image4 - A subclass of Map2D<Color4> that supports image loading and saving and conversion to Texture.
+
+ G3D::Image3uint8 - A subclass of Map2D<Color3uint8> that supports image loading and saving and conversion to Texture.
+
+ G3D::Image4uint8 - A subclass of Map2D<Color4uint8> that supports image loading and saving and conversion to Texture.
+
+ There are two type parameters-- the first (@ Storage) is the type
+ used to store the "pixel" values efficiently and
+ the second (@a Compute) is
+ the type operated on by computation. The Compute::Compute(Storage&) constructor
+ is used to convert between storage and computation types.
+ @a Storage is often an integer version of @a Compute, for example
+ <code>Map2D<double, uint8></code>. By default, the computation type is:
+
+ <pre>
+ Storage Computation
+
+ uint8 float32
+ uint16 float32
+ uint32 float64
+ uint64 float64
+
+ int8 float32
+ int16 float32
+ int32 float64
+ int64 float64
+
+ float32 float64
+ float64 float64
+
+ Vector2 Vector2
+ Vector2int16 Vector2
+
+ Vector3 Vector3
+ Vector3int16 Vector3
+
+ Vector4 Vector4
+
+ Color3 Color3
+ Color3uint8 Color3
+
+ Color4 Color4
+ Color4uint8 Color4
+ </pre>
+ Any other storage type defaults to itself as the computation type.
+
+ The computation type can be any that
+ supports lerp, +, -, *, /, and an empty constructor.
+
+ Assign value:
+
+ <code>im->set(x, y, 7);</code> or
+ <code>im->get(x, y) = 7;</code>
+
+ Read value:
+
+ <code>int c = im(x, y);</code>
+
+ Can also sample with nearest neighbor, bilinear, and bicubic
+ interpolation.
+
+ Sampling follows OpenGL conventions, where
+ pixel values represent grid points and (0.5, 0.5) is half-way
+ between two vertical and two horizontal grid points.
+ To draw an image of dimensions w x h with nearest neighbor
+ sampling, render pixels from [0, 0] to [w - 1, h - 1].
+
+ Under the WrapMode::CLAMP wrap mode, the value of bilinear interpolation
+ becomes constant outside [1, w - 2] horizontally. Nearest neighbor
+ interpolation is constant outside [0, w - 1] and bicubic outside
+ [3, w - 4]. The class does not offer quadratic interpolation because
+ the interpolation filter could not center over a pixel.
+
+ @author Morgan McGuire, morgan@cs.williams.edu
+ */
+template< typename Storage,
+typename Compute = typename G3D::_internal::_GetComputeType<Storage>::Type>
+class Map2D : public ReferenceCountedObject {
+
+//
+// It doesn't make sense to automatically convert from Compute back to Storage
+// because the rounding rule (and scaling) is application dependent.
+// Thus the interpolation methods all return type Compute.
+//
+
+public:
+
+ typedef Storage StorageType;
+ typedef Compute ComputeType;
+ typedef Map2D<Storage, Compute> Type;
+ typedef ReferenceCountedPointer<Map2D> Ref;
+
+protected:
+
+ Storage ZERO;
+
+ /** Width, in pixels. */
+ uint32 w;
+
+ /** Height, in pixels. */
+ uint32 h;
+
+ WrapMode _wrapMode;
+
+ /** 0 if no mutating method has been invoked
+ since the last call to setChanged(); */
+ AtomicInt32 m_changed;
+
+ Array<Storage> data;
+
+ /** Handles the exceptional cases from get */
+ const Storage& slowGet(int x, int y, WrapMode wrap) {
+ switch (wrap) {
+ case WrapMode::CLAMP:
+ return fastGet(iClamp(x, 0, w - 1), iClamp(y, 0, h - 1));
+
+ case WrapMode::TILE:
+ return fastGet(iWrap(x, w), iWrap(y, h));
+
+ case WrapMode::ZERO:
+ return ZERO;
+
+ case WrapMode::ERROR:
+ alwaysAssertM(((uint32)x < w) && ((uint32)y < h),
+ format("Index out of bounds: (%d, %d), w = %d, h = %d",
+ x, y, w, h));
+
+ // intentionally fall through
+ case WrapMode::IGNORE:
+ // intentionally fall through
+ default:
+ {
+ static Storage temp;
+ return temp;
+ }
+ }
+ }
+
+public:
+
+ /** Unsafe access to the underlying data structure with no wrapping support; requires that (x, y) is in bounds. */
+ inline const Storage& fastGet(int x, int y) const {
+ debugAssert(((uint32)x < w) && ((uint32)y < h));
+ return data[x + y * w];
+ }
+
+ /** Unsafe access to the underlying data structure with no wrapping support; requires that (x, y) is in bounds. */
+ inline void fastSet(int x, int y, const Storage& v) {
+ debugAssert(((uint32)x < w) && ((uint32)y < h));
+ data[x + y * w] = v;
+ }
+
+protected:
+
+ /** Given four control points and a value on the range [0, 1)
+ evaluates the Catmull-rom spline between the times of the
+ middle two control points */
+ Compute bicubic(const Compute* ctrl, double s) const {
+
+ // f = B * S * ctrl'
+
+ // B matrix: Catmull-Rom spline basis
+ static const double B[4][4] = {
+ { 0.0, -0.5, 1.0, -0.5},
+ { 1.0, 0.0, -2.5, 1.5},
+ { 0.0, 0.5, 2.0, -1.5},
+ { 0.0, 0.0, -0.5, 0.5}};
+
+ // S: Powers of the fraction
+ double S[4];
+ double s2 = s * s;
+ S[0] = 1.0;
+ S[1] = s;
+ S[2] = s2;
+ S[3] = s2 * s;
+
+ Compute sum(ZERO);
+
+ for (int c = 0; c < 4; ++c) {
+ double coeff = 0.0;
+ for (int power = 0; power < 4; ++power) {
+ coeff += B[c][power] * S[power];
+ }
+ sum += ctrl[c] * coeff;
+ }
+
+ return sum;
+ }
+
+
+ Map2D(int w, int h, WrapMode wrap) : w(0), h(0), _wrapMode(wrap), m_changed(1) {
+ ZERO = Storage(Compute(Storage()) * 0);
+ resize(w, h);
+ }
+
+public:
+
+ /**
+ Although Map2D is not threadsafe (except for the setChanged() method),
+ you can use this mutex to create your own threadsafe access to a Map2D.
+ Not used by the default implementation.
+ */
+ GMutex mutex;
+
+ static Ref create(int w = 0, int h = 0, WrapMode wrap = WrapMode::ERROR) {
+ return new Map2D(w, h, wrap);
+ }
+
+ /** Resizes without clearing, leaving garbage.
+ */
+ void resize(uint32 newW, uint32 newH) {
+ if ((newW != w) || (newH != h)) {
+ w = newW;
+ h = newH;
+ data.resize(w * h);
+ setChanged(true);
+ }
+ }
+
+ /**
+ Returns true if this map has been written to since the last call to setChanged(false).
+ This is useful if you are caching a texture map other value that must be recomputed
+ whenever this changes.
+ */
+ bool changed() {
+ return m_changed.value() != 0;
+ }
+
+ /** Set/unset the changed flag. */
+ void setChanged(bool c) {
+ m_changed = c ? 1 : 0;
+ }
+
+ /** Returns a pointer to the underlying row-major data. There is no padding at the end of the row.
+ Be careful--this will be reallocated during a resize. You should call setChanged(true) if you mutate the array.*/
+ Storage* getCArray() {
+ return data.getCArray();
+ }
+
+
+ const Storage* getCArray() const {
+ return data.getCArray();
+ }
+
+
+ /** Row-major array. You should call setChanged(true) if you mutate the array. */
+ Array<Storage>& getArray() {
+ return data;
+ }
+
+
+ const Array<Storage>& getArray() const {
+ return data;
+ }
+
+ /** is (x, y) strictly within the image bounds, or will it trigger some kind of wrap mode */
+ inline bool inBounds(int x, int y) const {
+ return (((uint32)x < w) && ((uint32)y < h));
+ }
+
+ /** is (x, y) strictly within the image bounds, or will it trigger some kind of wrap mode */
+ inline bool inBounds(const Vector2int16& v) const {
+ return inBounds(v.x, v.y);
+ }
+
+ /** Get the value at (x, y).
+
+ Note that the type of image->get(x, y) is
+ the storage type, not the computation
+ type. If the constructor promoting Storage to Compute rescales values
+ (as, for example Color3(Color3uint8&) does), this will not match the value
+ returned by Map2D::nearest.
+ */
+ inline const Storage& get(int x, int y, WrapMode wrap) const {
+ if (((uint32)x < w) && ((uint32)y < h)) {
+ return data[x + y * w];
+ } else {
+ // Remove the const to allow a slowGet on this object
+ // (we're returning a const reference so this is ok)
+ return const_cast<Type*>(this)->slowGet(x, y, wrap);
+ }
+# ifndef G3D_WIN32
+ // gcc gives a useless warning that the above code might reach the end of the function;
+ // we use this line to supress the warning.
+ return ZERO;
+# endif
+ }
+
+ inline const Storage& get(int x, int y) const {
+ return get(x, y, _wrapMode);
+ }
+
+ inline const Storage& get(const Vector2int16& p) const {
+ return get(p.x, p.y, _wrapMode);
+ }
+
+ inline const Storage& get(const Vector2int16& p, WrapMode wrap) const {
+ return get(p.x, p.y, wrap);
+ }
+
+ inline Storage& get(int x, int y, WrapMode wrap) {
+ return const_cast<Storage&>(const_cast<const Type*>(this)->get(x, y, wrap));
+# ifndef G3D_WIN32
+ // gcc gives a useless warning that the above code might reach the end of the function;
+ // we use this line to supress the warning.
+ return ZERO;
+# endif
+ }
+
+ inline Storage& get(int x, int y) {
+ return const_cast<Storage&>(const_cast<const Type*>(this)->get(x, y));
+# ifndef G3D_WIN32
+ // gcc gives a useless warning that the above code might reach the end of the function;
+ // we use this line to supress the warning.
+ return ZERO;
+# endif
+ }
+
+ inline Storage& get(const Vector2int16& p) {
+ return get(p.x, p.y);
+ }
+
+ /** Sets the changed flag to true */
+ inline void set(const Vector2int16& p, const Storage& v) {
+ set(p.x, p.y, v);
+ }
+
+ /** Sets the changed flag to true */
+ void set(int x, int y, const Storage& v, WrapMode wrap) {
+ setChanged(true);
+ if (((uint32)x < w) && ((uint32)y < h)) {
+ // In bounds, wrapping isn't an issue.
+ data[x + y * w] = v;
+ } else {
+ const_cast<Storage&>(slowGet(x, y, wrap)) = v;
+ }
+ }
+
+ void set(int x, int y, const Storage& v) {
+ set(x, y, v, _wrapMode);
+ }
+
+
+ void setAll(const Storage& v) {
+ for(int i = 0; i < data.size(); ++i) {
+ data[i] = v;
+ }
+ setChanged(true);
+ }
+
+ /** flips if @a flip is true*/
+ void maybeFlipVertical(bool flip) {
+ if (flip) {
+ flipVertical();
+ }
+ }
+
+ virtual void flipVertical() {
+ int halfHeight = h/2;
+ Storage* d = data.getCArray();
+ for (int y = 0; y < halfHeight; ++y) {
+ int o1 = y * w;
+ int o2 = (h - y - 1) * w;
+ for (int x = 0; x < (int)w; ++x) {
+ int i1 = o1 + x;
+ int i2 = o2 + x;
+ Storage temp = d[i1];
+ d[i1] = d[i2];
+ d[i2] = temp;
+ }
+ }
+ setChanged(true);
+ }
+
+ virtual void flipHorizontal() {
+ int halfWidth = w / 2;
+ Storage* d = data.getCArray();
+ for (int x = 0; x < halfWidth; ++x) {
+ for (int y = 0; y < (int)h; ++y) {
+ int i1 = y * w + x;
+ int i2 = y * w + (w - x - 1);
+ Storage temp = d[i1];
+ d[i1] = d[i2];
+ d[i2] = temp;
+ }
+ }
+ setChanged(true);
+ }
+
+ /**
+ Crops this map so that it only contains pixels between (x, y) and (x + w - 1, y + h - 1) inclusive.
+ */
+ virtual void crop(int newX, int newY, int newW, int newH) {
+ alwaysAssertM(newX + newW <= (int)w, "Cannot grow when cropping");
+ alwaysAssertM(newY + newH <= (int)h, "Cannot grow when cropping");
+ alwaysAssertM(newX >= 0 && newY >= 0, "Origin out of bounds.");
+
+ // Always safe to copy towards the upper left, provided
+ // that we're iterating towards the lower right. This lets us avoid
+ // reallocating the underlying array.
+ for (int y = 0; y < newH; ++y) {
+ for (int x = 0; x < newW; ++x) {
+ data[x + y * newW] = data[(x + newX) + (y + newY) * w];
+ }
+ }
+
+ resize(newW, newH);
+ }
+
+ /** iRounds to the nearest x0 and y0. */
+ virtual void crop(const Rect2D& rect) {
+ crop(iRound(rect.x0()), iRound(rect.y0()), iRound(rect.x1()) - iRound(rect.x0()), iRound(rect.y1()) - iRound(rect.y0()));
+ }
+
+ /** Returns the nearest neighbor. Pixel values are considered
+ to be at the upper left corner, so <code>image->nearest(x, y) == image(x, y)</code>
+ */
+ inline Compute nearest(float x, float y, WrapMode wrap) const {
+ return Compute(get(iRound(x), iRound(y), wrap));
+ }
+
+ inline Compute nearest(float x, float y) const {
+ return nearest(x, y, _wrapMode);
+ }
+
+ inline Compute nearest(const Vector2& p) const {
+ return nearest(p.x, p.y);
+ }
+
+ /** Returns the average value of all elements of the map */
+ Compute average() const {
+ if ((w == 0) || (h == 0)) {
+ return ZERO;
+ }
+
+ // To avoid overflows, compute the average of row averages
+
+ Compute rowSum = ZERO;
+ for (unsigned int y = 0; y < h; ++y) {
+ Compute sum = ZERO;
+ int offset = y * w;
+ for (unsigned int x = 0; x < w; ++x) {
+ sum += Compute(data[offset + x]);
+ }
+ rowSum += sum * (1.0f / w);
+ }
+
+ return rowSum * (1.0f / h);
+ }
+
+ /**
+ Needs to access elements from (floor(x), floor(y))
+ to (floor(x) + 1, floor(y) + 1) and will use
+ the wrap mode appropriately (possibly generating
+ out of bounds errors).
+
+ Guaranteed to match nearest(x, y) at integers. */
+ Compute bilinear(float x, float y, WrapMode wrap) const {
+ int i = iFloor(x);
+ int j = iFloor(y);
+
+ float fX = x - i;
+ float fY = y - j;
+
+ // Horizontal interpolation, first row
+ Compute t0(get(i, j, wrap));
+ Compute t1(get(i + 1, j, wrap));
+ Compute A = lerp(t0, t1, fX);
+
+ // Horizontal interpolation, second row
+ Compute t2(get(i, j + 1, wrap));
+ Compute t3(get(i + 1, j + 1, wrap));
+ Compute B = lerp(t2, t3, fX);
+
+ // Vertical interpolation
+ return lerp(A, B, fY);
+ }
+
+ Compute bilinear(float x, float y) const {
+ return bilinear(x, y, _wrapMode);
+ }
+
+ inline Compute bilinear(const Vector2& p) const {
+ return bilinear(p.x, p.y, _wrapMode);
+ }
+
+ inline Compute bilinear(const Vector2& p, WrapMode wrap) const {
+ return bilinear(p.x, p.y, wrap);
+ }
+
+ /**
+ Uses Catmull-Rom splines to interpolate between grid
+ values. Guaranteed to match nearest(x, y) at integers.
+ */
+ Compute bicubic(float x, float y, WrapMode wrap) const {
+ int i = iFloor(x);
+ int j = iFloor(y);
+ float fX = x - i;
+ float fY = y - j;
+
+ // 'static' prevents constructors from being called
+ // every time through this loop.
+ static Compute vsample[4];
+ for (int v = 0; v < 4; ++v) {
+
+ // Horizontal interpolation
+ static Compute hsample[4];
+ for (int u = 0; u < 4; ++u) {
+ hsample[u] = Compute(get(i + u - 1, j + v - 1, wrap));
+ }
+
+ vsample[v] = bicubic(hsample, fX);
+ }
+
+ // Vertical interpolation
+ return bicubic(vsample, fY);
+ }
+
+ Compute bicubic(float x, float y) const {
+ return bicubic(x, y, _wrapMode);
+ }
+
+ inline Compute bicubic(const Vector2& p, WrapMode wrap) const {
+ return bicubic(p.x, p.y, wrap);
+ }
+
+ inline Compute bicubic(const Vector2& p) const {
+ return bicubic(p.x, p.y, _wrapMode);
+ }
+
+ /** Pixel width */
+ inline int32 width() const {
+ return (int32)w;
+ }
+
+
+ /** Pixel height */
+ inline int32 height() const {
+ return (int32)h;
+ }
+
+
+ /** Dimensions in pixels */
+ Vector2int16 size() const {
+ return Vector2int16(w, h);
+ }
+
+ /** Rectangle from (0, 0) to (w, h) */
+ Rect2D rect2DBounds() const {
+ return Rect2D::xywh(0, 0, w, h);
+ }
+
+ /** Number of bytes occupied by the image data and this structure */
+ size_t sizeInMemory() const {
+ return data.size() * sizeof(Storage) + sizeof(*this);
+ }
+
+
+ WrapMode wrapMode() const {
+ return _wrapMode;
+ }
+
+
+ void setWrapMode(WrapMode m) {
+ _wrapMode = m;
+ }
+};
+
+
+
+}
+
+#endif // G3D_IMAGE_H
diff --git a/externals/g3dlite/G3D.lib/include/G3D/Matrix.h b/externals/g3dlite/G3D.lib/include/G3D/Matrix.h
new file mode 100644
index 00000000000..481af940324
--- /dev/null
+++ b/externals/g3dlite/G3D.lib/include/G3D/Matrix.h
@@ -0,0 +1,634 @@
+/**
+ @file Matrix.h
+ @author Morgan McGuire, morgan@cs.williams.edu
+
+ @created 2005-10-23
+ @edited 2007-07-18
+ */
+
+#ifndef G3D_MATRIX_H
+#define G3D_MATRIX_H
+
+#include "G3D/g3dmath.h"
+#include "G3D/Vector3.h"
+#include "G3D/Vector4.h"
+#include "G3D/Matrix3.h"
+#include "G3D/Matrix4.h"
+#include "G3D/ReferenceCount.h"
+
+namespace G3D {
+
+/**
+ N x M matrix.
+
+ The actual data is tracked internally by a reference counted pointer;
+ it is efficient to pass and assign Matrix objects because no data is actually copied.
+ This avoids the headache of pointers and allows natural math notation:
+
+ <PRE>
+ Matrix A, B, C;
+ // ...
+
+ C = A * f(B);
+ C = C.inverse();
+
+ A = Matrix::identity(4);
+ C = A;
+ C.set(0, 0, 2.0); // Triggers a copy of the data so that A remains unchanged.
+
+ // etc.
+
+ </PRE>
+
+ The Matrix::debugNumCopyOps and Matrix::debugNumAllocOps counters
+ increment every time an operation forces the copy and allocation of matrices. You
+ can use these to detect slow operations when efficiency is a major concern.
+
+ Some methods accept an output argument instead of returning a value. For example,
+ <CODE>A = B.transpose()</CODE> can also be invoked as <CODE>B.transpose(A)</CODE>.
+ The latter may be more efficient, since Matrix may be able to re-use the storage of
+ A (if it has approximatly the right size and isn't currently shared with another matrix).
+
+ @sa G3D::Matrix3, G3D::Matrix4, G3D::Vector2, G3D::Vector3, G3D::Vector4, G3D::CoordinateFrame
+
+ @beta
+ */
+class Matrix {
+public:
+ /**
+ Internal precision. Currently float, but this may become a templated class in the future
+ to allow operations like Matrix<double> and Matrix<ComplexFloat>.
+
+ Not necessarily a plain-old-data type (e.g., could ComplexFloat), but must be something
+ with no constructor, that can be safely memcpyd, and that has a bit pattern of all zeros
+ when zero.*/
+ typedef float T;
+
+ /** Incremented every time the elements of a matrix are copied. Useful for profiling your
+ own code that uses Matrix to determine when it is slow due to copying.*/
+ static int debugNumCopyOps;
+
+ /** Incremented every time a new matrix object is allocated. Useful for profiling your
+ own code that uses Matrix to determine when it is slow due to allocation.*/
+ static int debugNumAllocOps;
+
+private:
+public:
+
+ /** Used internally by Matrix.
+
+ Does not throw exceptions-- assumes the caller has taken care of
+ argument checking. */
+ class Impl : public ReferenceCountedObject {
+ public:
+
+ static void* operator new(size_t size) {
+ return System::malloc(size);
+ }
+
+ static void operator delete(void* p) {
+ System::free(p);
+ }
+
+ ~Impl();
+
+ private:
+ friend class Matrix;
+
+ /** elt[r][c] = the element. Pointers into data.*/
+ T** elt;
+
+ /** Row major data for the entire matrix. */
+ T* data;
+
+ /** The number of rows */
+ int R;
+
+ /** The number of columns */
+ int C;
+
+ int dataSize;
+
+ /** If R*C is much larger or smaller than the current, deletes all previous data
+ and resets to random data. Otherwise re-uses existing memory and just resets
+ R, C, and the row pointers. */
+ void setSize(int newRows, int newCols);
+
+ inline Impl() : elt(NULL), data(NULL), R(0), C(0), dataSize(0) {}
+
+ Impl(const Matrix3& M);
+
+ Impl(const Matrix4& M);
+
+ inline Impl(int r, int c) : elt(NULL), data(NULL), R(0), C(0), dataSize(0) {
+ setSize(r, c);
+ }
+
+ Impl& operator=(const Impl& m);
+
+ inline Impl(const Impl& B) : elt(NULL), data(NULL), R(0), C(0), dataSize(0) {
+ // Use the assignment operator
+ *this = B;
+ }
+
+ void setZero();
+
+ inline void set(int r, int c, T v) {
+ debugAssert(r < R);
+ debugAssert(c < C);
+ elt[r][c] = v;
+ }
+
+ inline const T& get(int r, int c) const {
+ debugAssert(r < R);
+ debugAssert(c < C);
+ return elt[r][c];
+ }
+
+ /** Multiplies this by B and puts the result in out. */
+ void mul(const Impl& B, Impl& out) const;
+
+ /** Ok if out == this or out == B */
+ void add(const Impl& B, Impl& out) const;
+
+ /** Ok if out == this or out == B */
+ void add(T B, Impl& out) const;
+
+ /** Ok if out == this or out == B */
+ void sub(const Impl& B, Impl& out) const;
+
+ /** Ok if out == this or out == B */
+ void sub(T B, Impl& out) const;
+
+ /** B - this */
+ void lsub(T B, Impl& out) const;
+
+ /** Ok if out == this or out == B */
+ void arrayMul(const Impl& B, Impl& out) const;
+
+ /** Ok if out == this or out == B */
+ void mul(T B, Impl& out) const;
+
+ /** Ok if out == this or out == B */
+ void arrayDiv(const Impl& B, Impl& out) const;
+
+ /** Ok if out == this or out == B */
+ void div(T B, Impl& out) const;
+
+ void negate(Impl& out) const;
+
+ /** Slow way of computing an inverse; for reference */
+ void inverseViaAdjoint(Impl& out) const;
+
+ /** Use Gaussian elimination with pivots to solve for the inverse destructively in place. */
+ void inverseInPlaceGaussJordan();
+
+ void adjoint(Impl& out) const;
+
+ /** Matrix of all cofactors */
+ void cofactor(Impl& out) const;
+
+ /**
+ Cofactor [r][c] is defined as C[r][c] = -1 ^(r+c) * det(A[r][c]),
+ where A[r][c] is the (R-1)x(C-1) matrix formed by removing row r and
+ column c from the original matrix.
+ */
+ T cofactor(int r, int c) const;
+
+ /** Ok if out == this or out == B */
+ void transpose(Impl& out) const;
+
+ T determinant() const;
+
+ /** Determinant computed without the given row and column */
+ T determinant(int r, int c) const;
+
+ void arrayLog(Impl& out) const;
+
+ void arrayExp(Impl& out) const;
+
+ void arraySqrt(Impl& out) const;
+
+ void arrayCos(Impl& out) const;
+
+ void arraySin(Impl& out) const;
+
+ void swapRows(int r0, int r1);
+
+ void swapAndNegateCols(int c0, int c1);
+
+ void mulRow(int r, const T& v);
+
+ void abs(Impl& out) const;
+
+ /** Makes a (R-1)x(C-1) copy of this matrix */
+ void withoutRowAndCol(int excludeRow, int excludeCol, Impl& out) const;
+
+ bool anyNonZero() const;
+
+ bool allNonZero() const;
+
+ void setRow(int r, const T* vals);
+
+ void setCol(int c, const T* vals);
+ };
+private:
+
+ typedef ReferenceCountedPointer<Impl> ImplRef;
+
+ ImplRef impl;
+
+ inline Matrix(ImplRef i) : impl(i) {}
+ inline Matrix(Impl* i) : impl(ImplRef(i)) {}
+
+ /** Used by SVD */
+ class SortRank {
+ public:
+ T value;
+ int col;
+
+ inline bool operator>(const SortRank& x) const {
+ return x.value > value;
+ }
+
+ inline bool operator<(const SortRank& x) const {
+ return x.value < value;
+ }
+
+ inline bool operator>=(const SortRank& x) const {
+ return x.value >= value;
+ }
+
+ inline bool operator<=(const SortRank& x) const {
+ return x.value <= value;
+ }
+
+ inline bool operator==(const SortRank& x) const {
+ return x.value == value;
+ }
+
+ inline bool operator!=(const SortRank& x) const {
+ return x.value != value;
+ }
+ };
+
+ Matrix vectorPseudoInverse() const;
+ Matrix partitionPseudoInverse() const;
+ Matrix colPartPseudoInverse() const;
+ Matrix rowPartPseudoInverse() const;
+
+ Matrix col2PseudoInverse(const Matrix& B) const;
+ Matrix col3PseudoInverse(const Matrix& B) const;
+ Matrix col4PseudoInverse(const Matrix& B) const;
+ Matrix row2PseudoInverse(const Matrix& B) const;
+ Matrix row3PseudoInverse(const Matrix& B) const;
+ Matrix row4PseudoInverse(const Matrix& B) const;
+
+public:
+
+ Matrix() : impl(new Impl(0, 0)) {}
+
+ Matrix(const Matrix3& M) : impl(new Impl(M)) {}
+
+ Matrix(const Matrix4& M) : impl(new Impl(M)) {}
+
+ template<class S>
+ static Matrix fromDiagonal(const Array<S>& d) {
+ Matrix D = zero(d.length(), d.length());
+ for (int i = 0; i < d.length(); ++i) {
+ D.set(i, i, d[i]);
+ }
+ return D;
+ }
+
+ static Matrix fromDiagonal(const Matrix& d);
+
+ /** Returns a new matrix that is all zero. */
+ Matrix(int R, int C) : impl(new Impl(R, C)) {
+ impl->setZero();
+ }
+
+ /** Returns a new matrix that is all zero. */
+ static Matrix zero(int R, int C);
+
+ /** Returns a new matrix that is all one. */
+ static Matrix one(int R, int C);
+
+ /** Returns a new identity matrix */
+ static Matrix identity(int N);
+
+ /** Uniformly distributed values between zero and one. */
+ static Matrix random(int R, int C);
+
+ /** The number of rows */
+ inline int rows() const {
+ return impl->R;
+ }
+
+ /** Number of columns */
+ inline int cols() const {
+ return impl->C;
+ }
+
+ /** Generally more efficient than A * B */
+ Matrix& operator*=(const T& B);
+
+ /** Generally more efficient than A / B */
+ Matrix& operator/=(const T& B);
+
+ /** Generally more efficient than A + B */
+ Matrix& operator+=(const T& B);
+
+ /** Generally more efficient than A - B */
+ Matrix& operator-=(const T& B);
+
+ /** No performance advantage over A * B because
+ matrix multiplication requires intermediate
+ storage. */
+ Matrix& operator*=(const Matrix& B);
+
+ /** Generally more efficient than A + B */
+ Matrix& operator+=(const Matrix& B);
+
+ /** Generally more efficient than A - B */
+ Matrix& operator-=(const Matrix& B);
+
+ /** Returns a new matrix that is a subset of this one,
+ from r1:r2 to c1:c2, inclusive.*/
+ Matrix subMatrix(int r1, int r2, int c1, int c2) const;
+
+ /** Matrix multiplication. To perform element-by-element multiplication,
+ see arrayMul. */
+ inline Matrix operator*(const Matrix& B) const {
+ Matrix C(impl->R, B.impl->C);
+ impl->mul(*B.impl, *C.impl);
+ return C;
+ }
+
+ /** See also A *= B, which is more efficient in many cases */
+ inline Matrix operator*(const T& B) const {
+ Matrix C(impl->R, impl->C);
+ impl->mul(B, *C.impl);
+ return C;
+ }
+
+ /** See also A += B, which is more efficient in many cases */
+ inline Matrix operator+(const Matrix& B) const {
+ Matrix C(impl->R, impl->C);
+ impl->add(*B.impl, *C.impl);
+ return C;
+ }
+
+ /** See also A -= B, which is more efficient in many cases */
+ inline Matrix operator-(const Matrix& B) const {
+ Matrix C(impl->R, impl->C);
+ impl->sub(*B.impl, *C.impl);
+ return C;
+ }
+
+ /** See also A += B, which is more efficient in many cases */
+ inline Matrix operator+(const T& v) const {
+ Matrix C(impl->R, impl->C);
+ impl->add(v, *C.impl);
+ return C;
+ }
+
+ /** See also A -= B, which is more efficient in many cases */
+ inline Matrix operator-(const T& v) const {
+ Matrix C(impl->R, impl->C);
+ impl->sub(v, *C.impl);
+ return C;
+ }
+
+
+ Matrix operator>(const T& scalar) const;
+
+ Matrix operator<(const T& scalar) const;
+
+ Matrix operator>=(const T& scalar) const;
+
+ Matrix operator<=(const T& scalar) const;
+
+ Matrix operator==(const T& scalar) const;
+
+ Matrix operator!=(const T& scalar) const;
+
+ /** scalar B - this */
+ inline Matrix lsub(const T& B) const {
+ Matrix C(impl->R, impl->C);
+ impl->lsub(B, *C.impl);
+ return C;
+ }
+
+ inline Matrix arrayMul(const Matrix& B) const {
+ Matrix C(impl->R, impl->C);
+ impl->arrayMul(*B.impl, *C.impl);
+ return C;
+ }
+
+ Matrix3 toMatrix3() const;
+
+ Matrix4 toMatrix4() const;
+
+ Vector2 toVector2() const;
+
+ Vector3 toVector3() const;
+
+ Vector4 toVector4() const;
+
+ /** Mutates this */
+ void arrayMulInPlace(const Matrix& B);
+
+ /** Mutates this */
+ void arrayDivInPlace(const Matrix& B);
+
+ // Declares an array unary method and its explicit-argument counterpart
+# define DECLARE_METHODS_1(method)\
+ inline Matrix method() const {\
+ Matrix C(impl->R, impl->C);\
+ impl->method(*C.impl);\
+ return C;\
+ }\
+ void method(Matrix& out) const;
+
+
+ DECLARE_METHODS_1(abs)
+ DECLARE_METHODS_1(arrayLog)
+ DECLARE_METHODS_1(arrayExp)
+ DECLARE_METHODS_1(arraySqrt)
+ DECLARE_METHODS_1(arrayCos)
+ DECLARE_METHODS_1(arraySin)
+ DECLARE_METHODS_1(negate)
+
+# undef DECLARE_METHODS_1
+
+ inline Matrix operator-() const {
+ return negate();
+ }
+
+ /**
+ A<SUP>-1</SUP> computed using the Gauss-Jordan algorithm,
+ for square matrices.
+ Run time is <I>O(R<sup>3</sup>)</I>, where <I>R</i> is the
+ number of rows.
+ */
+ inline Matrix inverse() const {
+ Impl* A = new Impl(*impl);
+ A->inverseInPlaceGaussJordan();
+ return Matrix(A);
+ }
+
+ inline T determinant() const {
+ return impl->determinant();
+ }
+
+ /**
+ A<SUP>T</SUP>
+ */
+ inline Matrix transpose() const {
+ Impl* A = new Impl(cols(), rows());
+ impl->transpose(*A);
+ return Matrix(A);
+ }
+
+ /** Transpose in place; more efficient than transpose */
+ void transpose(Matrix& out) const;
+
+ inline Matrix adjoint() const {
+ Impl* A = new Impl(cols(), rows());
+ impl->adjoint(*A);
+ return Matrix(A);
+ }
+
+ /**
+ (A<SUP>T</SUP>A)<SUP>-1</SUP>A<SUP>T</SUP>) computed
+ using SVD.
+
+ @param tolerance Use -1 for automatic tolerance.
+ */
+ Matrix pseudoInverse(float tolerance = -1) const;
+
+ /** Called from pseudoInverse when the matrix has size > 4 along some dimension.*/
+ Matrix svdPseudoInverse(float tolerance = -1) const;
+
+ /**
+ (A<SUP>T</SUP>A)<SUP>-1</SUP>A<SUP>T</SUP>) computed
+ using Gauss-Jordan elimination.
+ */
+ inline Matrix gaussJordanPseudoInverse() const {
+ Matrix trans = transpose();
+ return (trans * (*this)).inverse() * trans;
+ }
+
+ /** Singular value decomposition. Factors into three matrices
+ such that @a this = @a U * fromDiagonal(@a d) * @a V.transpose().
+
+ The matrix must have at least as many rows as columns.
+
+ Run time is <I>O(C<sup>2</sup>*R)</I>.
+
+ @param sort If true (default), the singular values
+ are arranged so that D is sorted from largest to smallest.
+ */
+ void svd(Matrix& U, Array<T>& d, Matrix& V, bool sort = true) const;
+
+ void set(int r, int c, T v);
+
+ void setCol(int c, const Matrix& vec);
+
+ void setRow(int r, const Matrix& vec);
+
+ Matrix col(int c) const;
+
+ Matrix row(int r) const;
+
+ T get(int r, int c) const;
+
+ Vector2int16 size() const {
+ return Vector2int16(rows(), cols());
+ }
+
+ int numElements() const {
+ return rows() * cols();
+ }
+
+ void swapRows(int r0, int r1);
+
+ /** Swaps columns c0 and c1 and negates both */
+ void swapAndNegateCols(int c0, int c1);
+
+ void mulRow(int r, const T& v);
+
+ /** Returns true if any element is non-zero */
+ bool anyNonZero() const;
+
+ /** Returns true if all elements are non-zero */
+ bool allNonZero() const;
+
+ inline bool allZero() const {
+ return !anyNonZero();
+ }
+
+ inline bool anyZero() const {
+ return !allNonZero();
+ }
+
+ /** Serializes in Matlab source format */
+ void serialize(TextOutput& t) const;
+
+ std::string toString(const std::string& name) const;
+
+ std::string toString() const {
+ static const std::string name = "";
+ return toString(name);
+ }
+
+ /** 2-norm squared: sum(squares). (i.e., dot product with itself) */
+ double normSquared() const;
+
+ /** 2-norm (sqrt(sum(squares)) */
+ double norm() const;
+
+ /**
+ Low-level SVD functionality. Useful for applications that do not want
+ to construct a Matrix but need to perform the SVD operation.
+
+ this = U * D * V'
+
+ Assumes that rows >= cols
+
+ @return NULL on success, a string describing the error on failure.
+ @param U rows x cols matrix to be decomposed, gets overwritten with U, a rows x cols matrix with orthogonal columns.
+ @param D vector of singular values of a (diagonal of the D matrix). Length cols.
+ @param V returns the right orthonormal transformation matrix, size cols x cols
+
+ @cite Based on Dianne Cook's implementation, which is adapted from
+ svdecomp.c in XLISP-STAT 2.1, which is code from Numerical Recipes
+ adapted by Luke Tierney and David Betz. The Numerical Recipes code
+ is adapted from Forsythe et al, who based their code on Golub and
+ Reinsch's original implementation.
+ */
+ static const char* svdCore(float** U, int rows, int cols, float* D, float** V);
+
+};
+
+}
+
+inline G3D::Matrix operator-(const G3D::Matrix::T& v, const G3D::Matrix& M) {
+ return M.lsub(v);
+}
+
+inline G3D::Matrix operator*(const G3D::Matrix::T& v, const G3D::Matrix& M) {
+ return M * v;
+}
+
+inline G3D::Matrix operator+(const G3D::Matrix::T& v, const G3D::Matrix& M) {
+ return M + v;
+}
+
+inline G3D::Matrix abs(const G3D::Matrix& M) {
+ return M.abs();
+}
+
+#endif
+
diff --git a/externals/g3dlite/G3D.lib/include/G3D/Matrix2.h b/externals/g3dlite/G3D.lib/include/G3D/Matrix2.h
new file mode 100644
index 00000000000..eaf4aefa220
--- /dev/null
+++ b/externals/g3dlite/G3D.lib/include/G3D/Matrix2.h
@@ -0,0 +1,69 @@
+#ifndef G3D_MATRIX2_H
+#define G3D_MATRIX2_H
+
+#include "G3D/platform.h"
+#include "G3D/Vector2.h"
+
+namespace G3D {
+
+/** @beta */
+class Matrix2 {
+private:
+
+ float data[2][2];
+
+public:
+
+ inline Matrix2() {
+ data[0][0] = 1.0f; data[0][1] = 0.0f;
+ data[1][0] = 0.0f; data[1][1] = 1.0f;
+ }
+
+ inline Matrix2(float v00, float v01, float v10, float v11) {
+ data[0][0] = v00; data[0][1] = v01;
+ data[1][0] = v10; data[1][1] = v11;
+ }
+
+ inline Vector2 operator*(const Vector2& v) const {
+ return Vector2(data[0][0] * v[0] + data[0][1] * v[1],
+ data[1][0] * v[0] + data[1][1] * v[1]);
+ }
+
+ inline Matrix2 inverse() const {
+ return Matrix2(data[0][0], data[1][0],
+ data[0][1], data[1][1]) * (1.0f / determinant());
+ }
+
+ inline Matrix2 transpose() const {
+ return Matrix2(data[0][0], data[1][0],
+ data[0][1], data[1][1]);
+ }
+
+ inline float determinant() const {
+ return data[0][0] * data[1][1] - data[0][1] * data[1][0];
+ }
+
+ inline Matrix2 operator*(float f) const {
+ return Matrix2(data[0][0] * f, data[0][1] * f,
+ data[1][0] * f, data[1][1] * f);
+ }
+
+ inline Matrix2 operator/(float f) const {
+ return Matrix2(data[0][0] / f, data[0][1] / f,
+ data[1][0] / f, data[1][1] / f);
+ }
+
+ inline float* operator[](int i) {
+ debugAssert(i >= 0 && i <= 2);
+ return data[i];
+ }
+
+ inline const float* operator[](int i) const {
+ debugAssert(i >= 0 && i <= 1);
+ return data[i];
+ }
+};
+
+}
+
+#endif
diff --git a/externals/g3dlite/G3D.lib/include/G3D/Matrix3.h b/externals/g3dlite/G3D.lib/include/G3D/Matrix3.h
new file mode 100644
index 00000000000..ad09cd3860f
--- /dev/null
+++ b/externals/g3dlite/G3D.lib/include/G3D/Matrix3.h
@@ -0,0 +1,323 @@
+/**
+ @file Matrix3.h
+
+ 3x3 matrix class
+
+ @maintainer Morgan McGuire, matrix@graphics3d.com
+
+ @cite Portions based on Dave Eberly's Magic Software Library at <A HREF="http://www.magic-software.com">http://www.magic-software.com</A>
+
+ @created 2001-06-02
+ @edited 2006-04-05
+ */
+
+#ifndef G3D_MATRIX3_H
+#define G3D_MATRIX3_H
+
+#include "G3D/platform.h"
+#include "G3D/Vector3.h"
+#include "G3D/Vector4.h"
+#include "G3D/debugAssert.h"
+
+#include <cstring>
+
+namespace G3D {
+
+/**
+ 3x3 matrix. Do not subclass.
+ */
+class Matrix3 {
+private:
+
+ float elt[3][3];
+
+ // Hidden operators
+ bool operator<(const Matrix3&) const;
+ bool operator>(const Matrix3&) const;
+ bool operator<=(const Matrix3&) const;
+ bool operator>=(const Matrix3&) const;
+
+public:
+
+ /** Initial values are undefined for performance. See also
+ Matrix3::zero(), Matrix3::identity(), Matrix3::fromAxisAngle, etc.*/
+ inline Matrix3() {}
+
+ Matrix3 (class BinaryInput& b);
+ Matrix3 (const float aafEntry[3][3]);
+ Matrix3 (const Matrix3& rkMatrix);
+ Matrix3 (float fEntry00, float fEntry01, float fEntry02,
+ float fEntry10, float fEntry11, float fEntry12,
+ float fEntry20, float fEntry21, float fEntry22);
+
+ bool fuzzyEq(const Matrix3& b) const;
+
+ /** Constructs a matrix from a quaternion.
+ @cite Graphics Gems II, p. 351--354
+ @cite Implementation from Watt and Watt, pg 362*/
+ Matrix3(const class Quat& q);
+
+ void serialize(class BinaryOutput& b) const;
+ void deserialize(class BinaryInput& b);
+
+ /**
+ Sets all elements.
+ */
+ void set(float fEntry00, float fEntry01, float fEntry02,
+ float fEntry10, float fEntry11, float fEntry12,
+ float fEntry20, float fEntry21, float fEntry22);
+
+ /**
+ * member access, allows use of construct mat[r][c]
+ */
+ inline float* operator[] (int iRow) {
+ debugAssert(iRow >= 0);
+ debugAssert(iRow < 3);
+ return (float*)&elt[iRow][0];
+ }
+
+ inline const float* operator[] (int iRow) const {
+ debugAssert(iRow >= 0);
+ debugAssert(iRow < 3);
+ return (const float*)&elt[iRow][0];
+ }
+
+ inline operator float* () {
+ return (float*)&elt[0][0];
+ }
+
+ inline operator const float* () const{
+ return (const float*)&elt[0][0];
+ }
+
+ /** @deprecated */
+ Vector3 getColumn (int iCol) const;
+ /** @deprecated */
+ Vector3 getRow (int iRow) const;
+
+ Vector3 column(int c) const;
+ const Vector3& row(int r) const;
+
+ void setColumn(int iCol, const Vector3 &vector);
+ void setRow(int iRow, const Vector3 &vector);
+
+ // assignment and comparison
+ inline Matrix3& operator= (const Matrix3& rkMatrix) {
+ memcpy(elt, rkMatrix.elt, 9 * sizeof(float));
+ return *this;
+ }
+
+ bool operator== (const Matrix3& rkMatrix) const;
+ bool operator!= (const Matrix3& rkMatrix) const;
+
+ // arithmetic operations
+ Matrix3 operator+ (const Matrix3& rkMatrix) const;
+ Matrix3 operator- (const Matrix3& rkMatrix) const;
+ /** Matrix-matrix multiply */
+ Matrix3 operator* (const Matrix3& rkMatrix) const;
+ Matrix3 operator- () const;
+
+ Matrix3& operator+= (const Matrix3& rkMatrix);
+ Matrix3& operator-= (const Matrix3& rkMatrix);
+ Matrix3& operator*= (const Matrix3& rkMatrix);
+
+ /**
+ * matrix * vector [3x3 * 3x1 = 3x1]
+ */
+ inline Vector3 operator* (const Vector3& v) const {
+ Vector3 kProd;
+
+ for (int r = 0; r < 3; ++r) {
+ kProd[r] =
+ elt[r][0] * v[0] +
+ elt[r][1] * v[1] +
+ elt[r][2] * v[2];
+ }
+
+ return kProd;
+ }
+
+
+ /**
+ * vector * matrix [1x3 * 3x3 = 1x3]
+ */
+ friend Vector3 operator* (const Vector3& rkVector,
+ const Matrix3& rkMatrix);
+
+ /**
+ * matrix * scalar
+ */
+ Matrix3 operator* (float fScalar) const;
+
+ /** scalar * matrix */
+ friend Matrix3 operator* (double fScalar, const Matrix3& rkMatrix);
+ friend Matrix3 operator* (float fScalar, const Matrix3& rkMatrix);
+ friend Matrix3 operator* (int fScalar, const Matrix3& rkMatrix);
+
+private:
+ /** Multiplication where out != A and out != B */
+ static void _mul(const Matrix3& A, const Matrix3& B, Matrix3& out);
+public:
+
+ /** Optimized implementation of out = A * B. It is safe (but slow) to call
+ with A, B, and out possibly pointer equal to one another.*/
+ // This is a static method so that it is not ambiguous whether "this"
+ // is an input or output argument.
+ inline static void mul(const Matrix3& A, const Matrix3& B, Matrix3& out) {
+ if ((&out == &A) || (&out == &B)) {
+ // We need a temporary anyway, so revert to the stack method.
+ out = A * B;
+ } else {
+ // Optimized in-place multiplication.
+ _mul(A, B, out);
+ }
+ }
+
+private:
+ static void _transpose(const Matrix3& A, Matrix3& out);
+public:
+
+ /** Optimized implementation of out = A.transpose(). It is safe (but slow) to call
+ with A and out possibly pointer equal to one another.
+
+ Note that <CODE>A.transpose() * v</CODE> can be computed
+ more efficiently as <CODE>v * A</CODE>.
+ */
+ inline static void transpose(const Matrix3& A, Matrix3& out) {
+ if (&A == &out) {
+ out = A.transpose();
+ } else {
+ _transpose(A, out);
+ }
+ }
+
+ /** Returns true if the rows and column L2 norms are 1.0 and the rows are orthogonal. */
+ bool isOrthonormal() const;
+
+ Matrix3 transpose () const;
+ bool inverse (Matrix3& rkInverse, float fTolerance = 1e-06) const;
+ Matrix3 inverse (float fTolerance = 1e-06) const;
+ float determinant () const;
+
+ /** singular value decomposition */
+ void singularValueDecomposition (Matrix3& rkL, Vector3& rkS,
+ Matrix3& rkR) const;
+ /** singular value decomposition */
+ void singularValueComposition (const Matrix3& rkL,
+ const Vector3& rkS, const Matrix3& rkR);
+
+ /** Gram-Schmidt orthonormalization (applied to columns of rotation matrix) */
+ void orthonormalize();
+
+ /** orthogonal Q, diagonal D, upper triangular U stored as (u01,u02,u12) */
+ void qDUDecomposition (Matrix3& rkQ, Vector3& rkD,
+ Vector3& rkU) const;
+
+ float spectralNorm () const;
+
+ /** matrix must be orthonormal */
+ void toAxisAngle(Vector3& rkAxis, float& rfRadians) const;
+
+ static Matrix3 fromDiagonal(const Vector3& d) {
+ return Matrix3(d.x, 0, 0,
+ 0, d.y, 0,
+ 0, 0, d.z);
+ }
+
+ static Matrix3 fromAxisAngle(const Vector3& rkAxis, float fRadians);
+
+ /**
+ * The matrix must be orthonormal. The decomposition is yaw*pitch*roll
+ * where yaw is rotation about the Up vector, pitch is rotation about the
+ * right axis, and roll is rotation about the Direction axis.
+ */
+ bool toEulerAnglesXYZ (float& rfYAngle, float& rfPAngle,
+ float& rfRAngle) const;
+ bool toEulerAnglesXZY (float& rfYAngle, float& rfPAngle,
+ float& rfRAngle) const;
+ bool toEulerAnglesYXZ (float& rfYAngle, float& rfPAngle,
+ float& rfRAngle) const;
+ bool toEulerAnglesYZX (float& rfYAngle, float& rfPAngle,
+ float& rfRAngle) const;
+ bool toEulerAnglesZXY (float& rfYAngle, float& rfPAngle,
+ float& rfRAngle) const;
+ bool toEulerAnglesZYX (float& rfYAngle, float& rfPAngle,
+ float& rfRAngle) const;
+ static Matrix3 fromEulerAnglesXYZ (float fYAngle, float fPAngle, float fRAngle);
+ static Matrix3 fromEulerAnglesXZY (float fYAngle, float fPAngle, float fRAngle);
+ static Matrix3 fromEulerAnglesYXZ (float fYAngle, float fPAngle, float fRAngle);
+ static Matrix3 fromEulerAnglesYZX (float fYAngle, float fPAngle, float fRAngle);
+ static Matrix3 fromEulerAnglesZXY (float fYAngle, float fPAngle, float fRAngle);
+ static Matrix3 fromEulerAnglesZYX (float fYAngle, float fPAngle, float fRAngle);
+
+ /** eigensolver, matrix must be symmetric */
+ void eigenSolveSymmetric (float afEigenvalue[3],
+ Vector3 akEigenvector[3]) const;
+
+ static void tensorProduct (const Vector3& rkU, const Vector3& rkV,
+ Matrix3& rkProduct);
+ std::string toString() const;
+
+ static const float EPSILON;
+
+ // Special values.
+ // The unguaranteed order of initialization of static variables across
+ // translation units can be a source of annoying bugs, so now the static
+ // special values (like Vector3::ZERO, Color3::WHITE, ...) are wrapped
+ // inside static functions that return references to them.
+ // These functions are intentionally not inlined, because:
+ // "You might be tempted to write [...] them as inline functions
+ // inside their respective header files, but this is something you
+ // must definitely not do. An inline function can be duplicated
+ // in every file in which it appears œóõ½ and this duplication
+ // includes the static object definition. Because inline functions
+ // automatically default to internal linkage, this would result in
+ // having multiple static objects across the various translation
+ // units, which would certainly cause problems. So you must
+ // ensure that there is only one definition of each wrapping
+ // function, and this means not making the wrapping functions inline",
+ // according to Chapter 10 of "Thinking in C++, 2nd ed. Volume 1" by Bruce Eckel,
+ // http://www.mindview.net/
+ static const Matrix3& zero();
+ static const Matrix3& identity();
+
+protected:
+
+ // support for eigensolver
+ void tridiagonal (float afDiag[3], float afSubDiag[3]);
+ bool qLAlgorithm (float afDiag[3], float afSubDiag[3]);
+
+ // support for singular value decomposition
+ static const float ms_fSvdEpsilon;
+ static const int ms_iSvdMaxIterations;
+ static void bidiagonalize (Matrix3& kA, Matrix3& kL,
+ Matrix3& kR);
+ static void golubKahanStep (Matrix3& kA, Matrix3& kL,
+ Matrix3& kR);
+
+ // support for spectral norm
+ static float maxCubicRoot (float afCoeff[3]);
+
+};
+
+
+//----------------------------------------------------------------------------
+/** <code>v * M == M.transpose() * v</code> */
+inline Vector3 operator* (const Vector3& rkPoint, const Matrix3& rkMatrix) {
+ Vector3 kProd;
+
+ for (int r = 0; r < 3; ++r) {
+ kProd[r] =
+ rkPoint[0] * rkMatrix.elt[0][r] +
+ rkPoint[1] * rkMatrix.elt[1][r] +
+ rkPoint[2] * rkMatrix.elt[2][r];
+ }
+
+ return kProd;
+}
+
+
+} // namespace
+
+#endif
+
diff --git a/externals/g3dlite/G3D.lib/include/G3D/Matrix4.h b/externals/g3dlite/G3D.lib/include/G3D/Matrix4.h
new file mode 100644
index 00000000000..5bb6cecfdf9
--- /dev/null
+++ b/externals/g3dlite/G3D.lib/include/G3D/Matrix4.h
@@ -0,0 +1,206 @@
+/**
+ @file Matrix4.h
+
+ 4x4 matrix class
+
+ @maintainer Morgan McGuire, matrix@graphics3d.com
+
+ @created 2003-10-02
+ @edited 2007-04-05
+ */
+
+#ifndef G3D_MATRIX4_H
+#define G3D_MATRIX4_H
+
+#ifdef _MSC_VER
+// Disable conditional expression is constant, which occurs incorrectly on inlined functions
+# pragma warning (push)
+# pragma warning( disable : 4127 )
+#endif
+
+#include "G3D/platform.h"
+#include "G3D/debugAssert.h"
+#include "G3D/Matrix3.h"
+#include "G3D/Vector3.h"
+
+namespace G3D {
+
+/**
+ A 4x4 matrix.
+
+ See also G3D::CoordinateFrame, G3D::Matrix3, G3D::Quat
+ */
+class Matrix4 {
+private:
+
+ float elt[4][4];
+
+ /**
+ Computes the determinant of the 3x3 matrix that lacks excludeRow
+ and excludeCol.
+ */
+ float subDeterminant(int excludeRow, int excludeCol) const;
+
+ // Hidden operators
+ bool operator<(const Matrix4&) const;
+ bool operator>(const Matrix4&) const;
+ bool operator<=(const Matrix4&) const;
+ bool operator>=(const Matrix4&) const;
+
+public:
+ 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);
+
+ /**
+ init should be <B>row major</B>.
+ */
+ Matrix4(const float* init);
+
+ /**
+ a is the upper left 3x3 submatrix and b is the upper right 3x1 submatrix. The last row of the created matrix is (0,0,0,1).
+ */
+ Matrix4(const class Matrix3& upper3x3, const class Vector3& lastCol = Vector3::zero());
+
+ Matrix4(const class CoordinateFrame& c);
+
+ Matrix4(const double* init);
+
+ Matrix4();
+
+ /** Produces an RT transformation that nearly matches this Matrix4.
+ Because a Matrix4 may not be precisely a rotation and translation,
+ this may introduce error. */
+ class CoordinateFrame approxCoordinateFrame() const;
+
+ // Special values.
+ // Intentionally not inlined: see Matrix3::identity() for details.
+ static const Matrix4& identity();
+ static const Matrix4& zero();
+
+ inline float* operator[](int r) {
+ debugAssert(r >= 0);
+ debugAssert(r < 4);
+ return (float*)&elt[r];
+ }
+
+ inline const float* operator[](int r) const {
+ debugAssert(r >= 0);
+ debugAssert(r < 4);
+ return (const float*)&elt[r];
+ }
+
+ inline operator float* () {
+ return (float*)&elt[0][0];
+ }
+
+ inline operator const float* () const {
+ return (const float*)&elt[0][0];
+ }
+
+ Matrix4 operator*(const Matrix4& other) const;
+
+ class Matrix3 upper3x3() const;
+
+ /** Homogeneous multiplication. Let k = M * [v w]^T. result = k.xyz() / k.w */
+ class Vector3 homoMul(const class Vector3& v, float w) const;
+
+ /**
+ Constructs an orthogonal projection matrix from the given parameters.
+ Near and far are the <b>NEGATIVE</b> of the near and far plane Z values
+ (to follow OpenGL conventions).
+ */
+ static Matrix4 orthogonalProjection(
+ float left,
+ float right,
+ float bottom,
+ float top,
+ float nearval,
+ float farval);
+
+ static Matrix4 orthogonalProjection(
+ const class Rect2D& rect,
+ float nearval,
+ float farval);
+
+ static Matrix4 perspectiveProjection(
+ float left,
+ float right,
+ float bottom,
+ float top,
+ float nearval,
+ float farval);
+
+ void setRow(int r, const class Vector4& v);
+ void setColumn(int c, const Vector4& v);
+
+ /** @deprecated */
+ Vector4 getRow(int r) const;
+ /** @deprecated */
+ Vector4 getColumn(int c) const;
+
+ const Vector4& row(int r) const;
+ Vector4 column(int c) const;
+
+ Matrix4 operator*(const float s) const;
+ Vector4 operator*(const Vector4& vector) const;
+
+ Matrix4 transpose() const;
+
+ bool operator!=(const Matrix4& other) const;
+ bool operator==(const Matrix4& other) const;
+
+ float determinant() const;
+ Matrix4 inverse() const;
+
+ /**
+ Transpose of the cofactor matrix (used in computing the inverse).
+ Note: This is in fact only one type of adjoint. More generally,
+ an adjoint of a matrix is any mapping of a matrix which possesses
+ certain properties. This returns the so-called adjugate
+ or classical adjoint.
+ */
+ Matrix4 adjoint() const;
+ Matrix4 cofactor() const;
+
+ /** Serializes row-major */
+ void serialize(class BinaryOutput& b) const;
+ void deserialize(class BinaryInput& b);
+
+ std::string toString() const;
+
+ /** 3D scale matrix */
+ inline static Matrix4 scale(const Vector3& v) {
+ return Matrix4(v.x, 0, 0, 0,
+ 0, v.y, 0, 0,
+ 0, 0, v.z, 0,
+ 0, 0, 0, 1);
+ }
+
+ /** 3D scale matrix */
+ inline static Matrix4 scale(float x, float y, float z) {
+ return scale(Vector3(x, y, z));
+ }
+
+ /** 3D scale matrix */
+ inline static Matrix4 scale(float s) {
+ return scale(s,s,s);
+ }
+
+ /** 3D translation matrix */
+ inline static Matrix4 translation(const Vector3& v) {
+ return Matrix4(Matrix3::identity(), v);
+ }
+};
+
+
+
+} // namespace
+
+#ifdef _MSC_VER
+# pragma warning (pop)
+#endif
+
+#endif
diff --git a/externals/g3dlite/G3D.lib/include/G3D/MeshAlg.h b/externals/g3dlite/G3D.lib/include/G3D/MeshAlg.h
new file mode 100644
index 00000000000..be381eff117
--- /dev/null
+++ b/externals/g3dlite/G3D.lib/include/G3D/MeshAlg.h
@@ -0,0 +1,718 @@
+/**
+ @file MeshAlg.h
+
+ Indexed Mesh algorithms.
+
+ @maintainer Morgan McGuire, matrix@graphics3d.com
+
+ @created 2003-09-14
+ @edited 2008-07-30
+*/
+
+#ifndef G3D_MESHALG_H
+#define G3D_MESHALG_H
+
+#include "G3D/platform.h"
+#include "G3D/Array.h"
+#include "G3D/Vector3.h"
+#include "G3D/CoordinateFrame.h"
+
+namespace G3D {
+
+/**
+ Indexed <B>mesh alg</B>orithms. You have to build your own mesh class.
+ <P>
+ No mesh class is provided with G3D because there isn't an "ideal"
+ mesh format-- one application needs keyframed animation, another
+ skeletal animation, a third texture coordinates, a fourth
+ cannot precompute information, etc. Instead of compromising, this
+ class implements the hard parts of mesh computation and you can write
+ your own ideal mesh class on top of it.
+ */
+class MeshAlg {
+public:
+
+ enum Primitive {LINES, LINE_STRIP, TRIANGLES, TRIANGLE_STRIP,
+ TRIANGLE_FAN, QUADS, QUAD_STRIP, POINTS};
+
+
+ /** Adjacency information for a vertex.
+ Does not contain the vertex position or normal,
+ which are stored in the MeshAlg::Geometry object.
+ <CODE>Vertex</CODE>s must be stored in an array
+ parallel to (indexed in the same way as)
+ MeshAlg::Geometry::vertexArray.
+ */
+ class Vertex {
+ public:
+ Vertex() {}
+
+ /**
+ Array of edges adjacent to this vertex.
+ Let e = edgeIndex[i].
+ edge[(e >= 0) ? e : ~e].vertexIndex[0] == this
+ vertex index.
+
+ Edges may be listed multiple times if they are
+ degenerate.
+ */
+ Array<int> edgeIndex;
+
+ /**
+ Returns true if e or ~e is in the edgeIndex list.
+ */
+ inline bool inEdge(int e) const {
+ return edgeIndex.contains(~e) || edgeIndex.contains(e);
+ }
+
+ /**
+ Array of faces containing this vertex. Faces
+ may be listed multiple times if they are degenerate.
+ */
+ Array<int> faceIndex;
+
+ inline bool inFace(int f) const {
+ debugAssert(f >= 0);
+ return faceIndex.contains(f);
+ }
+ };
+
+
+ /**
+ Oriented, indexed triangle.
+ */
+ class Face {
+ public:
+ Face();
+
+ /**
+ Used by Edge::faceIndex to indicate a missing face.
+ This is a large negative value.
+ */
+ static const int NONE;
+
+
+ /**
+ Vertices in the face in counter-clockwise order.
+ Degenerate faces may include the same vertex multiple times.
+ */
+ int vertexIndex[3];
+
+ inline bool containsVertex(int v) const {
+ return contains(vertexIndex, 3, v);
+ }
+
+ /**
+ Edge indices in counter-clockwise order. Edges are
+ undirected, so it is important to know which way
+ each edge is pointing in a face. This is encoded
+ using negative indices.
+
+ If <CODE>edgeIndex[i] >= 0</CODE> then this face
+ contains the directed edge
+ between vertex indices
+ <CODE>edgeArray[face.edgeIndex[i]].vertexIndex[0]</CODE>
+ and
+ <CODE>edgeArray[face.edgeIndex[i]].vertexIndex[1]</CODE>.
+
+ If <CODE>edgeIndex[i] < 0</CODE> then
+ <CODE>~edgeIndex[i]</CODE> (i.e. the two's
+ complement of) is used and this face contains the directed
+ edge between vertex indices
+ <CODE>edgeArray[~face.edgeIndex[i]].vertexIndex[0]</CODE>
+ and
+ <CODE>edgeArray[~face.edgeIndex[i]].vertexIndex[1]</CODE>.
+
+ Degenerate faces may include the same edge multiple times.
+ */
+ // Temporarily takes on the value Face::NONE during adjacency
+ // computation to indicate an edge that has not yet been assigned.
+ int edgeIndex[3];
+
+ inline bool containsEdge(int e) const {
+ if (e < 0) {
+ e = ~e;
+ }
+ return contains(edgeIndex, 3, e) || contains(edgeIndex, 3, ~e);
+ }
+
+ /** Contains the forward edge e if e >= 0 and the backward edge
+ ~e otherwise. */
+ inline bool containsDirectedEdge(int e) const {
+ return contains(edgeIndex, 3, e);
+ }
+ };
+
+
+ /** Oriented, indexed edge */
+ class Edge {
+ public:
+ Edge();
+
+ /** Degenerate edges may include the same vertex times. */
+ int vertexIndex[2];
+
+ inline bool containsVertex(int v) const {
+ return contains(vertexIndex, 2, v);
+ }
+
+ /**
+ The edge is directed <B>forward</B> in face 0
+ <B>backward</B> in face 1. Face index of MeshAlg::Face::NONE
+ indicates a boundary (a.k.a. crack, broken) edge.
+ */
+ int faceIndex[2];
+
+ /** Returns true if f is contained in the faceIndex array in either slot.
+ To see if it is forward in that face, just check edge.faceIndex[0] == f.*/
+ inline bool inFace(int f) const {
+ return contains(faceIndex, 2, f);
+ }
+
+ /**
+ Returns true if either faceIndex is NONE.
+ */
+ inline bool boundary() const {
+ return (faceIndex[0] == Face::NONE) ||
+ (faceIndex[1] == Face::NONE);
+ }
+
+ /**
+ Returns the reversed edge.
+ */
+ inline Edge reverse() const {
+ Edge e;
+ e.vertexIndex[0] = vertexIndex[1];
+ e.vertexIndex[1] = vertexIndex[0];
+ e.faceIndex[0] = faceIndex[1];
+ e.faceIndex[1] = faceIndex[0];
+ return e;
+ }
+ };
+
+
+ /**
+ Convenient for passing around the per-vertex data that changes under
+ animation. The faces and edges are needed to interpret
+ these values.
+ */
+ class Geometry {
+ public:
+ /** Vertex positions */
+ Array<Vector3> vertexArray;
+
+ /** Vertex normals */
+ Array<Vector3> normalArray;
+
+ /**
+ Assignment is optimized using SSE.
+ */
+ Geometry& operator=(const Geometry& src);
+
+ void clear() {
+ vertexArray.clear();
+ normalArray.clear();
+ }
+ };
+
+ /**
+ Given a set of vertices and a set of indices for traversing them
+ to create triangles, computes other mesh properties.
+
+ <B>Colocated vertices are treated as separate.</B> To have
+ colocated vertices collapsed (necessary for many algorithms,
+ like shadowing), weld the mesh before computing adjacency.
+
+ <I>Recent change: In version 6.00, colocated vertices were automatically
+ welded by this routine and degenerate faces and edges were removed. That
+ is no longer the case.</I>
+
+ Where two faces meet, there are two opposite directed edges. These
+ are collapsed into a single bidirectional edge in the edgeArray.
+ If four faces meet exactly at the same edge, that edge will appear
+ twice in the array, and so on. If an edge is a boundary of the mesh
+ (i.e. if the edge has only one adjacent face) it will appear in the
+ array with one face index set to MeshAlg::Face::NONE.
+
+ @param vertexGeometry %Vertex positions to use when deciding colocation.
+ @param indexArray Order to traverse vertices to make triangles
+ @param faceArray <I>Output</I>
+ @param edgeArray <I>Output</I>. Sorted so that boundary edges are at the end of the array.
+ @param vertexArray <I>Output</I>
+ */
+ static void computeAdjacency(
+ const Array<Vector3>& vertexGeometry,
+ const Array<int>& indexArray,
+ Array<Face>& faceArray,
+ Array<Edge>& edgeArray,
+ Array<Vertex>& vertexArray);
+
+ /**
+ @deprecated Use the other version of computeAdjacency, which takes Array<Vertex>.
+ @param facesAdjacentToVertex <I>Output</I> adjacentFaceArray[v] is an array of
+ indices for faces touching vertex index v
+ */
+ static void computeAdjacency(
+ const Array<Vector3>& vertexArray,
+ const Array<int>& indexArray,
+ Array<Face>& faceArray,
+ Array<Edge>& edgeArray,
+ Array< Array<int> >& facesAdjacentToVertex);
+
+ /**
+ Computes some basic mesh statistics including: min, max mean and median,
+ edge lengths; and min, mean, median, and max face area.
+
+ @param vertexGeometry %Vertex positions to use when deciding colocation.
+ @param indexArray Order to traverse vertices to make triangles
+ @param minEdgeLength Minimum edge length
+ @param meanEdgeLength Mean edge length
+ @param medianEdgeLength Median edge length
+ @param maxEdgeLength Max edge length
+ @param minFaceArea Minimum face area
+ @param meanFaceArea Mean face area
+ @param medianFaceArea Median face area
+ @param maxFaceArea Max face area
+ */
+ static void computeAreaStatistics(
+ const Array<Vector3>& vertexArray,
+ const Array<int>& indexArray,
+ double& minEdgeLength,
+ double& meanEdgeLength,
+ double& medianEdgeLength,
+ double& maxEdgeLength,
+ double& minFaceArea,
+ double& meanFaceArea,
+ double& medianFaceArea,
+ double& maxFaceArea);
+
+private:
+ /**
+ Computes the tangent space basis vectors for
+ a counter-clockwise oriented face.
+
+ @cite Max McGuire
+ */
+ static void computeTangentVectors(
+ const Vector3& normal,
+ const Vector3 position[3],
+ const Vector2 texCoord[3],
+ Vector3& tangent,
+ Vector3& binormal);
+
+ /** Helper for weldAdjacency */
+ static void weldBoundaryEdges(
+ Array<Face>& faceArray,
+ Array<Edge>& edgeArray,
+ Array<Vertex>& vertexArray);
+
+public:
+
+ /**
+ Computes tangent and binormal vectors,
+ which provide a (mostly) consistent
+ parameterization over the surface for
+ effects like bump mapping. In the resulting coordinate frame,
+ T = x (varies with texture s coordinate), B = y (varies with negative texture t coordinate),
+ and N = z for a right-handed coordinate frame. If a billboard is vertical on the screen
+ in view of the camera, the tangent space matches the camera's coordinate frame.
+
+ The vertex, texCoord, tangent, and binormal
+ arrays are parallel arrays.
+
+ The resulting tangent and binormal might not be exactly
+ perpendicular to each other. They are guaranteed to
+ be perpendicular to the normal.
+
+ @cite Max McGuire
+ */
+ static void computeTangentSpaceBasis(
+ const Array<Vector3>& vertexArray,
+ const Array<Vector2>& texCoordArray,
+ const Array<Vector3>& vertexNormalArray,
+ const Array<Face>& faceArray,
+ Array<Vector3>& tangent,
+ Array<Vector3>& binormal);
+
+ /** @deprecated */
+ static void computeNormals(
+ const Array<Vector3>& vertexArray,
+ const Array<Face>& faceArray,
+ const Array< Array<int> >& adjacentFaceArray,
+ Array<Vector3>& vertexNormalArray,
+ Array<Vector3>& faceNormalArray);
+
+ /**
+ Vertex normals are weighted by the area of adjacent faces.
+ Nelson Max showed this is superior to uniform weighting for
+ general meshes in jgt.
+
+ @param vertexNormalArray Output. Unit length
+ @param faceNormalArray Output. Degenerate faces produce zero magnitude normals. Unit length
+ @see weld
+ */
+ static void computeNormals(
+ const Array<Vector3>& vertexGeometry,
+ const Array<Face>& faceArray,
+ const Array<Vertex>& vertexArray,
+ Array<Vector3>& vertexNormalArray,
+ Array<Vector3>& faceNormalArray);
+
+ /** Computes unit length normals in place using the other computeNormals methods.
+ If you already have a face array use another method; it will be faster.
+ @see weld*/
+ static void computeNormals(
+ Geometry& geometry,
+ const Array<int>& indexArray);
+
+ /**
+ Computes face normals only. Significantly faster (especially if
+ normalize is false) than computeNormals.
+ @see weld
+ */
+ static void computeFaceNormals(
+ const Array<Vector3>& vertexArray,
+ const Array<Face>& faceArray,
+ Array<Vector3>& faceNormals,
+ bool normalize = true);
+
+ /**
+ Classifies each face as a backface or a front face relative
+ to the observer point P (which is at infinity when P.w = 0).
+ A face with normal exactly perpendicular to the observer vector
+ may be classified as either a front or a back face arbitrarily.
+ */
+ static void identifyBackfaces(
+ const Array<Vector3>& vertexArray,
+ const Array<Face>& faceArray,
+ const Vector4& P,
+ Array<bool>& backface);
+
+ /** A faster version of identifyBackfaces for the case where
+ face normals have already been computed */
+ static void identifyBackfaces(
+ const Array<Vector3>& vertexArray,
+ const Array<Face>& faceArray,
+ const Vector4& P,
+ Array<bool>& backface,
+ const Array<Vector3>& faceNormals);
+
+ /**
+ Welds nearby and colocated elements of the <I>oldVertexArray</I> together so that
+ <I>newVertexArray</I> contains no vertices within <I>radius</I> of one another.
+ Every vertex in newVertexPositions also appears in oldVertexPositions.
+ This is useful for downsampling meshes and welding cracks created by artist errors
+ or numerical imprecision.
+
+ The two integer arrays map indices back and forth between the arrays according to:
+ <PRE>
+ oldVertexArray[toOld[ni]] == newVertexArray[ni]
+ oldVertexArray[oi] == newVertexArray[toNew[ni]]
+ </PRE>
+
+ Note that newVertexPositions is never longer than oldVertexPositions
+ and is shorter when vertices are welded.
+
+ Welding with a large radius will effectively compute a lower level of detail for
+ the mesh.
+
+ The welding method runs in roughly linear time in the length of oldVertexArray--
+ a uniform spatial grid is used to achieve nearly constant time vertex collapses
+ for uniformly distributed vertices.
+
+ It is sometimes desirable to keep the original vertex ordering but
+ identify the unique vertices. The following code computes
+ array canonical s.t. canonical[v] = first occurance of
+ a vertex near oldVertexPositions[v] in oldVertexPositions.
+
+ <PRE>
+ Array<int> canonical(oldVertexPositions.size()), toNew, toOld;
+ computeWeld(oldVertexPositions, Array<Vector3>(), toNew, toOld, radius);
+ for (int v = 0; v < canonical.size(); ++v) {
+ canonical[v] = toOld[toNew[v]];
+ }
+ </PRE>
+
+ See also G3D::MeshAlg::weldAdjacency.
+
+ @cite The method is that described as the 'Grouper' in Baum, Mann, Smith, and Winget,
+ Making Radiosity Usable: Automatic Preprocessing and Meshing Techniques for
+ the Generation of Accurate Radiosity Solutions, Computer Graphics vol 25, no 4, July 1991.
+
+ @deprecated Use weld.
+ */
+ static void computeWeld(
+ const Array<Vector3>& oldVertexPositions,
+ Array<Vector3>& newVertexPositions,
+ Array<int>& toNew,
+ Array<int>& toOld,
+ double radius = G3D::fuzzyEpsilon);
+
+ /**
+ Modifies the face, edge, and vertex arrays in place so that
+ colocated (within radius) vertices are treated as identical.
+ Note that the vertexArray and corresponding geometry will
+ contain elements that are no longer used. In the vertexArray,
+ these elements are initialized to MeshAlg::Vertex() but not
+ removed (because removal would change the indexing).
+
+ This is a good preprocessing step for algorithms that are only
+ concerned with the shape of a mesh (e.g. cartoon rendering, fur, shadows)
+ and not the indexing of the vertices.
+
+ Use this method when you have already computed adjacency information
+ and want to collapse colocated vertices within that data without
+ disturbing the actual mesh vertices or indexing scheme.
+
+ If you have not computed adjacency already, use MeshAlg::computeWeld
+ instead and compute adjacency information after welding.
+
+ @deprecated Use weld.
+
+ @param faceArray Mutated in place. Size is maintained (degenerate
+ faces are <b>not</B> removed).
+ @param edgeArray Mutated in place. May shrink if boundary edges
+ are welded together.
+ @param vertexArray Mutated in place. Size is maintained (duplicate
+ vertices contain no adjacency info).
+ */
+ static void weldAdjacency(
+ const Array<Vector3>& originalGeometry,
+ Array<Face>& faceArray,
+ Array<Edge>& edgeArray,
+ Array<Vertex>& vertexArray,
+ double radius = G3D::fuzzyEpsilon);
+
+
+ /**
+ Counts the number of edges (in an edge array returned from
+ MeshAlg::computeAdjacency) that have only one adjacent face.
+ */
+ static int countBoundaryEdges(const Array<Edge>& edgeArray);
+
+
+ /**
+ Generates an array of integers from start to start + n - 1 that have run numbers
+ in series then omit the next skip before the next run. Useful for turning
+ a triangle list into an indexed face set.
+
+ Example:
+ <PRE>
+ createIndexArray(10, x);
+ // x = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
+
+ createIndexArray(5, x, 2);
+ // x = [2, 3, 4, 5, 6, 7]
+
+ createIndexArray(6, x, 0, 2, 1);
+ // x = [0, 1, 3, 4, 6, 7]
+ </PRE>
+ */
+ static void createIndexArray(
+ int n,
+ Array<int>& array,
+ int start = 0,
+ int run = 1,
+ int skip = 0);
+
+ /**
+ Computes a conservative, near-optimal axis aligned bounding box and sphere.
+
+ @cite The bounding sphere uses the method from J. Ritter. An effcient bounding sphere. In Andrew S. Glassner, editor, Graphics Gems. Academic Press, Boston, MA, 1990.
+
+ */
+ static void computeBounds(const Array<Vector3>& vertex, class Box& box, class Sphere& sphere);
+
+ /** Computes bounds for a subset of the vertices. It is ok if vertices appear more than once in the index array. */
+ static void computeBounds(const Array<Vector3>& vertex, const Array<int>& index, class Box& box, class Sphere& sphere);
+
+ /**
+ Mutates geometry, texCoord, and indexArray so that the output has collocated vertices collapsed (welded).
+
+ @param vertices Input and output
+ @param textureCoords Input and output
+ @param normals Output only
+ @param indices Input and output. This is an array of trilist indices.
+ @param oldToNewIndex Output argument
+ @param normalSmoothingAngle Varies from 0 (flat shading) to toRadians(180) for extremely smooth shading. Default is toRadians(70)
+ */
+ static void weld(
+ Array<Vector3>& vertices,
+ Array<Vector2>& textureCoords,
+ Array<Vector3>& normals,
+ Array<Array<int>*>& indices,
+ float normalSmoothingAngle = toRadians(70.0f),
+ float vertexWeldRadius = 0.0001f,
+ float textureWeldRadius = 0.0001f,
+ float normalWeldRadius = 0.01f);
+
+ inline static void weld(
+ Array<Vector3>& vertices,
+ Array<Vector2>& textureCoords,
+ Array<Vector3>& normals,
+ Array<int>& indices,
+ float normalSmoothingAngle = toRadians(70.0f),
+ float vertexWeldRadius = 0.0002f,
+ float textureWeldRadius = 0.00001f,
+ float normalWeldRadius = 0.00001f) {
+
+ Array<Array<int>*> meta;
+ meta.append(&indices);
+ weld(vertices, textureCoords, normals, meta, normalSmoothingAngle, vertexWeldRadius, textureWeldRadius, normalWeldRadius);
+ }
+
+ /**
+ In debug mode, asserts that the adjacency references between the
+ face, edge, and vertex array are consistent.
+ */
+ static void debugCheckConsistency(
+ const Array<Face>& faceArray,
+ const Array<Edge>& edgeArray,
+ const Array<Vertex>& vertexArray);
+
+ /**
+ Generates a unit square in the X-Z plane composed of a grid of wCells x hCells
+ squares and then transforms it by xform.
+
+ @param vertex Output vertices
+ @param texCoord Output texture coordinates
+ @param index Output triangle list indices
+ @param textureScale Lower-right texture coordinate
+ @param spaceCentered If true, the coordinates generated are centered at the origin before the transformation.
+ @param twoSided If true, matching top and bottom planes are generated.
+ */
+ static void generateGrid(
+ Array<Vector3>& vertex,
+ Array<Vector2>& texCoord,
+ Array<int>& index,
+ int wCells = 10,
+ int hCells = 10,
+ const Vector2& textureScale = Vector2(1,1),
+ bool spaceCentered = true,
+ bool twoSided = true,
+ const CoordinateFrame& xform = CoordinateFrame());
+
+ /** Converts quadlist (QUADS),
+ triangle fan (TRIANGLE_FAN),
+ tristrip(TRIANGLE_STRIP), and quadstrip (QUAD_STRIP) indices into
+ triangle list (TRIANGLES) indices and appends them to outIndices. */
+ template<class IndexType>
+ static void toIndexedTriList(
+ const Array<IndexType>& inIndices,
+ MeshAlg::Primitive inType,
+ Array<IndexType>& outIndices) {
+
+ debugAssert(
+ inType == MeshAlg::TRIANGLE_STRIP ||
+ inType == MeshAlg::TRIANGLE_FAN ||
+ inType == MeshAlg::QUADS ||
+ inType == MeshAlg::QUAD_STRIP);
+
+ const int inSize = inIndices.size();
+
+ switch(inType) {
+ case MeshAlg::TRIANGLE_FAN:
+ {
+ debugAssert(inSize >= 3);
+
+ int N = outIndices.size();
+ outIndices.resize(N + (inSize - 2) * 3);
+
+ for (IndexType i = 1, outIndex = N; i <= (inSize - 2); ++i, outIndex += 3) {
+ outIndices[outIndex] = inIndices[0];
+ outIndices[outIndex + 1] = inIndices[i];
+ outIndices[outIndex + 2] = inIndices[i + 1];
+ }
+
+ break;
+ }
+
+ case MeshAlg::TRIANGLE_STRIP:
+ {
+ debugAssert(inSize >= 3);
+
+ int N = outIndices.size();
+ outIndices.resize(N + (inSize - 2) * 3);
+
+ bool atEven = false;
+ for (IndexType i = 0, outIndex = N; i <= (inSize - 2); ++i, outIndex += 3) {
+ if (atEven) {
+ outIndices[outIndex] = inIndices[i + 1];
+ outIndices[outIndex + 1] = inIndices[i];
+ outIndices[outIndex + 2] = inIndices[i + 2];
+ atEven = false;
+ } else {
+ outIndices[outIndex] = inIndices[i];
+ outIndices[outIndex + 1] = inIndices[i + 1];
+ outIndices[outIndex + 2] = inIndices[i + 2];
+ atEven = true;
+ }
+ }
+
+ break;
+ }
+
+ case MeshAlg::QUADS:
+ {
+ debugAssert(inIndices.size() >= 4);
+
+ int N = outIndices.size();
+ outIndices.resize(N + (inSize / 4) * 3);
+
+ for (IndexType i = 0, outIndex = N; i <= (inSize - 4); i += 4, outIndex += 6) {
+ outIndices[outIndex] = inIndices[i];
+ outIndices[outIndex + 1] = inIndices[i + 1];
+ outIndices[outIndex + 2] = inIndices[i + 3];
+ outIndices[outIndex + 3] = inIndices[i + 1];
+ outIndices[outIndex + 4] = inIndices[i + 2];
+ outIndices[outIndex + 5] = inIndices[i + 3];
+ }
+
+ break;
+ }
+
+ case MeshAlg::QUAD_STRIP:
+ {
+ debugAssert(inIndices.size() >= 4);
+
+ int N = outIndices.size();
+ outIndices.resize(N + (inSize - 2) * 3);
+
+ for (IndexType i = 0, outIndex = N; i <= (inSize - 2); i += 2, outIndex += 6) {
+ outIndices[outIndex] = inIndices[i];
+ outIndices[outIndex + 1] = inIndices[i + 1];
+ outIndices[outIndex + 2] = inIndices[i + 2];
+ outIndices[outIndex + 3] = inIndices[i + 2];
+ outIndices[outIndex + 4] = inIndices[i + 1];
+ outIndices[outIndex + 5] = inIndices[i + 3];
+ }
+ break;
+ }
+ default:
+ alwaysAssertM(false, "Illegal argument");
+ }
+ }
+
+protected:
+
+ /**
+ Helper for computeAdjacency. If a directed edge with index e already
+ exists from i0 to i1 then e is returned. If a directed edge with index e
+ already exists from i1 to i0, ~e is returned (the complement) and
+ edgeArray[e] is set to f. Otherwise, a new edge is created from i0 to i1
+ with first face index f and its index is returned.
+
+ @param vertexArray Vertex positions to use when deciding colocation.
+
+ @param area Area of face f. When multiple edges of the same direction
+ are found between the same vertices (usually because of degenerate edges)
+ the face with larger area is kept in the edge table.
+ */
+ static int findEdgeIndex(
+ const Array<Vector3>& vertexArray,
+ Array<Edge>& geometricEdgeArray,
+ int i0, int i1, int f, double area);
+};
+}
+#endif
+
diff --git a/externals/g3dlite/G3D.lib/include/G3D/MeshBuilder.h b/externals/g3dlite/G3D.lib/include/G3D/MeshBuilder.h
new file mode 100644
index 00000000000..3256c0de823
--- /dev/null
+++ b/externals/g3dlite/G3D.lib/include/G3D/MeshBuilder.h
@@ -0,0 +1,82 @@
+/**
+ @file MeshBuilder.h
+
+ @maintainer Morgan McGuire, matrix@graphics3d.com
+
+ @created 2002-02-27
+ @edited 2004-10-04
+ */
+#ifndef G3D_MESHBUILDER_H
+#define G3D_MESHBUILDER_H
+
+#include "G3D/platform.h"
+#include "G3D/Array.h"
+#include "G3D/Vector3.h"
+#include "G3D/Triangle.h"
+
+namespace G3D {
+
+/**
+ Allows creation of optimized watertight meshes from unoptimized polygon soups.
+ See also G3D::MeshAlg for algorithms that operate on the output.
+ */
+class MeshBuilder {
+public:
+
+ /**
+ Set setWeldRadius to AUTO_WELD to weld vertices closer than 1/2
+ the smallest edge length in a model.
+ */
+ enum {AUTO_WELD = -100};
+
+private:
+ /** Indices of vertices in <B>or near</B> a grid cell. */
+ typedef Array<int> List;
+
+ std::string name;
+
+ /**
+ All of the triangles, as a long triangle list.
+ */
+ Array<Vector3> triList;
+
+ void centerTriList();
+ void computeBounds(Vector3& min, Vector3& max);
+
+ bool _twoSided;
+
+ /** Collapse radius */
+ double close;
+
+public:
+
+ inline MeshBuilder(bool twoSided = false) : _twoSided(twoSided), close(AUTO_WELD) {}
+
+ /** Writes the model to the arrays, which can then be used with
+ G3D::IFSModel::save and G3D::MeshAlg */
+ void commit(std::string& name, Array<int>& indexArray, Array<Vector3>& vertexArray);
+
+ /**
+ Adds a new triangle to the model. (Counter clockwise)
+ */
+ void addTriangle(const Vector3& a, const Vector3& b, const Vector3& c);
+
+ /**
+ Adds two new triangles to the model. (Counter clockwise)
+ */
+ void addQuad(const Vector3& a, const Vector3& b, const Vector3& c, const Vector3& d);
+
+ void addTriangle(const Triangle& t);
+
+ void setName(const std::string& n);
+
+ /** Vertices within this distance are considered identical.
+ Use AUTO_WELD (the default) to have the distance be a function of the model size.*/
+ void setWeldRadius(double r) {
+ close = r;
+ }
+};
+
+} // namespace
+
+#endif
diff --git a/externals/g3dlite/G3D.lib/include/G3D/NetAddress.h b/externals/g3dlite/G3D.lib/include/G3D/NetAddress.h
new file mode 100644
index 00000000000..8ed20a06690
--- /dev/null
+++ b/externals/g3dlite/G3D.lib/include/G3D/NetAddress.h
@@ -0,0 +1,132 @@
+#ifndef G3D_NETADDRESS_H
+#define G3D_NETADDRESS_H
+
+#include "G3D/platform.h"
+#include "G3D/Table.h"
+
+/** These control the version of Winsock used by G3D.
+ Version 2.0 is standard for G3D 6.09 and later.
+ Version 1.1 is standard for G3D 6.08 and earlier.
+ */
+#define G3D_WINSOCK_MAJOR_VERSION 2
+#define G3D_WINSOCK_MINOR_VERSION 0
+
+#ifdef G3D_WIN32
+# if (G3D_WINSOCK_MAJOR_VERSION == 2)
+# include <winsock2.h>
+# elif (G3D_WINSOCK_MAJOR_VERSION == 1)
+# include <winsock.h>
+# endif
+#else
+# include <sys/types.h>
+# include <sys/socket.h>
+# include <netinet/in.h>
+# ifndef SOCKADDR_IN
+# define SOCKADDR_IN struct sockaddr_in
+# endif
+# ifndef SOCKET
+# define SOCKET int
+# endif
+#endif
+
+#include "G3D/g3dmath.h"
+
+namespace G3D {
+
+class NetAddress {
+private:
+ friend class NetworkDevice;
+ friend class LightweightConduit;
+ friend class ReliableConduit;
+
+ /** Host byte order */
+ void init(uint32 host, uint16 port);
+ void init(const std::string& hostname, uint16 port);
+ NetAddress(const SOCKADDR_IN& a);
+ NetAddress(const struct in_addr& addr, uint16 port = 0);
+
+ SOCKADDR_IN addr;
+
+public:
+ /**
+ In host byte order
+ */
+ NetAddress(uint32 host, uint16 port = 0);
+
+ /**
+ @param port Specified in host byte order (i.e., don't worry about endian issues)
+ */
+ NetAddress(const std::string& hostname, uint16 port);
+
+ /**
+ @param hostnameAndPort in the form "hostname:port" or "ip:port"
+ */
+ NetAddress(const std::string& hostnameAndPort);
+
+ /**
+ @deprecated Use G3D::NetworkDevice::broadcastAddressArray()
+
+ @brief Creates a UDP broadcast address for use with a
+ G3D::LightweightConduit.
+
+ UDP broadcast allows one machine to send a packet to all machines
+ on the same local network. The IP portion of the address is
+ 0xFFFFFFFF, which indicates "broadcast" to the underlying socket
+ API. This feature is not available with the connection-based TCP
+ protocol abstracted by G3D::ReliableConduit; use multisend
+ instead.
+ */
+ static NetAddress broadcastAddress(uint16 port);
+
+ NetAddress();
+
+ void serialize(class BinaryOutput& b) const;
+ void deserialize(class BinaryInput& b);
+
+ /** @brief Returns true if this is not an illegal address. */
+ bool ok() const;
+
+ /** @brief Returns a value in host format (i.e., don't worry about
+ endian issues) */
+ inline uint32 ip() const {
+ return ntohl(addr.sin_addr.s_addr);
+ //return ntohl(addr.sin_addr.S_un.S_addr);
+ }
+
+ inline uint16 port() const {
+ return ntohs(addr.sin_port);
+ }
+
+ std::string ipString() const;
+ std::string toString() const;
+
+};
+
+std::ostream& operator<<(std::ostream& os, const NetAddress&);
+
+} // namespace G3D
+
+template <> struct HashTrait<G3D::NetAddress> {
+ static size_t hashCode(const G3D::NetAddress& key) {
+ return static_cast<size_t>(key.ip() + (static_cast<G3D::uint32>(key.port()) << 16));
+ }
+};
+
+namespace G3D {
+
+/**
+ Two addresses may point to the same computer but be != because
+ they have different IP's.
+ */
+inline bool operator==(const NetAddress& a, const NetAddress& b) {
+ return (a.ip() == b.ip()) && (a.port() == b.port());
+}
+
+
+inline bool operator!=(const NetAddress& a, const NetAddress& b) {
+ return !(a == b);
+}
+
+} // namespace G3D
+
+#endif
diff --git a/externals/g3dlite/G3D.lib/include/G3D/NetworkDevice.h b/externals/g3dlite/G3D.lib/include/G3D/NetworkDevice.h
new file mode 100644
index 00000000000..faffa56d690
--- /dev/null
+++ b/externals/g3dlite/G3D.lib/include/G3D/NetworkDevice.h
@@ -0,0 +1,738 @@
+/**
+ @file NetworkDevice.h
+
+ These classes abstract networking from the socket level to a
+ serialized messaging style that is more appropriate for games. The
+ performance has been tuned for sending many small messages. The
+ message protocol contains a header that prevents them from being used
+ with raw UDP/TCP (e.g. connecting to an HTTP server).
+
+ LightweightConduit and ReliableConduits have different interfaces
+ because they have different semantics. You would never want to
+ interchange them without rewriting the surrounding code.
+
+ NetworkDevice creates conduits because they need access to a global
+ log pointer and because I don't want non-reference counted conduits
+ being created.
+
+ Be careful with threads and reference counting. The reference
+ counters are not threadsafe, and are also not updated correctly if a
+ thread is explicitly killed. Since the conduits will be passed by
+ const XConduitRef& most of the time this doesn't appear as a major
+ problem. With non-blocking conduits, you should need few threads
+ anyway.
+
+ LightweightConduits preceed each message with a 4-byte host order
+ unsigned integer that is the message type. This does not appear in
+ the message serialization/deserialization.
+
+ ReliableConduits preceed each message with two 4-byte host order
+ unsigned integers. The first is the message type and the second
+ indicates the length of the rest of the data. The size does not
+ include the size of the header itself. The minimum message is 9
+ bytes:a 4-byte type, a 4-byte header equal to "1", and one byte of data.
+
+ @maintainer Morgan McGuire, morgan@graphics3d.com
+ @created 2002-11-22
+ @edited 2006-11-25
+ */
+
+#ifndef G3D_NETWORKDEVICE_H
+#define G3D_NETWORKDEVICE_H
+
+#include "G3D/platform.h"
+#include "G3D/NetAddress.h"
+
+#include <string>
+#include <iostream>
+#include "G3D/g3dmath.h"
+
+#include "G3D/ReferenceCount.h"
+#include "G3D/Array.h"
+#include "G3D/BinaryOutput.h"
+
+namespace G3D {
+
+class TextOutput;
+
+class Conduit : public ReferenceCountedObject {
+protected:
+ friend class NetworkDevice;
+ friend class NetListener;
+
+ uint64 mSent;
+ uint64 mReceived;
+ uint64 bSent;
+ uint64 bReceived;
+
+ SOCKET sock;
+
+ /**
+ Used for serialization. One per socket
+ to make this threadsafe.
+ */
+ BinaryOutput binaryOutput;
+
+ Conduit();
+
+public:
+
+ virtual ~Conduit();
+ uint64 bytesSent() const;
+ uint64 messagesSent() const;
+ uint64 bytesReceived() const;
+ uint64 messagesReceived() const;
+
+ /**
+ If true, receive will return true.
+ */
+ virtual bool messageWaiting();
+
+ /**
+ Returns the type of the waiting message (i.e. the type supplied
+ with send). The return value is zero when there is no message
+ waiting.
+
+ One way to use this is to have a Table mapping message types to
+ pre-allocated subclasses so receiving looks like:
+
+ <PRE>
+ // My base class for messages.
+ class Message {
+ virtual void serialize(BinaryOutput&) const;
+ virtual void deserialize(BinaryInput&);
+ virtual void process() = 0;
+ };
+
+ Message* m = table[conduit->waitingMessageType()];
+ conduit->receive(m);
+ m->process();
+ </PRE>
+
+ Another is to simply switch on the message type:
+
+ <pre>
+ switch (conduit->waitingMessageType()) {
+ case 0:
+ // No message
+ break;
+
+ case ENTITY_SPAWN_MSG:
+ {
+ EntitySpawnMsg m;
+ condiut->receive(m);
+ spawnEntity(m.id, m.position, m.modelID);
+ }
+ break;
+ ...
+ }
+ </pre>
+ */
+ virtual uint32 waitingMessageType() = 0;
+
+ /** Returns true if the connection is ok. */
+ bool ok() const;
+};
+
+typedef ReferenceCountedPointer<class ReliableConduit> ReliableConduitRef;
+
+#ifdef __GNUC__
+// Workaround for a known bug in gcc 4.x where htonl produces
+// a spurrious warning.
+// http://gcc.gnu.org/ml/gcc-bugs/2005-10/msg03270.html
+uint32 gcchtonl(uint32);
+#endif
+
+// Messaging and stream APIs must be supported on a single class because
+// sometimes an application will switch modes on a single socket. For
+// example, when transferring 3D level geometry during handshaking with
+// a game server.
+/**
+ A conduit that guarantees messages will arrive, intact and in order.
+ Create on the client using NetworkDevice::createReliableConduit and
+ on the server using NetListener::waitForConnection. Set the reference
+ counted pointer to NULL to disconnect.
+
+ To construct a ReliableConduit:
+ <OL>
+ <LI> Create a G3D::NetworkDevice (if you are using G3D::GApp, it creates
+ one for you) on the client and on the server.
+ <LI> On the server, create a G3D::NetListener using
+ G3D::NetworkDevice::createListener
+ <LI> On the server, invoke G3D::NetListener::waitForConnection.
+ <LI> On the client, call G3D::NetworkDevice::createReliableConduit.
+ You will need the server's G3D::NetAddress. Consider using
+ G3D::DiscoveryClient to find it via broadcasting.
+ </OL>
+
+ */
+class ReliableConduit : public Conduit {
+private:
+ friend class NetworkDevice;
+ friend class NetListener;
+
+ enum State {RECEIVING, HOLDING, NO_MESSAGE} state;
+
+ NetAddress addr;
+
+ /**
+ Type of the incoming message.
+ */
+ uint32 messageType;
+
+ /**
+ Total size of the incoming message (read from the header).
+ */
+ uint32 messageSize;
+
+ /** Shared buffer for receiving messages. */
+ void* receiveBuffer;
+
+ /** Total size of the receiveBuffer. */
+ size_t receiveBufferTotalSize;
+
+ /** Size occupied by the current message... so far. This will be
+ equal to messageSize when the whole message has arrived.
+ */
+ size_t receiveBufferUsedSize;
+
+ ReliableConduit(const NetAddress& addr);
+
+ ReliableConduit(const SOCKET& sock,
+ const NetAddress& addr);
+
+ template<typename T> static void serializeMessage
+ (uint32 t, const T& m, BinaryOutput& b) {
+
+ b.writeUInt32(t);
+
+ // Reserve space for the 4 byte size header
+ b.writeUInt32(0);
+
+ size_t L = b.length();
+ m.serialize(b);
+ if ((size_t)b.length() == L) {
+ // No data was created by serialization.
+ // We need to send at least one byte because receive assumes that
+ // a zero length message is an error.
+ b.writeUInt8(0xFF);
+ }
+
+ uint32 len = b.size() - 8;
+
+ // We send the length first to tell recv how much data to read.
+ // Here we abuse BinaryOutput a bit and write directly into
+ // its buffer, violating the abstraction.
+ // Note that we write to the second set of 4 bytes, which is
+ // the size field.
+ uint32* lenPtr = ((uint32*)b.getCArray()) + 1;
+ #if defined(__GNUC__)
+ *lenPtr = gcchtonl(len);
+ #else
+ *lenPtr = htonl(len);
+ #endif
+ }
+
+
+ void sendBuffer(const BinaryOutput& b);
+
+ /** Accumulates whatever part of the message (not the header) is
+ still waiting on the socket into the receiveBuffer during
+ state = RECEIVING mode. Closes the socket if anything goes
+ wrong. When receiveBufferUsedSize == messageSize, the entire
+ message has arrived. */
+ void receiveIntoBuffer();
+
+ /** Receives the messageType and messageSize from the socket. */
+ void receiveHeader();
+
+public:
+
+ /**
+ Client invokes this to connect to a server. The call blocks until the
+ conduit is opened. The conduit will not be ok() if it fails.
+ */
+ static ReliableConduitRef create(const NetAddress& address);
+
+ /** Closes the socket. */
+ ~ReliableConduit();
+
+
+ // The message is actually copied from the socket to an internal buffer during
+ // this call. Receive only deserializes.
+ virtual bool messageWaiting();
+
+ /**
+ Serializes the message and schedules it to be sent as soon as possible,
+ and then returns immediately. The message can be any <B>class</B> with
+ a serialize and deserialize method. On the receiving side,
+ use G3D::ReliableConduit::waitingMessageType() to detect the incoming
+ message and then invoke G3D::ReliableConduit::receive(msg) where msg
+ is of the same class as the message that was sent.
+
+ The actual data sent across the network is preceeded by the
+ message type and the size of the serialized message as a 32-bit
+ integer. The size is sent because TCP is a stream protocol and
+ doesn't have a concept of discrete messages.
+ */
+ template<typename T> inline void send(uint32 type, const T& message) {
+ binaryOutput.reset();
+ serializeMessage(type, message, binaryOutput);
+ sendBuffer(binaryOutput);
+ }
+
+ /** Sends an empty message with the given type. Useful for sending
+ commands that have no parameters. */
+ void send(uint32 type);
+
+ /** Send the same message to a number of conduits. Useful for sending
+ data from a server to many clients (only serializes once). */
+ template<typename T>
+ inline static void multisend(
+ const Array<ReliableConduitRef>& array,
+ uint32 type,
+ const T& m) {
+
+ if (array.size() > 0) {
+ array[0]->binaryOutput.reset();
+ serializeMessage(type, m, array[0]->binaryOutput);
+
+ for (int i = 0; i < array.size(); ++i) {
+ array[i]->sendBuffer(array[0]->binaryOutput);
+ }
+ }
+ }
+
+ virtual uint32 waitingMessageType();
+
+ /**
+ If a message is waiting, deserializes the waiting message into
+ message and returns true, otherwise returns false. You can
+ determine the type of the message (and therefore, the class
+ of message) using G3D::ReliableConduit::waitingMessageType().
+ */
+ template<typename T> inline bool receive(T& message) {
+ if (! messageWaiting()) {
+ return false;
+ }
+
+ debugAssert(state == HOLDING);
+ // Deserialize
+ BinaryInput b((uint8*)receiveBuffer, receiveBufferUsedSize, G3D_LITTLE_ENDIAN, BinaryInput::NO_COPY);
+ message.deserialize(b);
+
+ // Don't let anyone read this message again. We leave the buffer
+ // allocated for the next caller, however.
+ receiveBufferUsedSize = 0;
+ state = NO_MESSAGE;
+ messageType = 0;
+ messageSize = 0;
+
+ // Potentially read the next message.
+ messageWaiting();
+
+ return true;
+ }
+
+ /** Removes the current message from the queue. */
+ inline void receive() {
+ if (! messageWaiting()) {
+ return;
+ }
+ receiveBufferUsedSize = 0;
+ state = NO_MESSAGE;
+ messageType = 0;
+ messageSize = 0;
+
+ // Potentially read the next message.
+ messageWaiting();
+ }
+
+ NetAddress address() const;
+};
+
+
+typedef ReferenceCountedPointer<class LightweightConduit> LightweightConduitRef;
+
+/**
+ Provides fast but unreliable transfer of messages. On a LAN,
+ LightweightConduit will probably never drop messages but you
+ <I>might</I> get your messages out of order. On an internet
+ connection it might drop messages altogether. Messages are never
+ corrupted, however. LightweightConduit requires a little less setup
+ and overhead than ReliableConduit. ReliableConduit guarantees
+ message delivery and order but requires a persistent connection.
+
+ To set up a LightweightConduit (assuming you have already made
+ subclasses of G3D::NetMessage based on your application's
+ pcommunication protocol):
+
+[Server Side]
+<OL>
+<LI> Call LightweightConduit::create(port, true, false),
+where port is the port on which you will receive messages.
+
+<LI> Poll LightweightConduit::messageWaiting from your main loop. When
+it is true (or, equivalently, when LightweightConduit::waitingMessageType
+is non-zero) there is an incoming message.
+
+<LI> To read the incoming message, call LightweightConduit::receive with
+the appropriate class type, which mist have a deserialize method.
+LightweightConduit::waitingMessageType tells you what class is
+needed (you make up your own message constants for your program; numbers
+under 1000 are reserved for G3D's internal use).
+
+<LI> When done, simply set the G3D::LightweightConduitRef to NULL or let
+it go out of scope and the conduit cleans itself up automatically.
+</OL>
+
+[Client Side]
+<OL>
+<LI> Call G3D::LightweightConduit::create(). If you will
+broadcast to all servers on a LAN, set the third optional argument to
+true (the default is false for no broadcast). You can also set up the
+receive port as if it was a server to send and receive from a single
+LightweightConduit.
+
+<LI> To send, call G3D::LightweightConduit::send with the target address
+and a pointer to an instance of the message you want to send.
+
+<LI> When done, simply set the G3D::LightweightConduitRef to NULL or let
+it go out of scope and the conduit cleans itself up automatically.
+
+</OL>
+ */
+class LightweightConduit : public Conduit {
+private:
+ friend class NetworkDevice;
+
+ /**
+ True when waitingForMessageType has read the message
+ from the network into messageType/messageStream.
+ */
+ bool alreadyReadMessage;
+
+ /**
+ Origin of the received message.
+ */
+ NetAddress messageSender;
+
+ /**
+ The type of the last message received.
+ */
+ uint32 messageType;
+
+ /**
+ The message received (the type has already been read off).
+ */
+ Array<uint8> messageBuffer;
+
+ LightweightConduit(uint16 receivePort, bool enableReceive, bool enableBroadcast);
+
+ void sendBuffer(const NetAddress& a, BinaryOutput& b);
+
+ /** Maximum transmission unit (packet size in bytes) for this socket.
+ May vary between sockets. */
+ int MTU;
+
+
+ template<typename T>
+ void serializeMessage(
+ uint32 type,
+ const T& m,
+ BinaryOutput& b) const {
+
+ debugAssert(type != 0);
+ b.writeUInt32(type);
+ m.serialize(b);
+ b.writeUInt32(1);
+
+ debugAssertM(b.size() < MTU,
+ format("This LightweightConduit is limited to messages of "
+ "%d bytes (Ethernet hardware limit; this is the "
+ "'UDP MTU')", maxMessageSize()));
+
+ if (b.size() >= MTU) {
+ throw LightweightConduit::PacketSizeException(
+ format("This LightweightConduit is limited to messages of "
+ "%d bytes (Ethernet hardware limit; this is the "
+ "'UDP MTU')", maxMessageSize()),
+ b.size() - 4, // Don't count the type header
+ maxMessageSize());
+ }
+ }
+
+public:
+
+ static LightweightConduitRef create(uint16 receivePort, bool enableReceive, bool enableBroadcast);
+
+ class PacketSizeException {
+ public:
+ std::string message;
+ int serializedPacketSize;
+ int maxMessageSize;
+
+ inline PacketSizeException(const std::string& m, int s, int b) :
+ message(m),
+ serializedPacketSize(s),
+ maxMessageSize(b) {}
+ };
+
+ /** Closes the socket. */
+ ~LightweightConduit();
+
+ /** The maximum length of a message that can be sent
+ (G3D places a small header at the front of each UDP packet;
+ this is already taken into account by the value returned).
+ */
+ inline int maxMessageSize() const {
+ return MTU - 4;
+ }
+
+
+ template<typename T> inline void send(const NetAddress& a, uint32 type, const T& msg) {
+ binaryOutput.reset();
+ serializeMessage(type, msg, binaryOutput);
+ sendBuffer(a, binaryOutput);
+ }
+
+ /** Send the same message to multiple addresses (only serializes once).
+ Useful when server needs to send to a known list of addresses
+ (unlike direct UDP broadcast to all addresses on the subnet) */
+ template<typename T> inline void send(const Array<NetAddress>& a, uint32 type, const T& m) {
+ binaryOutput.reset();
+ serializeMessage(type, m, binaryOutput);
+
+ for (int i = 0; i < a.size(); ++i) {
+ sendBuffer(a[i], binaryOutput);
+ }
+ }
+
+ bool receive(NetAddress& sender);
+
+ template<typename T> inline bool receive(NetAddress& sender, T& message) {
+ bool r = receive(sender);
+ if (r) {
+ BinaryInput b((messageBuffer.getCArray() + 4),
+ messageBuffer.size() - 4,
+ G3D_LITTLE_ENDIAN, BinaryInput::NO_COPY);
+ message.deserialize(b);
+ }
+
+ return r;
+ }
+
+ inline bool receive() {
+ static NetAddress ignore;
+ return receive(ignore);
+ }
+
+ virtual uint32 waitingMessageType();
+
+
+ virtual bool messageWaiting();
+};
+
+///////////////////////////////////////////////////////////////////////////////
+
+typedef ReferenceCountedPointer<class NetListener> NetListenerRef;
+
+/**
+ Runs on the server listening for clients trying to make reliable connections.
+ */
+class NetListener : public ReferenceCountedObject {
+private:
+
+ friend class NetworkDevice;
+
+ SOCKET sock;
+
+ /** Port is in host byte order. */
+ NetListener(uint16 port);
+
+public:
+
+ static NetListenerRef create(const uint16 port);
+
+ ~NetListener();
+
+ /** Block until a connection is received. Returns NULL if
+ something went wrong. */
+ ReliableConduitRef waitForConnection();
+
+ /** True if a client is waiting (i.e. waitForConnection will
+ return immediately). */
+ bool clientWaiting() const;
+
+ bool ok() const;
+};
+
+
+///////////////////////////////////////////////////////////////////////////////
+
+/**
+ @brief Abstraction of network (socket) functionality.
+
+ An abstraction over sockets that provides a message-based network
+ infrastructure optimized for sending many small (~500 bytes) messages.
+ All functions always return immediately.
+
+ Create only one NetworkDevice per process (a WinSock restriction).
+
+ NetworkDevice is technically not thread safe. However, as long as
+ you use different conduits on different threads (or lock conduits
+ before sending), you will encounter no problems sharing the single
+ NetworkDevice across multiple threads. That is, do not invoke the same
+ Conduit's send or receive method on two threads at once.
+
+ This assumes that the underlying WinSock/BSD sockets implementation
+ is thread safe. That is not guaranteed, but in practice seems
+ to always be true (see
+ http://tangentsoft.net/wskfaq/intermediate.html#threadsafety)
+
+ <hr>
+
+ IP networks use "network byte order" (big-endian) for
+ communicating integers. "Host byte order" is the endian-ness of
+ the local machine (typically little-endian; see
+ System::endian). The C functions htonl() and ntohl() convert 32-bit
+ values between these formats. G3D only ever exposes host byte order,
+ so programmers rarely need to be aware of the distinction.
+
+ */
+class NetworkDevice {
+public:
+
+ /** @brief Description of an ethernet or wireless ethernet adapter.*/
+ class EthernetAdapter {
+ public:
+ /** Reverse-DNS of the ip address.*/
+ std::string hostname;
+
+ /** Name of the adapter */
+ std::string name;
+
+ /** IP address in host byte order.*/
+ uint32 ip;
+
+ /** Subnet mask in host byte order.*/
+ uint32 subnet;
+
+ /** UDP broadcast address in host byte order.*/
+ uint32 broadcast;
+
+ /** MAC (hardware) address, if known */
+ uint8 mac[6];
+
+ EthernetAdapter();
+
+ /** Produces a text description of this adapter */
+ void describe(TextOutput& t) const;
+ };
+
+private:
+
+ friend class Conduit;
+ friend class LightweightConduit;
+ friend class ReliableConduit;
+ friend class NetListener;
+
+ bool initialized;
+
+ Array<EthernetAdapter> m_adapterArray;
+
+ /** Broadcast addresses available on this machine,
+ extracted from m_adapterArray.*/
+ Array<uint32> m_broadcastAddresses;
+
+ /** Utility method. */
+ void closesocket(SOCKET& sock) const;
+
+ /** Utility method. Returns true on success.*/
+ bool bind(SOCKET sock, const NetAddress& addr) const;
+
+ /** The global instance */
+ static NetworkDevice* s_instance;
+
+ NetworkDevice();
+
+ bool init();
+
+ void _cleanup();
+
+ /** Called from init to update m_adapterArray and
+ m_broadcastAddresses. */
+ void addAdapter(const EthernetAdapter& a);
+
+public:
+
+ /** Prints an IP address to a string.
+ @param ip In host byte order.*/
+ static std::string formatIP(uint32 ip);
+
+ /** Prints a MAC address to a string. */
+ static std::string formatMAC(const uint8 mac[6]);
+
+ ~NetworkDevice();
+
+ /** Returns the available ethernet adapters for the current
+ machine that are online. Does not include the loopback adapter
+ for localhost.*/
+ inline const Array<EthernetAdapter>& adapterArray() const {
+ return m_adapterArray;
+ }
+
+ /** Returns the (unique) IP addresses for UDP broadcasting
+ extracted from adapterArray(). All are in host byte order. */
+ inline const Array<uint32>& broadcastAddressArray() const {
+ return m_broadcastAddresses;
+ }
+
+ /**
+ Returns NULL if there was a problem initializing the network.
+ */
+ static NetworkDevice* instance();
+
+ /**
+ Shuts down the network device (destroying the global instance).
+ */
+ static void cleanup();
+
+ /**
+ Prints a human-readable description of this machine
+ to the text output stream.
+ */
+ void describeSystem(
+ TextOutput& t);
+
+ void describeSystem(
+ std::string& s);
+
+ /** Returns the name (or one of the names) of this computer */
+ std::string localHostName() const;
+
+ /** There is often more than one address for the local host. This
+ returns all of them.
+ @deprecated Use adapterArray()
+ */
+ void localHostAddresses(Array<NetAddress>& array) const;
+};
+
+
+#ifdef __GNUC__
+inline uint32 gcchtonl(uint32 x) {
+ // This pragma fools gcc into surpressing all error messages,
+ // including the bogus one that it creates for htonl
+# pragma GCC system_header
+ return htonl(x);
+}
+#endif
+
+} // G3D namespace
+
+#ifndef _WIN32
+#undef SOCKADDR_IN
+#undef SOCKET
+#endif
+
+#endif
diff --git a/externals/g3dlite/G3D.lib/include/G3D/PhysicsFrame.h b/externals/g3dlite/G3D.lib/include/G3D/PhysicsFrame.h
new file mode 100644
index 00000000000..44eedb6846e
--- /dev/null
+++ b/externals/g3dlite/G3D.lib/include/G3D/PhysicsFrame.h
@@ -0,0 +1,74 @@
+/**
+ @file PhysicsFrame.h
+
+ @maintainer Morgan McGuire, matrix@graphics3d.com
+
+ @created 2002-07-08
+ @edited 2006-01-10
+*/
+
+#ifndef G3D_PHYSICSFRAME_H
+#define G3D_PHYSICSFRAME_H
+
+#include "G3D/platform.h"
+#include "G3D/Vector3.h"
+#include "G3D/Matrix3.h"
+#include "G3D/Quat.h"
+#include "G3D/CoordinateFrame.h"
+#include <math.h>
+#include <string>
+
+
+namespace G3D {
+
+/**
+ An RT transformation using a quaternion; suitable for
+ physics integration.
+
+ This interface is in "Beta" and will change in the next release.
+ */
+class PhysicsFrame {
+public:
+
+ Quat rotation;
+
+ /**
+ Takes object space points to world space.
+ */
+ Vector3 translation;
+
+ /**
+ Initializes to the identity frame.
+ */
+ PhysicsFrame();
+
+ /**
+ Purely translational force
+ */
+ PhysicsFrame(const Vector3& translation) : translation(translation) {}
+
+ PhysicsFrame(const CoordinateFrame& coordinateFrame);
+
+ /** Compose: create the transformation that is <I>other</I> followed by <I>this</I>.*/
+ PhysicsFrame operator*(const PhysicsFrame& other) const;
+
+ virtual ~PhysicsFrame() {}
+
+ CoordinateFrame toCoordinateFrame() const;
+
+ /**
+ Linear interpolation (spherical linear for the rotations).
+ */
+ PhysicsFrame lerp(
+ const PhysicsFrame& other,
+ float alpha) const;
+
+ void deserialize(class BinaryInput& b);
+
+ void serialize(class BinaryOutput& b) const;
+
+};
+
+} // namespace
+
+#endif
diff --git a/externals/g3dlite/G3D.lib/include/G3D/Plane.h b/externals/g3dlite/G3D.lib/include/G3D/Plane.h
new file mode 100644
index 00000000000..1f0b15e0fe4
--- /dev/null
+++ b/externals/g3dlite/G3D.lib/include/G3D/Plane.h
@@ -0,0 +1,161 @@
+/**
+ @file Plane.h
+
+ Plane class
+
+ @maintainer Morgan McGuire, matrix@graphics3d.com
+
+ @created 2001-06-02
+ @edited 2004-07-18
+*/
+
+#ifndef G3D_PLANE_H
+#define G3D_PLANE_H
+
+#include "G3D/platform.h"
+#include "G3D/Vector3.h"
+#include "G3D/Vector4.h"
+#include "G3D/debugAssert.h"
+
+namespace G3D {
+
+/**
+ An infinite 2D plane in 3D space.
+ */
+class Plane {
+private:
+
+ /** normal.Dot(x,y,z) = distance */
+ Vector3 _normal;
+ float _distance;
+
+ /**
+ Assumes the normal has unit length.
+ */
+ Plane(const Vector3& n, float d) : _normal(n), _distance(d) {
+ }
+
+public:
+
+ Plane() : _normal(Vector3::unitY()), _distance(0) {
+ }
+
+ /**
+ Constructs a plane from three points.
+ */
+ Plane(
+ const Vector3& point0,
+ const Vector3& point1,
+ const Vector3& point2);
+
+ /**
+ Constructs a plane from three points, where at most two are
+ at infinity (w = 0, not xyz = inf).
+ */
+ Plane(
+ Vector4 point0,
+ Vector4 point1,
+ Vector4 point2);
+
+ /**
+ The normal will be unitized.
+ */
+ Plane(
+ const Vector3& __normal,
+ const Vector3& point);
+
+ static Plane fromEquation(float a, float b, float c, float d);
+
+ Plane(class BinaryInput& b);
+ void serialize(class BinaryOutput& b) const;
+ void deserialize(class BinaryInput& b);
+
+ virtual ~Plane() {}
+
+ /**
+ Returns true if point is on the side the normal points to or
+ is in the plane.
+ */
+ inline bool halfSpaceContains(Vector3 point) const {
+ // Clamp to a finite range for testing
+ point = point.clamp(Vector3::minFinite(), Vector3::maxFinite());
+
+ // We can get away with putting values *at* the limits of the float32 range into
+ // a dot product, since the dot product is carried out on float64.
+ return _normal.dot(point) >= _distance;
+ }
+
+ /**
+ Returns true if point is on the side the normal points to or
+ is in the plane.
+ */
+ inline bool halfSpaceContains(const Vector4& point) const {
+ if (point.w == 0) {
+ return _normal.dot(point.xyz()) > 0;
+ } else {
+ return halfSpaceContains(point.xyz() / point.w);
+ }
+ }
+
+ /**
+ Returns true if point is on the side the normal points to or
+ is in the plane. Only call on finite points. Faster than halfSpaceContains.
+ */
+ inline bool halfSpaceContainsFinite(const Vector3& point) const {
+ debugAssert(point.isFinite());
+ return _normal.dot(point) >= _distance;
+ }
+
+ /**
+ Returns true if the point is nearly in the plane.
+ */
+ inline bool fuzzyContains(const Vector3 &point) const {
+ return fuzzyEq(point.dot(_normal), _distance);
+ }
+
+ inline const Vector3& normal() const {
+ return _normal;
+ }
+
+ /**
+ Returns distance from point to plane. Distance is negative if point is behind (not in plane in direction opposite normal) the plane.
+ */
+ inline float distance(const Vector3& x) const {
+ return (_normal.dot(x) - _distance);
+ }
+
+ inline Vector3 closestPoint(const Vector3& x) const {
+ return x + (_normal * (-distance(x)));
+ }
+
+ /** Returns normal * distance from origin */
+ Vector3 center() const {
+ return _normal * _distance;
+ }
+
+ /**
+ Inverts the facing direction of the plane so the new normal
+ is the inverse of the old normal.
+ */
+ void flip();
+
+ /**
+ Returns the equation in the form:
+
+ <CODE>normal.Dot(Vector3(<I>x</I>, <I>y</I>, <I>z</I>)) + d = 0</CODE>
+ */
+ void getEquation(Vector3 &normal, double& d) const;
+ void getEquation(Vector3 &normal, float& d) const;
+
+ /**
+ ax + by + cz + d = 0
+ */
+ void getEquation(double& a, double& b, double& c, double& d) const;
+ void getEquation(float& a, float& b, float& c, float& d) const;
+
+ std::string toString() const;
+};
+
+} // namespace
+
+#endif
diff --git a/externals/g3dlite/G3D.lib/include/G3D/PointAABSPTree.h b/externals/g3dlite/G3D.lib/include/G3D/PointAABSPTree.h
new file mode 100644
index 00000000000..c8d527a45c2
--- /dev/null
+++ b/externals/g3dlite/G3D.lib/include/G3D/PointAABSPTree.h
@@ -0,0 +1,1207 @@
+/**
+ @file PointAABSPTree.h
+
+ @maintainer Morgan McGuire, matrix@graphics3d.com
+
+ @created 2004-01-11
+ @edited 2008-11-02
+
+ Copyright 2000-2008, Morgan McGuire.
+ All rights reserved.
+
+ */
+
+#ifndef X_PointKDTree_H
+#define X_PointKDTree_H
+
+#include "G3D/platform.h"
+#include "G3D/Array.h"
+#include "G3D/Table.h"
+#include "G3D/Vector2.h"
+#include "G3D/Vector3.h"
+#include "G3D/Vector4.h"
+#include "G3D/AABox.h"
+#include "G3D/Sphere.h"
+#include "G3D/Box.h"
+#include "G3D/BinaryInput.h"
+#include "G3D/BinaryOutput.h"
+#include "G3D/CollisionDetection.h"
+#include "G3D/GCamera.h"
+#include "G3D/PositionTrait.h"
+#include <algorithm>
+
+
+///////////////////////////////////////////////////////
+
+/** @deprecated */
+inline void getPosition(const G3D::Vector3& v, G3D::Vector3& p) {
+ p = v;
+}
+
+/** @deprecated */
+inline void getPosition(const G3D::Vector4& v, G3D::Vector3& p) {
+ p = v.xyz();
+}
+
+/** @deprecated */
+inline void getPosition(const G3D::Vector2& v, G3D::Vector3& p) {
+ p.x = v.x;
+ p.y = v.y;
+ p.z = 0;
+}
+
+namespace G3D {
+
+/**
+ A set data structure that supports spatial queries using an axis-aligned
+ BSP tree for speed.
+
+ PointKDTree allows you to quickly find points in 3D that lie within
+ a box or sphere. For large sets of objects it is much faster
+ than testing each object for a collision. See also G3D::KDTree; this class
+ is optimized for point sets, e.g.,for use in photon mapping and mesh processing.
+
+ <B>Template Parameters</B>
+
+ <br>
+
+ <br>The template parameter <I>T</I> must be one for which
+ the following functions are overloaded:
+
+ <pre>
+ T::T(); <I>(public constructor of no arguments)</I>
+
+ template<> struct PositionTrait<class T> {
+ static void getPosition(const T& v, G3D::Vector3& p);};
+
+ template <> struct HashTrait<class T> {
+ static size_t hashCode(const T& key);};
+
+ template<> struct EqualsTrait<class T> {
+ static bool equals(const T& a, const T& b); };
+ </pre>
+
+ <p>
+
+ G3D provides these for the Vector2, Vector3, and Vector4 classes.
+ If you use a custom class, or a pointer to a custom class, you will need
+ to define those functions.
+
+ <B>Moving %Set Members</B>
+ <DT>It is important that objects do not move without updating the
+ PointKDTree. If the position of an object is about
+ to change, PointKDTree::remove it before they change and
+ PointKDTree::insert it again afterward. For objects
+ where the hashCode and == operator are invariant with respect
+ to the 3D position,
+ you can use the PointKDTree::update method as a shortcut to
+ insert/remove an object in one step after it has moved.
+
+
+ Note: Do not mutate any value once it has been inserted into PointKDTree. Values
+ are copied interally. All PointKDTree iterators convert to pointers to constant
+ values to reinforce this.
+
+ If you want to mutate the objects you intend to store in a PointKDTree
+ simply insert <I>pointers</I> to your objects instead of the objects
+ themselves, and ensure that the above operations are defined. (And
+ actually, because values are copied, if your values are large you may
+ want to insert pointers anyway, to save space and make the balance
+ operation faster.)
+
+ <B>Dimensions</B>
+ Although designed as a 3D-data structure, you can use the PointKDTree
+ for data distributed along 2 or 1 axes by simply returning bounds
+ that are always zero along one or more dimensions.
+
+*/
+template<class T,
+ class PositionFunc = PositionTrait<T>,
+ class HashFunc = HashTrait<T>,
+ class EqualsFunc = EqualsTrait<T> >
+class PointKDTree {
+protected:
+#define TreeType PointKDTree<T, PositionFunc, HashFunc, EqualsFunc>
+
+ // Unlike the KDTree, the PointKDTree assumes that T elements are
+ // small and keeps the handle and cached position together instead of
+ // placing them in separate bounds arrays. Also note that a copy of T
+ // is kept in the member table and that there is no indirection.
+ class Handle {
+ private:
+ Vector3 m_position;
+
+ public:
+ T value;
+
+ inline Handle() {}
+ inline Handle(const T& v) : value(v) {
+ PositionFunc::getPosition(v, m_position);
+ }
+
+ /** Used by makeNode to create fake handles for partitioning. */
+ void setPosition(const Vector3& v) {
+ m_position = v;
+ }
+
+ inline const Vector3& position() const {
+ return m_position;
+ }
+ };
+
+ /** Returns the bounds of the sub array. Used by makeNode. */
+ static AABox computeBounds(
+ const Array<Handle>& point) {
+
+ if (point.size() == 0) {
+ return AABox(Vector3::inf(), Vector3::inf());
+ }
+
+ AABox bounds(point[0].position());
+
+ for (int p = 0; p < point.size(); ++p) {
+ bounds.merge(point[p].position());
+ }
+
+ return bounds;
+ }
+
+ class Node {
+ public:
+
+ /** Spatial bounds on all values at this node and its children, based purely on
+ the parent's splitting planes. May be infinite */
+ AABox splitBounds;
+
+ Vector3::Axis splitAxis;
+
+ /** Location along the specified axis */
+ float splitLocation;
+
+ /** child[0] contains all values strictly
+ smaller than splitLocation along splitAxis.
+
+ child[1] contains all values strictly
+ larger.
+
+ Both may be NULL if there are not enough
+ values to bother recursing.
+ */
+ Node* child[2];
+
+ /** Values if this is a leaf node). */
+ Array<Handle> valueArray;
+
+ /** Creates node with NULL children */
+ Node() {
+ splitAxis = Vector3::X_AXIS;
+ splitLocation = 0;
+ splitBounds = AABox(-Vector3::inf(), Vector3::inf());
+ for (int i = 0; i < 2; ++i) {
+ child[i] = NULL;
+ }
+ }
+
+ /**
+ Doesn't clone children.
+ */
+ Node(const Node& other) : valueArray(other.valueArray) {
+ splitAxis = other.splitAxis;
+ splitLocation = other.splitLocation;
+ splitBounds = other.splitBounds;
+ for (int i = 0; i < 2; ++i) {
+ child[i] = NULL;
+ }
+ }
+
+ /** Copies the specified subarray of pt into point, NULLs the children.
+ Assumes a second pass will set splitBounds. */
+ Node(const Array<Handle>& pt) {
+ splitAxis = Vector3::X_AXIS;
+ splitLocation = 0;
+ for (int i = 0; i < 2; ++i) {
+ child[i] = NULL;
+ }
+ valueArray = pt;
+ }
+
+
+ /** Deletes the children (but not the values) */
+ ~Node() {
+ for (int i = 0; i < 2; ++i) {
+ delete child[i];
+ }
+ }
+
+
+ /** Returns true if this node is a leaf (no children) */
+ inline bool isLeaf() const {
+ return (child[0] == NULL) && (child[1] == NULL);
+ }
+
+
+ /**
+ Recursively appends all handles and children's handles
+ to the array.
+ */
+ void getHandles(Array<Handle>& handleArray) const {
+ handleArray.append(valueArray);
+ for (int i = 0; i < 2; ++i) {
+ if (child[i] != NULL) {
+ child[i]->getHandles(handleArray);
+ }
+ }
+ }
+
+
+ void verifyNode(const Vector3& lo, const Vector3& hi) {
+ // debugPrintf("Verifying: split %d @ %f [%f, %f, %f], [%f, %f, %f]\n",
+ // splitAxis, splitLocation, lo.x, lo.y, lo.z, hi.x, hi.y, hi.z);
+
+ debugAssert(lo == splitBounds.low());
+ debugAssert(hi == splitBounds.high());
+
+ for (int i = 0; i < valueArray.length(); ++i) {
+ const Vector3& b = valueArray[i].position();
+ debugAssert(splitBounds.contains(b));
+ }
+
+ if (child[0] || child[1]) {
+ debugAssert(lo[splitAxis] < splitLocation);
+ debugAssert(hi[splitAxis] > splitLocation);
+ }
+
+ Vector3 newLo = lo;
+ newLo[splitAxis] = splitLocation;
+ Vector3 newHi = hi;
+ newHi[splitAxis] = splitLocation;
+
+ if (child[0] != NULL) {
+ child[0]->verifyNode(lo, newHi);
+ }
+
+ if (child[1] != NULL) {
+ child[1]->verifyNode(newLo, hi);
+ }
+ }
+
+
+ /**
+ Stores the locations of the splitting planes (the structure but not the content)
+ so that the tree can be quickly rebuilt from a previous configuration without
+ calling balance.
+ */
+ static void serializeStructure(const Node* n, BinaryOutput& bo) {
+ if (n == NULL) {
+ bo.writeUInt8(0);
+ } else {
+ bo.writeUInt8(1);
+ n->splitBounds.serialize(bo);
+ serialize(n->splitAxis, bo);
+ bo.writeFloat32(n->splitLocation);
+ for (int c = 0; c < 2; ++c) {
+ serializeStructure(n->child[c], bo);
+ }
+ }
+ }
+
+ /** Clears the member table */
+ static Node* deserializeStructure(BinaryInput& bi) {
+ if (bi.readUInt8() == 0) {
+ return NULL;
+ } else {
+ Node* n = new Node();
+ n->splitBounds.deserialize(bi);
+ deserialize(n->splitAxis, bi);
+ n->splitLocation = bi.readFloat32();
+ for (int c = 0; c < 2; ++c) {
+ n->child[c] = deserializeStructure(bi);
+ }
+ }
+ }
+
+ /** Returns the deepest node that completely contains bounds. */
+ Node* findDeepestContainingNode(const Vector3& point) {
+
+ // See which side of the splitting plane the bounds are on
+ if (point[splitAxis] < splitLocation) {
+ // Point is on the low side. Recurse into the child
+ // if it exists.
+ if (child[0] != NULL) {
+ return child[0]->findDeepestContainingNode(point);
+ }
+ } else if (point[splitAxis] > splitLocation) {
+ // Point is on the high side, recurse into the child
+ // if it exists.
+ if (child[1] != NULL) {
+ return child[1]->findDeepestContainingNode(point);
+ }
+ }
+
+ // There was no containing child, so this node is the
+ // deepest containing node.
+ return this;
+ }
+
+ /** Appends all members that intersect the box.
+ If useSphere is true, members are tested against the sphere instead. */
+ void getIntersectingMembers(
+ const AABox& sphereBounds,
+ const Sphere& sphere,
+ Array<T>& members) const {
+
+ // Test all values at this node. Extract the
+ // underlying C array for speed
+ const int N = valueArray.size();
+ const Handle* handleArray = valueArray.getCArray();
+
+ const float r2 = square(sphere.radius);
+
+ // Copy the sphere center so that it is on the stack near the radius
+ const Vector3 center = sphere.center;
+ for (int v = 0; v < N; ++v) {
+ if ((center - handleArray[v].position()).squaredLength() <= r2) {
+ members.append(handleArray[v].value);
+ }
+ }
+
+ // If the left child overlaps the box, recurse into it
+ if (child[0] && (sphereBounds.low()[splitAxis] < splitLocation)) {
+ child[0]->getIntersectingMembers(sphereBounds, sphere, members);
+ }
+
+ // If the right child overlaps the box, recurse into it
+ if (child[1] && (sphereBounds.high()[splitAxis] > splitLocation)) {
+ child[1]->getIntersectingMembers(sphereBounds, sphere, members);
+ }
+ }
+
+ /** Appends all members that intersect the box.
+ If useSphere is true, members are tested against the sphere instead.
+
+ Implemented using both box and sphere tests to simplify the implementation
+ of a future beginSphereInteresection iterator using the same underlying
+ BoxIterator class.
+ */
+ void getIntersectingMembers(
+ const AABox& box,
+ const Sphere& sphere,
+ Array<T>& members,
+ bool useSphere) const {
+
+ // Test all values at this node
+ for (int v = 0; v < valueArray.size(); ++v) {
+ if ((useSphere && sphere.contains(valueArray[v].position())) ||
+ (! useSphere && box.contains(valueArray[v].position()))) {
+ members.append(valueArray[v].value);
+ }
+ }
+
+ // If the left child overlaps the box, recurse into it
+ if ((child[0] != NULL) && (box.low()[splitAxis] < splitLocation)) {
+ child[0]->getIntersectingMembers(box, sphere, members, useSphere);
+ }
+
+ // If the right child overlaps the box, recurse into it
+ if ((child[1] != NULL) && (box.high()[splitAxis] > splitLocation)) {
+ child[1]->getIntersectingMembers(box, sphere, members, useSphere);
+ }
+ }
+
+ /**
+ Recurse through the tree, assigning splitBounds fields.
+ */
+ void assignSplitBounds(const AABox& myBounds) {
+ splitBounds = myBounds;
+
+# ifdef G3D_DEBUG
+ if (child[0] || child[1]) {
+ debugAssert(splitBounds.high()[splitAxis] > splitLocation);
+ debugAssert(splitBounds.low()[splitAxis] < splitLocation);
+ }
+# endif
+
+ AABox childBounds[2];
+ myBounds.split(splitAxis, splitLocation, childBounds[0], childBounds[1]);
+
+ for (int c = 0; c < 2; ++c) {
+ if (child[c]) {
+ child[c]->assignSplitBounds(childBounds[c]);
+ }
+ }
+ }
+ };
+
+ class AxisComparator {
+ private:
+ Vector3::Axis sortAxis;
+
+ public:
+
+ AxisComparator(Vector3::Axis s) : sortAxis(s) {}
+
+ inline int operator()(const Handle& A, const Handle& B) const {
+ if (A.position()[sortAxis] > B.position()[sortAxis]) {
+ return -1;
+ } else if (A.position()[sortAxis] < B.position()[sortAxis]) {
+ return 1;
+ } else {
+ return 0;
+ }
+ }
+ };
+
+ /**
+ Recursively subdivides the subarray.
+
+ The source array will be cleared after it is used
+
+ Call assignSplitBounds() on the root node after making a tree.
+ */
+ Node* makeNode(
+ Array<Handle>& source,
+ Array<Handle>& temp,
+ int valuesPerNode,
+ int numMeanSplits) {
+
+ Node* node = NULL;
+
+ if (source.size() <= valuesPerNode) {
+ // Make a new leaf node
+ node = new Node(source);
+
+ // Set the pointers in the memberTable
+ for (int i = 0; i < source.size(); ++i) {
+ memberTable.set(source[i].value, node);
+ }
+
+ } else {
+ // Make a new internal node
+ node = new Node();
+
+ const AABox bounds = computeBounds(source);
+ const Vector3 extent = bounds.high() - bounds.low();
+
+ Vector3::Axis splitAxis = extent.primaryAxis();
+
+ float splitLocation;
+
+ Array<Handle> lt, gt;
+
+ if (numMeanSplits <= 0) {
+ source.medianPartition(lt, node->valueArray, gt, temp, AxisComparator(splitAxis));
+ splitLocation = node->valueArray[0].position()[splitAxis];
+
+ if ((node->valueArray.size() > source.size() / 2) &&
+ (source.size() > 10)) {
+ // Our median split put an awful lot of points on the splitting plane. Try a mean
+ // split instead
+ numMeanSplits = 1;
+ }
+ }
+
+ if (numMeanSplits > 0) {
+ // Compute the mean along the axis
+
+ splitLocation = (bounds.high()[splitAxis] +
+ bounds.low()[splitAxis]) / 2.0;
+
+ Handle splitHandle;
+ Vector3 v;
+ v[splitAxis] = splitLocation;
+ splitHandle.setPosition(v);
+
+ source.partition(splitHandle, lt, node->valueArray, gt, AxisComparator(splitAxis));
+ }
+
+# if defined(G3D_DEBUG) && defined(VERIFY_TREE)
+ for (int i = 0; i < lt.size(); ++i) {
+ const Vector3& v = lt[i].position();
+ debugAssert(v[splitAxis] < splitLocation);
+ }
+ for (int i = 0; i < gt.size(); ++i) {
+ debugAssert(gt[i].position()[splitAxis] > splitLocation);
+ }
+ for (int i = 0; i < node->valueArray.size(); ++i) {
+ debugAssert(node->valueArray[i].position()[splitAxis] == splitLocation);
+ }
+# endif
+
+ node->splitAxis = splitAxis;
+ node->splitLocation = splitLocation;
+
+ // Throw away the source array to save memory
+ source.fastClear();
+
+ if (lt.size() > 0) {
+ node->child[0] = makeNode(lt, temp, valuesPerNode, numMeanSplits - 1);
+ }
+
+ if (gt.size() > 0) {
+ node->child[1] = makeNode(gt, temp, valuesPerNode, numMeanSplits - 1);
+ }
+
+ // Add the values stored at this interior node to the member table
+ for(int i = 0; i < node->valueArray.size(); ++i) {
+ memberTable.set(node->valueArray[i].value, node);
+ }
+
+ }
+
+ return node;
+ }
+
+ /**
+ Recursively clone the passed in node tree, setting
+ pointers for members in the memberTable as appropriate.
+ called by the assignment operator.
+ */
+ Node* cloneTree(Node* src) {
+ Node* dst = new Node(*src);
+
+ // Make back pointers
+ for (int i = 0; i < dst->valueArray.size(); ++i) {
+ memberTable.set(dst->valueArray[i].value, dst);
+ }
+
+ // Clone children
+ for (int i = 0; i < 2; ++i) {
+ if (src->child[i] != NULL) {
+ dst->child[i] = cloneTree(src->child[i]);
+ }
+ }
+
+ return dst;
+ }
+
+ /** Maps members to the node containing them */
+ typedef Table<T, Node*, HashFunc, EqualsFunc> MemberTable;
+ MemberTable memberTable;
+
+ Node* root;
+
+public:
+
+ /** To construct a balanced tree, insert the elements and then call
+ PointKDTree::balance(). */
+ PointKDTree() : root(NULL) {}
+
+
+ PointKDTree(const PointKDTree& src) : root(NULL) {
+ *this = src;
+ }
+
+
+ PointKDTree& operator=(const PointKDTree& src) {
+ delete root;
+ // Clone tree takes care of filling out the memberTable.
+ root = cloneTree(src.root);
+ return *this;
+ }
+
+
+ ~PointKDTree() {
+ clear();
+ }
+
+ /**
+ Throws out all elements of the set and erases the structure of the tree.
+ */
+ void clear() {
+ memberTable.clear();
+ delete root;
+ root = NULL;
+ }
+
+ /** Removes all elements of the set while maintaining the structure of the tree */
+ void clearData() {
+ memberTable.clear();
+ Array<Node*> stack;
+ stack.push(root);
+ while (stack.size() > 0) {
+ Node* node = stack.pop();
+ node->valueArray.fastClear();
+
+ for (int i = 0; i < 2; ++i) {
+ if (node->child[i] != NULL) {
+ stack.push(node->child[i]);
+ }
+ }
+ }
+ }
+
+
+ int size() const {
+ return memberTable.size();
+ }
+
+ /**
+ Inserts an object into the set if it is not
+ already present. O(log n) time. Does not
+ cause the tree to be balanced.
+ */
+ void insert(const T& value) {
+ if (contains(value)) {
+ // Already in the set
+ return;
+ }
+
+ Handle h(value);
+
+ if (root == NULL) {
+ // This is the first node; create a root node
+ root = new Node();
+ }
+
+ Node* node = root->findDeepestContainingNode(h.position());
+
+ // Insert into the node
+ node->valueArray.append(h);
+
+ // Insert into the node table
+ memberTable.set(value, node);
+ }
+
+ /** Inserts each elements in the array in turn. If the tree
+ begins empty (no structure and no elements), this is faster
+ than inserting each element in turn. You still need to balance
+ the tree at the end.*/
+ void insert(const Array<T>& valueArray) {
+ // Pre-size the member table to avoid multiple allocations
+ memberTable.setSizeHint(valueArray.size() + size());
+
+ if (root == NULL) {
+ // Optimized case for an empty tree; don't bother
+ // searching or reallocating the root node's valueArray
+ // as we incrementally insert.
+ root = new Node();
+ root->valueArray.resize(valueArray.size());
+ for (int i = 0; i < valueArray.size(); ++i) {
+ // Insert in opposite order so that we have the exact same
+ // data structure as if we inserted each (i.e., order is reversed
+ // from array).
+ root->valueArray[valueArray.size() - i - 1] = Handle(valueArray[i]);
+ memberTable.set(valueArray[i], root);
+ }
+ } else {
+ // Insert at appropriate tree depth.
+ for (int i = 0; i < valueArray.size(); ++i) {
+ insert(valueArray[i]);
+ }
+ }
+ }
+
+
+ /**
+ Returns true if this object is in the set, otherwise
+ returns false. O(1) time.
+ */
+ bool contains(const T& value) {
+ return memberTable.containsKey(value);
+ }
+
+
+ /**
+ Removes an object from the set in O(1) time.
+ It is an error to remove members that are not already
+ present. May unbalance the tree.
+
+ Removing an element never causes a node (split plane) to be removed...
+ nodes are only changed when the tree is rebalanced. This behavior
+ is desirable because it allows the split planes to be serialized,
+ and then deserialized into an empty tree which can be repopulated.
+ */
+ void remove(const T& value) {
+ debugAssertM(contains(value),
+ "Tried to remove an element from a "
+ "PointKDTree that was not present");
+
+ Array<Handle>& list = memberTable[value]->valueArray;
+
+ // Find the element and remove it
+ for (int i = list.length() - 1; i >= 0; --i) {
+ if (list[i].value == value) {
+ list.fastRemove(i);
+ break;
+ }
+ }
+ memberTable.remove(value);
+ }
+
+
+ /**
+ If the element is in the set, it is removed.
+ The element is then inserted.
+
+ This is useful when the == and hashCode methods
+ on <I>T</I> are independent of the bounds. In
+ that case, you may call update(v) to insert an
+ element for the first time and call update(v)
+ again every time it moves to keep the tree
+ up to date.
+ */
+ void update(const T& value) {
+ if (contains(value)) {
+ remove(value);
+ }
+ insert(value);
+ }
+
+
+ /**
+ Rebalances the tree (slow). Call when objects
+ have moved substantially from their original positions
+ (which unbalances the tree and causes the spatial
+ queries to be slow).
+
+ @param valuesPerNode Maximum number of elements to put at
+ a node.
+
+ @param numMeanSplits numMeanSplits = 0 gives a
+ fully axis aligned BSP-tree, where the balance operation attempts to balance
+ the tree so that every splitting plane has an equal number of left
+ and right children (i.e. it is a <B>median</B> split along that axis).
+ This tends to maximize average performance; all querries will return in the same amount of time.
+
+ You can override this behavior by
+ setting a number of <B>mean</B> (average) splits. numMeanSplits = MAX_INT
+ creates a full oct-tree, which tends to optimize peak performance (some areas of the scene will terminate after few recursive splits) at the expense of
+ peak performance.
+ */
+ void balance(int valuesPerNode = 40, int numMeanSplits = 3) {
+ if (root == NULL) {
+ // Tree is empty
+ return;
+ }
+
+ Array<Handle> handleArray;
+ root->getHandles(handleArray);
+
+ // Delete the old tree
+ clear();
+
+ Array<Handle> temp;
+ root = makeNode(handleArray, temp, valuesPerNode, numMeanSplits);
+ temp.fastClear();
+
+ // Walk the tree, assigning splitBounds. We start with unbounded
+ // space.
+ root->assignSplitBounds(AABox::maxFinite());
+
+# ifdef _DEBUG
+ root->verifyNode(Vector3::minFinite(), Vector3::maxFinite());
+# endif
+ }
+
+private:
+
+ /**
+ Returns the elements
+
+ @param parentMask The mask that this node returned from culledBy.
+ */
+ static void getIntersectingMembers(
+ const Array<Plane>& plane,
+ Array<T>& members,
+ Node* node,
+ uint32 parentMask) {
+
+ int dummy;
+
+ if (parentMask == 0) {
+ // None of these planes can cull anything
+ for (int v = node->valueArray.size() - 1; v >= 0; --v) {
+ members.append(node->valueArray[v].value);
+ }
+
+ // Iterate through child nodes
+ for (int c = 0; c < 2; ++c) {
+ if (node->child[c]) {
+ getIntersectingMembers(plane, members, node->child[c], 0);
+ }
+ }
+ } else {
+
+ if (node->valueArray.size() > 0) {
+ // This is a leaf; check the points
+ debugAssertM(node->child[0] == NULL, "Malformed Point tree");
+ debugAssertM(node->child[1] == NULL, "Malformed Point tree");
+
+ // Test values at this node against remaining planes
+ for (int p = 0; p < plane.size(); ++p) {
+ if ((parentMask >> p) & 1 != 0) {
+ // Test against this plane
+ const Plane& curPlane = plane[p];
+ for (int v = node->valueArray.size() - 1; v >= 0; --v) {
+ if (curPlane.halfSpaceContains(node->valueArray[v].position())) {
+ members.append(node->valueArray[v].value);
+ }
+ }
+ }
+ }
+ } else {
+
+ uint32 childMask = 0xFFFFFF;
+
+ // Iterate through child nodes
+ for (int c = 0; c < 2; ++c) {
+ if (node->child[c] &&
+ ! node->child[c]->splitBounds.culledBy(plane, dummy, parentMask, childMask)) {
+ // This node was not culled
+ getIntersectingMembers(plane, members, node->child[c], childMask);
+ }
+ }
+ }
+ }
+ }
+
+public:
+
+ /**
+ Returns all members inside the set of planes.
+ @param members The results are appended to this array.
+ */
+ void getIntersectingMembers(const Array<Plane>& plane, Array<T>& members) const {
+ if (root == NULL) {
+ return;
+ }
+
+ getIntersectingMembers(plane, members, root, 0xFFFFFF);
+ }
+
+ /**
+ Typically used to find all visible
+ objects inside the view frustum (see also GCamera::getClipPlanes)... i.e. all objects
+ <B>not<B> culled by frustum.
+
+ Example:
+ <PRE>
+ Array<Object*> visible;
+ tree.getIntersectingMembers(camera.frustum(), visible);
+ // ... Draw all objects in the visible array.
+ </PRE>
+ @param members The results are appended to this array.
+ */
+ void getIntersectingMembers(const GCamera::Frustum& frustum, Array<T>& members) const {
+ Array<Plane> plane;
+
+ for (int i = 0; i < frustum.faceArray.size(); ++i) {
+ plane.append(frustum.faceArray[i].plane);
+ }
+
+ getIntersectingMembers(plane, members);
+ }
+
+ /**
+ C++ STL style iterator variable. See beginBoxIntersection().
+ The iterator overloads the -> (dereference) operator, so this
+ acts like a pointer to the current member.
+ */
+ // This iterator turns Node::getIntersectingMembers into a
+ // coroutine. It first translates that method from recursive to
+ // stack based, then captures the system state (analogous to a Scheme
+ // continuation) after each element is appended to the member array,
+ // and allowing the computation to be restarted.
+ class BoxIntersectionIterator {
+ private:
+ friend class TreeType;
+
+ /** True if this is the "end" iterator instance */
+ bool isEnd;
+
+ /** The box that we're testing against. */
+ AABox box;
+
+ /** Node that we're currently looking at. Undefined if isEnd
+ is true. */
+ Node* node;
+
+ /** Nodes waiting to be processed */
+ // We could use backpointers within the tree and careful
+ // state management to avoid ever storing the stack-- but
+ // it is much easier this way and only inefficient if the
+ // caller uses post increment (which they shouldn't!).
+ Array<Node*> stack;
+
+ /** The next index of current->valueArray to return.
+ Undefined when isEnd is true.*/
+ int nextValueArrayIndex;
+
+ BoxIntersectionIterator() : isEnd(true) {}
+
+ BoxIntersectionIterator(const AABox& b, const Node* root) :
+ isEnd(root == NULL), box(b),
+ node(const_cast<Node*>(root)), nextValueArrayIndex(-1) {
+
+ // We intentionally start at the "-1" index of the current
+ // node so we can use the preincrement operator to move
+ // ourselves to element 0 instead of repeating all of the
+ // code from the preincrement method. Note that this might
+ // cause us to become the "end" instance.
+ ++(*this);
+ }
+
+ public:
+
+ inline bool operator!=(const BoxIntersectionIterator& other) const {
+ return ! (*this == other);
+ }
+
+ bool operator==(const BoxIntersectionIterator& other) const {
+ if (isEnd) {
+ return other.isEnd;
+ } else if (other.isEnd) {
+ return false;
+ } else {
+ // Two non-end iterators; see if they match. This is kind of
+ // silly; users shouldn't call == on iterators in general unless
+ // one of them is the end iterator.
+ if ((box != other.box) || (node != other.node) ||
+ (nextValueArrayIndex != other.nextValueArrayIndex) ||
+ (stack.length() != other.stack.length())) {
+ return false;
+ }
+
+ // See if the stacks are the same
+ for (int i = 0; i < stack.length(); ++i) {
+ if (stack[i] != other.stack[i]) {
+ return false;
+ }
+ }
+
+ // We failed to find a difference; they must be the same
+ return true;
+ }
+ }
+
+ /**
+ Pre increment.
+ */
+ BoxIntersectionIterator& operator++() {
+ ++nextValueArrayIndex;
+
+ bool foundIntersection = false;
+ while (! isEnd && ! foundIntersection) {
+
+ // Search for the next node if we've exhausted this one
+ while ((! isEnd) && (nextValueArrayIndex >= node->valueArray.length())) {
+ // If we entered this loop, then the iterator has exhausted the elements at
+ // node (possibly because it just switched to a child node with no members).
+ // This loop continues until it finds a node with members or reaches
+ // the end of the whole intersection search.
+
+ // If the right child overlaps the box, push it onto the stack for
+ // processing.
+ if ((node->child[1] != NULL) &&
+ (box.high()[node->splitAxis] > node->splitLocation)) {
+ stack.push(node->child[1]);
+ }
+
+ // If the left child overlaps the box, push it onto the stack for
+ // processing.
+ if ((node->child[0] != NULL) &&
+ (box.low()[node->splitAxis] < node->splitLocation)) {
+ stack.push(node->child[0]);
+ }
+
+ if (stack.length() > 0) {
+ // Go on to the next node (which may be either one of the ones we
+ // just pushed, or one from farther back the tree).
+ node = stack.pop();
+ nextValueArrayIndex = 0;
+ } else {
+ // That was the last node; we're done iterating
+ isEnd = true;
+ }
+ }
+
+ // Search for the next intersection at this node until we run out of children
+ while (! isEnd && ! foundIntersection && (nextValueArrayIndex < node->valueArray.length())) {
+ if (box.intersects(node->valueArray[nextValueArrayIndex].bounds)) {
+ foundIntersection = true;
+ } else {
+ ++nextValueArrayIndex;
+ // If we exhaust this node, we'll loop around the master loop
+ // to find a new node.
+ }
+ }
+ }
+
+ return *this;
+ }
+
+ /**
+ Post increment (much slower than preincrement!).
+ */
+ BoxIntersectionIterator operator++(int) {
+ BoxIntersectionIterator old = *this;
+ ++this;
+ return old;
+ }
+
+ /** Overloaded dereference operator so the iterator can masquerade as a pointer
+ to a member */
+ const T& operator*() const {
+ alwaysAssertM(! isEnd, "Can't dereference the end element of an iterator");
+ return node->valueArray[nextValueArrayIndex].value;
+ }
+
+ /** Overloaded dereference operator so the iterator can masquerade as a pointer
+ to a member */
+ T const * operator->() const {
+ alwaysAssertM(! isEnd, "Can't dereference the end element of an iterator");
+ return &(stack.last()->valueArray[nextValueArrayIndex].value);
+ }
+
+ /** Overloaded cast operator so the iterator can masquerade as a pointer
+ to a member */
+ operator T*() const {
+ alwaysAssertM(! isEnd, "Can't dereference the end element of an iterator");
+ return &(stack.last()->valueArray[nextValueArrayIndex].value);
+ }
+ };
+
+
+ /**
+ Iterates through the members that intersect the box
+ */
+ BoxIntersectionIterator beginBoxIntersection(const AABox& box) const {
+ return BoxIntersectionIterator(box, root);
+ }
+
+ BoxIntersectionIterator endBoxIntersection() const {
+ // The "end" iterator instance
+ return BoxIntersectionIterator();
+ }
+
+ /**
+ Appends all members whose bounds intersect the box.
+ See also PointKDTree::beginBoxIntersection.
+ */
+ void getIntersectingMembers(const AABox& box, Array<T>& members) const {
+ if (root == NULL) {
+ return;
+ }
+ root->getIntersectingMembers(box, Sphere(Vector3::zero(), 0), members, false);
+ }
+
+
+ /**
+ @param members The results are appended to this array.
+ */
+ void getIntersectingMembers(const Sphere& sphere, Array<T>& members) const {
+ if (root == NULL) {
+ return;
+ }
+
+ AABox box;
+ sphere.getBounds(box);
+ root->getIntersectingMembers(box, sphere, members);
+
+ }
+
+
+ /**
+ Stores the locations of the splitting planes (the structure but not the content)
+ so that the tree can be quickly rebuilt from a previous configuration without
+ calling balance.
+ */
+ void serializeStructure(BinaryOutput& bo) const {
+ Node::serializeStructure(root, bo);
+ }
+
+ /** Clears the member table */
+ void deserializeStructure(BinaryInput& bi) {
+ clear();
+ root = Node::deserializeStructure(bi);
+ }
+
+ /**
+ Returns an array of all members of the set. See also PointKDTree::begin.
+ */
+ void getMembers(Array<T>& members) const {
+ memberTable.getKeys(members);
+ }
+
+
+ /**
+ C++ STL style iterator variable. See begin().
+ Overloads the -> (dereference) operator, so this acts like a pointer
+ to the current member.
+ */
+ class Iterator {
+ private:
+ friend class TreeType;
+
+ // Note: this is a Table iterator, we are currently defining
+ // Set iterator
+ typename MemberTable::Iterator it;
+
+ Iterator(const typename MemberTable::Iterator& it) : it(it) {}
+
+ public:
+ inline bool operator!=(const Iterator& other) const {
+ return !(*this == other);
+ }
+
+ bool operator==(const Iterator& other) const {
+ return it == other.it;
+ }
+
+ /**
+ Pre increment.
+ */
+ Iterator& operator++() {
+ ++it;
+ return *this;
+ }
+
+ /**
+ Post increment (slower than preincrement).
+ */
+ Iterator operator++(int) {
+ Iterator old = *this;
+ ++(*this);
+ return old;
+ }
+
+ const T& operator*() const {
+ return it->key;
+ }
+
+ T* operator->() const {
+ return &(it->key);
+ }
+
+ operator T*() const {
+ return &(it->key);
+ }
+ };
+
+
+ /**
+ C++ STL style iterator method. Returns the first member.
+ Use preincrement (++entry) to get to the next element (iteration
+ order is arbitrary).
+ Do not modify the set while iterating.
+ */
+ Iterator begin() const {
+ return Iterator(memberTable.begin());
+ }
+
+
+ /**
+ C++ STL style iterator method. Returns one after the last iterator
+ element.
+ */
+ Iterator end() const {
+ return Iterator(memberTable.end());
+ }
+#undef TreeType
+};
+
+#define PointAABSPTree PointKDTree
+
+}
+
+#endif
diff --git a/externals/g3dlite/G3D.lib/include/G3D/PointHashGrid.h b/externals/g3dlite/G3D.lib/include/G3D/PointHashGrid.h
new file mode 100644
index 00000000000..8e9726e82da
--- /dev/null
+++ b/externals/g3dlite/G3D.lib/include/G3D/PointHashGrid.h
@@ -0,0 +1,894 @@
+/**
+ @file PointHashGrid.h
+
+ @maintainer Morgan McGuire, morgan@cs.williams.edu
+ @created 2008-07-01
+ @edited 2008-11-02
+
+ Copyright 2000-2008, Morgan McGuire.
+ All rights reserved.
+*/
+#ifndef G3D_POINTHASHGRID_H
+#define G3D_POINTHASHGRID_H
+
+#include "G3D/platform.h"
+#include "G3D/EqualsTrait.h"
+#include "G3D/HashTrait.h"
+#include "G3D/Vector3.h"
+#include "G3D/Vector3int32.h"
+#include "G3D/Array.h"
+#include "G3D/Table.h"
+#include "G3D/AABox.h"
+#include "G3D/Sphere.h"
+
+namespace G3D {
+
+/**
+ Storage of data in a sparse 3D grid of point-based data. The
+ space cost for <I>n</I> elements is O(<I>n</I>). For data with
+ approximately uniform density (with respect to the radius hint),
+ the time cost of searching for neighbors is O(1).
+
+ <i>Value</i> must be supported by a G3D::PositionTrait,
+ G3D::EqualsTrait, and G3D::HashFunc. overrides are provided for
+ common G3D classes like G3D::Vector3.
+*/
+template<class Value,
+ class PosFunc = PositionTrait<Value>,
+ class EqualsFunc = EqualsTrait<Value>,
+ class HashFunc = HashTrait<Vector3int32> >
+class PointHashGrid {
+private:
+
+#define ThisType PointHashGrid<Value, PosFunc, EqualsFunc, HashFunc>
+
+ /** A value annotated with precomputed position and hash code.*/
+ class Entry {
+ public:
+ Vector3 position;
+ Value value;
+ };
+
+ /** One cell of the grid. */
+ typedef Array<Entry> Cell;
+ typedef Table<Vector3int32, Cell, HashFunc> CellTable;
+
+ /** The cube of +/-1 along each dimension. Initialized by initOffsetArray.*/
+ Vector3int32 m_offsetArray[3*3*3];
+
+ /** Incremented every time the data structure is mutated.
+ Used by the iterators to determine if the data structure
+ has changed since iteration began. */
+ int m_epoch;
+
+ /** Extent of a cell along one dimension. */
+ float m_cellWidth;
+
+ /** Conservative bounds; the actual data may be smaller. */
+ AABox m_bounds;
+
+ /** Number of elements. */
+ int m_size;
+
+ /** Non-empty cells indexed by grid position. Actual 3D position is
+ <code>position * m_cellWidth</code>*/
+ CellTable m_data;
+
+
+ /** Intentionally unimplemented: prevent copy construction. */
+ PointHashGrid(const ThisType&);
+
+
+ /** Intentionally unimplemented: prevent assignment. */
+ PointHashGrid& operator=(const ThisType&);
+
+
+ /** Locate the cell and index within that cell containing v. Called by
+ remove() and contains(). */
+ bool find(
+ const Value& v,
+ Vector3int32& foundCellCoord,
+ Cell*& foundCell,
+ int& index) {
+
+ Vector3 pos;
+ PosFunc::getPosition(v, pos);
+
+ Vector3int32 cellCoord;
+ getCellCoord(pos, cellCoord);
+ for (int i = 0; i < 27; ++i) {
+ Vector3int32 c = cellCoord + m_offsetArray[i];
+ Cell* cell = m_data.getPointer(c);
+ if (cell != NULL) {
+ // The cell exists
+ for (int j = 0; j < cell->size(); ++j) {
+ if (EqualsFunc::equals((*cell)[j].value, v)) {
+ foundCell = cell;
+ index = j;
+ foundCellCoord = c;
+ return true;
+ }
+ }
+ }
+ }
+
+ // Not found
+ return false;
+ }
+
+ /** Given a real-space position, returns the cell coord
+ containing it.*/
+ void getCellCoord(const Vector3& pos, Vector3int32& cellCoord) const {
+ for (int a = 0; a < 3; ++a) {
+ cellCoord[a] = iFloor(pos[a] / m_cellWidth);
+ }
+ }
+
+ /** Initializes m_offsetArray. */
+ void initOffsetArray() {
+ int i = 0;
+ Vector3int32 d;
+ for (d.x = -1; d.x <= +1; ++d.x) {
+ for (d.y = -1; d.y <= +1; ++d.y) {
+ for (d.z = -1; d.z <= +1; ++d.z) {
+ m_offsetArray[i] = d;
+ ++i;
+ }
+ }
+ }
+
+ // Put (0, 0, 0) first, so that contains() is most likely to find
+ // the value quickly.
+ i = (1 * 3 + 1) * 3 + 1;
+ debugAssert(m_offsetArray[i] == Vector3int32(0,0,0));
+ Vector3int32 temp = m_offsetArray[0];
+ m_offsetArray[0] = m_offsetArray[i];
+ m_offsetArray[i] = temp;
+ }
+
+public:
+
+ /**
+ @param radiusHint the radius that will typically be used with
+ beginSphereIntersection and beginBoxIntersection. If two <i>Value</i>s are equal,
+ their positions must be within this radius as well.
+ */
+ PointHashGrid(float radiusHint) : m_size(0) {
+ initOffsetArray();
+
+ debugAssertM(radiusHint > 0, "Cell radius must be positive");
+ m_cellWidth = radiusHint;
+ }
+
+ /**
+ If radiusHint is negative, it is automatically chosen to put
+ about 5 values in each grid cell (which means about 27 * 5
+ values for each beginIntersection call).
+ */
+ PointHashGrid(const Array<Value>& init, float radiusHint = -1.0f) : m_size(0) {
+ initOffsetArray();
+
+ Vector3 lo(Vector3::inf());
+ Vector3 hi(-lo);
+
+ // Compute bounds
+ Array<Entry> entry(init.size());
+ for (int i = 0; i < entry.size(); ++i) {
+ const Value& value = init[i];
+ Vector3 pos = m_posFunc(value);
+
+ entry[i].value = value;
+ entry[i].hashCode = m_hashFunc(value);
+ entry[i].position = pos;
+
+ lo = lo.min(pos);
+ hi = hi.max(pos);
+ }
+
+ m_bounds = AABox(lo, hi);
+
+ if (radiusHint <= 0) {
+ // Compute a good cell width based on the bounds.
+ //
+ // N numPerCell
+ // ----- = ---------
+ // volume r^3
+
+ float numPerCell = 5;
+ radiusHint =
+ (float)pow(numPerCell * m_bounds.volume() / init.size(), 1.0 / 3.0);
+
+ if (radiusHint == 0) {
+ // Volume must have been zero because all points were colocated.
+ radiusHint = 0.1f;
+ }
+ }
+
+ insert(init);
+ }
+
+ /** Returns the number of elements. */
+ inline int size() const {
+ return m_size;
+ }
+
+ /** Returns a conservative bounding box around the contents. This is
+ conservative because it is not updated when elements are removed. */
+ const AABox& conservativeBoxBounds() const {
+ return m_bounds;
+ }
+
+ /** Insert @a v at position @a p given by <code>getPosition(v, p)</code>.
+ Multiple elements that are equal may be inserted; all copies will be
+ in the data structure. */
+ void insert(const Value& v) {
+ Vector3 pos;
+ PosFunc::getPosition(v, pos);
+ Vector3int32 cellCoord;
+ getCellCoord(pos, cellCoord);
+
+ // See if the cell already exists
+ Cell* cell = m_data.getPointer(cellCoord);
+
+ if (cell == NULL) {
+ // The cell did not exist; create it
+ m_data.set(cellCoord, Cell());
+ cell = m_data.getPointer(cellCoord);
+ }
+ debugAssert(cell != NULL);
+
+ Entry& entry = cell->next();
+ entry.value = v;
+ entry.position = pos;
+
+ // Update the bounds
+ if (size() == 0) {
+ m_bounds = AABox(pos);
+ } else {
+ m_bounds.merge(pos);
+ }
+
+ ++m_size;
+ ++m_epoch;
+ }
+
+
+ /** Inserts all elements of the array. */
+ void insert(const Array<Value>& v) {
+ for (int i = 0; i < v.size(); ++i) {
+ insert(v[i]);
+ }
+ }
+
+
+ /** If there are multiple copies of an element, you must
+ delete them multiple times.
+
+ @param shrinkAsNeeded If <b>true</b>, deallocate underlying data
+ structures as they are emptied. False increases performace at
+ the cost of memory overhead for dynamic structures.
+
+ @return true if the element was found.
+ */
+ bool remove(const Value& v, bool shrinkIfNecessary = true) {
+ Cell* cell = NULL;
+ int index = 0;
+ Vector3int32 cellCoord;
+
+ if (find(v, cellCoord, cell, index)) {
+ cell->fastRemove(index, shrinkIfNecessary);
+ --m_size;
+ ++m_epoch;
+
+ if ((cell->size() == 0) && shrinkIfNecessary) {
+ // Remove the cell itself
+
+ // Drop our pointer, which is about to dangle
+ cell = NULL;
+ bool success = m_data.remove(cellCoord);
+ debugAssertM(success, "Data structure corrupt: "
+ "tried to remove a cell that doesn't exist.");
+ }
+
+ return true;
+
+ } else {
+ return false;
+ }
+ }
+
+ /** Removes all elements of @v. */
+ void remove(const Array<Value>& v, bool shrink = true) {
+ for (int i = 0; i < v.size(); ++i) {
+ remove(v[i], shrink);
+ }
+ }
+
+ ///////////////////////////////////////////////////////////////////////////
+ ///////////////////////////////////////////////////////////////////////////
+
+ class Iterator {
+ private:
+ friend class ThisType;
+
+ bool m_isEnd;
+
+ const ThisType* m_grid;
+
+ typename CellTable::Iterator m_tableIterator;
+
+ /** Index within m_tableIterator->value of the current value. */
+ int m_arrayIndex;
+
+ const int m_epoch;
+
+ /** End iterator. Note that the m_tableIterator is initialized to the end iterator
+ of a temporary value! This is ok because we'll never look at the value of the
+ m_tableIterator, since we're initializing the "end" Iterator.*/
+ Iterator() : m_isEnd(true), m_grid(NULL), m_tableIterator(CellTable().end()),
+ m_arrayIndex(0), m_epoch(0) {}
+
+ Iterator(const ThisType* grid) :
+ m_isEnd(false),
+ m_grid(grid),
+ m_tableIterator( grid->m_data.begin() ),
+ m_arrayIndex(0),
+ m_epoch(grid->m_epoch) { }
+
+ private:
+
+ const Value& value() const {
+ debugAssert(! m_isEnd);
+ debugAssertM(m_tableIterator->value.size() > m_arrayIndex,
+ "No more elements");
+ return m_tableIterator->value[m_arrayIndex].value;
+ }
+
+ public:
+
+ inline bool operator!=(const Iterator& other) const {
+ if (other.m_isEnd && m_isEnd) {
+ return false;
+ } else {
+ return (m_isEnd != other.m_isEnd) ||
+ (m_tableIterator != other.m_tableIterator) ||
+ (m_arrayIndex != other.m_arrayIndex);
+ }
+ }
+
+ bool operator==(const Iterator& other) const {
+ return !(*this != other);
+ }
+
+ /** Preincrement */
+ Iterator& operator++() {
+ debugAssert(! m_isEnd);
+ debugAssertM(m_epoch == m_grid->m_epoch,
+ "It is illegal to mutate the HashGrid "
+ "while iterating through it.");
+
+ ++m_arrayIndex;
+
+ if (m_arrayIndex >= m_tableIterator->value.size()) {
+ // Move on to the next cell
+ ++m_tableIterator;
+ m_arrayIndex = 0;
+
+ // Check to see if we're at the end
+ m_isEnd = (m_tableIterator == m_grid->m_data.end());
+ }
+
+ return *this;
+ }
+
+ /** Post increment (slower) */
+ Iterator operator++(int) {
+ debugAssert(! m_isEnd);
+ Iterator old = *this;
+ ++(*this);
+ return old;
+ }
+
+ const Value& operator*() const { return value(); }
+ const Value* operator->() const { return &value(); }
+ operator Value*() const { return &value(); }
+ }; // Iterator
+
+
+ /** Iterate through all members. It is an error to mutate the HashGrid
+ while iterating through it. Each member can be accessed by "dereferencing"
+ the iterator:
+
+ <pre>
+ for (Grid::Iterator i = grid.begin(); i != grid.end(), ++i) {
+ const Value& = *i;
+ ...
+ }
+ </pre>
+ */
+ Iterator begin() const {
+ return Iterator(this);
+ }
+
+ const Iterator& end() const {
+ static const Iterator it;
+ return it;
+ }
+
+ ///////////////////////////////////////////////////////////////////////////
+ ///////////////////////////////////////////////////////////////////////////
+
+ // Forward declaration required by older gcc versions for friend declaration in BoxIterator
+ class SphereIterator;
+ class BoxIterator {
+ private:
+ friend class ThisType;
+ friend class SphereIterator;
+
+ bool m_isEnd;
+
+ const ThisType* m_grid;
+
+ /** Lower bound on the boxes covered, inclusive. */
+ Vector3int32 m_lo;
+
+ /** Upper bound on the boxes covered, inclusive.*/
+ Vector3int32 m_hi;
+
+ /** If true, test values against m_box before returning them.*/
+ bool m_exact;
+
+ /** The underlying box in 3D space */
+ AABox m_box;
+
+ /** The iterator winds through the 3D grid between m_lo and (m_lo + m_extent) in
+ Z,Y,X-major order. This is the index keeping track of how
+ far it has come */
+ Vector3int32 m_current;
+
+ /** The current cell. */
+ Cell* m_cell;
+
+ /** Index within m_cell of the current value */
+ int m_arrayIndex;
+
+ const int m_epoch;
+
+
+ /** Called from advance() */
+ void advanceCell() {
+ do {
+ ++m_current.x;
+ if (m_current.x > m_hi.x) {
+ m_current.x = m_lo.x;
+ ++m_current.y;
+ if (m_current.y > m_hi.y) {
+ m_current.y = m_lo.y;
+ ++m_current.z;
+ if (m_current.z > m_hi.z) {
+ m_isEnd = true;
+ return;
+ }
+ }
+ }
+
+ // Pick up the new cell
+ m_cell = m_grid->m_data.getPointer(m_current);
+ // Keep advancing if the cell does not exist
+ } while ((m_cell == NULL) || (m_cell->size() == 0));
+ }
+
+ /** Advance to the next value */
+ void advance() {
+ debugAssert(! m_isEnd);
+
+ do {
+ ++m_arrayIndex;
+ bool inConstructor = (m_cell == NULL);
+ if (inConstructor || m_arrayIndex >= m_cell->size()) {
+ advanceCell();
+ m_arrayIndex = 0;
+
+ if (m_isEnd) {
+ // Ran out of values
+ return;
+ }
+ debugAssert(m_cell != NULL);
+ }
+
+ // Advance until we have a value that can be returned, either
+ // because we don't care about exactness or because it is
+ // guaranteed to be within the box.
+ } while (m_exact && ! m_box.contains(position()));
+ }
+
+
+ /** End iterator */
+ BoxIterator() : m_isEnd(true), m_grid(NULL), m_exact(true), m_current(0,0,0), m_cell(NULL), m_arrayIndex(0), m_epoch(0) {}
+
+ /** Begin iterator */
+ BoxIterator(const ThisType* grid, bool exact, const AABox& box) :
+ m_isEnd(false),
+ m_grid(grid),
+ m_exact(exact),
+ m_box(box),
+ m_current(-1, 0 ,0),
+ m_cell(NULL),
+ m_arrayIndex(0),
+ m_epoch(grid->m_epoch) {
+
+ m_grid->getCellCoord(box.low(), m_lo);
+ m_grid->getCellCoord(box.high(), m_hi);
+
+ // Get to the first value
+ m_current = m_lo;
+ // Back up one so that advancing takes us to the first
+ --m_current.x;
+ advance();
+ }
+
+ const Value& value() const {
+ debugAssert(! m_isEnd);
+ return (*m_cell)[m_arrayIndex].value;
+ }
+
+ /** Used by SphereIterator::advance() */
+ const Vector3& position() const {
+ debugAssert(! m_isEnd);
+ return (*m_cell)[m_arrayIndex].position;
+ }
+
+ public:
+
+ inline bool operator!=(const BoxIterator& other) const {
+ if (other.m_isEnd && m_isEnd) {
+ return false;
+ } else {
+ return (m_isEnd != other.m_isEnd) ||
+ (m_cell != other.m_cell) ||
+ (m_arrayIndex != other.m_arrayIndex);
+ }
+ }
+
+ bool operator==(const BoxIterator& other) const {
+ return !(*this != other);
+ }
+
+ /** Preincrement */
+ BoxIterator& operator++() {
+ debugAssert(! m_isEnd);
+ debugAssertM(m_epoch == m_grid->m_epoch,
+ "It is illegal to mutate the HashGrid "
+ "while iterating through it.");
+
+ advance();
+
+ return *this;
+ }
+
+ /** Post increment (slower) */
+ BoxIterator operator++(int) {
+ Iterator old = *this;
+ ++(*this);
+ return old;
+ }
+
+ const Value& operator*() const { return value(); }
+ const Value* operator->() const { return &value(); }
+ operator Value*() const { return &value(); }
+
+ bool hasMore() const {
+ return ! m_isEnd;
+ }
+ }; // BoxIterator
+
+ /**
+ Finds all values whose positions are within @a box. It is an error to
+ mutate the PointHashGrid while iterating through it.
+
+ @param exact If false, the iterator will execute more quickly but will likely return some
+ values that lie outside the box. Set exact = false if you are going to test the
+ results against the yourself box anyway.
+ */
+ BoxIterator beginBoxIntersection(const AABox& box, bool exact = true) const {
+ return BoxIterator(this, exact, box);
+ }
+
+ const BoxIterator& endBoxIntersection() const {
+ static const BoxIterator it;
+ return it;
+ }
+
+ ///////////////////////////////////////////////////////////////////////////
+ ///////////////////////////////////////////////////////////////////////////
+
+ class SphereIterator {
+ private:
+
+ friend class ThisType;
+
+ bool m_isEnd;
+ Sphere m_sphere;
+ BoxIterator m_boxIterator;
+
+ SphereIterator() : m_isEnd(true) {}
+
+ void advance() {
+ if (! m_boxIterator.hasMore()) {
+ m_isEnd = true;
+ return;
+ }
+
+ while (! m_sphere.contains(m_boxIterator.position())) {
+ ++m_boxIterator;
+
+ if (! m_boxIterator.hasMore()) {
+ m_isEnd = true;
+ return;
+ }
+ }
+ }
+
+ static AABox getBoundingBox(const Sphere& s) {
+ AABox box;
+ s.getBounds(box);
+ return box;
+ }
+
+ SphereIterator(const ThisType* grid, const Sphere& sphere) :
+ m_isEnd(false),
+ m_sphere(sphere),
+ m_boxIterator(grid, false, getBoundingBox(sphere)) {
+
+ // Find the first element that is actually in the sphere,
+ // not just the box.
+ advance();
+ }
+
+ const Value& value() const {
+ return *m_boxIterator;
+ }
+
+ // TODO: if the sphere is very big compared to radius, check each
+ // cell's box to see if the cell itself is actually inside the sphere
+ // before iterating through it, since there may be many boxes outside the sphere.
+
+ public:
+
+ inline bool operator!=(const SphereIterator& other) const {
+ if (other.m_isEnd && m_isEnd) {
+ return false;
+ } else {
+ return
+ (m_isEnd != other.m_isEnd) ||
+ (m_sphere != other.m_sphere) ||
+ (m_boxIterator != other.m_boxIterator);
+ }
+ }
+
+ bool operator==(const SphereIterator& other) const {
+ return !(*this != other);
+ }
+
+
+
+ /** Preincrement */
+ SphereIterator& operator++() {
+ debugAssert(! m_isEnd);
+
+ ++m_boxIterator;
+ advance();
+
+ return *this;
+ }
+
+ /** Post increment (slower) */
+ SphereIterator operator++(int) {
+ Iterator old = *this;
+ ++(*this);
+ return old;
+ }
+
+ const Value& operator*() const { return value(); }
+ const Value* operator->() const { return &value(); }
+ operator Value*() const { return &value(); }
+
+ bool hasMore() const {
+ return ! m_isEnd;
+ }
+ }; // SphereIterator
+
+ /**
+ Finds all values whose positions are within @a sphere. It is an error
+ to mutate the HashGrid while iterating through it.
+ */
+ SphereIterator beginSphereIntersection(const Sphere& sphere) const {
+ return SphereIterator(this, sphere);
+ }
+
+ const SphereIterator& endSphereIntersection() const {
+ static const SphereIterator it;
+ return it;
+ }
+
+ ///////////////////////////////////////////////////////////////////////////
+ ///////////////////////////////////////////////////////////////////////////
+
+ /**
+ Dereference to access the bounds() and size() [element count] of the underlying
+ cell objet.
+
+ Example:
+ <pre>
+ for(PointHashGrid<Vector3>::CellIterator iter = grid.beginCells(); iter != grid.endCells(); ++iter) {
+ entriesFound += iter->size();
+ }
+ */
+ class CellIterator {
+ private:
+ friend class ThisType;
+
+ bool m_isEnd;
+ const ThisType* m_grid;
+ typename CellTable::Iterator m_tableIterator;
+ const int m_epoch;
+
+
+ Cell& cell() {
+ return m_tableIterator->value;
+ }
+
+ public:
+
+ class CellObject {
+ friend class CellIterator;
+ private:
+ const CellIterator* m_parent;
+
+ CellObject() : m_parent(NULL) {}
+
+ public:
+
+ /** Returns the bounds on this cell */
+ AABox bounds() const {
+ const Vector3int32& k = m_parent->m_tableIterator->key;
+ return AABox(Vector3(k) * m_parent->m_cellWidth,
+ Vector3(k + Vector3int32(1, 1, 1)) * m_parent->m_cellWidth);
+ }
+
+ /** Number of elements inside this cell */
+ int size() const {
+ debugAssert(! m_parent->m_isEnd);
+ return m_parent->m_tableIterator->value.size();
+ }
+ };
+
+ private:
+ /** Used to make the indirection work.*/
+ CellObject m_indirection;
+
+ /** End iterator. Note that the m_tableIterator is initialized to the end iterator
+ of a temporary value! This is ok because we'll never look at the value of the
+ m_tableIterator, since we're initializing the "end" Iterator.*/
+ CellIterator() :
+ m_isEnd(true),
+ m_grid(NULL),
+ m_tableIterator( CellTable().end() ),
+ m_epoch(0) {}
+
+ CellIterator(const ThisType* grid) :
+ m_isEnd(false),
+ m_grid(grid),
+ m_tableIterator( grid->m_data.begin()),
+ m_epoch(grid->m_epoch) {
+ m_indirection.m_parent = this;
+ m_isEnd = ! m_tableIterator.hasMore();
+ }
+
+ public:
+
+ const CellObject& operator*() const { return m_indirection; }
+ const CellObject* operator->() const { return &m_indirection; }
+ operator CellObject*() const { return &m_indirection; }
+
+ inline bool operator!=(const CellIterator& other) const {
+ // != is called more often than == during iteration
+ return !(
+ (m_isEnd && other.m_isEnd) ||
+ ((m_isEnd == other.m_isEnd) &&
+ (m_tableIterator != other.m_tableIterator)));
+ }
+
+ bool operator==(const CellIterator& other) const {
+ return !(*this != other);
+ }
+
+ /** Preincrement */
+ CellIterator& operator++() {
+ debugAssertM(m_epoch == m_grid->m_epoch,
+ "It is illegal to mutate the HashGrid while "
+ "iterating through it.");
+ ++m_tableIterator;
+ m_isEnd = ! m_tableIterator.hasMore();
+ return *this;
+ }
+
+ /** Post increment (slower) */
+ CellIterator operator++(int) {
+ Iterator old = *this;
+ ++(*this);
+ return old;
+ }
+
+ bool hasMore() const {
+ return ! m_isEnd;
+ }
+ }; // CellIterator
+
+ /** Iterates through the non-empty cells. This is intended primarily for
+ debugging and visualizing the data structure.*/
+ CellIterator beginCells() const {
+ return CellIterator(this);
+ }
+
+ const CellIterator& endCells() const {
+ static const CellIterator it;
+ return it;
+ }
+
+ ///////////////////////////////////////////////////////////////////////////
+ ///////////////////////////////////////////////////////////////////////////
+
+ /** Returns true if there is a value that is exactly equal to @a. This will
+ check all neighboring cells to avoid roundoff error at cell boundaries.
+ */
+ bool contains(const Value& v) const {
+ Cell* cell = NULL;
+ int index = 0;
+ Vector3int32 cellCoord;
+ return const_cast<ThisType*>(this)->find(v, cellCoord, cell, index);
+ }
+
+ /** Calls delete on all of the values, which are assumed to be pointers.
+ This is a helper to avoid requiring you to iterate through the data
+ structure, removing and deleting each one. Clears the PointHashGrid at the
+ end.
+
+ Using objects (instead of pointers) or reference counted pointers is
+ recommended over using pointers and this deleteAll method.*/
+ void deleteAll() {
+ for (Iterator it = begin(); it.hasMore(); ++it) {
+ delete *it;
+ }
+ clear();
+ }
+
+ /** Removes all data.
+ @param shrink If true, underlying structures are deallocated as
+ they are freed.*/
+ void clear(bool shrink = true) {
+ m_size = 0;
+ m_bounds = AABox();
+ if (! shrink) {
+ // Remove all data
+ for (CellIterator it = beginCells(); it.hasMore(); ++it) {
+ it.cell().clear(true);
+ }
+ } else {
+ m_data.clear();
+ }
+ ++m_epoch;
+ }
+
+ int debugGetDeepestBucketSize() const {
+ return m_data.debugGetDeepestBucketSize();
+ }
+
+ float debugGetAverageBucketSize() const {
+ return m_data.debugGetAverageBucketSize();
+ }
+#undef ThisType
+};
+
+} // G3D
+#endif
diff --git a/externals/g3dlite/G3D.lib/include/G3D/Pointer.h b/externals/g3dlite/G3D.lib/include/G3D/Pointer.h
new file mode 100644
index 00000000000..2b80187bae5
--- /dev/null
+++ b/externals/g3dlite/G3D.lib/include/G3D/Pointer.h
@@ -0,0 +1,275 @@
+/**
+ @file Pointer.h
+
+ @maintainer Morgan McGuire, graphics3d.com
+
+ @created 2007-05-16
+ @edited 2007-06-22
+
+ Copyright 2000-2007, Morgan McGuire.
+ All rights reserved.
+ */
+#ifndef G3D_POINTER_H
+#define G3D_POINTER_H
+
+#include "G3D/debugAssert.h"
+#include "G3D/ReferenceCount.h"
+
+namespace G3D {
+
+/**
+ Acts like a pointer to a value of type ValueType (i.e.,
+ ValueType*), but can operate through accessor methods as well as on
+ a value in memory. This is useful for implementing scripting
+ languages and other applications that need to connect existing APIs
+ by reference.
+
+ Because the accessors require values to be passed by value (instead of by reference)
+ this is primarily useful for objects whose memory size is small.
+
+ <pre>
+ class Foo {
+ public:
+ void setEnabled(bool b);
+ bool getEnabled() const;
+ };
+
+ Foo f;
+ bool b;
+
+ Pointer<bool> p1(&b);
+ Pointer<bool> p2(&f, &Foo::getEnabled, &Foo::setEnabled);
+
+ *p1 = true;
+ *p2 = false;
+ *p2 = *p1; \/\/ Value assignment
+ p2 = p1; \/\/ Pointer aliasing
+
+ \/\/ Or, equivalently:
+ p1.setValue(true);
+ p2.setValue(false);
+
+ p2.setValue(p1.getValue());
+ p2 = p1;
+ </pre>
+
+ <i>Note:</i> Because of the way that dereference is implemented, you cannot pass <code>*p</code> through a function
+ that takes varargs (...), e.g., <code>printf("%d", *p)</code> will produce a compile-time error. Instead use
+ <code>printf("%d",(bool)*p)</code> or <code>printf("%d", p.getValue())</code>.
+
+ */
+template<class ValueType>
+class Pointer {
+private:
+
+ class Interface {
+ public:
+ virtual ~Interface() {};
+ virtual void set(ValueType b) = 0;
+ virtual ValueType get() const = 0;
+ virtual Interface* clone() const = 0;
+ };
+
+ class Memory : public Interface {
+ private:
+
+ ValueType* value;
+
+ public:
+
+ Memory(ValueType* value) : value(value) {
+ debugAssert(value != NULL);
+ }
+
+ virtual void set(ValueType v) {
+ *value = v;
+ }
+
+ virtual ValueType get() const {
+ return *value;
+ }
+
+ virtual Interface* clone() const {
+ return new Memory(value);
+ }
+ };
+
+ template<class T, typename GetMethod, typename SetMethod>
+ class Accessor : public Interface {
+ private:
+
+ T* object;
+ GetMethod getMethod;
+ SetMethod setMethod;
+
+ public:
+
+ Accessor(T* object,
+ GetMethod getMethod,
+ SetMethod setMethod) : object(object), getMethod(getMethod), setMethod(setMethod) {
+ debugAssert(object != NULL);
+ }
+
+ virtual void set(ValueType v) {
+ (object->*setMethod)(v);
+ }
+
+ virtual ValueType get() const {
+ return (object->*getMethod)();
+ }
+
+ virtual Interface* clone() const {
+ return new Accessor(object, getMethod, setMethod);
+ }
+ };
+
+
+ template<class T, typename GetMethod, typename SetMethod>
+ class RefAccessor : public Interface {
+ private:
+
+ ReferenceCountedPointer<T> object;
+ GetMethod getMethod;
+ SetMethod setMethod;
+
+ public:
+
+ RefAccessor(
+ const ReferenceCountedPointer<T>& object,
+ GetMethod getMethod,
+ SetMethod setMethod) : object(object), getMethod(getMethod), setMethod(setMethod) {
+
+ debugAssert(object != NULL);
+ }
+
+ virtual void set(ValueType v) {
+ (object.pointer()->*setMethod)(v);
+ }
+
+ virtual ValueType get() const {
+ return (object.pointer()->*getMethod)();
+ }
+
+ virtual Interface* clone() const {
+ return new RefAccessor(object, getMethod, setMethod);
+ }
+ };
+
+
+ Interface* interface;
+
+public:
+
+ Pointer() : interface(NULL) {};
+
+ /** Allows implicit cast from real pointer */
+ Pointer(ValueType* v) : interface(new Memory(v)) {}
+
+ // Assignment
+ inline Pointer& operator=(const Pointer& r) {
+ delete interface;
+ if (r.interface != NULL) {
+ interface = r.interface->clone();
+ } else {
+ interface = NULL;
+ }
+ return this[0];
+ }
+
+ Pointer(const Pointer& p) : interface(NULL) {
+ this[0] = p;
+ }
+
+ template<class Class>
+ Pointer(const ReferenceCountedPointer<Class>& object,
+ ValueType (Class::*getMethod)() const,
+ void (Class::*setMethod)(ValueType)) :
+ interface(new RefAccessor<Class, ValueType (Class::*)() const, void (Class::*)(ValueType)>(object, getMethod, setMethod)) {}
+
+ template<class Class>
+ Pointer(const ReferenceCountedPointer<Class>& object,
+ const ValueType& (Class::*getMethod)() const,
+ void (Class::*setMethod)(ValueType)) :
+ interface(new RefAccessor<Class, const ValueType& (Class::*)() const, void (Class::*)(ValueType)>(object, getMethod, setMethod)) {}
+
+ template<class Class>
+ Pointer(const ReferenceCountedPointer<Class>& object,
+ ValueType (Class::*getMethod)() const,
+ void (Class::*setMethod)(const ValueType&)) :
+ interface(new RefAccessor<Class, ValueType (Class::*)() const, void (Class::*)(const ValueType&)>(object, getMethod, setMethod)) {}
+
+ template<class Class>
+ Pointer(const ReferenceCountedPointer<Class>& object,
+ const ValueType& (Class::*getMethod)() const,
+ void (Class::*setMethod)(const ValueType&)) :
+ interface(new RefAccessor<Class, const ValueType& (Class::*)() const, void (Class::*)(const ValueType&)>(object, getMethod, setMethod)) {}
+
+ template<class Class>
+ Pointer(Class* object,
+ const ValueType& (Class::*getMethod)() const,
+ void (Class::*setMethod)(const ValueType&)) :
+ interface(new Accessor<Class, const ValueType& (Class::*)() const, void (Class::*)(const ValueType&)>(object, getMethod, setMethod)) {}
+
+ template<class Class>
+ Pointer(Class* object,
+ ValueType (Class::*getMethod)() const,
+ void (Class::*setMethod)(const ValueType&)) :
+ interface(new Accessor<Class, ValueType (Class::*)() const, void (Class::*)(const ValueType&)>(object, getMethod, setMethod)) {}
+
+ template<class Class>
+ Pointer(Class* object,
+ const ValueType& (Class::*getMethod)() const,
+ void (Class::*setMethod)(ValueType)) :
+ interface(new Accessor<Class, const ValueType& (Class::*)() const, void (Class::*)(ValueType)>(object, getMethod, setMethod)) {}
+
+ template<class Class>
+ Pointer(Class* object,
+ ValueType (Class::*getMethod)() const,
+ void (Class::*setMethod)(ValueType)) :
+ interface(new Accessor<Class, ValueType (Class::*)() const, void (Class::*)(ValueType)>(object, getMethod, setMethod)) {}
+
+ ~Pointer() {
+ delete interface;
+ }
+
+ inline const ValueType getValue() const {
+ debugAssert(interface != NULL);
+ return interface->get();
+ }
+
+ inline void setValue(const ValueType& v) {
+ debugAssert(interface != NULL);
+ interface->set(v);
+ }
+
+ class IndirectValue {
+ private:
+
+ friend class Pointer;
+ Pointer* pointer;
+ IndirectValue(Pointer* p) : pointer(p) {}
+
+ public:
+
+ void operator=(const ValueType& v) {
+ pointer->setValue(v);
+ }
+
+ operator ValueType() const {
+ return pointer->getValue();
+ }
+
+ };
+
+ inline IndirectValue operator*() {
+ return IndirectValue(this);
+ }
+
+ inline const ValueType operator*() const {
+ return getValue();
+ }
+};
+
+}
+
+#endif
diff --git a/externals/g3dlite/G3D.lib/include/G3D/PositionTrait.h b/externals/g3dlite/G3D.lib/include/G3D/PositionTrait.h
new file mode 100644
index 00000000000..67a4f64138a
--- /dev/null
+++ b/externals/g3dlite/G3D.lib/include/G3D/PositionTrait.h
@@ -0,0 +1,7 @@
+#ifndef G3D_POSITIONTRAIT_H
+#define G3D_POSITIONTRAIT_H
+
+template<typename Value>
+struct PositionTrait{};
+
+#endif
diff --git a/externals/g3dlite/G3D.lib/include/G3D/Quat.h b/externals/g3dlite/G3D.lib/include/G3D/Quat.h
new file mode 100644
index 00000000000..d4b892623d8
--- /dev/null
+++ b/externals/g3dlite/G3D.lib/include/G3D/Quat.h
@@ -0,0 +1,725 @@
+/**
+ @file Quat.h
+
+ Quaternion
+
+ @maintainer Morgan McGuire, matrix@graphics3d.com
+
+ @created 2002-01-23
+ @edited 2006-05-10
+ */
+
+#ifndef G3D_QUAT_H
+#define G3D_QUAT_H
+
+#include "G3D/platform.h"
+#include "G3D/g3dmath.h"
+#include "G3D/Vector3.h"
+#include "G3D/Matrix3.h"
+#include <string>
+
+namespace G3D {
+
+/**
+ Unit quaternions are used in computer graphics to represent
+ rotation about an axis. Any 3x3 rotation matrix can
+ be stored as a quaternion.
+
+ A quaternion represents the sum of a real scalar and
+ an imaginary vector: ix + jy + kz + w. A unit quaternion
+ representing a rotation by A about axis v has the form
+ [sin(A/2)*v, cos(A/2)]. For a unit quaternion, q.conj() == q.inverse()
+ is a rotation by -A about v. -q is the same rotation as q
+ (negate both the axis and angle).
+
+ A non-unit quaterion q represents the same rotation as
+ q.unitize() (Dam98 pg 28).
+
+ Although quaternion-vector operations (eg. Quat + Vector3) are
+ well defined, they are not supported by this class because
+ they typically are bugs when they appear in code.
+
+ Do not subclass.
+
+ <B>BETA API -- subject to change</B>
+ @cite Erik B. Dam, Martin Koch, Martin Lillholm, Quaternions, Interpolation and Animation. Technical Report DIKU-TR-98/5, Department of Computer Science, University of Copenhagen, Denmark. 1998.
+ */
+class Quat {
+private:
+ // Hidden operators
+ bool operator<(const Quat&) const;
+ bool operator>(const Quat&) const;
+ bool operator<=(const Quat&) const;
+ bool operator>=(const Quat&) const;
+
+public:
+
+ /**
+ q = [sin(angle / 2) * axis, cos(angle / 2)]
+
+ In Watt & Watt's notation, s = w, v = (x, y, z)
+ In the Real-Time Rendering notation, u = (x, y, z), w = w
+ */
+ float x, y, z, w;
+
+ /**
+ Initializes to a zero degree rotation.
+ */
+ inline Quat() : x(0), y(0), z(0), w(1) {}
+
+ Quat(
+ const Matrix3& rot);
+
+ inline Quat(float _x, float _y, float _z, float _w) :
+ x(_x), y(_y), z(_z), w(_w) {}
+
+ /** Defaults to a pure vector quaternion */
+ inline Quat(const Vector3& v, float _w = 0) : x(v.x), y(v.y), z(v.z), w(_w) {
+ }
+
+ /**
+ The real part of the quaternion.
+ */
+ inline const float& real() const {
+ return w;
+ }
+
+ inline float& real() {
+ return w;
+ }
+
+ /** Note: two quats can represent the Quat::sameRotation and not be equal. */
+ bool fuzzyEq(const Quat& q) {
+ return G3D::fuzzyEq(x, q.x) && G3D::fuzzyEq(y, q.y) && G3D::fuzzyEq(z, q.z) && G3D::fuzzyEq(w, q.w);
+ }
+
+ /** True if these quaternions represent the same rotation (note that every rotation is
+ represented by two values; q and -q).
+ */
+ bool sameRotation(const Quat& q) {
+ return fuzzyEq(q) || fuzzyEq(-q);
+ }
+
+ inline Quat operator-() const {
+ return Quat(-x, -y, -z, -w);
+ }
+
+ /**
+ Returns the imaginary part (x, y, z)
+ */
+ inline const Vector3& imag() const {
+ return *(reinterpret_cast<const Vector3*>(this));
+ }
+
+ inline Vector3& imag() {
+ return *(reinterpret_cast<Vector3*>(this));
+ }
+
+ /** q = [sin(angle/2)*axis, cos(angle/2)] */
+ static Quat fromAxisAngleRotation(
+ const Vector3& axis,
+ float angle);
+
+ /** Returns the axis and angle of rotation represented
+ by this quaternion (i.e. q = [sin(angle/2)*axis, cos(angle/2)]) */
+ void toAxisAngleRotation(
+ Vector3& axis,
+ double& angle) const;
+
+ void toAxisAngleRotation(
+ Vector3& axis,
+ float& angle) const {
+ double d;
+ toAxisAngleRotation(axis, d);
+ angle = (float)d;
+ }
+
+ Matrix3 toRotationMatrix() const;
+
+ void toRotationMatrix(
+ Matrix3& rot) const;
+
+ /**
+ Spherical linear interpolation: linear interpolation along the
+ shortest (3D) great-circle route between two quaternions.
+
+ Note: Correct rotations are expected between 0 and PI in the right order.
+
+ @cite Based on Game Physics -- David Eberly pg 538-540
+ @param threshold Critical angle between between rotations at which
+ the algorithm switches to normalized lerp, which is more
+ numerically stable in those situations. 0.0 will always slerp.
+ */
+ Quat slerp(
+ const Quat& other,
+ float alpha,
+ float threshold = 0.05f) const;
+
+ /** Normalized linear interpolation of quaternion components. */
+ Quat nlerp(const Quat& other, float alpha) const;
+
+ /**
+ Negates the imaginary part.
+ */
+ inline Quat conj() const {
+ return Quat(-x, -y, -z, w);
+ }
+
+ inline float sum() const {
+ return x + y + z + w;
+ }
+
+ inline float average() const {
+ return sum() / 4.0f;
+ }
+
+ inline Quat operator*(float s) const {
+ return Quat(x * s, y * s, z * s, w * s);
+ }
+
+ inline Quat& operator*=(float s) {
+ x *= s;
+ y *= s;
+ z *= s;
+ w *= s;
+ return *this;
+ }
+
+ /** @cite Based on Watt & Watt, page 360 */
+ friend Quat operator* (float s, const Quat& q);
+
+ inline Quat operator/(float s) const {
+ return Quat(x / s, y / s, z / s, w / s);
+ }
+
+ inline float dot(const Quat& other) const {
+ return (x * other.x) + (y * other.y) + (z * other.z) + (w * other.w);
+ }
+
+ /** Note that q<SUP>-1</SUP> = q.conj() for a unit quaternion.
+ @cite Dam99 page 13 */
+ inline Quat inverse() const {
+ return conj() / dot(*this);
+ }
+
+ Quat operator-(const Quat& other) const;
+
+ Quat operator+(const Quat& other) const;
+
+ /**
+ Quaternion multiplication (composition of rotations).
+ Note that this does not commute.
+ */
+ Quat operator*(const Quat& other) const;
+
+ /* (*this) * other.inverse() */
+ Quat operator/(const Quat& other) const {
+ return (*this) * other.inverse();
+ }
+
+
+ /** Is the magnitude nearly 1.0? */
+ inline bool isUnit(float tolerance = 1e-5) const {
+ return abs(dot(*this) - 1.0f) < tolerance;
+ }
+
+
+ inline float magnitude() const {
+ return sqrtf(dot(*this));
+ }
+
+ inline Quat log() const {
+ if ((x == 0) && (y == 0) && (z == 0)) {
+ if (w > 0) {
+ return Quat(0, 0, 0, ::logf(w));
+ } else if (w < 0) {
+ // Log of a negative number. Multivalued, any number of the form
+ // (PI * v, ln(-q.w))
+ return Quat((float)pi(), 0, 0, ::logf(-w));
+ } else {
+ // log of zero!
+ return Quat((float)nan(), (float)nan(), (float)nan(), (float)nan());
+ }
+ } else {
+ // Partly imaginary.
+ float imagLen = sqrtf(x * x + y * y + z * z);
+ float len = sqrtf(imagLen * imagLen + w * w);
+ float theta = atan2f(imagLen, (float)w);
+ float t = theta / imagLen;
+ return Quat(t * x, t * y, t * z, ::logf(len));
+ }
+ }
+ /** log q = [Av, 0] where q = [sin(A) * v, cos(A)].
+ Only for unit quaternions
+ debugAssertM(isUnit(), "Log only defined for unit quaternions");
+ // Solve for A in q = [sin(A)*v, cos(A)]
+ Vector3 u(x, y, z);
+ double len = u.magnitude();
+
+ if (len == 0.0) {
+ return
+ }
+ double A = atan2((double)w, len);
+ Vector3 v = u / len;
+
+ return Quat(v * A, 0);
+ }
+ */
+
+ /** exp q = [sin(A) * v, cos(A)] where q = [Av, 0].
+ Only defined for pure-vector quaternions */
+ inline Quat exp() const {
+ debugAssertM(w == 0, "exp only defined for vector quaternions");
+ Vector3 u(x, y, z);
+ float A = u.magnitude();
+ Vector3 v = u / A;
+ return Quat(sinf(A) * v, cosf(A));
+ }
+
+
+ /**
+ Raise this quaternion to a power. For a rotation, this is
+ the effect of rotating x times as much as the original
+ quaterion.
+
+ Note that q.pow(a).pow(b) == q.pow(a + b)
+ @cite Dam98 pg 21
+ */
+ inline Quat pow(float x) const {
+ return (log() * x).exp();
+ }
+
+ inline void unitize() {
+ float mag2 = dot(*this);
+ if (! G3D::fuzzyEq(mag2, 1.0f)) {
+ *this *= rsq(mag2);
+ }
+ }
+
+ /**
+ Returns a unit quaterion obtained by dividing through by
+ the magnitude.
+ */
+ inline Quat toUnit() const {
+ Quat x = *this;
+ x.unitize();
+ return x;
+ }
+
+ /**
+ The linear algebra 2-norm, sqrt(q dot q). This matches
+ the value used in Dam's 1998 tech report but differs from the
+ n(q) value used in Eberly's 1999 paper, which is the square of the
+ norm.
+ */
+ inline float norm() const {
+ return magnitude();
+ }
+
+ // access quaternion as q[0] = q.x, q[1] = q.y, q[2] = q.z, q[3] = q.w
+ //
+ // WARNING. These member functions rely on
+ // (1) Quat not having virtual functions
+ // (2) the data packed in a 4*sizeof(float) memory block
+ const float& operator[] (int i) const;
+ float& operator[] (int i);
+
+ /** Generate uniform random unit quaternion (i.e. random "direction")
+ @cite From "Uniform Random Rotations", Ken Shoemake, Graphics Gems III.
+ */
+ static Quat unitRandom();
+
+ void deserialize(class BinaryInput& b);
+ void serialize(class BinaryOutput& b) const;
+
+ // 2-char swizzles
+
+ Vector2 xx() const;
+ Vector2 yx() const;
+ Vector2 zx() const;
+ Vector2 wx() const;
+ Vector2 xy() const;
+ Vector2 yy() const;
+ Vector2 zy() const;
+ Vector2 wy() const;
+ Vector2 xz() const;
+ Vector2 yz() const;
+ Vector2 zz() const;
+ Vector2 wz() const;
+ Vector2 xw() const;
+ Vector2 yw() const;
+ Vector2 zw() const;
+ Vector2 ww() const;
+
+ // 3-char swizzles
+
+ Vector3 xxx() const;
+ Vector3 yxx() const;
+ Vector3 zxx() const;
+ Vector3 wxx() const;
+ Vector3 xyx() const;
+ Vector3 yyx() const;
+ Vector3 zyx() const;
+ Vector3 wyx() const;
+ Vector3 xzx() const;
+ Vector3 yzx() const;
+ Vector3 zzx() const;
+ Vector3 wzx() const;
+ Vector3 xwx() const;
+ Vector3 ywx() const;
+ Vector3 zwx() const;
+ Vector3 wwx() const;
+ Vector3 xxy() const;
+ Vector3 yxy() const;
+ Vector3 zxy() const;
+ Vector3 wxy() const;
+ Vector3 xyy() const;
+ Vector3 yyy() const;
+ Vector3 zyy() const;
+ Vector3 wyy() const;
+ Vector3 xzy() const;
+ Vector3 yzy() const;
+ Vector3 zzy() const;
+ Vector3 wzy() const;
+ Vector3 xwy() const;
+ Vector3 ywy() const;
+ Vector3 zwy() const;
+ Vector3 wwy() const;
+ Vector3 xxz() const;
+ Vector3 yxz() const;
+ Vector3 zxz() const;
+ Vector3 wxz() const;
+ Vector3 xyz() const;
+ Vector3 yyz() const;
+ Vector3 zyz() const;
+ Vector3 wyz() const;
+ Vector3 xzz() const;
+ Vector3 yzz() const;
+ Vector3 zzz() const;
+ Vector3 wzz() const;
+ Vector3 xwz() const;
+ Vector3 ywz() const;
+ Vector3 zwz() const;
+ Vector3 wwz() const;
+ Vector3 xxw() const;
+ Vector3 yxw() const;
+ Vector3 zxw() const;
+ Vector3 wxw() const;
+ Vector3 xyw() const;
+ Vector3 yyw() const;
+ Vector3 zyw() const;
+ Vector3 wyw() const;
+ Vector3 xzw() const;
+ Vector3 yzw() const;
+ Vector3 zzw() const;
+ Vector3 wzw() const;
+ Vector3 xww() const;
+ Vector3 yww() const;
+ Vector3 zww() const;
+ Vector3 www() const;
+
+ // 4-char swizzles
+
+ Vector4 xxxx() const;
+ Vector4 yxxx() const;
+ Vector4 zxxx() const;
+ Vector4 wxxx() const;
+ Vector4 xyxx() const;
+ Vector4 yyxx() const;
+ Vector4 zyxx() const;
+ Vector4 wyxx() const;
+ Vector4 xzxx() const;
+ Vector4 yzxx() const;
+ Vector4 zzxx() const;
+ Vector4 wzxx() const;
+ Vector4 xwxx() const;
+ Vector4 ywxx() const;
+ Vector4 zwxx() const;
+ Vector4 wwxx() const;
+ Vector4 xxyx() const;
+ Vector4 yxyx() const;
+ Vector4 zxyx() const;
+ Vector4 wxyx() const;
+ Vector4 xyyx() const;
+ Vector4 yyyx() const;
+ Vector4 zyyx() const;
+ Vector4 wyyx() const;
+ Vector4 xzyx() const;
+ Vector4 yzyx() const;
+ Vector4 zzyx() const;
+ Vector4 wzyx() const;
+ Vector4 xwyx() const;
+ Vector4 ywyx() const;
+ Vector4 zwyx() const;
+ Vector4 wwyx() const;
+ Vector4 xxzx() const;
+ Vector4 yxzx() const;
+ Vector4 zxzx() const;
+ Vector4 wxzx() const;
+ Vector4 xyzx() const;
+ Vector4 yyzx() const;
+ Vector4 zyzx() const;
+ Vector4 wyzx() const;
+ Vector4 xzzx() const;
+ Vector4 yzzx() const;
+ Vector4 zzzx() const;
+ Vector4 wzzx() const;
+ Vector4 xwzx() const;
+ Vector4 ywzx() const;
+ Vector4 zwzx() const;
+ Vector4 wwzx() const;
+ Vector4 xxwx() const;
+ Vector4 yxwx() const;
+ Vector4 zxwx() const;
+ Vector4 wxwx() const;
+ Vector4 xywx() const;
+ Vector4 yywx() const;
+ Vector4 zywx() const;
+ Vector4 wywx() const;
+ Vector4 xzwx() const;
+ Vector4 yzwx() const;
+ Vector4 zzwx() const;
+ Vector4 wzwx() const;
+ Vector4 xwwx() const;
+ Vector4 ywwx() const;
+ Vector4 zwwx() const;
+ Vector4 wwwx() const;
+ Vector4 xxxy() const;
+ Vector4 yxxy() const;
+ Vector4 zxxy() const;
+ Vector4 wxxy() const;
+ Vector4 xyxy() const;
+ Vector4 yyxy() const;
+ Vector4 zyxy() const;
+ Vector4 wyxy() const;
+ Vector4 xzxy() const;
+ Vector4 yzxy() const;
+ Vector4 zzxy() const;
+ Vector4 wzxy() const;
+ Vector4 xwxy() const;
+ Vector4 ywxy() const;
+ Vector4 zwxy() const;
+ Vector4 wwxy() const;
+ Vector4 xxyy() const;
+ Vector4 yxyy() const;
+ Vector4 zxyy() const;
+ Vector4 wxyy() const;
+ Vector4 xyyy() const;
+ Vector4 yyyy() const;
+ Vector4 zyyy() const;
+ Vector4 wyyy() const;
+ Vector4 xzyy() const;
+ Vector4 yzyy() const;
+ Vector4 zzyy() const;
+ Vector4 wzyy() const;
+ Vector4 xwyy() const;
+ Vector4 ywyy() const;
+ Vector4 zwyy() const;
+ Vector4 wwyy() const;
+ Vector4 xxzy() const;
+ Vector4 yxzy() const;
+ Vector4 zxzy() const;
+ Vector4 wxzy() const;
+ Vector4 xyzy() const;
+ Vector4 yyzy() const;
+ Vector4 zyzy() const;
+ Vector4 wyzy() const;
+ Vector4 xzzy() const;
+ Vector4 yzzy() const;
+ Vector4 zzzy() const;
+ Vector4 wzzy() const;
+ Vector4 xwzy() const;
+ Vector4 ywzy() const;
+ Vector4 zwzy() const;
+ Vector4 wwzy() const;
+ Vector4 xxwy() const;
+ Vector4 yxwy() const;
+ Vector4 zxwy() const;
+ Vector4 wxwy() const;
+ Vector4 xywy() const;
+ Vector4 yywy() const;
+ Vector4 zywy() const;
+ Vector4 wywy() const;
+ Vector4 xzwy() const;
+ Vector4 yzwy() const;
+ Vector4 zzwy() const;
+ Vector4 wzwy() const;
+ Vector4 xwwy() const;
+ Vector4 ywwy() const;
+ Vector4 zwwy() const;
+ Vector4 wwwy() const;
+ Vector4 xxxz() const;
+ Vector4 yxxz() const;
+ Vector4 zxxz() const;
+ Vector4 wxxz() const;
+ Vector4 xyxz() const;
+ Vector4 yyxz() const;
+ Vector4 zyxz() const;
+ Vector4 wyxz() const;
+ Vector4 xzxz() const;
+ Vector4 yzxz() const;
+ Vector4 zzxz() const;
+ Vector4 wzxz() const;
+ Vector4 xwxz() const;
+ Vector4 ywxz() const;
+ Vector4 zwxz() const;
+ Vector4 wwxz() const;
+ Vector4 xxyz() const;
+ Vector4 yxyz() const;
+ Vector4 zxyz() const;
+ Vector4 wxyz() const;
+ Vector4 xyyz() const;
+ Vector4 yyyz() const;
+ Vector4 zyyz() const;
+ Vector4 wyyz() const;
+ Vector4 xzyz() const;
+ Vector4 yzyz() const;
+ Vector4 zzyz() const;
+ Vector4 wzyz() const;
+ Vector4 xwyz() const;
+ Vector4 ywyz() const;
+ Vector4 zwyz() const;
+ Vector4 wwyz() const;
+ Vector4 xxzz() const;
+ Vector4 yxzz() const;
+ Vector4 zxzz() const;
+ Vector4 wxzz() const;
+ Vector4 xyzz() const;
+ Vector4 yyzz() const;
+ Vector4 zyzz() const;
+ Vector4 wyzz() const;
+ Vector4 xzzz() const;
+ Vector4 yzzz() const;
+ Vector4 zzzz() const;
+ Vector4 wzzz() const;
+ Vector4 xwzz() const;
+ Vector4 ywzz() const;
+ Vector4 zwzz() const;
+ Vector4 wwzz() const;
+ Vector4 xxwz() const;
+ Vector4 yxwz() const;
+ Vector4 zxwz() const;
+ Vector4 wxwz() const;
+ Vector4 xywz() const;
+ Vector4 yywz() const;
+ Vector4 zywz() const;
+ Vector4 wywz() const;
+ Vector4 xzwz() const;
+ Vector4 yzwz() const;
+ Vector4 zzwz() const;
+ Vector4 wzwz() const;
+ Vector4 xwwz() const;
+ Vector4 ywwz() const;
+ Vector4 zwwz() const;
+ Vector4 wwwz() const;
+ Vector4 xxxw() const;
+ Vector4 yxxw() const;
+ Vector4 zxxw() const;
+ Vector4 wxxw() const;
+ Vector4 xyxw() const;
+ Vector4 yyxw() const;
+ Vector4 zyxw() const;
+ Vector4 wyxw() const;
+ Vector4 xzxw() const;
+ Vector4 yzxw() const;
+ Vector4 zzxw() const;
+ Vector4 wzxw() const;
+ Vector4 xwxw() const;
+ Vector4 ywxw() const;
+ Vector4 zwxw() const;
+ Vector4 wwxw() const;
+ Vector4 xxyw() const;
+ Vector4 yxyw() const;
+ Vector4 zxyw() const;
+ Vector4 wxyw() const;
+ Vector4 xyyw() const;
+ Vector4 yyyw() const;
+ Vector4 zyyw() const;
+ Vector4 wyyw() const;
+ Vector4 xzyw() const;
+ Vector4 yzyw() const;
+ Vector4 zzyw() const;
+ Vector4 wzyw() const;
+ Vector4 xwyw() const;
+ Vector4 ywyw() const;
+ Vector4 zwyw() const;
+ Vector4 wwyw() const;
+ Vector4 xxzw() const;
+ Vector4 yxzw() const;
+ Vector4 zxzw() const;
+ Vector4 wxzw() const;
+ Vector4 xyzw() const;
+ Vector4 yyzw() const;
+ Vector4 zyzw() const;
+ Vector4 wyzw() const;
+ Vector4 xzzw() const;
+ Vector4 yzzw() const;
+ Vector4 zzzw() const;
+ Vector4 wzzw() const;
+ Vector4 xwzw() const;
+ Vector4 ywzw() const;
+ Vector4 zwzw() const;
+ Vector4 wwzw() const;
+ Vector4 xxww() const;
+ Vector4 yxww() const;
+ Vector4 zxww() const;
+ Vector4 wxww() const;
+ Vector4 xyww() const;
+ Vector4 yyww() const;
+ Vector4 zyww() const;
+ Vector4 wyww() const;
+ Vector4 xzww() const;
+ Vector4 yzww() const;
+ Vector4 zzww() const;
+ Vector4 wzww() const;
+ Vector4 xwww() const;
+ Vector4 ywww() const;
+ Vector4 zwww() const;
+ Vector4 wwww() const;
+};
+
+inline Quat exp(const Quat& q) {
+ return q.exp();
+}
+
+inline Quat log(const Quat& q) {
+ return q.log();
+}
+
+inline G3D::Quat operator*(double s, const G3D::Quat& q) {
+ return q * (float)s;
+}
+
+inline G3D::Quat operator*(float s, const G3D::Quat& q) {
+ return q * s;
+}
+
+inline float& Quat::operator[] (int i) {
+ debugAssert(i >= 0);
+ debugAssert(i < 4);
+ return ((float*)this)[i];
+}
+
+inline const float& Quat::operator[] (int i) const {
+ debugAssert(i >= 0);
+ debugAssert(i < 4);
+ return ((float*)this)[i];
+}
+
+inline Quat Quat::operator-(const Quat& other) const {
+ return Quat(x - other.x, y - other.y, z - other.z, w - other.w);
+}
+
+inline Quat Quat::operator+(const Quat& other) const {
+ return Quat(x + other.x, y + other.y, z + other.z, w + other.w);
+}
+
+} // Namespace G3D
+
+// Outside the namespace to avoid overloading confusion for C++
+inline G3D::Quat pow(const G3D::Quat& q, double x) {
+ return q.pow((float)x);
+}
+
+
+#endif
diff --git a/externals/g3dlite/G3D.lib/include/G3D/Queue.h b/externals/g3dlite/G3D.lib/include/G3D/Queue.h
new file mode 100644
index 00000000000..b64eced820a
--- /dev/null
+++ b/externals/g3dlite/G3D.lib/include/G3D/Queue.h
@@ -0,0 +1,355 @@
+/**
+ @file Queue.h
+
+ @maintainer Morgan McGuire, graphics3d.com
+
+ @created 2002-07-09
+ @edited 2005-08-20
+ */
+
+#ifndef G3D_QUEUE_H
+#define G3D_QUEUE_H
+
+#include "G3D/platform.h"
+#include "G3D/System.h"
+#include "G3D/debug.h"
+
+namespace G3D {
+
+/**
+ Locate the indices of the break between of the two
+ sections of the circular queue. These are used to
+ construct two for loops that iterate over the whole
+ sequence without using the modulo operator.
+
+ [0 ... secondEnd) [head .... firstEnd)
+ */
+#define FIND_ENDS \
+ int firstEnd = head + num;\
+ int secondEnd = 0;\
+ if (firstEnd > numAllocated) {\
+ secondEnd = firstEnd - numAllocated;\
+ firstEnd = numAllocated;\
+ }
+
+
+/**
+ Dynamic queue that uses a circular buffer for performance.
+
+ Faster than std::deque for objects with constructors.
+ */
+template <class T>
+class Queue {
+private:
+ //
+ // |<---- num ---->|
+ // [ | | | | | | | | | | | | | ]
+ // ^
+ // |
+ // head
+ //
+ //
+
+ /**
+ Only num elements are initialized.
+ */
+ T* data;
+
+ /**
+ Index of the next element to be dequeue-d in data.
+ */
+ int head;
+
+ /**
+ Number of elements (including head) that are visible and initialized.
+ */
+ int num;
+
+ /**
+ Size of data array in elements.
+ */
+ int numAllocated;
+
+ /** If a clear was needed, assumes it already occured */
+ void _copy(const Queue& other) {
+ debugAssert(data == NULL);
+ data = (T*)System::malloc(sizeof(T) * other.numAllocated);
+ debugAssert(data);
+ head = other.head;
+ num = other.num;
+ numAllocated = other.numAllocated;
+
+ FIND_ENDS;
+
+ for (int i = head; i < firstEnd; ++i) {
+ new (data + i)T(other.data[i]);
+ }
+
+ for (int i = 0; i < secondEnd; ++i) {
+ new (data + i)T(other.data[i]);
+ }
+ }
+
+
+ /**
+ Computes a data array index from a queue position. The queue position
+ may be negative.
+ */
+ inline int index(int i) const {
+ return (head + i + numAllocated) % numAllocated;
+ }
+
+ /**
+ Allocates newSize elements and repacks the array.
+ */
+ void repackAndRealloc(int newSize) {
+ // TODO: shrink queue
+ T* old = data;
+ data = (T*)System::malloc(newSize * sizeof(T));
+ debugAssert(data != NULL);
+
+ FIND_ENDS;
+
+ int j = 0;
+ for (int i = head; i < firstEnd; ++i, ++j) {
+ new (data + j)T(old[i]);
+ (old + i)->~T();
+ }
+
+ for (int i = 0; i < secondEnd; ++i, ++j) {
+ new (data + j)T(old[i]);
+ (old + i)->~T();
+ }
+
+ head = 0;
+ System::free(old);
+ numAllocated = newSize;
+ }
+
+ /**
+ Ensure that there is at least one element between
+ the tail and head, wrapping around in the circular
+ buffer.
+ */
+ inline void reserveSpace() {
+ if (num == numAllocated) {
+ repackAndRealloc(numAllocated * 3 + 20);
+ }
+ }
+
+public:
+
+ Queue() :
+ data(NULL),
+ head(0),
+ num(0),
+ numAllocated(0) {
+ }
+
+
+ /**
+ Copy constructor
+ */
+ Queue(const Queue& other) : data(NULL) {
+ _copy(other);
+ }
+
+
+ /**
+ Destructor does not delete() the objects if T is a pointer type
+ (e.g. T = int*) instead, it deletes the pointers themselves and
+ leaves the objects. Call deleteAll if you want to dealocate
+ the objects referenced.
+ */
+ virtual ~Queue() {
+ clear();
+ }
+
+ /**
+ Insert a new element into the front of the queue
+ (a traditional queue only uses pushBack).
+ */
+ inline void pushFront(const T& e) {
+ reserveSpace();
+
+ // Get the index of head-1
+ int i = index(-1);
+
+ // Call the constructor on the newly exposed element.
+ new (data + i)T(e);
+
+ // Reassign the head to point to this index
+ head = i;
+ ++num;
+ }
+
+ /**
+ Insert a new element at the end of the queue.
+ */
+ inline void pushBack(const T& e) {
+ reserveSpace();
+
+ // Get the index of 1+tail
+ int i = index(num);
+
+ // Initialize that element
+ new (data + i)T(e);
+ ++num;
+ }
+
+ /**
+ pushBack
+ */
+ inline void enqueue(const T& e) {
+ pushBack(e);
+ }
+
+
+ /**
+ Remove the last element from the queue. The queue will never
+ shrink in size. (A typical queue only uses popFront).
+ */
+ inline T popBack() {
+ int tail = index(num - 1);
+ T result(data[tail]);
+
+ // Call the destructor
+ (data + tail)->~T();
+ --num;
+
+ return result;
+ }
+
+ /**
+ Remove the next element from the head of the queue. The queue will never
+ shrink in size. */
+ inline T popFront() {
+ T result(data[head]);
+ // Call the destructor
+ (data + head)->~T();
+ head = (head + 1) % numAllocated;
+ --num;
+ return result;
+ }
+
+
+ /**
+ popFront
+ */
+ inline T dequeue() {
+ return popFront();
+ }
+
+ /**
+ Removes all elements (invoking their destructors).
+
+ @param freeStorage If false, the underlying array is not deallocated
+ (allowing fast push in the future), however, the size of the Queue
+ is reported as zero.
+
+ */
+ void clear(bool freeStorage = true) {
+
+ FIND_ENDS;
+
+ // Invoke the destructors on the elements
+ int i;
+ for (i = head; i < firstEnd; ++i) {
+ (data + i)->~T();
+ }
+
+ for (i = 0; i < secondEnd; ++i) {
+ (data + i)->~T();
+ }
+
+ num = 0;
+ head = 0;
+ if (freeStorage) {
+ numAllocated = 0;
+ System::free(data);
+ data = NULL;
+ }
+ }
+
+ /** Clear without freeing the underlying array. */
+ void fastClear() {
+ clear(false);
+ }
+
+ /**
+ Assignment operator.
+ */
+ Queue& operator=(const Queue& other) {
+ clear();
+ _copy(other);
+ return *this;
+ }
+
+ /**
+ Number of elements in the queue.
+ */
+ inline int size() const {
+ return num;
+ }
+
+ /**
+ Number of elements in the queue.
+ */
+ inline int length() const {
+ return size();
+ }
+
+ /**
+ Performs bounds checks in debug mode
+ */
+ inline T& operator[](int n) {
+ debugAssert((n >= 0) && (n < num));
+ return data[index(n)];
+ }
+
+ /**
+ Performs bounds checks in debug mode
+ */
+ inline const T& operator[](int n) const {
+ debugAssert((n >= 0) && (n < num));
+ return data[index(n)];
+ }
+
+
+ /**
+ Returns true if the given element is in the queue.
+ */
+ bool contains(const T& e) const {
+ for (int i = 0; i < size(); ++i) {
+ if ((*this)[i] == e) {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ /**
+ Calls delete on all objects[0...size-1]
+ and sets the queue size to zero.
+ */
+ void deleteAll() {
+ FIND_ENDS;
+
+ int i;
+ for (i = 0; i < secondEnd; ++i) {
+ delete data[i];
+ }
+
+ for (i = head; i < firstEnd; ++i) {
+ delete data[i];
+ }
+ clear();
+ }
+};
+
+#undef FIND_ENDS
+
+}; // namespace
+
+#endif
diff --git a/externals/g3dlite/G3D.lib/include/G3D/Ray.h b/externals/g3dlite/G3D.lib/include/G3D/Ray.h
new file mode 100644
index 00000000000..ab43c82933a
--- /dev/null
+++ b/externals/g3dlite/G3D.lib/include/G3D/Ray.h
@@ -0,0 +1,329 @@
+/**
+ @file Ray.h
+
+ Ray class
+
+ @maintainer Morgan McGuire, matrix@graphics3d.com
+
+ @created 2002-07-12
+ @edited 2006-02-21
+ */
+
+#ifndef G3D_RAY_H
+#define G3D_RAY_H
+
+#include "G3D/platform.h"
+#include "G3D/Vector3.h"
+#include "G3D/Triangle.h"
+
+namespace G3D {
+
+/**
+ A 3D Ray.
+ */
+class Ray {
+private:
+ Ray(const Vector3& origin, const Vector3& direction) {
+ this->origin = origin;
+ this->direction = direction;
+ }
+
+public:
+ Vector3 origin;
+
+ /**
+ Not unit length
+ */
+ Vector3 direction;
+
+ Ray() : origin(Vector3::zero()), direction(Vector3::zero()) {}
+
+ Ray(class BinaryInput& b);
+ void serialize(class BinaryOutput& b) const;
+ void deserialize(class BinaryInput& b);
+
+ /**
+ Creates a Ray from a origin and a (nonzero) direction.
+ */
+ static Ray fromOriginAndDirection(const Vector3& point, const Vector3& direction) {
+ return Ray(point, direction);
+ }
+
+ Ray unit() const {
+ return Ray(origin, direction.unit());
+ }
+
+ /**
+ Returns the closest point on the Ray to point.
+ */
+ Vector3 closestPoint(const Vector3& point) const {
+ float t = direction.dot(point - this->origin);
+ if (t < 0) {
+ return this->origin;
+ } else {
+ return this->origin + direction * t;
+ }
+ }
+
+ /**
+ Returns the closest distance between point and the Ray
+ */
+ float distance(const Vector3& point) const {
+ return (closestPoint(point) - point).magnitude();
+ }
+
+ /**
+ Returns the point where the Ray and plane intersect. If there
+ is no intersection, returns a point at infinity.
+
+ Planes are considered one-sided, so the ray will not intersect
+ a plane where the normal faces in the traveling direction.
+ */
+ Vector3 intersection(const class Plane& plane) const;
+
+ /**
+ Returns the distance until intersection with the (solid) sphere.
+ Will be 0 if inside the sphere, inf if there is no intersection.
+
+ The ray direction is <B>not</B> normalized. If the ray direction
+ has unit length, the distance from the origin to intersection
+ is equal to the time. If the direction does not have unit length,
+ the distance = time * direction.length().
+
+ See also the G3D::CollisionDetection "movingPoint" methods,
+ which give more information about the intersection.
+ */
+ float intersectionTime(const class Sphere& sphere) const;
+
+ float intersectionTime(const class Plane& plane) const;
+
+ float intersectionTime(const class Box& box) const;
+
+ float intersectionTime(const class AABox& box) const;
+
+ /**
+ The three extra arguments are the weights of vertices 0, 1, and 2
+ at the intersection point; they are useful for texture mapping
+ and interpolated normals.
+ */
+ float intersectionTime(
+ const Vector3& v0, const Vector3& v1, const Vector3& v2,
+ const Vector3& edge01, const Vector3& edge02,
+ double& w0, double& w1, double& w2) const;
+
+ /**
+ Ray-triangle intersection for a 1-sided triangle. Fastest version.
+ @cite http://www.acm.org/jgt/papers/MollerTrumbore97/
+ http://www.graphics.cornell.edu/pubs/1997/MT97.html
+ */
+ inline float intersectionTime(
+ const Vector3& vert0,
+ const Vector3& vert1,
+ const Vector3& vert2,
+ const Vector3& edge01,
+ const Vector3& edge02) const;
+
+
+ inline float intersectionTime(
+ const Vector3& vert0,
+ const Vector3& vert1,
+ const Vector3& vert2) const {
+
+ return intersectionTime(vert0, vert1, vert2, vert1 - vert0, vert2 - vert0);
+ }
+
+
+ inline float intersectionTime(
+ const Vector3& vert0,
+ const Vector3& vert1,
+ const Vector3& vert2,
+ double& w0,
+ double& w1,
+ double& w2) const {
+
+ return intersectionTime(vert0, vert1, vert2, vert1 - vert0, vert2 - vert0, w0, w1, w2);
+ }
+
+ /* One-sided triangle
+ */
+ inline float intersectionTime(const Triangle& triangle) const {
+ return intersectionTime(
+ triangle.vertex(0), triangle.vertex(1), triangle.vertex(2),
+ triangle.edge01(), triangle.edge02());
+ }
+
+ inline float intersectionTime(
+ const Triangle& triangle,
+ double& w0,
+ double& w1,
+ double& w2) const {
+ return intersectionTime(triangle.vertex(0), triangle.vertex(1), triangle.vertex(2),
+ triangle.edge01(), triangle.edge02(), w0, w1, w2);
+ }
+
+ /** Refracts about the normal
+ using G3D::Vector3::refractionDirection
+ and bumps the ray slightly from the newOrigin. */
+ Ray refract(
+ const Vector3& newOrigin,
+ const Vector3& normal,
+ float iInside,
+ float iOutside) const;
+
+ /** Reflects about the normal
+ using G3D::Vector3::reflectionDirection
+ and bumps the ray slightly from
+ the newOrigin. */
+ Ray reflect(
+ const Vector3& newOrigin,
+ const Vector3& normal) const;
+};
+
+
+#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];
+
+inline float Ray::intersectionTime(
+ const Vector3& vert0,
+ const Vector3& vert1,
+ const Vector3& vert2,
+ const Vector3& edge1,
+ const Vector3& edge2) const {
+
+ (void)vert1;
+ (void)vert2;
+
+ // Barycenteric coords
+ float u, v;
+
+ float tvec[3], pvec[3], qvec[3];
+
+ // begin calculating determinant - also used to calculate U parameter
+ CROSS(pvec, direction, edge2);
+
+ // if determinant is near zero, ray lies in plane of triangle
+ const float det = DOT(edge1, pvec);
+
+ if (det < EPSILON) {
+ return (float)inf();
+ }
+
+ // calculate distance from vert0 to ray origin
+ SUB(tvec, origin, vert0);
+
+ // calculate U parameter and test bounds
+ u = DOT(tvec, pvec);
+ if ((u < 0.0f) || (u > det)) {
+ // Hit the plane outside the triangle
+ return (float)inf();
+ }
+
+ // prepare to test V parameter
+ CROSS(qvec, tvec, edge1);
+
+ // calculate V parameter and test bounds
+ v = DOT(direction, qvec);
+ if ((v < 0.0f) || (u + v > det)) {
+ // Hit the plane outside the triangle
+ return (float)inf();
+ }
+
+
+ // Case where we don't need correct (u, v):
+ const float t = DOT(edge2, qvec);
+
+ if (t >= 0.0f) {
+ // Note that det must be positive
+ return t / det;
+ } else {
+ // We had to travel backwards in time to intersect
+ return (float)inf();
+ }
+}
+
+
+inline float Ray::intersectionTime(
+ const Vector3& vert0,
+ const Vector3& vert1,
+ const Vector3& vert2,
+ const Vector3& edge1,
+ const Vector3& edge2,
+ double& w0,
+ double& w1,
+ double& w2) const {
+
+ (void)vert1;
+ (void)vert2;
+
+ // Barycenteric coords
+ float u, v;
+
+ float tvec[3], pvec[3], qvec[3];
+
+ // begin calculating determinant - also used to calculate U parameter
+ CROSS(pvec, direction, edge2);
+
+ // if determinant is near zero, ray lies in plane of triangle
+ const float det = DOT(edge1, pvec);
+
+ if (det < EPSILON) {
+ return (float)inf();
+ }
+
+ // calculate distance from vert0 to ray origin
+ SUB(tvec, origin, vert0);
+
+ // calculate U parameter and test bounds
+ u = DOT(tvec, pvec);
+ if ((u < 0.0f) || (u > det)) {
+ // Hit the plane outside the triangle
+ return (float)inf();
+ }
+
+ // prepare to test V parameter
+ CROSS(qvec, tvec, edge1);
+
+ // calculate V parameter and test bounds
+ v = DOT(direction, qvec);
+ if ((v < 0.0f) || (u + v > det)) {
+ // Hit the plane outside the triangle
+ return (float)inf();
+ }
+
+ float t = DOT(edge2, qvec);
+
+ if (t >= 0) {
+ const float inv_det = 1.0f / det;
+ t *= inv_det;
+ u *= inv_det;
+ v *= inv_det;
+
+ w0 = (1.0f - u - v);
+ w1 = u;
+ w2 = v;
+
+ return t;
+ } else {
+ // We had to travel backwards in time to intersect
+ return (float)inf();
+ }
+}
+
+#undef EPSILON
+#undef CROSS
+#undef DOT
+#undef SUB
+
+}// namespace
+
+#endif
diff --git a/externals/g3dlite/G3D.lib/include/G3D/Rect2D.h b/externals/g3dlite/G3D.lib/include/G3D/Rect2D.h
new file mode 100644
index 00000000000..a6c0b7cd0cb
--- /dev/null
+++ b/externals/g3dlite/G3D.lib/include/G3D/Rect2D.h
@@ -0,0 +1,391 @@
+/**
+ @file Rect2D.h
+
+ @maintainer Morgan McGuire, matrix@graphics3d.com
+
+ @created 2003-11-13
+ @created 2008-11-16
+
+ Copyright 2000-2008, Morgan McGuire.
+ All rights reserved.
+ */
+
+#ifndef G3D_RECT2D_H
+#define G3D_RECT2D_H
+
+// Linux defines this as a macro
+#ifdef border
+#undef border
+#endif
+
+#include "G3D/platform.h"
+#include "G3D/Array.h"
+#include "G3D/Vector2.h"
+
+namespace G3D {
+
+/**
+ If you are using this class for pixel rectangles, keep in mind that the last
+ pixel you can draw to is at x0() + width() - 1.
+ */
+class Rect2D {
+private:
+ Vector2 min, max;
+
+ /**
+ Returns true if the whole polygon is clipped.
+ @param p Value of the point
+ @param axis Index [0 or 1] of the axis to clip along?
+ @param clipGreater Are we clipping greater than or less than the line?
+ @param inPoly Polygon being clipped
+ @param outPoly The clipped polygon
+ */
+ template<class T>
+ static bool clipSide2D(
+ const float p, bool clipGreater, int axis,
+ const Array<T>& inPoly, Array<T>& outPoly) {
+
+ outPoly.clear();
+ int i0 = -1;
+
+ Vector2 pt1;
+ bool c1 = true;
+
+ float negate = clipGreater ? -1 : 1;
+
+ // Find a point that is not clipped
+ for (i0 = 0; (i0 < inPoly.length()) && c1; ++i0) {
+ pt1 = inPoly[i0];
+ c1 = (negate * pt1[axis]) < (negate * p);
+ }
+
+ // We incremented i0 one time to many
+ --i0;
+
+ if (c1) {
+ // We could not find an unclipped point
+ return true;
+ }
+
+ outPoly.append(pt1);
+
+ // for each point in inPoly,
+ // if the point is outside the side and the previous one was also outside, continue
+ // if the point is outside the side and the previous one was inside, cut the line
+ // if the point is inside the side and the previous one was also inside, append the points
+ // if the point is inside the side and the previous one was outside, cut the line
+ for (int i = 1; i <= inPoly.length(); ++i) {
+ T pt2 = inPoly[(i + i0) % inPoly.length()];
+ bool c2 = (negate * pt2[axis]) < (negate * p);
+
+ if (c1 ^ c2) {
+
+ if (!c1 && c2 && (i > 1)) {
+ // Unclipped to clipped trasition and not the first iteration
+ outPoly.append(pt1);
+ }
+
+ // only one point is clipped, find where the line crosses the clipping plane
+
+
+ float alpha;
+ if (pt2[axis] == pt1[axis]) {
+ alpha = 0;
+ } else {
+ alpha = (p - pt1[axis]) / (pt2[axis] - pt1[axis]);
+ }
+ outPoly.append(pt1.lerp(pt2, alpha));
+ } else if (! (c1 || c2) && (i != 1)) {
+ // neither point is clipped (don't do this the first time
+ // because we appended the first pt before the loop)
+ outPoly.append(pt1);
+ }
+
+ pt1 = pt2;
+ c1 = c2;
+ }
+
+ return false;
+ }
+
+public:
+
+ inline Rect2D() : min(0, 0), max(0, 0) {}
+
+ /** Creates a rectangle at 0,0 with the given width and height*/
+ inline Rect2D(const Vector2& wh) : min(0, 0), max(wh.x, wh.y) {}
+
+ /** Computes a rectangle that contains both @a a and @a b.
+ Note that even if @a or @b has zero area, its origin will be included.*/
+ inline Rect2D(const Rect2D& a, const Rect2D& b) {
+ min = a.min.min(b.min);
+ max = a.max.max(b.max);
+ }
+
+ /** @brief Uniformly random point on the interior */
+ inline Vector2 randomPoint() const {
+ return Vector2(uniformRandom(0, max.x - min.x) + min.x,
+ uniformRandom(0, max.y - min.y) + min.y);
+ }
+
+ inline float width() const {
+ return max.x - min.x;
+ }
+
+ inline float height() const {
+ return max.y - min.y;
+ }
+
+ inline float x0() const {
+ return min.x;
+ }
+
+ inline float x1() const {
+ return max.x;
+ }
+
+ inline float y0() const {
+ return min.y;
+ }
+
+ inline float y1() const {
+ return max.y;
+ }
+
+ /** Min, min corner */
+ inline Vector2 x0y0() const {
+ return min;
+ }
+
+ inline Vector2 x1y0() const {
+ return Vector2(max.x, min.y);
+ }
+
+ inline Vector2 x0y1() const {
+ return Vector2(min.x, max.y);
+ }
+
+ /** Max,max corner */
+ inline Vector2 x1y1() const {
+ return max;
+ }
+
+ /** Width and height */
+ inline Vector2 wh() const {
+ return max - min;
+ }
+
+ inline Vector2 center() const {
+ return (max + min) * 0.5;
+ }
+
+ inline static Rect2D xyxy(float x0, float y0, float x1, float y1) {
+ Rect2D r;
+
+ r.min.x = G3D::min(x0, x1);
+ r.min.y = G3D::min(y0, y1);
+ r.max.x = G3D::max(x0, x1);
+ r.max.y = G3D::max(y0, y1);
+
+ return r;
+ }
+
+ Rect2D lerp(const Rect2D& other, float alpha) const {
+ Rect2D out;
+
+ out.min = min.lerp(other.min, alpha);
+ out.max = max.lerp(other.max, alpha);
+
+ return out;
+ }
+
+ inline static Rect2D xyxy(const Vector2& v0, const Vector2& v1) {
+ Rect2D r;
+
+ r.min = v0.min(v1);
+ r.max = v0.max(v1);
+
+ return r;
+ }
+
+ inline static Rect2D xywh(float x, float y, float w, float h) {
+ return xyxy(x, y, x + w, y + h);
+ }
+
+ inline static Rect2D xywh(const Vector2& v, const Vector2& w) {
+ return xyxy(v.x, v.y, v.x + w.x, v.y + w.y);
+ }
+
+ inline bool contains(const Vector2& v) const {
+ return (v.x >= min.x) && (v.y >= min.y) && (v.x <= max.x) && (v.y <= max.y);
+ }
+
+ inline bool contains(const Rect2D& r) const {
+ return (min.x <= r.min.x) && (min.y <= r.min.y) &&
+ (max.x >= r.max.x) && (max.y >= r.max.y);
+ }
+
+ /** True if there is non-zero area to the intersection between @a this and @a r.
+ Note that two rectangles that are adjacent do not intersect because there is
+ zero area to the overlap, even though one of them "contains" the corners of the other.*/
+ inline bool intersects(const Rect2D& r) const {
+ return (min.x < r.max.x) && (min.y < r.max.y) &&
+ (max.x > r.min.x) && (max.y > r.min.y);
+ }
+
+ /** Like intersection, but counts the adjacent case as touching. */
+ inline bool intersectsOrTouches(const Rect2D& r) const {
+ return (min.x <= r.max.x) && (min.y <= r.max.y) &&
+ (max.x >= r.min.x) && (max.y >= r.min.y);
+ }
+
+ inline Rect2D operator*(float s) const {
+ return xyxy(min.x * s, min.y * s, max.x * s, max.y * s);
+ }
+
+ inline Rect2D operator/(float s) const {
+ return xyxy(min / s, max / s);
+ }
+
+ inline Rect2D operator/(const Vector2& s) const {
+ return xyxy(min / s, max / s);
+ }
+
+ inline Rect2D operator+(const Vector2& v) const {
+ return xyxy(min + v, max + v);
+ }
+
+ inline Rect2D operator-(const Vector2& v) const {
+ return xyxy(min - v, max - v);
+ }
+
+ inline bool operator==(const Rect2D& other) const {
+ return (min == other.min) && (max == other.max);
+ }
+
+ inline bool operator!=(const Rect2D& other) const {
+ return (min != other.min) || (max != other.max);
+ }
+
+ /** Returns the corners in the order: (min,min), (max,min), (max,max), (min,max). */
+ inline Vector2 corner(int i) const {
+ debugAssert(i >= 0 && i < 4);
+ switch (i & 3) {
+ case 0:
+ return Vector2(min.x, min.y);
+ case 1:
+ return Vector2(max.x, min.y);
+ case 2:
+ return Vector2(max.x, max.y);
+ case 3:
+ return Vector2(min.x, max.y);
+ default:
+ // Should never get here
+ return Vector2(0, 0);
+ }
+ }
+
+
+ /** @deprecated
+ @sa expand() */
+ inline Rect2D border(float delta) const {
+ return Rect2D::xywh(x0() + delta,
+ y0() + delta,
+ width() - 2.0f * delta,
+ height() - 2.0f * delta);
+ }
+
+ /** Returns a new Rect2D that is bigger/smaller by the specified amount
+ (negative is shrink.) */
+ inline Rect2D expand(float delta) const {
+ float newX = x0() - delta;
+ float newY = y0() - delta;
+ float newW = width() + 2.0f * delta;
+ float newH = height() + 2.0f * delta;
+
+ if (newW < 0.0f) {
+ newX = (x0() + width()) / 2.0f;
+ newW = 0.0f;
+ }
+
+ if (newH < 0.0f) {
+ newY = (y0() + height()) / 2.0f;
+ newH = 0.0f;
+ }
+ return Rect2D::xywh(newX, newY, newW, newH);
+ }
+
+ /**
+ Clips so that the rightmost point of the outPoly is at rect.x1 (e.g. a 800x600 window produces
+ rightmost point 799, not 800). The results are suitable for pixel rendering if iRounded.
+ Templated so that it will work for Vector2,3,4 (the z and w components are interpolated linearly).
+ The template parameter must define T.lerp and contain x and y components.
+
+ If the entire polygon is clipped by a single side, the result will be empty.
+ The result might also have zero area but not be empty.
+ */
+ template<class T>
+ void clip(const Array<T>& inPoly, Array<T>& outPoly) const {
+
+ const bool greaterThan = true;
+ const bool lessThan = false;
+ const int X = 0;
+ const int Y = 1;
+
+ Array<T> temp;
+
+ bool entirelyClipped =
+ clipSide2D(x0(), lessThan, X, inPoly, temp) ||
+ clipSide2D(x1(), greaterThan, X, temp, outPoly) ||
+ clipSide2D(y0(), lessThan, Y, outPoly, temp) ||
+ clipSide2D(y1(), greaterThan, Y, temp, outPoly);
+
+ if (entirelyClipped) {
+ outPoly.clear();
+ }
+ }
+
+
+ /** Returns the largest, centered Rect2D that can fit inside this
+ while maintaining the aspect ratio of x:y. Convenient for
+ displaying images in odd-shaped windows.
+ */
+ Rect2D largestCenteredSubRect(float ww, float hh) const {
+ float textureAspect = hh / ww;
+ float viewAspect = height() / width();
+
+ if (viewAspect > textureAspect) {
+ // The view is too tall
+ float h = width() * textureAspect;
+ float y = (height() - h) / 2;
+ return Rect2D::xywh(0, y, width(), h) + corner(0);
+ } else {
+ // The view is too wide
+ float w = height() / textureAspect;
+ float x = (width() - w) / 2;
+ return Rect2D::xywh(x, 0, w, height()) + corner(0);
+ }
+ }
+
+ /**
+ Returns the overlap region between the two rectangles. This may have zero area
+ if they do not intersect. See the two-Rect2D constructor for a way to compute
+ a union-like rectangle.
+ */
+ Rect2D intersect(const Rect2D& other) const {
+ if (intersects(other)) {
+ return Rect2D::xyxy(min.max(other.min), max.min(other.max));
+ }else{
+ return Rect2D::xywh(0, 0, 0, 0);
+ }
+ }
+
+ float area() const {
+ return width() * height();
+ }
+};
+
+typedef Rect2D AABox2D;
+}
+
+#endif
diff --git a/externals/g3dlite/G3D.lib/include/G3D/ReferenceCount.h b/externals/g3dlite/G3D.lib/include/G3D/ReferenceCount.h
new file mode 100644
index 00000000000..f55a22be0e1
--- /dev/null
+++ b/externals/g3dlite/G3D.lib/include/G3D/ReferenceCount.h
@@ -0,0 +1,597 @@
+/**
+ @file ReferenceCount.h
+
+ Reference Counting Garbage Collector for C++
+
+ @maintainer Morgan McGuire, matrix@graphics3d.com
+ @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 2008-09-25
+*/
+
+#ifndef G3D_RGC_H
+#define G3D_RGC_H
+
+#include "G3D/platform.h"
+#include "G3D/debug.h"
+#include "G3D/AtomicInt32.h"
+
+namespace G3D {
+
+/** Base class for WeakReferenceCountedPointer */
+class _WeakPtr {
+public:
+ inline virtual ~_WeakPtr() {}
+
+protected:
+ friend class ReferenceCountedObject;
+
+ /** Called by ReferenceCountedObject to tell a weak pointer that its underlying object was collected. */
+ virtual void objectCollected() = 0;
+};
+
+/** Used internally by ReferenceCountedObject */
+class _WeakPtrLinkedList {
+public:
+ _WeakPtr* weakPtr;
+ _WeakPtrLinkedList* next;
+
+ inline _WeakPtrLinkedList() : weakPtr(NULL), next(NULL) {}
+
+ /** Inserts this node into the head of the list that previously had n as its head. */
+ inline _WeakPtrLinkedList(_WeakPtr* p, _WeakPtrLinkedList* n) : weakPtr(p), next(n) {}
+};
+
+/**
+ Objects that are reference counted inherit from this. Subclasses
+ <B>must</B> have a public destructor (the default destructor is fine)
+ and <B>publicly</B> inherit ReferenceCountedObject.
+
+ Multiple inheritance from a reference counted object is dangerous-- use
+ at your own risk.
+
+ ReferenceCountedPointer and ReferenceCountedObject are threadsafe.
+ You can create and drop references on multiple threads without
+ violating integrity. WeakReferenceCountedPointer is <i>not</i>
+ threadsafe. Introducing a weak pointer destroys all thread safety,
+ even for strong pointers to the same object (this is inherent in the
+ design of the class; we cannot fix it without slowing down the
+ performance of reference counted objects.)
+
+ <B>Usage Example</B>
+
+ <PRE>
+
+class Foo : public G3D::ReferenceCountedObject {
+public:
+ int x;
+};
+
+class Bar : public Foo {};
+
+typedef G3D::ReferenceCountedPointer<Foo> FooRef;
+typedef G3D::WeakReferenceCountedPointer<Foo> WeakFooRef;
+typedef G3D::ReferenceCountedPointer<Bar> BarRef;
+
+
+int main(int argc, char *argv[]) {
+
+ WeakFooRef x;
+
+ {
+ FooRef a = new Foo();
+
+ // Reference count == 1
+
+ x = a;
+ // Weak references do not increase count
+
+ {
+ FooRef b = a;
+ // Reference count == 2
+ }
+
+ // Reference count == 1
+ }
+ // No more strong references; object automatically deleted.
+ // x is set to NULL automatically.
+
+ // Example of using dynamic cast on reference counted objects
+ BarRef b = new Bar();
+
+ // No cast needed to go down the heirarchy.
+ FooRef f = b;
+
+ // We can't cast the reference object because it is a class.
+ // Instead we must extract the pointer and cast that:
+ b = dynamic_cast<Bar*>(&*f);
+
+ return 0;
+}
+</PRE>
+
+@deprecated To be replaced by boost::shared_ptr in 7.0
+ */
+class ReferenceCountedObject {
+public:
+
+ /**
+ The long name is to keep this from accidentally conflicting with
+ a subclass's variable name. Do not use or explicitly manipulate
+ this value--its type may change in the future and is not part
+ of the supported API.
+ */
+ AtomicInt32 ReferenceCountedObject_refCount;
+
+ /**
+ Linked list of all weak pointers that reference this (some may be
+ on the stack!). Do not use or explicitly manipulate this value.
+ */
+ _WeakPtrLinkedList* ReferenceCountedObject_weakPointer;
+
+protected:
+
+ ReferenceCountedObject() :
+ ReferenceCountedObject_refCount(0),
+ ReferenceCountedObject_weakPointer(0) {
+
+ debugAssertM(isValidHeapPointer(this),
+ "Reference counted objects must be allocated on the heap.");
+ }
+
+public:
+
+ /** Automatically called immediately before the object is deleted.
+ This is not called from the destructor because it needs to be invoked
+ before the subclass destructor.
+ */
+ void ReferenceCountedObject_zeroWeakPointers() {
+ // Tell all of my weak pointers that I'm gone.
+
+ _WeakPtrLinkedList* node = ReferenceCountedObject_weakPointer;
+
+ while (node != 0) {
+
+ // 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;
+ }
+ }
+
+ virtual ~ReferenceCountedObject() {}
+
+
+ /**
+ Note: copies will initially start out with 0
+ references and 0 weak references like any other object.
+ */
+ 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& 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;
+ }
+};
+
+
+
+/**
+ Use ReferenceCountedPointer<T> in place of T* in your program.
+ T must subclass ReferenceCountedObject.
+@deprecated To be replaced by boost::shared_ptr in 7.0
+ */
+template <class T>
+class ReferenceCountedPointer {
+private:
+
+ T* m_pointer;
+
+public:
+ typedef T element_type;
+
+ inline T* pointer() const {
+ return m_pointer;
+ }
+
+private:
+
+ /** Nulls out the pointer and drops a reference. If the reference
+ count hits zero. */
+ void zeroPointer() {
+ if (m_pointer != NULL) {
+
+ debugAssert(G3D::isValidHeapPointer(m_pointer));
+ debugAssertM(m_pointer->ReferenceCountedObject_refCount.value() > 0,
+ "Dangling reference detected.");
+
+ // Only delete if this instance caused the count to hit
+ // exactly zero. If there is a race condition, the value
+ // may be zero after decrement returns, but only one of
+ // the instances will get a zero return value.
+ if (m_pointer->ReferenceCountedObject_refCount.decrement() == 0) {
+ // We held the last reference, so delete the object.
+ // This test is threadsafe because there is no way for
+ // the reference count to increase after the last
+ // reference was dropped (assuming the application does
+ // not voilate the class abstraction).
+ //debugPrintf(" delete 0x%x\n", m_pointer);
+
+ // We must zero the weak pointers *before* deletion in case there
+ // are cycles of weak references.
+ // Note that since there are no strong references at this point,
+ // it is perfectly fair to zero the weak pointers anyway.
+ m_pointer->ReferenceCountedObject_zeroWeakPointers();
+ delete m_pointer;
+ }
+
+ m_pointer = NULL;
+ }
+ }
+
+ /** Non-atomic (except for the referencec increment). Can only be
+ called in contexts like the copy constructor or initial
+ constructor where it is known that the reference count will
+ not hit zero on some other thread. */
+ void setPointer(T* x) {
+ if (x != m_pointer) {
+ zeroPointer();
+
+ if (x != NULL) {
+ debugAssert(G3D::isValidHeapPointer(x));
+
+ m_pointer = x;
+
+ // Note that the ref count can be zero if this is the
+ // first pointer to it
+ debugAssertM(m_pointer->ReferenceCountedObject_refCount.value() >= 0,
+ "Negative reference count detected.");
+ m_pointer->ReferenceCountedObject_refCount.increment();
+ }
+ }
+ }
+
+public:
+
+ inline ReferenceCountedPointer() : m_pointer(NULL) {}
+
+ /**
+ Allow silent cast <i>to</i> the base class.
+
+ <pre>
+ SubRef s = new Sub();
+ BaseRef b = s;
+ </pre>
+
+ i.e., compile-time subtyping rule
+ RCP&lt;<I>T</I>&gt; &lt;: RCP&lt;<I>S</I>&gt; if <I>T</I> &lt;: <I>S</I>
+ */
+ template <class S>
+ inline ReferenceCountedPointer(const ReferenceCountedPointer<S>& p) :
+ m_pointer(NULL) {
+ setPointer(p.pointer());
+ }
+
+# if (! defined(MSC_VER) || (MSC_VER >= 1300))
+ /**
+ Explicit cast to a subclass. Acts like dynamic cast; the result will be NULL if
+ the cast cannot succeed. Not supported on VC6.
+ <pre>
+ SubRef s = new Sub();
+ BaseRef b = s;
+ s = b.downcast<Sub>(); // Note that the template argument is the object type, not the pointer type.
+ </pre>
+ */
+ template <class S>
+ ReferenceCountedPointer<S> downcast() {
+ return ReferenceCountedPointer<S>(dynamic_cast<S*>(m_pointer));
+ }
+
+ template <class S>
+ const ReferenceCountedPointer<S> downcast() const {
+ return ReferenceCountedPointer<S>(dynamic_cast<const S*>(m_pointer));
+ }
+# endif
+
+ // We need an explicit version of the copy constructor as well or
+ // the default copy constructor will be used.
+ inline ReferenceCountedPointer(const ReferenceCountedPointer<T>& p) : m_pointer(NULL) {
+ setPointer(p.m_pointer);
+ }
+
+ /** Allows construction from a raw pointer. That object will thereafter be
+ reference counted -- do not call delete on it. */
+ inline ReferenceCountedPointer(T* p) : m_pointer(NULL) {
+ setPointer(p);
+ }
+
+ inline ~ReferenceCountedPointer() {
+ zeroPointer();
+ }
+
+ inline size_t hashCode() const {
+ return reinterpret_cast<size_t>(m_pointer);;
+ }
+
+ inline const ReferenceCountedPointer<T>& operator=(const ReferenceCountedPointer<T>& p) {
+ setPointer(p.m_pointer);
+ return *this;
+ }
+
+ inline ReferenceCountedPointer<T>& operator=(T* p) {
+ setPointer(p);
+ return *this;
+ }
+
+ inline bool operator==(const ReferenceCountedPointer<T>& y) const {
+ return (m_pointer == y.m_pointer);
+ }
+
+ inline bool operator!=(const ReferenceCountedPointer<T>& y) const {
+ return (m_pointer != y.m_pointer);
+ }
+
+ bool operator < (const ReferenceCountedPointer<T>& y) const {
+ return (m_pointer < y.m_pointer);
+ }
+
+ bool operator > (const ReferenceCountedPointer<T>& y) const {
+ return (m_pointer > y.m_pointer);
+ }
+
+ bool operator <= (const ReferenceCountedPointer<T>& y) const {
+ return (m_pointer <= y.m_pointer);
+ }
+
+ bool operator >= (const ReferenceCountedPointer<T>& y) const {
+ return (m_pointer >= y.m_pointer);
+ }
+
+ inline T& operator*() const {
+ debugAssertM(m_pointer != NULL, "Dereferenced a NULL ReferenceCountedPointer");
+ return (*m_pointer);
+ }
+
+ inline T* operator->() const {
+ debugAssertM(m_pointer != NULL, "Dereferenced a NULL ReferenceCountedPointer");
+ return m_pointer;
+ }
+
+ inline bool isNull() const {
+ return (m_pointer == NULL);
+ }
+
+ inline bool notNull() const {
+ return (m_pointer != NULL);
+ }
+
+ // TODO: distinguish between last strong and last any pointer
+ /**
+ Returns true if this is the last reference to an object.
+ Useful for flushing memoization caches-- a cache that holds the last
+ reference is unnecessarily keeping an object alive.
+
+ <b>Not threadsafe.</b>
+
+ @deprecated Use WeakReferenceCountedPointer for caches
+ */
+ inline int isLastReference() const {
+ return (m_pointer->ReferenceCountedObject_refCount.value() == 1);
+ }
+};
+
+
+/**
+ A weak pointer allows the object it references to be garbage collected.
+ Weak pointers are commonly used in caches, where it is important to hold
+ a pointer to an object without keeping that object alive solely for the
+ cache's benefit (i.e., the object can be collected as soon as all
+ pointers to it <B>outside</B> the cache are gone). They are also convenient
+ for adding back-pointers in tree and list structures.
+
+ Weak pointers may become NULL at any point (when their target is collected).
+ Therefore the only way to reference the target is to convert to a strong
+ pointer and then check that it is not NULL.
+
+@deprecated To be replaced by boost::weak_ptr in 7.0
+ */
+template <class T>
+class WeakReferenceCountedPointer : public _WeakPtr {
+private:
+
+ /** NULL if the object has been collected. */
+ T* pointer;
+
+public:
+ /**
+ Creates a strong pointer, which prevents the object from being
+ garbage collected. The strong pointer may be NULL, which means
+ that the underlying.
+ */
+ // There is intentionally no way to check if the
+ // WeakReferenceCountedPointer has a null reference without
+ // creating a strong pointer since there is no safe way to use
+ // that information-- the pointer could be collected by a
+ // subsequent statement.
+ ReferenceCountedPointer<T> createStrongPtr() const {
+ // TODO: What if the object's destructor is called while we
+ // are in this method?
+ return ReferenceCountedPointer<T>(pointer);
+ }
+
+
+private:
+
+ /**
+ Thread issues: safe because this is only called when another
+ object is guaranteed to keep p alive for the duration of this
+ call.
+ */
+ void setPointer(T* p) {
+ // TODO: must prevent the object from being collected while in
+ // this method
+
+ zeroPointer();
+ pointer = p;
+
+ if (pointer != 0) {
+ // TODO: threadsafe: must update the list atomically
+
+ // Add myself to the head of my target's list of weak pointers
+ _WeakPtrLinkedList* head =
+ new _WeakPtrLinkedList
+ (this,
+ pointer->ReferenceCountedObject_weakPointer);
+
+ pointer->ReferenceCountedObject_weakPointer = head;
+ } else {
+
+ }
+ }
+
+
+ /**
+ Removes this from its target's list of weak pointers. Called
+ when the weak pointer goes out of scope.
+
+ Thread issues: depends on the thread safety of createStrongPtr.
+ */
+ void zeroPointer() {
+ // Grab a strong reference to prevent the object from being collected while we
+ // are traversing its list.
+ ReferenceCountedPointer<T> strong = createStrongPtr();
+
+ // If the following test fails then the object was collected before we
+ // reached it.
+ if (strong.notNull()) {
+ debugAssertM(pointer->ReferenceCountedObject_weakPointer != NULL,
+ "Weak pointer exists without a backpointer from the object.");
+
+ // Remove myself from my target's list of weak pointers
+ _WeakPtrLinkedList** node = &pointer->ReferenceCountedObject_weakPointer;
+ while ((*node)->weakPtr != this) {
+ node = &((*node)->next);
+ debugAssertM(*node != NULL,
+ "Weak pointer exists without a backpointer from the object (2).");
+ }
+
+ // Node must now point at the node for me. Remove node and
+ // close the linked list behind it.
+ _WeakPtrLinkedList* temp = *node;
+ *node = temp->next;
+
+ // Now delete the node corresponding to me
+ delete temp;
+ }
+
+ pointer = NULL;
+ }
+
+public:
+
+ WeakReferenceCountedPointer() : pointer(0) {}
+
+ /**
+ Allow compile time subtyping rule
+ RCP&lt;<I>T</I>&gt; &lt;: RCP&lt;<I>S</I>&gt; if <I>T</I> &lt;: <I>S</I>
+ */
+ template <class S>
+ inline WeakReferenceCountedPointer(const WeakReferenceCountedPointer<S>& p) : pointer(0) {
+ // Threadsafe: the object cannot be collected while the other pointer exists.
+ setPointer(p.pointer);
+ }
+
+ template <class S>
+ inline WeakReferenceCountedPointer(const ReferenceCountedPointer<S>& p) : pointer(0) {
+ // Threadsafe: the object cannot be collected while the other
+ // pointer exists.
+ setPointer(p.pointer());
+ }
+
+ // Gets called a *lot* when weak pointers are on the stack
+ WeakReferenceCountedPointer(
+ const WeakReferenceCountedPointer<T>& weakPtr) : pointer(0) {
+ setPointer(weakPtr.pointer);
+ }
+
+ WeakReferenceCountedPointer(
+ const ReferenceCountedPointer<T>& strongPtr) : pointer(0) {
+ setPointer(strongPtr.pointer());
+ }
+
+ ~WeakReferenceCountedPointer() {
+ zeroPointer();
+ }
+
+ WeakReferenceCountedPointer<T>& operator=(const WeakReferenceCountedPointer<T>& other) {
+ // Threadsafe: the object cannot be collected while the other pointer exists.
+
+ // I now point at other's target
+ setPointer(other.pointer);
+
+ return *this;
+ }
+
+ WeakReferenceCountedPointer<T>& operator=(const ReferenceCountedPointer<T>& other) {
+
+ // Threadsafe: the object cannot be collected while the other pointer exists.
+
+ // I now point at other's target
+ setPointer(other.pointer());
+
+ return *this;
+ }
+
+ bool operator==(const WeakReferenceCountedPointer<T>& other) const {
+ return pointer == other.pointer;
+ }
+
+ bool operator!=(const WeakReferenceCountedPointer<T>& other) const {
+ return pointer != other.pointer;
+ }
+
+ bool operator < (const WeakReferenceCountedPointer<T>& y) const {
+ return (pointer < y.pointer);
+ }
+
+ bool operator > (const WeakReferenceCountedPointer<T>& y) const {
+ return (pointer > y.pointer);
+ }
+
+ bool operator <= (const WeakReferenceCountedPointer<T>& y) const {
+ return (pointer <= y.pointer);
+ }
+
+ bool operator >= (const ReferenceCountedPointer<T>& y) const {
+ return (pointer >= y.pointer);
+ }
+
+protected:
+
+ /** Invoked by the destructor on ReferenceCountedPointer. */
+ virtual void objectCollected() {
+ debugAssertM(pointer != NULL,
+ "Removed a weak pointer twice.");
+ pointer = NULL;
+ }
+
+};
+
+} // namespace
+
+#endif
+
diff --git a/externals/g3dlite/G3D.lib/include/G3D/RegistryUtil.h b/externals/g3dlite/G3D.lib/include/G3D/RegistryUtil.h
new file mode 100644
index 00000000000..4b47be5f4bd
--- /dev/null
+++ b/externals/g3dlite/G3D.lib/include/G3D/RegistryUtil.h
@@ -0,0 +1,97 @@
+/**
+ @file RegistryUtil.h
+
+ @created 2006-04-06
+ @edited 2006-04-06
+
+ Copyright 2000-2006, Morgan McGuire.
+ All rights reserved.
+*/
+
+#ifndef G3D_REGISTRYUTIL_H
+#define G3D_REGISTRYUTIL_H
+
+#include "G3D/platform.h"
+#include "G3D/g3dmath.h"
+
+// This file is only used on Windows
+#ifdef G3D_WIN32
+
+#include <string>
+
+namespace G3D {
+
+/**
+ Provides generalized Windows registry querying.
+
+ All key names are one string in the format:
+ "[base key]\[sub-keys]"
+
+ A value must now be provided for every query.
+ An empty value string will use the (Default) value.
+
+ [base key] can be any of the following:
+ HKEY_CLASSES_ROOT
+ HKEY_CURRENT_CONFIG
+ HKEY_CURRENT_USER
+ HKEY_LOCAL_MACHINE
+ HKEY_PERFORMANCE_DATA
+ HKEY_PERFORMANCE_NLSTEXT
+ HKEY_PERFORMANCE_TEXT
+ HKEY_USERS
+
+ valueExists() should be used to validate a key+value before reading or writing
+ to ensure that a debug assert or false return is for a different error during
+ reads and writes.
+
+ All read and write calls will assert when a key will not open for reasons other
+ that it does not exist. All read and write calls will assert when the value cannot
+ be read or written for any reason.
+*/
+class RegistryUtil {
+
+public:
+ /** returns true if the key exists and the current user has permission to read */
+ static bool keyExists(const std::string& key);
+
+ /** returns true if the key exists and the current user has permission to read */
+ static bool valueExists(const std::string& key, const std::string& value);
+
+ /** returns false if the key could not be read for any reason. */
+ static bool readInt32(const std::string& key, const std::string& value, int32& data);
+
+ /**
+ Reads an arbitrary amount of data from a binary registry key.
+ returns false if the key could not be read for any reason.
+
+ @beta
+ @param data pointer to the output buffer of sufficient size. Pass NULL as data in order to have available data size returned in dataSize.
+ @param dataSize size of the output buffer. When NULL is passed for data, contains the size of available data on successful return.
+ */
+ static bool readBytes(const std::string& key, const std::string& value, uint8* data, uint32& dataSize);
+
+ /** returns false if the key could not be read for any reason. */
+ static bool readString(const std::string& key, const std::string& value, std::string& data);
+
+ /** returns false if the key could not be written for any reason. */
+ static bool writeInt32(const std::string& key, const std::string& value, int32 data);
+
+ /**
+ Writes an arbitrary amount of data to a binary registry key.
+ returns false if the key could not be written for any reason.
+
+ @param data pointer to the input buffer
+ @param dataSize size of the input buffer that should be written
+ */
+ static bool writeBytes(const std::string& key, const std::string& value, const uint8* data, uint32 dataSize);
+
+ /** returns false if the key could not be written for any reason. */
+ static bool writeString(const std::string& key, const std::string& value, const std::string& data);
+
+};
+
+} // namespace G3D
+
+#endif // G3D_WIN32
+
+#endif // G3D_REGISTRYTUIL_H
diff --git a/externals/g3dlite/G3D.lib/include/G3D/Set.h b/externals/g3dlite/G3D.lib/include/G3D/Set.h
new file mode 100644
index 00000000000..629d802cdd4
--- /dev/null
+++ b/externals/g3dlite/G3D.lib/include/G3D/Set.h
@@ -0,0 +1,160 @@
+/**
+ @file Set.h
+
+ Hash set
+
+ @maintainer Morgan McGuire, matrix@graphics3d.com
+
+ @created 2001-12-09
+ @edited 2006-02-20
+ */
+
+#ifndef G3D_SET_H
+#define G3D_SET_H
+
+#include "G3D/platform.h"
+#include "G3D/Table.h"
+#include <assert.h>
+#include <string>
+
+namespace G3D {
+
+/**
+ An unordered data structure that has at most one of each element.
+ Provides O(1) time insert, remove, and member test (contains).
+
+ Set uses G3D::Table internally, which means that the template type T
+ must define a hashCode and operator== function. See G3D::Table for
+ a discussion of these functions.
+ */
+// There is not copy constructor or assignment operator defined because
+// the default ones are correct for Set.
+template<class T>
+class Set {
+
+ /**
+ If an object is a member, it is contained in
+ this table.
+ */
+ Table<T, bool> memberTable;
+
+public:
+
+ virtual ~Set() {}
+
+ int size() const {
+ return (int)memberTable.size();
+ }
+
+ bool contains(const T& member) const {
+ return memberTable.containsKey(member);
+ }
+
+ /**
+ Inserts into the table if not already present.
+ */
+ void insert(const T& member) {
+ memberTable.set(member, true);
+ }
+
+ /**
+ It is an error to remove members that are not already
+ present.
+ */
+ void remove(const T& member) {
+ memberTable.remove(member);
+ }
+
+ Array<T> getMembers() const {
+ return memberTable.getKeys();
+ }
+
+ void getMembers(Array<T>& keyArray) const {
+ memberTable.getKeys(keyArray);
+ }
+
+ void clear() {
+ memberTable.clear();
+ }
+
+ void deleteAll() {
+ getMembers().deleteAll();
+ clear();
+ }
+
+ /**
+ C++ STL style iterator variable. See begin().
+ */
+ class Iterator {
+ private:
+ friend class Set<T>;
+
+ // Note: this is a Table iterator, we are currently defining
+ // Set iterator
+ typename Table<T, bool>::Iterator it;
+
+ Iterator(const typename Table<T, bool>::Iterator& it) : it(it) {}
+
+ public:
+ inline bool operator!=(const Iterator& other) const {
+ return !(*this == other);
+ }
+
+ bool operator==(const Iterator& other) const {
+ return it == other.it;
+ }
+
+ /**
+ Pre increment.
+ */
+ Iterator& operator++() {
+ ++it;
+ return *this;
+ }
+
+ /**
+ Post increment (slower than preincrement).
+ */
+ Iterator operator++(int) {
+ Iterator old = *this;
+ ++(*this);
+ return old;
+ }
+
+ const T& operator*() const {
+ return it->key;
+ }
+
+ T* operator->() const {
+ return &(it->key);
+ }
+
+ operator T*() const {
+ return &(it->key);
+ }
+ };
+
+
+ /**
+ C++ STL style iterator method. Returns the first member.
+ Use preincrement (++entry) to get to the next element.
+ Do not modify the set while iterating.
+ */
+ Iterator begin() const {
+ return Iterator(memberTable.begin());
+ }
+
+
+ /**
+ C++ STL style iterator method. Returns one after the last iterator
+ element.
+ */
+ const Iterator end() const {
+ return Iterator(memberTable.end());
+ }
+};
+
+}
+
+#endif
+
diff --git a/externals/g3dlite/G3D.lib/include/G3D/Sphere.h b/externals/g3dlite/G3D.lib/include/G3D/Sphere.h
new file mode 100644
index 00000000000..7d4c412185d
--- /dev/null
+++ b/externals/g3dlite/G3D.lib/include/G3D/Sphere.h
@@ -0,0 +1,143 @@
+/**
+ @file Sphere.h
+
+ Sphere class
+
+ @maintainer Morgan McGuire, matrix@graphics3d.com
+
+ @created 2001-06-02
+ @edited 2008-10-07
+ */
+
+#ifndef G3D_SPHERE_H
+#define G3D_SPHERE_H
+
+#include "G3D/platform.h"
+#include "G3D/Vector3.h"
+#include "G3D/Array.h"
+#include "G3D/Sphere.h"
+
+namespace G3D {
+
+/**
+ Sphere.
+ */
+class Sphere {
+private:
+
+ static int32 dummy;
+
+public:
+ Vector3 center;
+ float radius;
+
+ Sphere() {
+ center = Vector3::zero();
+ radius = 0;
+ }
+
+ Sphere(class BinaryInput& b);
+ void serialize(class BinaryOutput& b) const;
+ void deserialize(class BinaryInput& b);
+
+ Sphere(
+ const Vector3& center,
+ float radius) {
+
+ this->center = center;
+ this->radius = radius;
+ }
+
+ virtual ~Sphere() {}
+
+ bool operator==(const Sphere& other) const {
+ return (center == other.center) && (radius == other.radius);
+ }
+
+ bool operator!=(const Sphere& other) const {
+ return !((center == other.center) && (radius == other.radius));
+ }
+
+ /**
+ Returns true if point is less than or equal to radius away from
+ the center.
+ */
+ bool contains(const Vector3& point) const;
+
+ /**
+ @deprecated Use culledBy(Array<Plane>&)
+ */
+ bool culledBy(
+ const class Plane* plane,
+ int numPlanes,
+ int32& cullingPlaneIndex,
+ const uint32 testMask,
+ uint32& childMask) const;
+
+ /**
+ @deprecated Use culledBy(Array<Plane>&)
+ */
+ bool culledBy(
+ const class Plane* plane,
+ int numPlanes,
+ int32& cullingPlaneIndex = dummy,
+ const uint32 testMask = 0xFFFFFFFF) const;
+
+ /**
+ See AABox::culledBy
+ */
+ bool culledBy(
+ const Array<Plane>& plane,
+ int32& cullingPlaneIndex,
+ const uint32 testMask,
+ uint32& childMask) const;
+
+ /**
+ Conservative culling test that does not produce a mask for children.
+ */
+ bool culledBy(
+ const Array<Plane>& plane,
+ int32& cullingPlaneIndex = dummy,
+ const uint32 testMask = 0xFFFFFFFF) const;
+
+ virtual std::string toString() const;
+
+ float volume() const;
+
+ float area() const;
+
+ /**
+ Uniformly distributed on the surface.
+ */
+ Vector3 randomSurfacePoint() const;
+
+ /**
+ Uniformly distributed on the interior (includes surface)
+ */
+ Vector3 randomInteriorPoint() const;
+
+ void getBounds(class AABox& out) const;
+
+ bool intersects(const Sphere& other) const;
+
+ /** Translates the sphere */
+ Sphere operator+(const Vector3& v) const {
+ return Sphere(center + v, radius);
+ }
+
+ /** Translates the sphere */
+ Sphere operator-(const Vector3& v) const {
+ return Sphere(center - v, radius);
+ }
+};
+
+}
+
+template <> struct HashTrait<G3D::Sphere> {
+ static size_t hashCode(const G3D::Sphere& key) {
+ return static_cast<size_t>(key.center.hashCode() + (key.radius * 13));
+ }
+};
+
+
+#endif
diff --git a/externals/g3dlite/G3D.lib/include/G3D/Spline.h b/externals/g3dlite/G3D.lib/include/G3D/Spline.h
new file mode 100644
index 00000000000..af9b08230b8
--- /dev/null
+++ b/externals/g3dlite/G3D.lib/include/G3D/Spline.h
@@ -0,0 +1,367 @@
+/**
+ @file Spline.h
+
+ @author Morgan McGuire, morgan@cs.williams.edu
+ */
+
+#ifndef G3D_SPLINE_H
+#define G3D_SPLINE_H
+
+#include "G3D/platform.h"
+#include "G3D/Array.h"
+#include "G3D/g3dmath.h"
+#include "G3D/Matrix4.h"
+#include "G3D/Vector4.h"
+
+namespace G3D {
+
+/** Common implementation code for all G3D::Spline template parameters */
+class SplineBase {
+public:
+
+ /** Times at which control points occur. Must have the same
+ number of elements as Spline::control. */
+ Array<float> time;
+
+ /** If cyclic, then the control points will be assumed to wrap around.
+ If not cyclic, then the tangents at the ends of the spline
+ point to the final control points.*/
+ bool cyclic;
+
+ /** For a cyclic spline, this is the time elapsed between the last
+ control point and the first. If less than or equal to zero this is
+ assumed to be:
+
+ (time[0] - time[1] + .
+ time[time.size() - 1] - time[time.size() - 2]) / 2.
+ */
+ float finalInterval;
+
+ SplineBase() : cyclic(true), finalInterval(-1) {}
+
+ virtual ~SplineBase() {}
+
+ /** See specification for Spline::finalInterval; this handles the
+ non-positive case. Returns 0 if not cyclic. */
+ float getFinalInterval() const;
+
+ /** Returns the amount of time covered by this spline in one
+ period. For a cyclic spline, this contains the final
+ interval.*/
+ float duration() const;
+
+ /** Computes the derivative spline basis from the control point version. */
+ static Matrix4 computeBasis();
+
+protected:
+
+ /** Assumes that t0 <= s < tn. called by computeIndex. */
+ void computeIndexInBounds(float s, int& i, float& u) const;
+
+public:
+
+ /**
+ Given a time @a s, finds @a i and 0 <= @a u < 1 such that
+ @s = time[@a i] * @a u + time[@a i + 1] * (1 - @a u). Note that
+ @i may be outside the bounds of the time and control arrays;
+ use getControl to handle wraparound and extrapolation issues.
+
+ This function takes expected O(1) time for control points with
+ uniform time sampled control points or for uniformly
+ distributed random time samples, but may take O( log time.size() ) time
+ in the worst case.
+
+ Called from evaluate().
+ */
+ void computeIndex(float s, int& i, float& u) const;
+};
+
+
+/**
+ Smooth parameteric curve implemented using a piecewise 3rd-order
+ Catmull-Rom spline curve. The spline is considered infinite and may
+ either continue linearly from the specified control points or cycle
+ through them. Control points are spaced uniformly in time at unit
+ intervals by default, but irregular spacing may be explicitly
+ specified.
+
+ The dimension of the spline can be set by varying the Control
+ template parameter. For a 1D function, use Spline<float>. For a
+ curve in the plane, Spline<Vector2>. Note that <i>any</i> template
+ parameter that supports operator+(Control) and operator*(float) can
+ be used; you can make splines out of G3D::Vector4, G3D::Matrix3, or
+ your own classes.
+
+ To provide shortest-path interpolation, subclass G3D::Spline and
+ override ensureShortestPath(). To provide normalization of
+ interpolated points (e.g., projecting Quats onto the unit
+ hypersphere) override correct().
+
+ See Real Time Rendering, 2nd edition, ch 12 for a general discussion
+ of splines and their properties.
+
+ @sa G3D::UprightSpline, G3D::QuatSpline
+ */
+template<typename Control>
+class Spline : public SplineBase {
+protected:
+ /** The additive identity control point. */
+ Control zero;
+
+public:
+
+ /** Control points. Must have the same number of elements as
+ Spline::time.*/
+ Array<Control> control;
+
+ Spline() {
+ static Control x;
+ // Hide the fact from C++ that we are using an
+ // uninitialized variable here by pointer arithmetic.
+ // This is ok because any type that is a legal control
+ // point also supports multiplication by float.
+ zero = *(&x) * 0.0f;
+ }
+
+ /** Appends a control point at a specific time that must be
+ greater than that of the previous point. */
+ void append(float t, const Control& c) {
+ debugAssertM((time.size() == 0) || (t > time.last()),
+ "Control points must have monotonically increasing times.");
+ time.append(t);
+ control.append(c);
+ debugAssert(control.size() == time.size());
+ }
+
+
+ /** Appends control point spaced in time based on the previous
+ control point, or spaced at unit intervals if this is the
+ first control point. */
+ void append(const Control& c) {
+ switch (time.size()) {
+ case 0:
+ append(0, c);
+ break;
+
+ case 1:
+ if (time[0] == 0) {
+ append(1, c);
+ } else {
+ append(time[0], c);
+ }
+ break;
+
+ default:
+ append(2 * time[time.size() - 1] - time[time.size() - 2], c);
+ }
+ debugAssert(control.size() == time.size());
+ }
+
+ /** Erases all control points and times, but retains the state of
+ cyclic and finalInterval.
+ */
+ void clear() {
+ control.clear();
+ time.clear();
+ }
+
+
+ /** Number of control points */
+ int size() const {
+ debugAssert(time.size() == control.size());
+ return control.size();
+ }
+
+
+ /** Returns the requested control point and time sample based on
+ array index. If the array index is out of bounds, wraps (for
+ a cyclic spline) or linearly extrapolates (for a non-cyclic
+ spline), assuming time intervals follow the first or last
+ sample recorded.
+
+ Calls correct() on the control point if it was extrapolated.
+
+ Returns 0 if there are no control points.
+
+ @sa Spline::control and Spline::time for the underlying
+ control point array; Spline::computeIndex to find the index
+ given a time.
+ */
+ void getControl(int i, float& t, Control& c) const {
+ int N = control.size();
+ if (N == 0) {
+ c = zero;
+ t = 0;
+ } else if (cyclic) {
+ c = control[iWrap(i, N)];
+
+ if (i < 0) {
+ // Wrapped around bottom
+
+ // Number of times we wrapped around the cyclic array
+ int wraps = (N + 1 - i) / N;
+ int j = (i + wraps * N) % N;
+ t = time[j] - wraps * duration();
+
+ } else if (i < N) {
+
+ t = time[i];
+
+ } else {
+ // Wrapped around top
+
+ // Number of times we wrapped around the cyclic array
+ int wraps = i / N;
+ int j = i % N;
+ t = time[j] + wraps * duration();
+ }
+
+ } else if (i < 0) {
+ // Are there enough points to extrapolate?
+ if (N >= 2) {
+ // Step away from control point 0
+ float dt = time[1] - time[0];
+
+ // Extrapolate (note; i is negative)
+ c = control[1] * float(i) + control[0] * float(1 - i);
+ correct(c);
+ t = dt * i + time[0];
+
+ } else {
+ // Just clamp
+ c = control[0];
+
+ // Only 1 time; assume 1s intervals
+ t = time[0] + i;
+ }
+
+ } else if (i >= N) {
+ if (N >= 2) {
+ float dt = time[N - 1] - time[N - 2];
+
+ // Extrapolate
+ c = control[N - 1] * float(i - N + 2) + control[N - 2] * -float(i - N + 1);
+ correct(c);
+ t = time[N - 1] + dt * (i - N + 1);
+
+ } else {
+ // Return the last, clamping
+ c = control.last();
+ // Only 1 time; assume 1s intervals
+ t = time[0] + i;
+ }
+ } else {
+ // In bounds
+ c = control[i];
+ t = time[i];
+ }
+ }
+
+protected:
+
+ /** Returns a series of N control points and times, fixing
+ boundary issues. The indices may be assumed to be treated
+ cyclically. */
+ void getControls(int i, float* T, Control* A, int N) const {
+ for (int j = 0; j < N; ++j) {
+ getControl(i + j, T[j], A[j]);
+ }
+ ensureShortestPath(A, N);
+ }
+
+ /**
+ Mutates the array of N control points. It is useful to override this
+ method by one that wraps the values if they are angles or quaternions
+ for which "shortest path" interpolation is significant.
+ */
+ virtual void ensureShortestPath(Control* A, int N) const {}
+
+ /** Normalize or otherwise adjust this interpolated Control. */
+ virtual void correct(Control& A) const {}
+
+public:
+
+
+ /**
+ Return the position at time s. The spline is defined outside
+ of the time samples by extrapolation or cycling.
+ */
+ Control evaluate(float s) const {
+ debugAssertM(control.size() == time.size(), "Corrupt spline: wrong number of control points.");
+
+ /*
+ @cite http://www.gamedev.net/reference/articles/article1497.asp
+ Derivation of basis matrix follows.
+
+ Given control points with positions p[i] at times t[i], 0 <= i <= 3, find the position
+ at time t[1] <= s <= t[2].
+
+ Let u = s - t[0]
+ Let U = [u^0 u^1 u^2 u^3] = [1 u u^2 u^3]
+ Let dt0 = t[0] - t[-1]
+ Let dt1 = t[1] - t[0]
+ Let dt2 = t[2] - t[1]
+ */
+
+ // Index of the first control point (i.e., the u = 0 point)
+ int i = 0;
+ // Fractional part of the time
+ float u = 0;
+
+ computeIndex(s, i, u);
+
+ Control p[4];
+ float t[4];
+ getControls(i - 1, t, p, 4);
+ float dt0 = t[1] - t[0];
+ float dt1 = t[2] - t[1];
+ float dt2 = t[3] - t[2];
+
+ static const Matrix4 basis = computeBasis();
+
+ // Powers of u
+ Vector4 uvec((float)(u*u*u), (float)(u*u), (float)u, 1.0f);
+
+ // Compute the weights on each of the control points.
+ const Vector4& weights = uvec * basis;
+
+ // Compute the weighted sum of the neighboring control points.
+ Control sum;
+
+ const Control& p0 = p[0];
+ const Control& p1 = p[1];
+ const Control& p2 = p[2];
+ const Control& p3 = p[3];
+
+ const Control& dp0 = p1 + (p0*-1.0f);
+ const Control& dp1 = p2 + (p1*-1.0f);
+ const Control& dp2 = p3 + (p2*-1.0f);
+
+ // The factor of 1/2 from averaging two time intervals is
+ // already factored into the basis
+
+ // tan1 = (dp0 / dt0 + dp1 / dt1) * ((dt0 + dt1) * 0.5);
+ // The last term normalizes for unequal time intervals
+ float x = (dt0 + dt1) * 0.5f;
+ float n0 = x / dt0;
+ float n1 = x / dt1;
+ float n2 = x / dt2;
+ const Control& dp1n1 = dp1 * n1;
+ const Control& tan1 = dp0 * n0 + dp1n1;
+ const Control& tan2 = dp1n1 + dp2 * n2;
+
+ sum =
+ tan1 * weights[0]+
+ p1 * weights[1] +
+ p2 * weights[2] +
+ tan2 * weights[3];
+
+
+ correct(sum);
+ return sum;
+ }
+};
+
+}
+
+#endif
diff --git a/externals/g3dlite/G3D.lib/include/G3D/Stopwatch.h b/externals/g3dlite/G3D.lib/include/G3D/Stopwatch.h
new file mode 100644
index 00000000000..aee3b0f69c9
--- /dev/null
+++ b/externals/g3dlite/G3D.lib/include/G3D/Stopwatch.h
@@ -0,0 +1,108 @@
+/**
+ @file Stopwatch.h
+
+ @maintainer Morgan McGuire, matrix@graphics3d.com
+
+ @created 2005-10-05
+ @edited 2005-10-05
+
+ Copyright 2000-2003, Morgan McGuire.
+ All rights reserved.
+ */
+
+#ifndef G3D_STOPWATCH_H
+#define G3D_STOPWATCH_H
+
+#include "G3D/platform.h"
+#include "G3D/Queue.h"
+#include "G3D/G3DGameUnits.h"
+#include "G3D/g3dmath.h"
+
+namespace G3D {
+
+/**
+ Utility class for profiling duration and frame rates.
+ */
+class Stopwatch {
+private:
+ /** True between tick and tock */
+ bool inBetween;
+
+ /** The initial cycle count. */
+ uint64 cycleStart;
+
+ /** The time at which tick was called. */
+ RealTime timeStart;
+
+ /** The time at which the previous tock was called, -1 if never. */
+ RealTime lastTockTime;
+
+ RealTime lastDuration;
+ int64 lastCycleCount;
+
+ /** Frames per second. */
+ double m_fps;
+
+ /** Weighted fps */
+ double emwaFPS;
+ double m_smoothFPS;
+
+ /** Weighted duration */
+ RealTime emwaDuration;
+
+ /** The overhead for calling into the class. */
+ int64 cycleOverhead;
+
+ /** Called from the constructor. */
+ void computeOverhead();
+
+public:
+
+ Stopwatch();
+
+ /** Returns the number of times that tick was called per wall-clock second;
+ e.g. frames-per-second. */
+ double FPS() const {
+ return m_fps;
+ }
+
+ /** Amount of time between the most recent tick and tock calls. 0 if tick has
+ never been called. */
+ RealTime elapsedTime() const {
+ return lastDuration;
+ }
+
+ /** Time-smoothed value that is stable to the nearest 1%.
+ This is useful if you are displaying elapsed time in real-time
+ and want a stable number.*/
+ RealTime smoothElapsedTime() const {
+ return emwaDuration;
+ }
+
+ /** Time-smoothed value of fps that is stable to the nearest integer for fps > 10 and
+ to the first decimal place for fps <= 10.
+ This is useful if you
+ are displaying the frame rate in real-time and want a stable (readable) number.*/
+ double smoothFPS() const {
+ return m_smoothFPS;
+ }
+
+ /** The elapsed cycle time between tick and tock. An attempt is made to factor out all
+ tick/tock overhead, so that back-to-back calls should return zero.
+ Unreliable on non-x86 platforms.*/
+ uint64 elapsedCycles() const {
+ return lastCycleCount;
+ }
+
+ /** Call at the beginning of the period that you want timed. */
+ void tick();
+
+ /** Call at the end of the period that you want timed. */
+ void tock();
+};
+
+
+}
+
+#endif
+
diff --git a/externals/g3dlite/G3D.lib/include/G3D/System.h b/externals/g3dlite/G3D.lib/include/G3D/System.h
new file mode 100644
index 00000000000..3755dc5e36f
--- /dev/null
+++ b/externals/g3dlite/G3D.lib/include/G3D/System.h
@@ -0,0 +1,390 @@
+/**
+ @file System.h
+
+ @maintainer Morgan McGuire, matrix@graphics3d.com
+
+ @cite Rob Wyatt http://www.gamasutra.com/features/wyatts_world/19990709/processor_detection_01.htm
+ @cite Benjamin Jurke http://www.flipcode.com/cgi-bin/msg.cgi?showThread=COTD-ProcessorDetectionClass&forum=cotd&id=-1
+ @cite Michael Herf http://www.stereopsis.com/memcpy.html
+
+ @created 2003-01-25
+ @edited 2008-10-14
+ */
+
+#ifndef G3D_SYSTEM_H
+#define G3D_SYSTEM_H
+
+#include "G3D/platform.h"
+#include "G3D/g3dmath.h"
+#include "G3D/G3DGameUnits.h"
+#include "G3D/BinaryFormat.h"
+#include <string>
+
+#ifdef G3D_OSX
+# include <CoreServices/CoreServices.h>
+#endif
+
+namespace G3D {
+
+/**
+ Routine used by the demos to find the data. Searches in
+ ../data, ../../data, etc. up to 5 levels back. Checks
+ common locations like c:\libraries\g3d-<version>\data
+ and some hard-coded paths on the Brown University file
+ system.
+ */
+std::string demoFindData(bool errorIfNotFound = true);
+
+/** G3D, SDL, and IJG libraries require license documentation
+ to be distributed with your program. This generates the
+ string that must appear in your documentation.
+ <B>Your program can be commercial, closed-source</B> under
+ any license you want.*/
+std::string license();
+
+/**
+The order in which the bytes of an integer are stored on a machine. Intel/AMD chips tend to be G3D_LITTLE_ENDIAN, Mac PPC's and Suns are G3D_BIG_ENDIAN. However, this is primarily used to specify the byte order of file formats, which are fixed.
+*/
+enum G3DEndian {
+ G3D_BIG_ENDIAN, G3D_LITTLE_ENDIAN
+};
+
+/**
+ OS and processor abstraction. The first time any method is called the processor
+ will be analyzed. Future calls are then fast.
+
+ Timing function overview:
+ System::getCycleCount
+ - actual cycle count
+
+ System::getTick
+ - High-resolution time in seconds since program started
+
+ System::getLocalTime
+ - High-resolution time in seconds since Jan 1, 1970
+ (because it is stored in a double, this may be less
+ accurate than getTick)
+
+ */
+class System {
+public:
+
+ /** Called automatically by the other System routines.*/
+ static void init();
+
+ /** */
+ static bool hasMMX();
+
+ /** */
+ static bool hasCPUID();
+
+ /** */
+ static bool hasSSE();
+
+ /** */
+ static bool hasSSE2();
+
+ /** */
+ static bool hasSSE3();
+
+ /** */
+ static bool has3DNow();
+
+
+ /** */
+ static bool hasRDTSC();
+
+ static const std::string& cpuVendor();
+
+ /** e.g. "Windows", "GNU/Linux" */
+ static const std::string& operatingSystem();
+
+ /** */
+ static const std::string& cpuArchitecture();
+
+ /**
+ Returns the endianness of this machine.
+ */
+ static G3DEndian machineEndian();
+
+ /**
+ Returns the current date as a string in the form YYYY-MM-DD
+ */
+ static std::string currentDateString();
+
+ /**
+ Guarantees that the start of the array is aligned to the
+ specified number of bytes.
+ */
+ static void* alignedMalloc(size_t bytes, size_t alignment);
+
+ /**
+ Uses pooled storage to optimize small allocations (1 byte to 5 kilobytes).
+ Can be 10x to 100x faster than calling ::malloc or new.
+
+ The result must be freed with free.
+
+ Threadsafe on Win32.
+
+ @sa calloc realloc OutOfMemoryCallback free
+ */
+ static void* malloc(size_t bytes);
+
+ static void* calloc(size_t n, size_t x);
+
+ /**
+ @param size Size of memory that the system was trying to allocate
+ @param recoverable If true, the system will attempt to allocate again
+ if the callback returns true. If false, malloc is going to return
+ NULL and this invocation is just to notify the application.
+ @return Return true to force malloc to attempt allocation again if the
+ error was recoverable.
+ */
+ typedef bool (*OutOfMemoryCallback)(size_t size, bool recoverable);
+
+ /**
+ When System::malloc fails to allocate memory because the system is
+ out of memory, it invokes this handler (if it is not NULL).
+ The argument to the callback is the amount of memory that malloc
+ was trying to allocate when it ran out. If the callback returns
+ true, System::malloc will attempt to allocate the memory again.
+ If the callback returns false, then System::malloc will return NULL.
+
+ You can use outOfMemoryCallback to free data structures or to
+ register the failure.
+ */
+ static OutOfMemoryCallback outOfMemoryCallback;
+
+ /**
+ Version of realloc that works with System::malloc.
+ */
+ static void* realloc(void* block, size_t bytes);
+
+ /** Returns a string describing how well System::malloc is using its internal pooled storage.
+ "heap" memory was slow to allocate; the other data sizes are comparatively fast.*/
+ static std::string mallocPerformance();
+ static void resetMallocPerformanceCounters();
+
+ /**
+ Returns a string describing the current usage of the buffer pools used for
+ optimizing System::malloc.
+ */
+ static std::string mallocStatus();
+
+ /**
+ Free data allocated with System::malloc.
+
+ Threadsafe on Win32.
+ */
+ static void free(void* p);
+
+ /**
+ Frees memory allocated with alignedMalloc.
+ */
+ static void alignedFree(void* ptr);
+
+ /** An implementation of memcpy that may be up to 2x as fast as the C library
+ one on some processors. Guaranteed to have the same behavior as memcpy
+ in all cases. */
+ static void memcpy(void* dst, const void* src, size_t numBytes);
+
+ /** An implementation of memset that may be up to 2x as fast as the C library
+ one on some processors. Guaranteed to have the same behavior as memset
+ in all cases. */
+ static void memset(void* dst, uint8 value, size_t numBytes);
+
+ /**
+ Returns the fully qualified filename for the currently running executable.
+
+ This is more reliable than arg[0], which may be intentionally set
+ to an incorrect value by a calling program, relative to a now
+ non-current directory, or obfuscated by sym-links.
+
+ @cite Linux version written by Nicolai Haehnle <prefect_@gmx.net>, http://www.flipcode.com/cgi-bin/msg.cgi?showThread=COTD-getexename&forum=cotd&id=-1
+ */
+ static std::string currentProgramFilename();
+
+ /** Name of this program. Note that you can mutate this string to set your app name explicitly.*/
+ static std::string& appName();
+
+ /** G3D Version string */
+ static const std::string& version();
+
+ /**
+ Either Debug or Release, depending on whether _DEBUG was defined at compile-time for the library.
+ */
+ static const std::string& build();
+
+ /**
+ Causes the current thread to yield for the specified duration
+ and consume almost no CPU.
+ The sleep will be extremely precise; it uses System::time()
+ to calibrate the exact yeild time.
+ */
+ static void sleep(RealTime t);
+
+ /**
+ Clears the console.
+ Console programs only.
+ */
+ static void consoleClearScreen();
+
+ /**
+ Returns true if a key is waiting.
+ Console programs only.
+ */
+ static bool consoleKeyPressed();
+
+ /**
+ Blocks until a key is read (use consoleKeyPressed to determine if
+ a key is waiting to be read) then returns the character code for
+ that key.
+ */
+ static int consoleReadKey();
+
+ /**
+ The actual time (measured in seconds since
+ Jan 1 1970 midnight).
+
+ Adjusted for local timezone and daylight savings
+ time. This is as accurate and fast as getCycleCount().
+ */
+ static RealTime time();
+
+ /**
+ To count the number of cycles a given operation takes:
+
+ <PRE>
+ unsigned long count;
+ System::beginCycleCount(count);
+ ...
+ System::endCycleCount(count);
+ // count now contains the cycle count for the intervening operation.
+
+ */
+ static void beginCycleCount(uint64& cycleCount);
+ static void endCycleCount(uint64& cycleCount);
+
+ static uint64 getCycleCount();
+
+ /** Set an environment variable for the current process */
+ static void setEnv(const std::string& name, const std::string& value);
+
+ /** Get an environment variable for the current process. Returns NULL if the variable doesn't exist. */
+ static const char* getEnv(const std::string& name);
+
+ /**
+ Prints a human-readable description of this machine
+ to the text output stream. Either argument may be NULL.
+ */
+ static void describeSystem(
+ class TextOutput& t);
+
+ static void describeSystem(
+ std::string& s);
+
+ /** Returns the speed of processor 0 in MHz.
+ Always returns 0 on linux.*/
+ static int cpuSpeedMHz();
+
+
+ /** On Win32, returns the clipboard text contents. Does nothing on other
+ platforms (yet) */
+ static std::string getClipboardText();
+
+ /** Copies the text to the clipboard on Win32. */
+ static void setClipboardText(const std::string& s);
+
+ /**
+ Tries to locate the resource by looking in related directories.
+ If found, returns the full path to the resource, otherwise
+ returns the empty string.
+ */
+ static std::string findDataFile(const std::string& full, bool errorIfNotFound = true);
+
+ /**
+ Sets the path that the application is using as its data directory.
+ Used by findDataDir as an initial search location. GApp sets this
+ upon constrution.
+ */
+ static void setAppDataDir(const std::string& path);
+
+private:
+ /**
+ (CKO) Note: Not sure why these are specifically needed
+ for OS X. I made them private though.
+ */
+# ifdef G3D_OSX
+ static long m_OSXCPUSpeed; //In Cycles/Second
+ static double m_secondsPerNS;
+# endif
+};
+
+
+#ifdef _MSC_VER
+ inline uint64 System::getCycleCount() {
+ uint32 timehi, timelo;
+
+ // Use the assembly instruction rdtsc, which gets the current
+ // cycle count (since the process started) and puts it in edx:eax.
+ __asm
+ {
+ rdtsc;
+ mov timehi, edx;
+ mov timelo, eax;
+ }
+
+ return ((uint64)timehi << 32) + (uint64)timelo;
+ }
+
+#elif defined(G3D_LINUX)
+
+ inline uint64 System::getCycleCount() {
+ uint32 timehi, timelo;
+
+ __asm__ __volatile__ (
+ "rdtsc "
+ : "=a" (timelo),
+ "=d" (timehi)
+ : );
+
+ return ((uint64)timehi << 32) + (uint64)timelo;
+ }
+
+#elif defined(G3D_OSX)
+
+ inline uint64 System::getCycleCount() {
+ //Note: To put off extra processing until the end, this does not
+ //return the actual clock cycle count. It is a bus cycle count.
+ //When endCycleCount() is called, it converts the two into a difference
+ //of clock cycles
+
+ return (uint64) UnsignedWideToUInt64(UpTime());
+ //return (uint64) mach_absolute_time();
+ }
+
+#endif
+
+inline void System::beginCycleCount(uint64& cycleCount) {
+ cycleCount = getCycleCount();
+}
+
+
+inline void System::endCycleCount(uint64& cycleCount) {
+ #ifndef G3D_OSX
+ cycleCount = getCycleCount() - cycleCount;
+ #else
+ AbsoluteTime end = UpTime();
+ init();
+ Nanoseconds diffNS =
+ AbsoluteDeltaToNanoseconds(end, UInt64ToUnsignedWide(cycleCount));
+ cycleCount =
+ (uint64) ((double) (System::m_OSXCPUSpeed) *
+ (double) UnsignedWideToUInt64(diffNS) * m_secondsPerNS);
+ #endif
+}
+
+
+} // namespace
+
+#endif
diff --git a/externals/g3dlite/G3D.lib/include/G3D/Table.h b/externals/g3dlite/G3D.lib/include/G3D/Table.h
new file mode 100644
index 00000000000..9ccd4f5c101
--- /dev/null
+++ b/externals/g3dlite/G3D.lib/include/G3D/Table.h
@@ -0,0 +1,770 @@
+/**
+ @file Table.h
+
+ Templated hash table class.
+
+ @maintainer Morgan McGuire, morgan@cs.williams.edu
+ @created 2001-04-22
+ @edited 2008-07-01
+ Copyright 2000-2008, Morgan McGuire.
+ All rights reserved.
+ */
+
+#ifndef G3D_TABLE_H
+#define G3D_TABLE_H
+
+#include <cstddef>
+#include <string>
+
+#include "G3D/platform.h"
+#include "G3D/Array.h"
+#include "G3D/debug.h"
+#include "G3D/System.h"
+#include "G3D/g3dmath.h"
+#include "G3D/EqualsTrait.h"
+#include "G3D/HashTrait.h"
+
+#ifdef _MSC_VER
+# pragma warning (push)
+ // Debug name too long warning
+# pragma warning (disable : 4786)
+#endif
+
+namespace G3D {
+
+/**
+ An unordered data structure mapping keys to values.
+
+ Key must be a pointer, an int, a std::string or provide overloads for:
+
+ <PRE>
+ template<> struct HashTrait<class Key> {
+ static size_t hashCode(const Key& key) { return reinterpret_cast<size_t>( ... ); }
+ };
+ </PRE>
+
+ and one of
+
+ <PRE>
+ template<> struct EqualsTrait<class Key>{
+ static bool equals(const Key& a, const Key& b) { return ... ; }
+ };
+
+
+ bool operator==(const Key&, const Key&);
+ </PRE>
+
+ G3D pre-defines HashTrait specializations for common types (like <CODE>int</CODE> and <CODE>std::string</CODE>).
+ If you use a Table with a different type you must write those functions yourself. For example,
+ an enum would use:
+
+ <PRE>
+ template<> struct HashTrait<MyEnum> {
+ static size_t equals(const MyEnum& key) const { return reinterpret_cast<size_t>( key ); }
+ };
+ </PRE>
+
+ And rely on the default enum operator==.
+
+
+ Periodically check that debugGetLoad() is low (> 0.1). When it gets near
+ 1.0 your hash function is badly designed and maps too many inputs to
+ the same output.
+ */
+template<class Key, class Value, class HashFunc = HashTrait<Key>, class EqualsFunc = EqualsTrait<Key> >
+class Table {
+public:
+
+ /**
+ The pairs returned by iterator.
+ */
+ class Entry {
+ public:
+ Key key;
+ Value value;
+ Entry() {}
+ Entry(const Key& k, const Value& v) : key(k), value(v) {}
+ };
+
+private:
+
+ typedef Table<Key, Value, HashFunc, EqualsFunc> ThisType;
+
+ /**
+ Linked list nodes used internally by HashTable.
+ */
+ class Node {
+ public:
+ Entry entry;
+ size_t hashCode;
+ Node* next;
+
+ /** Provide pooled allocation for speed. */
+ inline void* operator new (size_t size) {
+ return System::malloc(size);
+ }
+
+ inline void operator delete (void* p) {
+ System::free(p);
+ }
+
+ Node(const Key& k, const Value& v, size_t h, Node* n)
+ : entry(k, v), hashCode(h), next(n) {
+ }
+
+ /**
+ Clones a whole chain;
+ */
+ Node* clone() {
+ return new Node(this->entry.key, this->entry.value, hashCode, (next == NULL) ? NULL : next->clone());
+ }
+ };
+
+ void checkIntegrity() const {
+# ifdef G3D_DEBUG
+ debugAssert(bucket == NULL || isValidHeapPointer(bucket));
+ for (size_t b = 0; b < numBuckets; ++b) {
+ Node* node = bucket[b];
+ debugAssert(node == NULL || isValidHeapPointer(node));
+ while (node != NULL) {
+ debugAssert(node == NULL || isValidHeapPointer(node));
+ node = node->next;
+ }
+ }
+# endif
+ }
+
+ /**
+ Number of elements in the table.
+ */
+ size_t _size;
+
+ /**
+ Array of Node*.
+ We don't use Array<Node*> because Table is lower level.
+ Some elements may be NULL.
+ */
+ Node** bucket;
+
+ /**
+ Length of the bucket array.
+ */
+ size_t numBuckets;
+
+ /**
+ Re-hashes for a larger bucket size.
+ */
+ void resize(size_t newSize) {
+
+ // Hang onto the old bucket array
+ Node** oldBucket = bucket;
+
+ // Allocate a new bucket array with the new size
+ bucket = (Node**)System::calloc(sizeof(Node*), newSize);
+ debugAssertM(bucket != NULL, "System::calloc returned NULL. Out of memory.");
+ // Move each node to its new hash location
+ for (size_t b = 0; b < numBuckets; ++b) {
+ Node* node = oldBucket[b];
+
+ // There is a linked list of nodes at this bucket
+ while (node != NULL) {
+ // Hang onto the old next pointer
+ Node* nextNode = node->next;
+
+ // Insert at the head of the list for bucket[i]
+ size_t i = node->hashCode % newSize;
+ node->next = bucket[i];
+ bucket[i] = node;
+
+ // Move on to the next node
+ node = nextNode;
+ }
+
+ // Drop the old pointer for cleanliness when debugging
+ oldBucket[b] = NULL;
+ }
+
+ // Delete the old storage
+ System::free(oldBucket);
+ this->numBuckets = newSize;
+
+ checkIntegrity();
+ }
+
+
+ void copyFrom(const ThisType& h) {
+ if (&h == this) {
+ return;
+ }
+
+ debugAssert(bucket == NULL);
+ _size = h._size;
+ numBuckets = h.numBuckets;
+ bucket = (Node**)System::calloc(sizeof(Node*), numBuckets);
+
+ for (size_t b = 0; b < numBuckets; b++) {
+ if (h.bucket[b] != NULL) {
+ bucket[b] = h.bucket[b]->clone();
+ }
+ }
+
+ checkIntegrity();
+ }
+
+ /**
+ Frees the heap structures for the nodes.
+ */
+ void freeMemory() {
+ checkIntegrity();
+
+ for (size_t b = 0; b < numBuckets; b++) {
+ Node* node = bucket[b];
+ while (node != NULL) {
+ Node* next = node->next;
+ delete node;
+ node = next;
+ }
+ bucket[b] = NULL;
+ }
+ System::free(bucket);
+ bucket = NULL;
+ numBuckets = 0;
+ _size = 0;
+ }
+
+
+public:
+
+ /**
+ Creates an empty hash table. This causes some heap allocation to occur.
+ */
+ Table() : bucket(NULL) {
+ numBuckets = 10;
+ _size = 0;
+ bucket = (Node**)System::calloc(sizeof(Node*), numBuckets);
+ checkIntegrity();
+ }
+
+ /**
+ Recommends that the table resize to anticipate at least this number of elements.
+ */
+ void setSizeHint(size_t n) {
+ size_t s = n * 3;
+ if (s > numBuckets) {
+ resize(s);
+ }
+ }
+
+ /**
+ Destroys all of the memory allocated by the table, but does <B>not</B>
+ call delete on keys or values if they are pointers. If you want to
+ deallocate things that the table points at, use getKeys() and Array::deleteAll()
+ to delete them.
+ */
+ virtual ~Table() {
+ freeMemory();
+ }
+
+ Table(const ThisType& h) {
+ numBuckets = 0;
+ _size = 0;
+ bucket = NULL;
+ this->copyFrom(h);
+ checkIntegrity();
+ }
+
+
+ Table& operator=(const ThisType& h) {
+ // No need to copy if the argument is this
+ if (this != &h) {
+ // Free the existing nodes
+ freeMemory();
+ this->copyFrom(h);
+ checkIntegrity();
+ }
+ return *this;
+ }
+
+ /**
+ Returns the length of the deepest bucket.
+ */
+ size_t debugGetDeepestBucketSize() const {
+ size_t deepest = 0;
+
+ for (size_t b = 0; b < numBuckets; b++) {
+ size_t count = 0;
+ Node* node = bucket[b];
+ while (node != NULL) {
+ node = node->next;
+ ++count;
+ }
+
+ if (count > deepest) {
+ deepest = count;
+ }
+ }
+
+ return deepest;
+ }
+
+ /**
+ Returns the average size of non-empty buckets.
+ */
+ float debugGetAverageBucketSize() const {
+ size_t num = 0;
+ size_t count = 0;
+
+ for (size_t b = 0; b < numBuckets; b++) {
+ Node* node = bucket[b];
+ if (node != NULL) {
+ ++num;
+ while (node != NULL) {
+ node = node->next;
+ ++count;
+ }
+ }
+ }
+
+ return (float)((double)count / num);
+ }
+
+ /**
+ A small load (close to zero) means the hash table is acting very
+ efficiently most of the time. A large load (close to 1) means
+ the hash table is acting poorly-- all operations will be very slow.
+ A large load will result from a bad hash function that maps too
+ many keys to the same code.
+ */
+ double debugGetLoad() const {
+ return debugGetDeepestBucketSize() / (double)size();
+ }
+
+ /**
+ Returns the number of buckets.
+ */
+ size_t debugGetNumBuckets() const {
+ return numBuckets;
+ }
+
+ /**
+ C++ STL style iterator variable. See begin().
+ */
+ class Iterator {
+ private:
+ friend class Table<Key, Value, HashFunc, EqualsFunc>;
+
+ /**
+ Bucket index.
+ */
+ size_t index;
+
+ /**
+ Linked list node.
+ */
+ Node* node;
+ ThisType* table;
+ size_t numBuckets;
+ Node** bucket;
+ bool isDone;
+
+ /**
+ Creates the end iterator.
+ */
+ Iterator(const ThisType* table) : table(const_cast<ThisType*>(table)) {
+ isDone = true;
+ }
+
+ Iterator(const ThisType* table, size_t numBuckets, Node** bucket) :
+ table(const_cast<ThisType*>(table)),
+ numBuckets(numBuckets),
+ bucket(bucket) {
+
+ if (numBuckets == 0) {
+ // Empty table
+ isDone = true;
+ return;
+ }
+
+ index = 0;
+ node = bucket[index];
+ isDone = false;
+ findNext();
+ }
+
+ /**
+ Finds the next element, setting isDone if one can't be found.
+ Looks at the current element first.
+ */
+ void findNext() {
+ while (node == NULL) {
+ index++;
+ if (index >= numBuckets) {
+ isDone = true;
+ break;
+ } else {
+ node = bucket[index];
+ }
+ }
+ }
+
+ public:
+ inline bool operator!=(const Iterator& other) const {
+ return !(*this == other);
+ }
+
+ bool operator==(const Iterator& other) const {
+ if (other.isDone || isDone) {
+ // Common case; check against isDone.
+ return (isDone == other.isDone) && (other.table == table);
+ } else {
+ return
+ (table == other.table) &&
+ (node == other.node) &&
+ (index == other.index);
+ }
+ }
+
+ /**
+ Pre increment.
+ */
+ Iterator& operator++() {
+ node = node->next;
+ findNext();
+ return *this;
+ }
+
+ /**
+ Post increment (slower than preincrement).
+ */
+ Iterator operator++(int) {
+ Iterator old = *this;
+ ++(*this);
+ return old;
+ }
+
+ const Entry& operator*() const {
+ return node->entry;
+ }
+
+ Entry* operator->() const {
+ return &(node->entry);
+ }
+
+ operator Entry*() const {
+ return &(node->entry);
+ }
+
+ bool hasMore() const {
+ return ! isDone;
+ }
+ };
+
+
+ /**
+ C++ STL style iterator method. Returns the first Entry, which
+ contains a key and value. Use preincrement (++entry) to get to
+ the next element. Do not modify the table while iterating.
+ */
+ Iterator begin() const {
+ return Iterator(this, numBuckets, bucket);
+ }
+
+ /**
+ C++ STL style iterator method. Returns one after the last iterator
+ element.
+ */
+ const Iterator end() const {
+ return Iterator(this);
+ }
+
+ /**
+ Removes all elements
+ */
+ void clear() {
+ freeMemory();
+ numBuckets = 10;
+ _size = 0;
+ bucket = (Node**)System::calloc(sizeof(Node*), numBuckets);
+ }
+
+
+ /**
+ Returns the number of keys.
+ */
+ size_t size() const {
+ return _size;
+ }
+
+
+ /**
+ If you insert a pointer into the key or value of a table, you are
+ responsible for deallocating the object eventually. Inserting
+ key into a table is O(1), but may cause a potentially slow rehashing.
+ */
+ void set(const Key& key, const Value& value) {
+ size_t code = HashFunc::hashCode(key);
+ size_t b = code % numBuckets;
+
+ // Go to the bucket
+ Node* n = bucket[b];
+
+ // No bucket, so this must be the first
+ if (n == NULL) {
+ bucket[b] = new Node(key, value, code, NULL);
+ ++_size;
+ return;
+ }
+
+ size_t bucketLength = 1;
+
+ // Sometimes a bad hash code will cause all elements
+ // to collide. Detect this case and don't rehash when
+ // it occurs; nothing good will come from the rehashing.
+ bool allSameCode = true;
+
+ // Try to find the node
+ do {
+ allSameCode = allSameCode && (code == n->hashCode);
+
+ if ((code == n->hashCode) && EqualsFunc::equals(n->entry.key, key)) {
+ // Replace the existing node.
+ n->entry.value = value;
+ return;
+ }
+
+ n = n->next;
+ ++bucketLength;
+ } while (n != NULL);
+
+ const size_t maxBucketLength = 3;
+ // (Don't bother changing the size of the table if all entries
+ // have the same hashcode--they'll still collide)
+ if ((bucketLength > maxBucketLength) &&
+ ! allSameCode &&
+ (numBuckets < _size * 15)) {
+
+ // This bucket was really large; rehash if all elements
+ // don't have the same hashcode the number of buckets is
+ // reasonable.
+
+ // Back off the scale factor as the number of buckets gets
+ // large
+ float f = 3.0f;
+ if (numBuckets > 1000000) {
+ f = 1.5f;
+ } else if (numBuckets > 100000) {
+ f = 2.0f;
+ }
+ int newSize = iMax((int)(numBuckets * f) + 1, (int)(_size * f));
+ resize(newSize);
+ }
+
+ // Not found; insert at the head.
+ b = code % numBuckets;
+ bucket[b] = new Node(key, value, code, bucket[b]);
+ ++_size;
+ }
+
+ /**
+ Removes an element from the table if it is present.
+ @return true if the element was found and removed, otherwise false
+ */
+ bool remove(const Key& key) {
+
+ size_t code = HashFunc::hashCode(key);
+ size_t b = code % numBuckets;
+
+ // Go to the bucket
+ Node* n = bucket[b];
+
+ if (n == NULL) {
+ return false;
+ }
+
+ Node* previous = NULL;
+
+ // Try to find the node
+ do {
+ if ((code == n->hashCode) && EqualsFunc::equals(n->entry.key, key)) {
+ // This is the node; remove it
+
+ // Replace the previous's next pointer
+ if (previous == NULL) {
+ bucket[b] = n->next;
+ } else {
+ previous->next = n->next;
+ }
+
+ // Delete the node
+ delete n;
+ --_size;
+ return true;
+ }
+
+ previous = n;
+ n = n->next;
+ } while (n != NULL);
+
+
+ return false;
+ //alwaysAssertM(false, "Tried to remove a key that was not in the table.");
+ }
+
+ /**
+ Returns the value associated with key.
+ @deprecated Use get(key, val) or getPointer(key)
+ */
+ Value& get(const Key& key) const {
+
+ size_t code = HashFunc::hashCode(key);
+ size_t b = code % numBuckets;
+
+ Node* node = bucket[b];
+
+ while (node != NULL) {
+ if ((node->hashCode == code) && EqualsFunc::equals(node->entry.key, key)) {
+ return node->entry.value;
+ }
+ node = node->next;
+ }
+
+ debugAssertM(false, "Key not found");
+ // The next line is here just to make
+ // a compiler warning go away.
+ return node->entry.value;
+ }
+
+
+ /** Returns a pointer to the element if it exists, or NULL if it does not.
+ Note that if your value type <i>is</i> a pointer, the return value is
+ a pointer to a pointer. Do not remove the element while holding this
+ pointer.
+
+ It is easy to accidentally mis-use this method. Consider making
+ a Table<Value*> and using get(key, val) instead, which makes you manage
+ the memory for the values yourself and is less likely to result in
+ pointer errors.
+ */
+ Value* getPointer(const Key& key) const {
+ size_t code = HashFunc::hashCode(key);
+ size_t b = code % numBuckets;
+
+ Node* node = bucket[b];
+
+ while (node != NULL) {
+ if ((node->hashCode == code) && EqualsFunc::equals(node->entry.key, key)) {
+ // found key
+ return &(node->entry.value);
+ }
+ node = node->next;
+ }
+
+ // Failed to find key
+ return NULL;
+ }
+
+ /**
+ If the key is present in the table, val is set to the associated value and returns true.
+ If the key is not present, returns false.
+ */
+ bool get(const Key& key, Value& val) const {
+ Value* v = getPointer(key);
+ if (v != NULL) {
+ val = *v;
+ return true;
+ } else {
+ return false;
+ }
+ }
+
+ /**
+ Returns true if key is in the table.
+ */
+ bool containsKey(const Key& key) const {
+ size_t code = HashFunc::hashCode(key);
+ size_t b = code % numBuckets;
+
+ Node* node = bucket[b];
+
+ while (node != NULL) {
+ if ((node->hashCode == code) && EqualsFunc::equals(node->entry.key, key)) {
+ return true;
+ }
+ node = node->next;
+ } while (node != NULL);
+
+ return false;
+ }
+
+
+ /**
+ Short syntax for get.
+ */
+ inline Value& operator[](const Key &key) const {
+ return get(key);
+ }
+
+
+ /**
+ Returns an array of all of the keys in the table.
+ You can iterate over the keys to get the values.
+ @deprecated
+ */
+ Array<Key> getKeys() const {
+ Array<Key> keyArray;
+ getKeys(keyArray);
+ return keyArray;
+ }
+
+ void getKeys(Array<Key>& keyArray) const {
+ keyArray.resize(0, DONT_SHRINK_UNDERLYING_ARRAY);
+ for (size_t i = 0; i < numBuckets; i++) {
+ Node* node = bucket[i];
+ while (node != NULL) {
+ keyArray.append(node->entry.key);
+ node = node->next;
+ }
+ }
+ }
+
+ /**
+ Calls delete on all of the keys and then clears the table.
+ */
+ void deleteKeys() {
+ for (size_t i = 0; i < numBuckets; i++) {
+ Node* node = bucket[i];
+ while (node != NULL) {
+ delete node->entry.key;
+ node = node->next;
+ }
+ }
+ clear();
+ }
+
+ /**
+ Calls delete on all of the values. This is unsafe--
+ do not call unless you know that each value appears
+ at most once.
+
+ Does not clear the table, so you are left with a table
+ of NULL pointers.
+ */
+ void deleteValues() {
+ for (size_t i = 0; i < numBuckets; ++i) {
+ Node* node = bucket[i];
+ while (node != NULL) {
+ delete node->entry.value;
+ node->entry.value = NULL;
+ node = node->next;
+ }
+ }
+ }
+};
+
+} // namespace
+
+#ifdef _MSC_VER
+# pragma warning (pop)
+#endif
+
+#endif
diff --git a/externals/g3dlite/G3D.lib/include/G3D/TextInput.h b/externals/g3dlite/G3D.lib/include/G3D/TextInput.h
new file mode 100644
index 00000000000..b6dcad39b8b
--- /dev/null
+++ b/externals/g3dlite/G3D.lib/include/G3D/TextInput.h
@@ -0,0 +1,693 @@
+/**
+ @file TextInput.h
+
+ Simple text lexer/tokenizer.
+
+ @maintainer Morgan McGuire, morgan@graphics3d.com
+
+ @cite Based on a lexer written by Aaron Orenstein.
+
+ @created 2002-11-27
+ @edited 2006-10-24
+
+ Copyright 2000-2007, Morgan McGuire.
+ All rights reserved.
+ */
+
+#ifndef G3D_TEXTINPUT_H
+#define G3D_TEXTINPUT_H
+
+#include "G3D/platform.h"
+#include "G3D/Array.h"
+#include "G3D/Set.h"
+#include <string>
+#include <queue>
+#include <ctype.h>
+#include <stdio.h>
+
+namespace G3D {
+
+/**
+ For use with TextInput.
+ */
+class Token {
+public:
+
+ /**
+ More detailed type information than Type.
+ */
+ enum ExtendedType {
+ DOUBLE_QUOTED_TYPE,
+ SINGLE_QUOTED_TYPE,
+ SYMBOL_TYPE,
+ FLOATING_POINT_TYPE,
+ INTEGER_TYPE,
+ BOOLEAN_TYPE,
+ END_TYPE
+ };
+
+ /**
+ Strings are enclosed in quotes, symbols are not.
+ */
+ enum Type {
+ STRING = DOUBLE_QUOTED_TYPE,
+ SYMBOL = SYMBOL_TYPE,
+ NUMBER = FLOATING_POINT_TYPE,
+ BOOLEAN = BOOLEAN_TYPE,
+ END = END_TYPE
+ };
+
+private:
+
+ friend class TextInput;
+
+ /**
+ Holds the actual value, which might be any type. If a number, it will be
+ parsed at runtime.
+ */
+ std::string _string;
+
+ bool _bool;
+ int _line;
+ int _character;
+ Type _type;
+ ExtendedType _extendedType;
+
+public:
+
+ Token() :
+ _string(""),
+ _bool(false),
+ _line(0),
+ _character(0),
+ _type(END),
+ _extendedType(END_TYPE) {}
+
+ Token(Type t, ExtendedType e, const std::string& s, int L, int c)
+ : _string(s), _bool(false), _line(L), _character(c), _type(t), _extendedType(e) {}
+
+ Token(Type t, ExtendedType e, const std::string& s, bool b, int L, int c)
+ : _string(s), _bool(b), _line(L), _character(c), _type(t), _extendedType(e) {}
+
+ Type type() const {
+ return _type;
+ }
+
+ ExtendedType extendedType() const {
+ return _extendedType;
+ }
+
+ /**
+ The value of a single or double quote string (not including the quotes),
+ the name of a symbol, or the exact textual representation of a number as
+ parsed from the input.
+ */
+ const std::string& string() const {
+ return _string;
+ }
+
+ bool boolean() const {
+ return _bool;
+ }
+
+ /**
+ Starting line of the input from which this token was parsed. Starts
+ at 1.
+ */
+ int line() const {
+ return _line;
+ }
+
+ /**
+ Starting character position in the input line from which this token was
+ parsed. Starts at 1.
+ */
+ int character() const {
+ return _character;
+ }
+
+ /** Return the numeric value for a number type, or zero if this is
+ not a number type.
+ */
+ double number() const;
+};
+
+
+/**
+ A simple style tokenizer for reading text files. TextInput handles a
+ superset of C++,Java, Matlab, and Bash code text including single
+ line comments, block comments, quoted strings with escape sequences,
+ and operators. TextInput recognizes several categories of tokens,
+ which are separated by white space, quotation marks, or the end of a
+ recognized operator:
+
+ <ul>
+ <li><CODE>Token::SINGLE_QUOTED_TYPE</CODE> string of characters surrounded by single quotes, e.g., 'x', '\0', 'foo'.
+ <li><CODE>Token::DOUBLE_QUOTED_TYPE</CODE> string of characters surrounded by double quotes, e.g., "x", "abc\txyz", "b o b".
+ <li><CODE>Token::SYMBOL_TYPE</CODE> legal C++ operators, keywords, and identifiers. e.g., >=, Foo, _X, class, {
+ <li><CODE>Token::INTEGER_TYPE</CODE> numbers without decimal places or exponential notation. e.g., 10, 0x17F, 32, 0, -155
+ <li><CODE>Token::FLOATING_POINT_TYPE</CODE> numbers with decimal places or exponential notation. e.g., 1e3, -1.2, .4, 0.5
+ <li><CODE>Token::BOOLEAN_TYPE</CODE> special symbols like "true" and "false"; the exact details can be configured in TextInput::Settings
+ </ul>
+
+ <P>The special ".." and "..." tokens are always recognized in
+ addition to normal C++ operators. Additional tokens can be made
+ available by changing the Settings.
+
+ Negative numbers are handled specially because of the ambiguity between unary minus and negative numbers--
+ see the note on TextInput::read.
+
+ TextInput does not have helper functions for types with non-obvious
+ formatting, or helpers that would be redundant. Use the serialize
+ methods instead for parsing specific types like int, Vector3, and
+ Color3.
+
+ Inside quoted strings escape sequences are converted. Thus the
+ string token for ["a\nb"] is 'a', followed by a newline, followed by
+ 'b'. Outside of quoted strings, escape sequences are not converted,
+ so the token sequence for [a\nb] is symbol 'a', symbol '\', symbol
+ 'nb' (this matches what a C++ parser would do). The exception is
+ that a specified TextInput::Settings::otherCommentCharacter preceeded
+ by a backslash is assumed to be an escaped comment character and is
+ returned as a symbol token instead of being parsed as a comment
+ (this is what a LaTex or VRML parser would do).
+
+ <B>Examples</B>
+
+ <PRE>
+ TextInput ti(TextInput::FROM_STRING, "name = \"Max\", height = 6");
+
+ Token t;
+
+ t = ti.read();
+ debugAssert(t.type == Token::SYMBOL);
+ debugAssert(t.sval == "name");
+
+ ti.read();
+ debugAssert(t.type == Token::SYMBOL);
+ debugAssert(t.sval == "=");
+
+ std::string name = ti.read().sval;
+ ti.read();
+ </PRE>
+
+ <PRE>
+ TextInput ti(TextInput::FROM_STRING, "name = \"Max\", height = 6");
+ ti.readSymbols("name", "=");
+ std::string name = ti.readString();
+ ti.readSymbols(",", "height", "=");
+ double height = ti. readNumber();
+ </PRE>
+
+ Assumes that the file is not modified once opened.
+ */
+class TextInput {
+public:
+
+ /** Tokenizer configuration options. */
+ class Settings {
+ public:
+ /** If true, slash-star marks a multi-line comment. Default
+ is true. */
+ bool cComments;
+
+ /** If true, // begins a single line comment. Default is true. */
+ bool cppComments;
+
+ /** If true, \r, \n, \t, \0, \\ and other escape sequences inside
+ strings are converted to the equivalent C++ escaped character.
+ If false, backslashes are treated literally. It is convenient to
+ set to false if reading Windows paths, for example, like
+ c:\foo\bar.
+
+ Default is true.
+ */
+ bool escapeSequencesInStrings;
+
+ /** If non-NUL, specifies a character that begins single line
+ comments ('#' and '%' are popular choices). This is independent
+ of the cppComments flag. If the character appears in text with a
+ backslash in front of it, it is considered escaped and is not
+ treated as a comment character.
+
+ Default is '\0'.
+ */
+ char otherCommentCharacter;
+
+ /** Another (optional) 1-comment character. Useful for files that
+ support multiple comment syntaxes. Default is '\0'.
+ */
+ char otherCommentCharacter2;
+
+ /** If true, "-1" parses as the number -1 instead of the
+ symbol "-" followed by the number 1. Default is true.*/
+ bool signedNumbers;
+
+ /** If true, strings can be marked with single quotes (e.g.,
+ 'aaa'). If false, the quote character is parsed as a
+ symbol. Default is true. Backquote (`) is always parsed
+ as a symbol. */
+ bool singleQuotedStrings;
+
+ /** If set to a non-empty string, that string will be used in
+ place of the real file name (or in place of a pseudonym
+ constructed from the buffer if given FROM_STRING) in
+ tokens and exceptions.
+
+ Default is empty.
+ */
+ std::string sourceFileName;
+
+
+ /** Added to the line number reported by peekLineNumber and in
+ exceptions. Useful for concatenating files that are
+ parsed separately. Default is zero. */
+ int startingLineNumberOffset;
+
+ /**
+ Parse -1.#IND00 as the floating point number returned by
+ nan(), -1.#INF00 as -inf(), and 1.#INF00 as inf(). Note
+ that the C99 standard specifies that a variety of formats
+ like "NaN" and "nan" are to be used; these are easier to
+ parse yourself and not currently supported by readNumber.
+
+ An alternative to specifying msvcSpecials is to read numbers as:
+ <pre>
+ Token x = t.read();
+ Token y = t.peek();
+ if ((x.string() == "-1.") &&
+ (y.string() == "#INF00") &&
+ (y.character() == x.character() + 3) &&
+ (y.line() == x.line()) {
+ t.read();
+ return nan();
+ }
+ // ... similar cases for inf
+ </pre>
+
+ If the single-comment character was #, the floating point
+ special format overrides the comment and will be parsed
+ instead.
+
+ If signedNumbers is false msvcSpecials will not be parsed.
+
+ Default is true. */
+ bool msvcSpecials;
+
+ /**
+ Parse the following set of useful proof symbols:
+
+ =>
+ ::>
+ <::
+ :>
+ <:
+ |-
+ ::=
+ :=
+ <-
+
+ Default is false.
+ */
+ bool proofSymbols;
+
+ /**
+ When parsing booleans and msvcSpecials, is case significant?
+ Default is {true}
+ */
+ bool caseSensitive;
+
+ /** All symbols that will become the 'true' boolean token. See also caseSensitive.
+ Clear this value to disable parsing of true booleans.
+
+ Default is {true}.
+ */
+ Set<std::string> trueSymbols;
+
+ /** See trueSymbols. Default is {false}*/
+ Set<std::string> falseSymbols;
+
+ Settings ();
+ };
+
+private:
+
+ std::deque<Token> stack;
+
+ /**
+ Characters to be tokenized.
+ */
+ Array<char> buffer;
+
+ /**
+ Offset of current character (the next character to consumed) in
+ input buffer.
+ */
+ unsigned int currentCharOffset;
+
+ /**
+ Line number of next character to be consumed from the input buffer. (1
+ indicates first line of input.)
+
+ Note that this is the line number of the @e next character to be
+ consumed from the input, not the line number of the @e last character
+ consumed!
+ */
+ unsigned int lineNumber;
+
+ /**
+ Character number (within the line) of the next character to be consumed
+ from the input buffer. (1 indicates first character of the line).
+
+ Note that this is the character number of the @e next character to be
+ consumed from the input, not the character number of the @e last
+ character consumed!
+ */
+ unsigned int charNumber;
+
+ /** Configuration options. This includes the file name that will be
+ reported in tokens and exceptions. */
+ Settings options;
+
+ void init();
+
+ /**
+ Consumes the next character from the input buffer, and returns that
+ character. Updates lineNumber and charNumber to reflect the location of
+ the next character in the input buffer.
+
+ Note: you shouldn't be using the return value of this function in most
+ cases. In general, you should peekInputChar() to get the next
+ character, determine what to do with it, then consume it with this
+ function (or with eatAndPeekInputChar()). Given that usage, in most
+ instances you already know what this function would return!
+ */
+ int eatInputChar();
+
+ /**
+ Returns the next character from the input buffer, without consuming any
+ characters. Can also be used to look deeper into the input buffer.
+ Does not modify lineNumber or charNumber.
+
+ @param distance Index of the character in the input buffer to peek at,
+ relative to the next character. Default is 0, for the next character in
+ the input buffer.
+ */
+ int peekInputChar(unsigned int distance = 0);
+
+ /**
+ Helper function to consume the next character in the input buffer and
+ peek at the one following (without consuming it).
+ */
+ inline int eatAndPeekInputChar() {
+ eatInputChar();
+ return peekInputChar(0);
+ }
+
+ /**
+ Read the next token, returning an END token if no more input is
+ available.
+ */
+ Token nextToken();
+
+ /**
+ Helper for nextToken. Appends characters to t._string until the end
+ delimiter is reached.
+
+ When called, the next character in the input buffer should be first the
+ first character after the opening delimiter character.
+ */
+ void parseQuotedString(unsigned char delimiter, Token& t);
+
+public:
+
+ class TokenException {
+ public:
+ /** Name of file being parsed when exception occurred. */
+ std::string sourceFile;
+
+ /** Line number of start of token which caused the exception. 1 is
+ the first line of the file. Note that you can use
+ TextInput::Settings::startingLineNumberOffset to shift the effective line
+ number that is reported.
+ */
+ int line;
+
+ /** Character number in the line of start of token which caused the
+ exception. 1 is the character in the line.
+ */
+ int character;
+
+ /** Pre-formatted error message */
+ std::string message;
+
+ virtual ~TokenException() {}
+
+ protected:
+
+ TokenException(
+ const std::string& src,
+ int ln,
+ int ch);
+
+ };
+
+ /** While parsing a number of the form 1.#IN?00, ? was
+ not 'D' or 'F'. */
+ class BadMSVCSpecial : public TokenException {
+ public:
+
+ BadMSVCSpecial(
+ const std::string& src,
+ int ln,
+ int ch);
+ };
+
+ /** Thrown by the read methods. */
+ class WrongTokenType : public TokenException {
+ public:
+ Token::Type expected;
+ Token::Type actual;
+
+ WrongTokenType(
+ const std::string& src,
+ int ln,
+ int ch,
+ Token::Type e,
+ Token::Type a);
+ };
+
+ class WrongSymbol : public TokenException {
+ public:
+ std::string expected;
+ std::string actual;
+
+ WrongSymbol(
+ const std::string& src,
+ int ln,
+ int ch,
+ const std::string& e,
+ const std::string& a);
+ };
+
+
+ /** String read from input did not match expected string. */
+ class WrongString : public TokenException {
+ public:
+ std::string expected;
+ std::string actual;
+
+ WrongString(
+ const std::string& src,
+ int ln,
+ int ch,
+ const std::string& e,
+ const std::string& a);
+ };
+
+ TextInput(const std::string& filename, const Settings& settings = Settings());
+
+ enum FS {FROM_STRING};
+ /** Creates input directly from a string. The first argument must be
+ TextInput::FROM_STRING.
+ */
+ TextInput(FS fs, const std::string& str, const Settings& settings = Settings());
+
+ /** Returns true while there are tokens remaining. */
+ bool hasMore();
+
+ /** Read the next token (which will be the END token if ! hasMore()).
+
+ Signed numbers can be handled in one of two modes. If the option
+ TextInput::Settings::signedNumbers is true,
+ A '+' or '-' immediately before a number is prepended onto that number and
+ if there is intervening whitespace, it is read as a separate symbol.
+
+ If TextInput::Settings::signedNumbers is false,
+ read() does not distinguish between a plus or minus symbol next
+ to a number and a positive/negative number itself. For example, "x - 1" and "x -1"
+ will be parsed the same way by read().
+
+ In both cases, readNumber() will contract a leading "-" or "+" onto
+ a number.
+ */
+ Token read();
+
+
+ /** Read one token (or possibly two) as a number or throws
+ WrongTokenType, and returns the number.
+
+ If the first token in the input is a number, it is returned directly.
+
+ If TextInput::Settings::signedNumbers is false and the input stream
+ contains a '+' or '-' symbol token immediately followed by a number
+ token, both tokens will be consumed and a single token will be
+ returned by this method.
+
+ WrongTokenType will be thrown if one of the input conditions
+ described above is not satisfied. When an exception is thrown, no
+ tokens are consumed.
+ */
+ double readNumber();
+
+ bool readBoolean();
+
+ /** Reads a string token or throws WrongTokenType, and returns the token.
+
+ Use this method (rather than readString) if you want the token's
+ location as well as its value.
+
+ WrongTokenType will be thrown if the next token in the input stream
+ is not a string. When an exception is thrown, no tokens are
+ consumed.
+ */
+ Token readStringToken();
+
+ /** Like readStringToken, but returns the token's string.
+
+ Use this method (rather than readStringToken) if you want the token's
+ value but don't really care about its location in the input. Use of
+ readStringToken is encouraged for better error reporting.
+ */
+ std::string readString();
+
+ /** Reads a specific string token or throws either WrongTokenType or
+ WrongString. If the next token in the input is a string matching @p
+ s, it will be consumed.
+
+ Use this method if you want to match a specific string from the
+ input. In that case, typically error reporting related to the token
+ is only going to occur because of a mismatch, so no location
+ information is needed by the caller.
+
+ WrongTokenType will be thrown if the next token in the input stream
+ is not a string. WrongString will be thrown if the next token in the
+ input stream is a string but does not match the @p s parameter. When
+ an exception is thrown, no tokens are consumed.
+ */
+ void readString(const std::string& s);
+
+
+ /** Reads a symbol token or throws WrongTokenType, and returns the token.
+
+ Use this method (rather than readSymbol) if you want the token's
+ location as well as its value.
+
+ WrongTokenType will be thrown if the next token in the input stream
+ is not a symbol. When an exception is thrown, no tokens are
+ consumed.
+ */
+ Token readSymbolToken();
+
+ /** Like readSymbolToken, but returns the token's string.
+
+ Use this method (rather than readSymbolToken) if you want the token's
+ value but don't really care about its location in the input. Use of
+ readSymbolToken is encouraged for better error reporting.
+ */
+ std::string readSymbol();
+
+ /** Reads a specific symbol token or throws either WrongTokenType or
+ WrongSymbol. If the next token in the input is a symbol matching @p
+ symbol, it will be consumed.
+
+ Use this method if you want to match a specific symbol from the
+ input. In that case, typically error reporting related to the token
+ is only going to occur because of a mismatch, so no location
+ information is needed by the caller.
+
+ WrongTokenType will be thrown if the next token in the input stream
+ is not a symbol. WrongSymbol will be thrown if the next token in the
+ input stream is a symbol but does not match the @p symbol parameter.
+ When an exception is thrown, no tokens are consumed.
+ */
+ void readSymbol(const std::string& symbol);
+
+
+ /** Read a series of two specific symbols. See readSymbol. */
+ inline void readSymbols(const std::string& s1, const std::string& s2) {
+ readSymbol(s1);
+ readSymbol(s2);
+ }
+
+ /** Read a series of three specific symbols. See readSymbol. */
+ inline void readSymbols(
+ const std::string& s1,
+ const std::string& s2,
+ const std::string& s3) {
+ readSymbol(s1);
+ readSymbol(s2);
+ readSymbol(s3);
+ }
+
+ /** Read a series of four specific symbols. See readSymbol. */
+ inline void readSymbols(
+ const std::string& s1,
+ const std::string& s2,
+ const std::string& s3,
+ const std::string& s4) {
+ readSymbol(s1);
+ readSymbol(s2);
+ readSymbol(s3);
+ readSymbol(s4);
+ }
+
+ /** Return a copy of the next token in the input stream, but don't remove
+ it from the input stream.
+ */
+ Token peek();
+
+ /** Returns the line number for the @e next token. See also peek. */
+ int peekLineNumber();
+
+ /** Returns the character number (relative to the line) for the @e next
+ token in the input stream. See also peek.
+ */
+ int peekCharacterNumber();
+
+ /** Take a previously read token and push it back at the front of the
+ input stream.
+
+ Can be used in the case where more than one token of read-ahead is
+ needed (i.e., when peek doesn't suffice).
+ */
+ void push(const Token& t);
+
+ /** Returns the filename from which this input is drawn, or the first few
+ characters of the string if created from a string.
+ If options::filename is non-empty that will replace the
+ true filename.*/
+ const std::string& filename() const;
+};
+
+void deserialize(bool& b, TextInput& ti);
+void deserialize(int& b, TextInput& ti);
+void deserialize(uint8& b, TextInput& ti);
+void deserialize(double& b, TextInput& ti);
+void deserialize(float& b, TextInput& ti);
+void deserialize(std::string& b, TextInput& ti);
+
+} // namespace
+
+#endif
+
diff --git a/externals/g3dlite/G3D.lib/include/G3D/TextOutput.h b/externals/g3dlite/G3D.lib/include/G3D/TextOutput.h
new file mode 100644
index 00000000000..6ae7d14fe00
--- /dev/null
+++ b/externals/g3dlite/G3D.lib/include/G3D/TextOutput.h
@@ -0,0 +1,249 @@
+/**
+ @file TextOutput.h
+
+ @maintainer Morgan McGuire, morgan@graphics3d.com
+ @created 2004-06-21
+ @edited 2006-10-24
+
+ Copyright 2000-2007, Morgan McGuire.
+ All rights reserved.
+ */
+
+#ifndef G3D_TEXTOUTPUT_H
+#define G3D_TEXTOUTPUT_H
+
+#include "G3D/platform.h"
+#include "G3D/Array.h"
+#include <string>
+
+namespace G3D {
+
+/**
+ Convenient formatting of ASCII text written to a file.
+ <P>
+
+ The core writeString, writeNumber, and writeSymbol methods map to TextInput's
+ methods. Number and Symbol each print an additional space that is used to
+ separate adjacent tokens.
+
+ TextOutput::printf allows arbitrary text to be conveniently dumped
+ en-masse. Use [de]serialize(bool, TextOutput) and other overloads to read/write
+ primitive types in a standardized manner and
+
+ <P>
+ When a word-wrap line break occurs, all whitespace between words is replaced
+ with a single newline (the newline may be two characters-- see
+ G3D::TextOutput::Options::NewlineStyle). Word wrapping occurs against
+ the number of columns specified by Options::numColumns, <I>minus</I> the current
+ indent level.
+
+ Indenting adds the specified number of spaces immediately after a newline.
+ If a newline was followed by spaces in the original string, these are added
+ to the indent spaces. Indenting <B>will</B> indent blank lines and will leave
+ indents after the last newline of a file (if the indent level is non-zero at the end).
+
+ <P><B>Serialization/Marshalling</B>
+ <DT>Text serialization is accomplished using TextOutput by defining the pair of
+ methods:
+
+ <PRE>
+ void serialize(TextOutput& to) const;
+ void deserialize(TextInput& ti);
+ </PRE>
+
+ See also G3D::TextInput.
+
+ <P>
+ <B>BETA API</B>
+ <DT>This API is subject to change in future versions.
+ */
+class TextOutput {
+public:
+
+ class Settings {
+ public:
+ /**
+ WRAP_NONE Word wrapping is disabled
+ WRAP_WITHOUT_BREAKING Word-wrap, but don't break continuous lines that
+ are longer than numColumns (default)
+ WRAP_ALWAYS Wrap even if it means breaking a continuous line or
+ a quoted string.
+
+ Word wrapping is only allowed at whitespaces ('\n', '\r', '\t', ' '); it
+ will not occur after commas, punctuation, minus signs, or any other characters
+ */
+ enum WordWrapMode {WRAP_NONE, WRAP_WITHOUT_BREAKING, WRAP_ALWAYS};
+
+ /** Defaults to WRAP_WITHOUT_BREAKING */
+ WordWrapMode wordWrap;
+
+ /** Is word-wrapping allowed to insert newlines inside double quotes?
+ Default: false */
+ bool allowWordWrapInsideDoubleQuotes;
+
+ /** Number of columns for word wrapping. Default: 8 */
+ int numColumns;
+
+ /** Number of spaces in each indent. Default: 4 */
+ int spacesPerIndent;
+
+ /** Style of newline used by word wrapping and by (optional) conversion.
+ default: Windows: NEWLINE_WINDOWS, Linux, OS X: NEWLINE_UNIX.
+ */
+ enum NewlineStyle {NEWLINE_WINDOWS, NEWLINE_UNIX};
+
+ NewlineStyle newlineStyle;
+
+ /** If true, all newlines are converted to NewlineStyle regardless of
+ how they start out. Default: true. */
+ bool convertNewlines;
+
+ /** Used by writeBoolean */
+ std::string trueSymbol;
+
+ /** Used by writeBoolean */
+ std::string falseSymbol;
+
+ Settings() :
+ wordWrap(WRAP_WITHOUT_BREAKING),
+ allowWordWrapInsideDoubleQuotes(false),
+ numColumns(80),
+ spacesPerIndent(4),
+ convertNewlines(true),
+ trueSymbol("true"),
+ falseSymbol("false") {
+ #ifdef G3D_WIN32
+ newlineStyle = NEWLINE_WINDOWS;
+ #else
+ newlineStyle = NEWLINE_UNIX;
+ #endif
+ }
+ };
+
+private:
+
+ /** Used by indentAndAppend to tell when we are writing the
+ first character of a new line.
+
+ So that push/popIndent work correctly, we cannot indent
+ immediately after writing a newline. Instead we must
+ indent on writing the first character <B>after</B> that
+ newline.
+ */
+ bool startingNewLine;
+
+ /** Number of characters at the end of the buffer since the last newline */
+ int currentColumn;
+
+ /** True if we have seen an open " and no close ".*/
+ bool inDQuote;
+
+ /** Empty if there is none */
+ std::string filename;
+
+ Array<char> data;
+
+ Settings option;
+
+ /** Number of indents to prepend before each line. Always set using setIndentLevel.*/
+ int indentLevel;
+
+ void setIndentLevel(int i);
+
+ /** Actual number of spaces to indent. */
+ int indentSpaces;
+
+ /** the newline character(s) */
+ std::string newline;
+
+ void setOptions(const Settings& _opt);
+
+ /** Converts to the desired newlines. Called from vprintf */
+ void convertNewlines(const std::string& in, std::string& out);
+
+ /** Called from vprintf */
+ void wordWrapIndentAppend(const std::string& str);
+
+ /** Appends the character to data, indenting whenever a newline is encountered.
+ Called from wordWrapIndentAppend */
+ void indentAppend(char c);
+
+public:
+
+ explicit TextOutput(const std::string& filename, const Settings& options = Settings());
+
+ /** Constructs a text output that can later be commited to a string instead of a file.*/
+ explicit TextOutput(const Settings& options = Settings());
+
+ /** Commit to the filename specified on the constructor.
+ <B>Not</B> called from the destructor; you must call
+ it yourself.
+ @param flush If true (default) the file is ready for reading when the method returns, otherwise
+ the method returns immediately and writes the file in the background.*/
+ void commit(bool flush = true);
+
+ /** Commits to this string */
+ void commitString(std::string& string);
+
+ /** Increase indent level by 1 */
+ void pushIndent();
+
+ void popIndent();
+
+ /** Produces a new string that contains the output */
+ std::string commitString();
+
+ /** Writes a quoted string. Special characters in the string (e.g., \, \t, \n) are escaped so that
+ TextInput will produce the identical string on reading.*/
+ void writeString(const std::string& string);
+
+ void writeBoolean(bool b);
+
+ void writeNumber(double n);
+
+ void writeNumber(int n);
+
+ void writeNewline();
+ void writeNewlines(int numLines);
+
+ /** The symbol is written without quotes. Symbols are required to begin with a
+ letter or underscore and contain only letters, underscores, and numbers
+ or be a C++ symbol (e.g. "{", "(", "++", etc.)
+ so that they may be properly parsed by TextInput::readSymbol. Symbols are
+ printed with a trailing space.*/
+ void writeSymbol(const std::string& string);
+
+ /** Convenient idiom for writing multiple symbols in a row, e.g.
+ writeSymbols("name", "="); The empty symbols are not written.
+ */
+ void 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 = "");
+
+ /** Normal printf conventions. Note that the output will be reformatted
+ for word-wrapping and newlines */
+ void __cdecl printf(const char* fmt, ...)
+ G3D_CHECK_PRINTF_METHOD_ARGS;
+
+ // Can't pass by reference because that confuses va_start
+ void __cdecl printf(const std::string fmt, ...);
+ void __cdecl vprintf(const char* fmt, va_list argPtr)
+ G3D_CHECK_VPRINTF_METHOD_ARGS;
+};
+
+// Primitive serializers
+void serialize(const bool& b, TextOutput& to);
+void serialize(const int& b, TextOutput& to);
+void serialize(const uint8& b, TextOutput& to);
+void serialize(const double& b, TextOutput& to);
+void serialize(const float& b, TextOutput& to);
+void serialize(const std::string& b, TextOutput& to);
+void serialize(const char* b, TextOutput& to);
+
+}
+
+#endif
diff --git a/externals/g3dlite/G3D.lib/include/G3D/ThreadSet.h b/externals/g3dlite/G3D.lib/include/G3D/ThreadSet.h
new file mode 100644
index 00000000000..59c57b062ae
--- /dev/null
+++ b/externals/g3dlite/G3D.lib/include/G3D/ThreadSet.h
@@ -0,0 +1,79 @@
+#ifndef G3D_THREADSET_H
+#define G3D_THREADSET_H
+
+#include "G3D/platform.h"
+#include "G3D/Array.h"
+#include "G3D/ReferenceCount.h"
+#include "G3D/GThread.h"
+
+namespace G3D {
+
+/** Manages a set of threads. All methods are threadsafe except for
+ the iterator begin/end.
+
+ @beta*/
+class ThreadSet : public ReferenceCountedObject {
+public:
+ /** Intended to allow future use with a template parameter.*/
+ typedef GThread Thread;
+
+ typedef ReferenceCountedPointer<Thread> ThreadRef;
+ typedef ReferenceCountedPointer<ThreadSet> Ref;
+ typedef Array<ThreadRef>::Iterator Iterator;
+ typedef Array<ThreadRef>::ConstIterator ConstIterator;
+
+private:
+
+ /** Protects m_thread */
+ GMutex m_lock;
+
+ /** Threads in the set */
+ Array<ThreadRef> m_thread;
+
+public:
+
+ /** Total number of threads (some of which may be completed). */
+ int size() const;
+
+ /** Number of threads that have been started */
+ int numStarted() const;
+
+ /** Start all threads that are not currently started */
+ void start() const;
+
+ /** Terminate all threads that are currently started */
+ void terminate() const;
+
+ /** Waits until all started threads have completed. */
+ void waitForCompletion() const;
+
+ /** Remove all (not stopping them) */
+ void clear();
+
+ /** Removes completed threads and returns the new size.*/
+ int removeCompleted();
+
+ /** Inserts a new thread, if it is not already present, and
+ returns the new number of threads.*/
+ int insert(const ThreadRef& t);
+
+ /** Removes a thread. Returns true if the thread was present and
+ removed. */
+ bool remove(const ThreadRef& t);
+
+ bool contains(const ThreadRef& t) const;
+
+ /** It is an error to mutate the ThreadSet while iterating through it. */
+ Iterator begin();
+
+ Iterator end();
+
+ ConstIterator begin() const;
+
+ ConstIterator end() const;
+};
+
+
+} // namespace G3D
+
+#endif
diff --git a/externals/g3dlite/G3D.lib/include/G3D/Triangle.h b/externals/g3dlite/G3D.lib/include/G3D/Triangle.h
new file mode 100644
index 00000000000..8b67acf4624
--- /dev/null
+++ b/externals/g3dlite/G3D.lib/include/G3D/Triangle.h
@@ -0,0 +1,143 @@
+/**
+ @file Triangle.h
+
+ @maintainer Morgan McGuire, matrix@graphics3d.com
+
+ @created 2003-04-05
+ @edited 2008-10-06
+
+ @cite Random point method by Greg Turk, Generating random points in triangles. In A. S. Glassner, ed., Graphics Gems, pp. 24-28. Academic Press, 1990
+
+ Copyright 2000-2008, Morgan McGuire.
+ All rights reserved.
+ */
+
+#ifndef G3D_TRIANGLE_H
+#define G3D_TRIANGLE_H
+
+#include "G3D/platform.h"
+#include "G3D/g3dmath.h"
+#include "G3D/Vector3.h"
+#include "G3D/Plane.h"
+#include "G3D/BoundsTrait.h"
+#include "G3D/debugAssert.h"
+#include <string>
+
+namespace G3D {
+
+/**
+ A generic triangle representation. This should not be used
+ as the underlying triangle for creating models; it is intended
+ for providing fast property queries but requires a lot of
+ storage and is mostly immutable.
+ */
+class Triangle {
+private:
+ friend class CollisionDetection;
+ friend class Ray;
+
+ Vector3 _vertex[3];
+
+ /** edgeDirection[i] is the normalized vector v[i+1] - v[i] */
+ Vector3 edgeDirection[3];
+ float edgeMagnitude[3];
+ Plane _plane;
+ Vector3::Axis _primaryAxis;
+
+ /** vertex[1] - vertex[0] */
+ Vector3 _edge01;
+
+ /** vertex[2] - vertex[0] */
+ Vector3 _edge02;
+
+ float _area;
+
+ void init(const Vector3& v0, const Vector3& v1, const Vector3& v2);
+
+public:
+
+ Triangle(class BinaryInput& b);
+ void serialize(class BinaryOutput& b);
+ void deserialize(class BinaryInput& b);
+
+ Triangle();
+
+ Triangle(const Vector3& v0, const Vector3& v1, const Vector3& v2);
+
+ ~Triangle();
+
+ /** 0, 1, or 2 */
+ inline const Vector3& vertex(int n) const {
+ debugAssert((n >= 0) && (n < 3));
+ return _vertex[n];
+ }
+
+ /** vertex[1] - vertex[0] */
+ inline const Vector3& edge01() const {
+ return _edge01;
+ }
+
+ /** vertex[2] - vertex[0] */
+ inline const Vector3& edge02() const {
+ return _edge02;
+ }
+
+ float area() const;
+
+ Vector3::Axis primaryAxis() const {
+ return _primaryAxis;
+ }
+
+ const Vector3& normal() const;
+
+ /** Barycenter */
+ Vector3 center() const;
+
+ const Plane& plane() const;
+
+ /** Returns a random point in the triangle. */
+ Vector3 randomPoint() const;
+
+ inline void getRandomSurfacePoint(Vector3& P,
+ Vector3& N = Vector3::dummy) const {
+ P = randomPoint();
+ N = normal();
+ }
+ /**
+ For two triangles to be equal they must have
+ the same vertices <I>in the same order</I>.
+ That is, vertex[0] == vertex[0], etc.
+ */
+ inline bool operator==(const Triangle& other) const {
+ for (int i = 0; i < 3; ++i) {
+ if (_vertex[i] != other._vertex[i]) {
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ inline size_t hashCode() const {
+ return
+ _vertex[0].hashCode() +
+ (_vertex[1].hashCode() >> 2) +
+ (_vertex[2].hashCode() >> 3);
+ }
+
+ void getBounds(class AABox&) const;
+
+};
+
+} // namespace G3D
+
+template <> struct HashTrait<G3D::Triangle> {
+ static size_t hashCode(const G3D::Triangle& key) { return key.hashCode(); }
+};
+
+
+template<> struct BoundsTrait<class G3D::Triangle> {
+ static void getBounds(const G3D::Triangle& t, G3D::AABox& out) { t.getBounds(out); }
+};
+
+#endif
diff --git a/externals/g3dlite/G3D.lib/include/G3D/UprightFrame.h b/externals/g3dlite/G3D.lib/include/G3D/UprightFrame.h
new file mode 100644
index 00000000000..52db3080b80
--- /dev/null
+++ b/externals/g3dlite/G3D.lib/include/G3D/UprightFrame.h
@@ -0,0 +1,83 @@
+/**
+ @file UprightFrame.h
+
+ @author Morgan McGuire, morgan@cs.williams.edu
+ */
+
+#ifndef G3D_UPRIGHTFRAME_H
+#define G3D_UPRIGHTFRAME_H
+
+#include "G3D/platform.h"
+#include "G3D/Spline.h"
+#include "G3D/Vector3.h"
+#include "G3D/CoordinateFrame.h"
+
+namespace G3D {
+
+/**
+ Coordinate frame expressed in Euler angles.
+ Unlike a G3D::Quat, UprightFrame always keeps the reference frame from rolling about its own z axis.
+ Particularly useful for cameras.
+
+ @sa G3D::CoordinateFrame, G3D::Matrix4, G3D::PhysicsFrame, G3D::UprightSpline, G3D::UprightSplineManipulator
+ */
+class UprightFrame {
+public:
+
+ Vector3 translation;
+
+ /** -pi/2 < pitch < pi/2 in radians about the X-axis */
+ float pitch;
+
+ /** In radians about the Y-axis */
+ float yaw;
+
+ inline UprightFrame(const Vector3& t = Vector3::zero(), float p = 0, float y = 0)
+ : translation(t), pitch(p), yaw(y) {}
+
+ UprightFrame(const CoordinateFrame& cframe);
+
+ CoordinateFrame toCoordinateFrame() const;
+
+ /** Supports implicit cast to CoordinateFrame */
+ inline operator CoordinateFrame() const {
+ return toCoordinateFrame();
+ }
+
+ /** Required for use with spline */
+ UprightFrame operator+(const UprightFrame& other) const;
+
+ /** Required for use with spline */
+ UprightFrame operator*(const float k) const;
+
+ /**
+ Unwraps the yaw values in the elements of the array such that
+ they still represent the same angles but strictly increase/decrease
+ without wrapping about zero. For use with Spline<UprightFrame>
+ */
+ static void unwrapYaw(UprightFrame* a, int N);
+
+ void serialize(class BinaryOutput& b) const;
+ void deserialize(class BinaryInput& b);
+};
+
+/** Shortest-path linear velocity spline for camera positions. Always keeps the camera from rolling.
+@sa G3D::UprightSplineManipulator, G3D::UprightFrame
+*/
+class UprightSpline : public Spline<UprightFrame> {
+protected:
+
+ virtual void ensureShortestPath(UprightFrame* A, int N) const {
+ UprightFrame::unwrapYaw(A, N);
+ }
+
+public:
+
+ void serialize(class BinaryOutput& b) const;
+ void deserialize(class BinaryInput& b);
+
+};
+
+}
+
+#endif
diff --git a/externals/g3dlite/G3D.lib/include/G3D/Vector2.h b/externals/g3dlite/G3D.lib/include/G3D/Vector2.h
new file mode 100644
index 00000000000..b610a1a3500
--- /dev/null
+++ b/externals/g3dlite/G3D.lib/include/G3D/Vector2.h
@@ -0,0 +1,457 @@
+/**
+ @file Vector2.h
+
+ 2D vector class
+
+ @maintainer Morgan McGuire, morgan@cs.williams.edu
+
+ @created 2001-06-02
+ @edited 2008-11-30
+
+ Copyright 2000-2008, Morgan McGuire.
+ All rights reserved.
+*/
+
+#ifndef G3D_VECTOR2_H
+#define G3D_VECTOR2_H
+
+#include <string>
+
+#include "G3D/platform.h"
+#include "G3D/g3dmath.h"
+#include "G3D/Table.h"
+#include "G3D/HashTrait.h"
+#include "G3D/Vector2int16.h"
+
+namespace G3D {
+
+class Vector2;
+class Vector3;
+class Vector4;
+
+/**
+ Do not subclass-- this implementation makes assumptions about the
+ memory layout.
+ */
+class Vector2 {
+private:
+ // Hidden operators
+ bool operator<(const Vector2&) const;
+ bool operator>(const Vector2&) const;
+ bool operator<=(const Vector2&) const;
+ bool operator>=(const Vector2&) const;
+
+public:
+ float x;
+ float y;
+
+ /** Creates the zero vector */
+ Vector2();
+ Vector2(class TextInput& t);
+ Vector2(class BinaryInput& b);
+ Vector2(float x, float y);
+ Vector2(float coordinate[2]);
+ Vector2(double coordinate[2]);
+ Vector2(const Vector2& other);
+ Vector2(const class Vector2int16& other);
+
+ void serialize(class BinaryOutput& b) const;
+ void deserialize(class BinaryInput& b);
+
+ void serialize(class TextOutput& t) const;
+ void deserialize(class TextInput& t);
+
+ float& operator[](int i);
+ const float& operator[](int i) const;
+ operator float*();
+ operator const float*() const;
+
+ // assignment and comparison
+ Vector2& operator=(const Vector2& other);
+ bool operator==(const Vector2& other) const;
+ bool operator!=(const Vector2& other) const;
+ size_t hashCode() const;
+ bool fuzzyEq(const Vector2& other) const;
+ bool fuzzyNe(const Vector2& other) const;
+
+ /** Returns true if this vector has finite length */
+ bool isFinite() const;
+
+ /** Returns true if this vector has length == 0 */
+ bool isZero() const;
+
+ /** Returns true if this vector has length == 1 */
+ bool isUnit() const;
+
+ // arithmetic operations
+ Vector2 operator+(const Vector2& v) const;
+ Vector2 operator-(const Vector2& v) const;
+ Vector2 operator*(float s) const;
+
+ /** Array (pointwise) multiplication */
+ Vector2 operator*(const Vector2& v) const;
+
+ /** Array division */
+ Vector2 operator/(const Vector2& v) const;
+ Vector2 operator/(float s) const;
+
+ /** Unary minus */
+ Vector2 operator-() const;
+
+ /** x + y */
+ inline float sum() const {
+ return x + y;
+ }
+
+ /**
+ Linear interpolation
+ */
+ inline Vector2 lerp(const Vector2& v, float alpha) const {
+ return (*this) + (v - *this) * alpha;
+ }
+
+ inline Vector2 clamp(const Vector2& low, const Vector2& high) const {
+ return Vector2(
+ G3D::clamp(x, low.x, high.x),
+ G3D::clamp(y, low.y, high.y));
+ }
+
+ inline Vector2 clamp(float low, float high) const {
+ return Vector2(
+ (float)G3D::clamp(x, low, high),
+ (float)G3D::clamp(y, low, high));
+ }
+
+ // arithmetic updates
+ Vector2& operator+=(const Vector2&);
+ Vector2& operator-=(const Vector2&);
+ Vector2& operator*=(float);
+ Vector2& operator/=(float);
+ Vector2& operator*=(const Vector2&);
+ Vector2& operator/=(const Vector2&);
+
+ // vector operations
+
+ /** */
+ float length() const;
+
+ /** Returns a unit-length vector */
+ Vector2 direction() const;
+
+ /**
+ Potentially less accurate but faster than direction().
+ Only works if System::hasSSE is true.
+ */
+ Vector2 fastDirection() const {
+ return direction();
+ }
+
+ float squaredLength() const;
+ float dot(const Vector2& s) const;
+
+ /**
+ Make this vector have unit length and return the old length.
+ If the vector length was less than tolerance, do not normalize.
+ */
+ float unitize(float fTolerance = 1e-06);
+
+ Vector2 min(const Vector2& v) const;
+ Vector2 max(const Vector2& v) const;
+
+ /** Uniformly distributed random vector on the unit sphere */
+ static Vector2 random();
+
+ // Special values.
+ // Intentionally not inlined: see Matrix3::identity() for details.
+ static const Vector2& zero();
+ inline static const Vector2& one() { static const Vector2 v(1, 1); return v; }
+ static const Vector2& unitX();
+ static const Vector2& unitY();
+ static const Vector2& inf();
+ static const Vector2& nan();
+ /** smallest (most negative) representable vector */
+ static const Vector2& minFinite();
+ /** Largest representable vector */
+ static const Vector2& maxFinite();
+
+ std::string toString() const;
+
+ // 2-char swizzles
+
+ Vector2 xx() const;
+ Vector2 yx() const;
+ Vector2 xy() const;
+ Vector2 yy() const;
+
+ // 3-char swizzles
+
+ Vector3 xxx() const;
+ Vector3 yxx() const;
+ Vector3 xyx() const;
+ Vector3 yyx() const;
+ Vector3 xxy() const;
+ Vector3 yxy() const;
+ Vector3 xyy() const;
+ Vector3 yyy() const;
+
+ // 4-char swizzles
+
+ Vector4 xxxx() const;
+ Vector4 yxxx() const;
+ Vector4 xyxx() const;
+ Vector4 yyxx() const;
+ Vector4 xxyx() const;
+ Vector4 yxyx() const;
+ Vector4 xyyx() const;
+ Vector4 yyyx() const;
+ Vector4 xxxy() const;
+ Vector4 yxxy() const;
+ Vector4 xyxy() const;
+ Vector4 yyxy() const;
+ Vector4 xxyy() const;
+ Vector4 yxyy() const;
+ Vector4 xyyy() const;
+ Vector4 yyyy() const;
+
+};
+
+inline Vector2 operator*(double s, const Vector2& v) {
+ return v * (float)s;
+}
+
+inline Vector2 operator*(float s, const Vector2& v) {
+ return v * s;
+}
+
+inline Vector2 operator*(int s, const Vector2& v) {
+ return v * (float)s;
+}
+
+
+inline Vector2::Vector2 () : x(0.0f), y(0.0f) {
+}
+
+
+inline Vector2::Vector2(float _x, float _y) : x(_x), y(_y) {
+}
+
+
+inline Vector2::Vector2 (float afCoordinate[2]) {
+ x = afCoordinate[0];
+ y = afCoordinate[1];
+}
+
+
+
+inline Vector2::Vector2 (double afCoordinate[2]) {
+ x = (float)afCoordinate[0];
+ y = (float)afCoordinate[1];
+}
+
+
+inline Vector2::Vector2 (const Vector2& rkVector) {
+ x = rkVector.x;
+ y = rkVector.y;
+}
+
+
+inline Vector2::Vector2 (const Vector2int16& v) : x(v.x), y(v.y) {
+}
+
+
+inline float& Vector2::operator[] (int i) {
+ return ((float*)this)[i];
+}
+
+
+inline const float& Vector2::operator[] (int i) const {
+ return ((float*)this)[i];
+}
+
+
+inline Vector2::operator float* () {
+ return (float*)this;
+}
+
+inline Vector2::operator const float* () const {
+ return (float*)this;
+}
+
+
+inline Vector2& Vector2::operator= (const Vector2& rkVector) {
+ x = rkVector.x;
+ y = rkVector.y;
+ return *this;
+}
+
+
+inline bool Vector2::operator== (const Vector2& rkVector) const {
+ return ( x == rkVector.x && y == rkVector.y);
+}
+
+
+inline bool Vector2::operator!= (const Vector2& rkVector) const {
+ return ( x != rkVector.x || y != rkVector.y);
+}
+
+
+inline Vector2 Vector2::operator+ (const Vector2& rkVector) const {
+ return Vector2(x + rkVector.x, y + rkVector.y);
+}
+
+
+inline Vector2 Vector2::operator- (const Vector2& rkVector) const {
+ return Vector2(x - rkVector.x, y - rkVector.y);
+}
+
+
+inline Vector2 Vector2::operator* (float fScalar) const {
+ return Vector2(fScalar*x, fScalar*y);
+}
+
+
+
+inline Vector2 Vector2::operator- () const {
+ return Vector2( -x, -y);
+}
+
+
+
+inline Vector2& Vector2::operator+= (const Vector2& rkVector) {
+ x += rkVector.x;
+ y += rkVector.y;
+ return *this;
+}
+
+
+
+inline Vector2& Vector2::operator-= (const Vector2& rkVector) {
+ x -= rkVector.x;
+ y -= rkVector.y;
+ return *this;
+}
+
+
+
+inline Vector2& Vector2::operator*= (float fScalar) {
+ x *= fScalar;
+ y *= fScalar;
+ return *this;
+}
+
+
+
+
+inline Vector2& Vector2::operator*= (const Vector2& rkVector) {
+ x *= rkVector.x;
+ y *= rkVector.y;
+ return *this;
+}
+
+
+
+inline Vector2& Vector2::operator/= (const Vector2& rkVector) {
+ x /= rkVector.x;
+ y /= rkVector.y;
+ return *this;
+}
+
+
+inline Vector2 Vector2::operator* (const Vector2& rkVector) const {
+ return Vector2(x * rkVector.x, y * rkVector.y);
+}
+
+
+
+inline Vector2 Vector2::operator/ (const Vector2& rkVector) const {
+ return Vector2(x / rkVector.x, y / rkVector.y);
+}
+
+
+inline float Vector2::squaredLength () const {
+ return x*x + y*y;
+}
+
+
+inline float Vector2::length () const {
+ return sqrtf(x*x + y*y);
+}
+
+
+inline Vector2 Vector2::direction () const {
+ float lenSquared = x * x + y * y;
+
+ if (lenSquared != 1.0f) {
+ return *this / sqrtf(lenSquared);
+ } else {
+ return *this;
+ }
+}
+
+
+
+inline float Vector2::dot (const Vector2& rkVector) const {
+ return x*rkVector.x + y*rkVector.y;
+}
+
+
+
+inline Vector2 Vector2::min(const Vector2 &v) const {
+ return Vector2(G3D::min(v.x, x), G3D::min(v.y, y));
+}
+
+
+
+inline Vector2 Vector2::max(const Vector2 &v) const {
+ return Vector2(G3D::max(v.x, x), G3D::max(v.y, y));
+}
+
+
+
+inline bool Vector2::fuzzyEq(const Vector2& other) const {
+ return G3D::fuzzyEq((*this - other).squaredLength(), 0);
+}
+
+
+
+inline bool Vector2::fuzzyNe(const Vector2& other) const {
+ return G3D::fuzzyNe((*this - other).squaredLength(), 0);
+}
+
+
+
+inline bool Vector2::isFinite() const {
+ return G3D::isFinite(x) && G3D::isFinite(y);
+}
+
+
+
+inline bool Vector2::isZero() const {
+ return (x == 0.0f) && (y == 0.0f);
+}
+
+
+
+inline bool Vector2::isUnit() const {
+ return squaredLength() == 1.0f;
+}
+
+} // namespace G3D
+
+template <>
+struct HashTrait<G3D::Vector2> {
+ static size_t hashCode(const G3D::Vector2& key) {
+ return key.hashCode();
+ }
+};
+
+
+// Intentionally outside namespace to avoid operator overloading confusion
+inline G3D::Vector2 operator*(double s, const G3D::Vector2& v) {
+ return v * (float)s;
+}
+inline G3D::Vector2 operator*(int s, const G3D::Vector2& v) {
+ return v * (float)s;
+}
+
+#endif
diff --git a/externals/g3dlite/G3D.lib/include/G3D/Vector2int16.h b/externals/g3dlite/G3D.lib/include/G3D/Vector2int16.h
new file mode 100644
index 00000000000..b7149ad6c90
--- /dev/null
+++ b/externals/g3dlite/G3D.lib/include/G3D/Vector2int16.h
@@ -0,0 +1,137 @@
+/**
+ @file Vector2int16.h
+
+ @maintainer Morgan McGuire, matrix@brown.edu
+
+ @created 2003-08-09
+ @edited 2004-01-03
+
+ Copyright 2000-2006, Morgan McGuire.
+ All rights reserved.
+ */
+
+#ifndef VECTOR2INT16_H
+#define VECTOR2INT16_H
+
+#include "G3D/platform.h"
+#include "G3D/g3dmath.h"
+#include "G3D/HashTrait.h"
+
+namespace G3D {
+
+/**
+ A Vector2 that packs its fields into uint16s.
+ */
+#ifdef G3D_WIN32
+ // Switch to tight alignment
+ #pragma pack(push, 2)
+#endif
+
+class Vector2int16 {
+private:
+ // Hidden operators
+ bool operator<(const Vector2int16&) const;
+ bool operator>(const Vector2int16&) const;
+ bool operator<=(const Vector2int16&) const;
+ bool operator>=(const Vector2int16&) const;
+
+public:
+ G3D::int16 x;
+ G3D::int16 y;
+
+ Vector2int16() : x(0), y(0) {}
+ Vector2int16(G3D::int16 _x, G3D::int16 _y) : x(_x), y(_y){}
+ Vector2int16(const class Vector2& v);
+ Vector2int16(class BinaryInput& bi);
+
+ inline G3D::int16& operator[] (int i) {
+ debugAssert(((unsigned int)i) <= 1);
+ return ((G3D::int16*)this)[i];
+ }
+
+ inline const G3D::int16& operator[] (int i) const {
+ debugAssert(((unsigned int)i) <= 1);
+ return ((G3D::int16*)this)[i];
+ }
+
+ inline Vector2int16 operator+(const Vector2int16& other) const {
+ return Vector2int16(x + other.x, y + other.y);
+ }
+
+ inline Vector2int16 operator-(const Vector2int16& other) const {
+ return Vector2int16(x - other.x, y - other.y);
+ }
+
+ inline Vector2int16 operator*(const Vector2int16& other) const {
+ return Vector2int16(x * other.x, y * other.y);
+ }
+
+ inline Vector2int16 operator*(const int s) const {
+ return Vector2int16(x * s, y * s);
+ }
+
+ inline Vector2int16& operator+=(const Vector2int16& other) {
+ x += other.x;
+ y += other.y;
+ return *this;
+ }
+
+ /** Shifts both x and y */
+ inline Vector2int16 operator>>(const int s) const {
+ return Vector2int16(x >> s, y >> s);
+ }
+
+ /** Shifts both x and y */
+ inline Vector2int16 operator<<(const int s) const {
+ return Vector2int16(x << s, y << s);
+ }
+
+ inline Vector2int16& operator-=(const Vector2int16& other) {
+ x -= other.x;
+ y -= other.y;
+ return *this;
+ }
+
+ inline Vector2int16& operator*=(const Vector2int16& other) {
+ x *= other.x;
+ y *= other.y;
+ return *this;
+ }
+
+ Vector2int16 clamp(const Vector2int16& lo, const Vector2int16& hi);
+
+ inline bool operator== (const Vector2int16& rkVector) const {
+ return ((int32*)this)[0] == ((int32*)&rkVector)[0];
+ }
+
+ inline bool operator!= (const Vector2int16& rkVector) const {
+ return ((int32*)this)[0] != ((int32*)&rkVector)[0];
+ }
+
+ Vector2int16 max(const Vector2int16& v) const {
+ return Vector2int16(iMax(x, v.x), iMax(y, v.y));
+ }
+
+ Vector2int16 min(const Vector2int16& v) const {
+ return Vector2int16(iMin(x, v.x), iMin(y, v.y));
+ }
+
+ void serialize(class BinaryOutput& bo) const;
+ void deserialize(class BinaryInput& bi);
+}
+#if defined(G3D_LINUX) || defined(G3D_OSX)
+ __attribute((aligned(1)))
+#endif
+;
+
+#ifdef G3D_WIN32
+ #pragma pack(pop)
+#endif
+
+}
+
+template<> struct HashTrait<G3D::Vector2int16> {
+ static size_t hashCode(const G3D::Vector2int16& key) { return static_cast<size_t>(key.x + ((int)key.y << 16)); }
+};
+
+#endif
diff --git a/externals/g3dlite/G3D.lib/include/G3D/Vector3.h b/externals/g3dlite/G3D.lib/include/G3D/Vector3.h
new file mode 100644
index 00000000000..d37638a229d
--- /dev/null
+++ b/externals/g3dlite/G3D.lib/include/G3D/Vector3.h
@@ -0,0 +1,761 @@
+/**
+ @file Vector3.h
+
+ 3D vector class
+
+ @maintainer Morgan McGuire, morgan@cs.williams.edu
+
+ @created 2001-06-02
+ @edited 2008-11-01
+ Copyright 2000-2008, Morgan McGuire.
+ All rights reserved.
+ */
+
+#ifndef G3D_VECTOR3_H
+#define G3D_VECTOR3_H
+
+#include "G3D/platform.h"
+#include "G3D/g3dmath.h"
+#include "G3D/Vector2.h"
+#include "G3D/Table.h"
+#include "G3D/HashTrait.h"
+#include "G3D/PositionTrait.h"
+#include "G3D/Vector2.h"
+#include <iostream>
+#include <string>
+
+//----------------------------------------------------------------------------
+#ifdef SSE
+ // If you receive an error on this line, it is because you do not have the file
+ // xmmintrin.h needed for MMX & SSE extensions. Download and install
+ //
+ // http://download.microsoft.com/download/vstudio60ent/SP5/Wideband-Full/WIN98Me/EN-US/vs6sp5.exe
+ // and
+ // http://download.microsoft.com/download/vb60ent/Update/6/W9X2KXP/EN-US/vcpp5.exe
+ //
+ // to get this file.
+# include <xmmintrin.h>
+#endif
+
+namespace G3D {
+
+class Vector2;
+class Vector3;
+class Vector4;
+class Vector4int8;
+
+/**
+ <B>Swizzles</B>
+ Vector classes have swizzle operators, e.g. <CODE>v.xy()</CODE>, that
+ allow selection of arbitrary sub-fields. These cannot be used as write
+ masks. Examples
+
+ <PRE>
+Vector3 v(1, 2, 3);
+Vector3 j;
+Vector2 b;
+
+b = v.xz();
+j = b.xx();
+</PRE>
+
+
+ <B>Warning</B>
+
+ Do not subclass-- this implementation makes assumptions about the
+ memory layout.
+ */
+class Vector3 {
+private:
+ /**
+ Reflect this vector about the (not necessarily unit) normal.
+ Note that if used for a collision or ray reflection you
+ must negate the resulting vector to get a direction pointing
+ <I>away</I> from the collision.
+
+ <PRE>
+ V' N V
+
+ r ^ -,
+ \ | /
+ \|/
+ </PRE>
+
+ See also Vector3::reflectionDirection
+ */
+ Vector3 reflectAbout(const Vector3& normal) const;
+
+ // Hidden operators
+ bool operator<(const Vector3&) const;
+ bool operator>(const Vector3&) const;
+ bool operator<=(const Vector3&) const;
+ bool operator>=(const Vector3&) const;
+
+public:
+ // construction
+ Vector3();
+
+ /** Divides by 127 */
+ Vector3(const Vector4int8&);
+ explicit Vector3(class BinaryInput& b);
+ Vector3(float _x, float _y, float _z);
+ explicit Vector3(const class Vector2& v, float _z);
+ explicit Vector3(float coordinate[3]);
+ explicit Vector3(double coordinate[3]);
+ Vector3(const Vector3& rkVector);
+ Vector3(const class Vector3int16& v);
+ explicit Vector3(class TextInput& t);
+
+ /** Format is three float32's */
+ void serialize(class BinaryOutput& b) const;
+ void deserialize(class BinaryInput& b);
+
+ /** Format is "(%f, %f, %f)" */
+ void serialize(class TextOutput& t) const;
+ void deserialize(class TextInput& t);
+
+ // coordinates
+ float x, y, z;
+
+ // access vector V as V[0] = V.x, V[1] = V.y, V[2] = V.z
+ //
+ // WARNING. These member functions rely on
+ // (1) Vector3 not having virtual functions
+ // (2) the data packed in a 3*sizeof(float) memory block
+ const float& operator[] (int i) const;
+ float& operator[] (int i);
+
+ inline operator float* () {
+ return (float*)this;
+ }
+
+ operator const float* () const {
+ return (float*)this;
+ }
+
+ enum Axis {X_AXIS=0, Y_AXIS=1, Z_AXIS=2, DETECT_AXIS=-1};
+
+ /**
+ Returns the largest dimension. Particularly convenient for determining
+ which plane to project a triangle onto for point-in-polygon tests.
+ */
+ Axis primaryAxis() const;
+
+ // assignment and comparison
+ Vector3& operator= (const Vector3& rkVector);
+ bool operator== (const Vector3& rkVector) const;
+ bool operator!= (const Vector3& rkVector) const;
+ size_t hashCode() const;
+ bool fuzzyEq(const Vector3& other) const;
+ bool fuzzyNe(const Vector3& other) const;
+
+ /** Returns true if this vector has finite length. */
+ bool isFinite() const;
+
+ /** Returns true if this vector has length ~= 0 */
+ bool isZero() const;
+
+ /** Returns true if this vector has length ~= 1 */
+ bool isUnit() const;
+
+ // arithmetic operations
+ Vector3 operator+ (const Vector3& v) const;
+ Vector3 operator- (const Vector3& v) const;
+ Vector3 operator* (float s) const;
+ Vector3 operator/ (float s) const;
+ Vector3 operator* (const Vector3& v) const;
+ Vector3 operator/ (const Vector3& v) const;
+ Vector3 operator- () const;
+
+ // arithmetic updates
+ Vector3& operator+= (const Vector3& v);
+ Vector3& operator-= (const Vector3& v);
+ Vector3& operator*= (float s);
+ Vector3& operator/= (float s);
+ Vector3& operator*= (const Vector3& v);
+ Vector3& operator/= (const Vector3& v);
+
+ /** Same as magnitude */
+ float length() const;
+
+ float magnitude() const;
+
+ /**
+ The result is a nan vector if the length is almost zero.
+ */
+ Vector3 direction() const;
+
+ /**
+ Potentially less accurate but faster than direction().
+ Only works if System::hasSSE is true.
+ */
+ Vector3 fastDirection() const;
+
+
+ /**
+ See also G3D::Ray::reflect.
+ The length is 1.
+ <PRE>
+ V' N V
+
+ r ^ /
+ \ | /
+ \|'-
+ </PRE>
+ */
+ Vector3 reflectionDirection(const Vector3& normal) const;
+
+
+ /**
+ Returns Vector3::zero() if the length is nearly zero, otherwise
+ returns a unit vector.
+ */
+ inline Vector3 directionOrZero() const {
+ float mag = magnitude();
+ if (G3D::fuzzyEq(mag, 0.0f)) {
+ return Vector3::zero();
+ } else if (G3D::fuzzyEq(mag, 1.0f)) {
+ return *this;
+ } else {
+ return *this * (1.0f / mag);
+ }
+ }
+
+ /**
+ Returns the direction of a refracted ray,
+ where iExit is the index of refraction for the
+ previous material and iEnter is the index of refraction
+ for the new material. Like Vector3::reflectionDirection,
+ the result has length 1 and is
+ pointed <I>away</I> from the intersection.
+
+ Returns Vector3::zero() in the case of total internal refraction.
+
+ @param iOutside The index of refraction (eta) outside
+ (on the <I>positive</I> normal side) of the surface.
+
+ @param iInside The index of refraction (eta) inside
+ (on the <I>negative</I> normal side) of the surface.
+
+ See also G3D::Ray::refract.
+ <PRE>
+ N V
+
+ ^ /
+ | /
+ |'-
+ __--
+ V'<--
+ </PRE>
+ */
+ Vector3 refractionDirection(
+ const Vector3& normal,
+ float iInside,
+ float iOutside) const;
+
+ /** Synonym for direction */
+ inline Vector3 unit() const {
+ return direction();
+ }
+
+ /** Returns a normalized vector. May be computed with lower
+ precision than unit */
+ inline Vector3 fastUnit() const {
+ return fastDirection();
+ }
+
+ /** Same as squaredMagnitude */
+ float squaredLength() const;
+
+ float squaredMagnitude () const;
+
+ float dot(const Vector3& rkVector) const;
+
+ float unitize(float tolerance = 1e-06);
+
+ /** Cross product. Note that two cross products in a row
+ can be computed more cheaply: v1 x (v2 x v3) = (v1 dot v3) v2 - (v1 dot v2) v3.
+ */
+ Vector3 cross(const Vector3& rkVector) const;
+ Vector3 unitCross (const Vector3& rkVector) const;
+
+ /**
+ Returns a matrix such that v.cross() * w = v.cross(w).
+ <PRE>
+ [ 0 -v.z v.y ]
+ [ v.z 0 -v.x ]
+ [ -v.y v.x 0 ]
+ </PRE>
+ */
+ class Matrix3 cross() const;
+
+ Vector3 min(const Vector3 &v) const;
+ Vector3 max(const Vector3 &v) const;
+
+ /** Smallest element */
+ inline float min() const {
+ return G3D::min(G3D::min(x, y), z);
+ }
+
+ /** Largest element */
+ inline float max() const {
+ return G3D::max(G3D::max(x, y), z);
+ }
+
+ std::string toString() const;
+
+ inline Vector3 clamp(const Vector3& low, const Vector3& high) const {
+ return Vector3(
+ G3D::clamp(x, low.x, high.x),
+ G3D::clamp(y, low.y, high.y),
+ G3D::clamp(z, low.z, high.z));
+ }
+
+ inline Vector3 clamp(float low, float high) const {
+ return Vector3(
+ G3D::clamp(x, low, high),
+ G3D::clamp(y, low, high),
+ G3D::clamp(z, low, high));
+ }
+
+ /**
+ Linear interpolation
+ */
+ inline Vector3 lerp(const Vector3& v, float alpha) const {
+ return (*this) + (v - *this) * alpha;
+ }
+
+ /** Gram-Schmidt orthonormalization. */
+ static void orthonormalize (Vector3 akVector[3]);
+
+ /** Random unit vector, uniformly distributed */
+ static Vector3 random();
+
+ /** Random unit vector, distributed
+ so that the probability of V is proportional
+ to max(V dot Normal, 0).
+
+ @cite Henrik Wann Jensen, Realistic Image Synthesis using Photon Mapping eqn 2.24
+ */
+ static Vector3 cosRandom(const Vector3& normal);
+
+
+ /**
+ Random vector distributed over the hemisphere about normal.
+ */
+ static Vector3 hemiRandom(const Vector3& normal);
+
+ // Input W must be initialize to a nonzero vector, output is {U,V,W}
+ // an orthonormal basis. A hint is provided about whether or not W
+ // is already unit length.
+ static void generateOrthonormalBasis (Vector3& rkU, Vector3& rkV,
+ Vector3& rkW, bool bUnitLengthW = true);
+
+ inline float sum() const {
+ return x + y + z;
+ }
+
+ inline float average() const {
+ return sum() / 3.0f;
+ }
+
+ // Special values.
+ inline static const Vector3& zero() { static Vector3 v(0, 0, 0); return v; }
+ inline static const Vector3& one() { static Vector3 v(1, 1, 1); return v; }
+ inline static const Vector3& unitX() { static Vector3 v(1, 0, 0); return v; }
+ inline static const Vector3& unitY() { static Vector3 v(0, 1, 0); return v; }
+ inline static const Vector3& unitZ() { static Vector3 v(0, 0, 1); return v; }
+ inline static const Vector3& inf() { static Vector3 v((float)G3D::inf(), (float)G3D::inf(), (float)G3D::inf()); return v; }
+ inline static const Vector3& nan() { static Vector3 v((float)G3D::nan(), (float)G3D::nan(), (float)G3D::nan()); return v; }
+ /** Smallest (most negative) representable vector */
+ inline static const Vector3& minFinite(){ static Vector3 v(-FLT_MAX, -FLT_MAX, -FLT_MAX); return v; }
+ /** Largest representable vector */
+ inline static const Vector3& maxFinite(){ static Vector3 v(FLT_MAX, FLT_MAX, FLT_MAX); return v; }
+
+ // 2-char swizzles
+
+ Vector2 xx() const;
+ Vector2 yx() const;
+ Vector2 zx() const;
+ Vector2 xy() const;
+ Vector2 yy() const;
+ Vector2 zy() const;
+ Vector2 xz() const;
+ Vector2 yz() const;
+ Vector2 zz() const;
+
+ // 3-char swizzles
+
+ Vector3 xxx() const;
+ Vector3 yxx() const;
+ Vector3 zxx() const;
+ Vector3 xyx() const;
+ Vector3 yyx() const;
+ Vector3 zyx() const;
+ Vector3 xzx() const;
+ Vector3 yzx() const;
+ Vector3 zzx() const;
+ Vector3 xxy() const;
+ Vector3 yxy() const;
+ Vector3 zxy() const;
+ Vector3 xyy() const;
+ Vector3 yyy() const;
+ Vector3 zyy() const;
+ Vector3 xzy() const;
+ Vector3 yzy() const;
+ Vector3 zzy() const;
+ Vector3 xxz() const;
+ Vector3 yxz() const;
+ Vector3 zxz() const;
+ Vector3 xyz() const;
+ Vector3 yyz() const;
+ Vector3 zyz() const;
+ Vector3 xzz() const;
+ Vector3 yzz() const;
+ Vector3 zzz() const;
+
+ // 4-char swizzles
+
+ Vector4 xxxx() const;
+ Vector4 yxxx() const;
+ Vector4 zxxx() const;
+ Vector4 xyxx() const;
+ Vector4 yyxx() const;
+ Vector4 zyxx() const;
+ Vector4 xzxx() const;
+ Vector4 yzxx() const;
+ Vector4 zzxx() const;
+ Vector4 xxyx() const;
+ Vector4 yxyx() const;
+ Vector4 zxyx() const;
+ Vector4 xyyx() const;
+ Vector4 yyyx() const;
+ Vector4 zyyx() const;
+ Vector4 xzyx() const;
+ Vector4 yzyx() const;
+ Vector4 zzyx() const;
+ Vector4 xxzx() const;
+ Vector4 yxzx() const;
+ Vector4 zxzx() const;
+ Vector4 xyzx() const;
+ Vector4 yyzx() const;
+ Vector4 zyzx() const;
+ Vector4 xzzx() const;
+ Vector4 yzzx() const;
+ Vector4 zzzx() const;
+ Vector4 xxxy() const;
+ Vector4 yxxy() const;
+ Vector4 zxxy() const;
+ Vector4 xyxy() const;
+ Vector4 yyxy() const;
+ Vector4 zyxy() const;
+ Vector4 xzxy() const;
+ Vector4 yzxy() const;
+ Vector4 zzxy() const;
+ Vector4 xxyy() const;
+ Vector4 yxyy() const;
+ Vector4 zxyy() const;
+ Vector4 xyyy() const;
+ Vector4 yyyy() const;
+ Vector4 zyyy() const;
+ Vector4 xzyy() const;
+ Vector4 yzyy() const;
+ Vector4 zzyy() const;
+ Vector4 xxzy() const;
+ Vector4 yxzy() const;
+ Vector4 zxzy() const;
+ Vector4 xyzy() const;
+ Vector4 yyzy() const;
+ Vector4 zyzy() const;
+ Vector4 xzzy() const;
+ Vector4 yzzy() const;
+ Vector4 zzzy() const;
+ Vector4 xxxz() const;
+ Vector4 yxxz() const;
+ Vector4 zxxz() const;
+ Vector4 xyxz() const;
+ Vector4 yyxz() const;
+ Vector4 zyxz() const;
+ Vector4 xzxz() const;
+ Vector4 yzxz() const;
+ Vector4 zzxz() const;
+ Vector4 xxyz() const;
+ Vector4 yxyz() const;
+ Vector4 zxyz() const;
+ Vector4 xyyz() const;
+ Vector4 yyyz() const;
+ Vector4 zyyz() const;
+ Vector4 xzyz() const;
+ Vector4 yzyz() const;
+ Vector4 zzyz() const;
+ Vector4 xxzz() const;
+ Vector4 yxzz() const;
+ Vector4 zxzz() const;
+ Vector4 xyzz() const;
+ Vector4 yyzz() const;
+ Vector4 zyzz() const;
+ Vector4 xzzz() const;
+ Vector4 yzzz() const;
+ Vector4 zzzz() const;
+
+ /** A value that can be passed to ignore a parameter. Never look at the result of dummy. */
+ static Vector3 dummy;
+};
+
+inline G3D::Vector3 operator*(float s, const G3D::Vector3& v) {
+ return v * s;
+}
+
+inline G3D::Vector3 operator*(double s, const G3D::Vector3& v) {
+ return v * (float)s;
+}
+
+inline G3D::Vector3 operator*(int s, const G3D::Vector3& v) {
+ return v * (float)s;
+}
+
+std::ostream& operator<<(std::ostream& os, const Vector3&);
+
+
+void serialize(const Vector3::Axis& a, class BinaryOutput& bo);
+void deserialize(Vector3::Axis& a, class BinaryInput& bo);
+
+
+//----------------------------------------------------------------------------
+inline Vector3::Vector3() : x(0.0f), y(0.0f), z(0.0f) {
+}
+
+//----------------------------------------------------------------------------
+
+inline Vector3::Vector3 (float fX, float fY, float fZ) : x(fX), y(fY), z(fZ) {
+}
+
+//----------------------------------------------------------------------------
+inline Vector3::Vector3 (float V[3]) : x(V[0]), y(V[1]), z(V[2]){
+}
+//----------------------------------------------------------------------------
+inline Vector3::Vector3 (double V[3]) : x((float)V[0]), y((float)V[1]), z((float)V[2]){
+}
+
+//----------------------------------------------------------------------------
+inline Vector3::Vector3 (const Vector3& V) : x(V.x), y(V.y), z(V.z) {
+}
+
+//----------------------------------------------------------------------------
+
+//inline Vector3::Vector3 (const __m128& m) {
+ // Cast from SSE packed floats
+// *this = *(Vector3*)&m;
+//}
+
+//----------------------------------------------------------------------------
+inline const float& Vector3::operator[] (int i) const {
+ return ((float*)this)[i];
+}
+
+inline float& Vector3::operator[] (int i) {
+ return ((float*)this)[i];
+}
+
+
+//----------------------------------------------------------------------------
+inline Vector3& Vector3::operator= (const Vector3& rkVector) {
+ x = rkVector.x;
+ y = rkVector.y;
+ z = rkVector.z;
+ return *this;
+}
+
+//----------------------------------------------------------------------------
+
+inline bool Vector3::fuzzyEq(const Vector3& other) const {
+ return G3D::fuzzyEq((*this - other).squaredMagnitude(), 0);
+}
+
+//----------------------------------------------------------------------------
+
+inline bool Vector3::fuzzyNe(const Vector3& other) const {
+ return G3D::fuzzyNe((*this - other).squaredMagnitude(), 0);
+}
+
+//----------------------------------------------------------------------------
+
+inline bool Vector3::isFinite() const {
+ return G3D::isFinite(x) && G3D::isFinite(y) && G3D::isFinite(z);
+}
+
+//----------------------------------------------------------------------------
+inline bool Vector3::operator== (const Vector3& rkVector) const {
+ return ( x == rkVector.x && y == rkVector.y && z == rkVector.z );
+}
+
+//----------------------------------------------------------------------------
+inline bool Vector3::operator!= (const Vector3& rkVector) const {
+ return ( x != rkVector.x || y != rkVector.y || z != rkVector.z );
+}
+
+//----------------------------------------------------------------------------
+inline Vector3 Vector3::operator+ (const Vector3& rkVector) const {
+ return Vector3(x + rkVector.x, y + rkVector.y, z + rkVector.z);
+}
+
+//----------------------------------------------------------------------------
+inline Vector3 Vector3::operator- (const Vector3& rkVector) const {
+ return Vector3(x - rkVector.x, y - rkVector.y, z - rkVector.z);
+}
+
+//----------------------------------------------------------------------------
+inline Vector3 Vector3::operator* (const Vector3& rkVector) const {
+ return Vector3(x * rkVector.x, y * rkVector.y, z * rkVector.z);
+}
+
+inline Vector3 Vector3::operator*(float f) const {
+ return Vector3(x * f, y * f, z * f);
+}
+
+//----------------------------------------------------------------------------
+inline Vector3 Vector3::operator/ (const Vector3& rkVector) const {
+ return Vector3(x / rkVector.x, y / rkVector.y, z / rkVector.z);
+}
+
+//----------------------------------------------------------------------------
+inline Vector3 Vector3::operator- () const {
+ return Vector3(-x, -y, -z);
+}
+
+//----------------------------------------------------------------------------
+inline Vector3& Vector3::operator+= (const Vector3& rkVector) {
+ x += rkVector.x;
+ y += rkVector.y;
+ z += rkVector.z;
+ return *this;
+}
+
+//----------------------------------------------------------------------------
+inline Vector3& Vector3::operator-= (const Vector3& rkVector) {
+ x -= rkVector.x;
+ y -= rkVector.y;
+ z -= rkVector.z;
+ return *this;
+}
+
+//----------------------------------------------------------------------------
+inline Vector3& Vector3::operator*= (float fScalar) {
+ x *= fScalar;
+ y *= fScalar;
+ z *= fScalar;
+ return *this;
+}
+
+//----------------------------------------------------------------------------
+inline Vector3& Vector3::operator*= (const Vector3& rkVector) {
+ x *= rkVector.x;
+ y *= rkVector.y;
+ z *= rkVector.z;
+ return *this;
+}
+
+//----------------------------------------------------------------------------
+inline Vector3& Vector3::operator/= (const Vector3& rkVector) {
+ x /= rkVector.x;
+ y /= rkVector.y;
+ z /= rkVector.z;
+ return *this;
+}
+
+//----------------------------------------------------------------------------
+inline float Vector3::squaredMagnitude () const {
+ return x*x + y*y + z*z;
+}
+
+//----------------------------------------------------------------------------
+inline float Vector3::squaredLength () const {
+ return squaredMagnitude();
+}
+
+//----------------------------------------------------------------------------
+inline float Vector3::magnitude() const {
+ return sqrtf(x*x + y*y + z*z);
+}
+
+//----------------------------------------------------------------------------
+inline float Vector3::length() const {
+ return magnitude();
+}
+
+//----------------------------------------------------------------------------
+inline Vector3 Vector3::direction () const {
+ float lenSquared = squaredMagnitude();
+ float invSqrt = 1.0f / sqrtf(lenSquared);
+ return Vector3(x * invSqrt, y * invSqrt, z * invSqrt);
+}
+
+//----------------------------------------------------------------------------
+
+inline Vector3 Vector3::fastDirection () const {
+ float lenSquared = x * x + y * y + z * z;
+ float invSqrt = rsq(lenSquared);
+ return Vector3(x * invSqrt, y * invSqrt, z * invSqrt);
+}
+
+//----------------------------------------------------------------------------
+inline float Vector3::dot (const Vector3& rkVector) const {
+ return x*rkVector.x + y*rkVector.y + z*rkVector.z;
+}
+
+//----------------------------------------------------------------------------
+inline Vector3 Vector3::cross (const Vector3& rkVector) const {
+ return Vector3(y*rkVector.z - z*rkVector.y, z*rkVector.x - x*rkVector.z,
+ x*rkVector.y - y*rkVector.x);
+}
+
+//----------------------------------------------------------------------------
+inline Vector3 Vector3::unitCross (const Vector3& rkVector) const {
+ Vector3 kCross(y*rkVector.z - z*rkVector.y, z*rkVector.x - x*rkVector.z,
+ x*rkVector.y - y*rkVector.x);
+ kCross.unitize();
+ return kCross;
+}
+
+//----------------------------------------------------------------------------
+inline Vector3 Vector3::min(const Vector3 &v) const {
+ return Vector3(G3D::min(v.x, x), G3D::min(v.y, y), G3D::min(v.z, z));
+}
+
+//----------------------------------------------------------------------------
+inline Vector3 Vector3::max(const Vector3 &v) const {
+ return Vector3(G3D::max(v.x, x), G3D::max(v.y, y), G3D::max(v.z, z));
+}
+
+//----------------------------------------------------------------------------
+inline bool Vector3::isZero() const {
+ return G3D::fuzzyEq(squaredMagnitude(), 0.0f);
+}
+
+//----------------------------------------------------------------------------
+
+inline bool Vector3::isUnit() const {
+ return G3D::fuzzyEq(squaredMagnitude(), 1.0f);
+}
+
+} // namespace G3D
+
+
+template <>
+struct HashTrait<G3D::Vector3> {
+ static size_t hashCode(const G3D::Vector3& key) {
+ return key.hashCode();
+ }
+};
+
+
+template<> struct PositionTrait<class G3D::Vector2> {
+ static void getPosition(const G3D::Vector2& v, G3D::Vector3& p) { p = G3D::Vector3(v, 0); }
+};
+
+template<> struct PositionTrait<class G3D::Vector3> {
+ static void getPosition(const G3D::Vector3& v, G3D::Vector3& p) { p = v; }
+};
+
+
+#endif
diff --git a/externals/g3dlite/G3D.lib/include/G3D/Vector3int16.h b/externals/g3dlite/G3D.lib/include/G3D/Vector3int16.h
new file mode 100644
index 00000000000..f3f30a5bab4
--- /dev/null
+++ b/externals/g3dlite/G3D.lib/include/G3D/Vector3int16.h
@@ -0,0 +1,130 @@
+/**
+ @file Vector3int16.h
+
+ @maintainer Morgan McGuire, matrix@brown.edu
+
+ @created 2003-04-07
+ @edited 2003-06-24
+ Copyright 2000-2004, Morgan McGuire.
+ All rights reserved.
+ */
+
+#ifndef VECTOR3INT16_H
+#define VECTOR3INT16_H
+
+#include "G3D/platform.h"
+#include "G3D/g3dmath.h"
+#include "G3D/HashTrait.h"
+
+namespace G3D {
+
+/**
+ A Vector3 that packs its fields into uint16s.
+ */
+#ifdef G3D_WIN32
+ // Switch to tight alignment
+ #pragma pack(push, 2)
+#endif
+
+class Vector3int16 {
+private:
+ // Hidden operators
+ bool operator<(const Vector3int16&) const;
+ bool operator>(const Vector3int16&) const;
+ bool operator<=(const Vector3int16&) const;
+ bool operator>=(const Vector3int16&) const;
+
+public:
+ G3D::int16 x;
+ G3D::int16 y;
+ G3D::int16 z;
+
+ Vector3int16() : x(0), y(0), z(0) {}
+ Vector3int16(G3D::int16 _x, G3D::int16 _y, G3D::int16 _z) : x(_x), y(_y), z(_z) {}
+ Vector3int16(const class Vector3& v);
+ Vector3int16(class BinaryInput& bi);
+
+ void serialize(class BinaryOutput& bo) const;
+ void deserialize(class BinaryInput& bi);
+
+ inline G3D::int16& operator[] (int i) {
+ debugAssert(i <= 2);
+ return ((G3D::int16*)this)[i];
+ }
+
+ inline const G3D::int16& operator[] (int i) const {
+ debugAssert(i <= 2);
+ return ((G3D::int16*)this)[i];
+ }
+
+ inline Vector3int16 operator+(const Vector3int16& other) const {
+ return Vector3int16(x + other.x, y + other.y, z + other.z);
+ }
+
+ inline Vector3int16 operator-(const Vector3int16& other) const {
+ return Vector3int16(x - other.x, y - other.y, z - other.z);
+ }
+
+ inline Vector3int16 operator*(const Vector3int16& other) const {
+ return Vector3int16(x * other.x, y * other.y, z * other.z);
+ }
+
+ inline Vector3int16 operator*(const int s) const {
+ return Vector3int16(x * s, y * s, z * s);
+ }
+
+ inline Vector3int16& operator+=(const Vector3int16& other) {
+ x += other.x;
+ y += other.y;
+ z += other.y;
+ return *this;
+ }
+
+ inline Vector3int16& operator-=(const Vector3int16& other) {
+ x -= other.x;
+ y -= other.y;
+ z -= other.z;
+ return *this;
+ }
+
+ inline Vector3int16& operator*=(const Vector3int16& other) {
+ x *= other.x;
+ y *= other.y;
+ z *= other.z;
+ return *this;
+ }
+
+ inline bool operator== (const Vector3int16& rkVector) const {
+ return ( x == rkVector.x && y == rkVector.y && z == rkVector.z );
+ }
+
+ inline bool operator!= (const Vector3int16& rkVector) const {
+ return ( x != rkVector.x || y != rkVector.y || z != rkVector.z );
+ }
+
+ Vector3int16 max(const Vector3int16& v) const {
+ return Vector3int16(iMax(x, v.x), iMax(y, v.y), iMax(z, v.z));
+ }
+
+ Vector3int16 min(const Vector3int16& v) const {
+ return Vector3int16(iMin(x, v.x), iMin(y, v.y), iMin(z, v.z));
+ }
+
+ std::string toString() const;
+}
+#if defined(G3D_LINUX) || defined(G3D_OSX)
+ __attribute((aligned(1)))
+#endif
+;
+
+#ifdef G3D_WIN32
+ #pragma pack(pop)
+#endif
+
+}
+
+template <> struct HashTrait<G3D::Vector3int16> {
+ static size_t hashCode(const G3D::Vector3int16& key) { return static_cast<size_t>(key.x + ((int)key.y << 5) + ((int)key.z << 10)); }
+};
+
+#endif
diff --git a/externals/g3dlite/G3D.lib/include/G3D/Vector3int32.h b/externals/g3dlite/G3D.lib/include/G3D/Vector3int32.h
new file mode 100644
index 00000000000..01c8581b47a
--- /dev/null
+++ b/externals/g3dlite/G3D.lib/include/G3D/Vector3int32.h
@@ -0,0 +1,138 @@
+/**
+ @file Vector3int32.h
+
+ @maintainer Morgan McGuire, matrix@brown.edu
+
+ @created 2008-07-01
+ @edited 2008-07-01
+ Copyright 2000-2008, Morgan McGuire.
+ All rights reserved.
+ */
+
+#ifndef VECTOR3INT32_H
+#define VECTOR3INT32_H
+
+#include "G3D/platform.h"
+#include "G3D/g3dmath.h"
+#include "G3D/HashTrait.h"
+
+namespace G3D {
+
+/**
+ A Vector3 that packs its fields into uint32s.
+ */
+#ifdef G3D_WIN32
+ // Switch to tight alignment
+ #pragma pack(push, 4)
+#endif
+
+class Vector3int32 {
+private:
+ // Hidden operators
+ bool operator<(const Vector3int32&) const;
+ bool operator>(const Vector3int32&) const;
+ bool operator<=(const Vector3int32&) const;
+ bool operator>=(const Vector3int32&) const;
+
+public:
+ G3D::int32 x;
+ G3D::int32 y;
+ G3D::int32 z;
+
+ Vector3int32() : x(0), y(0), z(0) {}
+ Vector3int32(int _x, int _y, int _z) : x(_x), y(_y), z(_z) {}
+ Vector3int32(const class Vector3int16& v);
+ Vector3int32(const class Vector3& v);
+ Vector3int32(class BinaryInput& bi);
+
+ void serialize(class BinaryOutput& bo) const;
+ void deserialize(class BinaryInput& bi);
+
+ inline G3D::int32& operator[] (int i) {
+ debugAssert(i <= 2);
+ return ((G3D::int32*)this)[i];
+ }
+
+ inline const G3D::int32& operator[] (int i) const {
+ debugAssert(i <= 2);
+ return ((G3D::int32*)this)[i];
+ }
+
+ inline Vector3int32 operator+(const Vector3int32& other) const {
+ return Vector3int32(x + other.x, y + other.y, z + other.z);
+ }
+
+ inline Vector3int32 operator-(const Vector3int32& other) const {
+ return Vector3int32(x - other.x, y - other.y, z - other.z);
+ }
+
+ inline Vector3int32 operator*(const Vector3int32& other) const {
+ return Vector3int32(x * other.x, y * other.y, z * other.z);
+ }
+
+ inline Vector3int32 operator*(const int s) const {
+ return Vector3int32(x * s, y * s, z * s);
+ }
+
+ inline Vector3int32& operator+=(const Vector3int32& other) {
+ x += other.x;
+ y += other.y;
+ z += other.y;
+ return *this;
+ }
+
+ inline Vector3int32& operator-=(const Vector3int32& other) {
+ x -= other.x;
+ y -= other.y;
+ z -= other.z;
+ return *this;
+ }
+
+ inline Vector3int32& operator*=(const Vector3int32& other) {
+ x *= other.x;
+ y *= other.y;
+ z *= other.z;
+ return *this;
+ }
+
+ inline bool operator== (const Vector3int32& rkVector) const {
+ return ( x == rkVector.x && y == rkVector.y && z == rkVector.z );
+ }
+
+ inline bool operator!= (const Vector3int32& rkVector) const {
+ return ( x != rkVector.x || y != rkVector.y || z != rkVector.z );
+ }
+
+ Vector3int32 max(const Vector3int32& v) const {
+ return Vector3int32(iMax(x, v.x), iMax(y, v.y), iMax(z, v.z));
+ }
+
+ Vector3int32 min(const Vector3int32& v) const {
+ return Vector3int32(iMin(x, v.x), iMin(y, v.y), iMin(z, v.z));
+ }
+
+ std::string toString() const;
+}
+#if defined(G3D_LINUX) || defined(G3D_OSX)
+ __attribute((aligned(1)))
+#endif
+;
+
+#ifdef G3D_WIN32
+ #pragma pack(pop)
+#endif
+
+}
+
+template <> struct HashTrait<G3D::Vector3int32> {
+ static size_t hashCode(const G3D::Vector3int32& key) {
+ // Mask for the top bit of a uint32
+ const G3D::uint32 top = (1 << 31);
+ // Mask for the bottom 10 bits of a uint32
+ const G3D::uint32 bot = 0x000003FF;
+ return static_cast<size_t>(((key.x & top) | ((key.y & top) >> 1) | ((key.z & top) >> 2)) |
+ (((key.x & bot) << 19) ^ ((key.y & bot) << 10) ^ (key.z & bot)));
+ }
+};
+
+#endif
diff --git a/externals/g3dlite/G3D.lib/include/G3D/Vector4.h b/externals/g3dlite/G3D.lib/include/G3D/Vector4.h
new file mode 100644
index 00000000000..d60ff76ea8b
--- /dev/null
+++ b/externals/g3dlite/G3D.lib/include/G3D/Vector4.h
@@ -0,0 +1,717 @@
+/**
+ @file Vector4.h
+
+ Homogeneous vector class.
+
+ @maintainer Morgan McGuire, morgan@cs.williams.edu
+
+ @created 2002-07-09
+ @edited 2008-11-01
+
+ Copyright 2000-2008, Morgan McGuire.
+ All rights reserved.
+ */
+
+#ifndef G3D_VECTOR4_H
+#define G3D_VECTOR4_H
+
+#include "G3D/platform.h"
+#include "G3D/g3dmath.h"
+#include "G3D/Vector3.h"
+#include "G3D/Vector2.h"
+#include "G3D/Table.h"
+#include "G3D/HashTrait.h"
+#include "G3D/PositionTrait.h"
+#include <string>
+
+namespace G3D {
+
+class Vector2;
+class Vector3;
+class Vector4;
+class Vector4int8;
+
+/**
+ Do not subclass-- this implementation makes assumptions about the
+ memory layout.
+ */
+class Vector4 {
+private:
+ // Hidden operators
+ bool operator<(const Vector4&) const;
+ bool operator>(const Vector4&) const;
+ bool operator<=(const Vector4&) const;
+ bool operator>=(const Vector4&) const;
+
+public:
+ // construction
+ Vector4();
+ Vector4(float fX, float fY, float fZ, float fW);
+ Vector4(float afCoordinate[4]);
+ Vector4(const Vector4& rkVector);
+ Vector4(const class Color4& c);
+ Vector4(const Vector3& rkVector, float fW);
+ Vector4(const Vector2& v1, const Vector2& v2);
+ Vector4(const Vector2& v1, float fz, float fw);
+
+ /** Divides by 127 when converting */
+ Vector4(const Vector4int8&);
+
+ Vector4(class BinaryInput& b);
+ void serialize(class BinaryOutput& b) const;
+ void deserialize(class BinaryInput& b);
+
+ // coordinates
+ float x, y, z, w;
+
+ // access vector V as V[0] = V.x, V[1] = V.y, V[2] = V.z, etc.
+ //
+ // WARNING. These member functions rely on
+ // (1) Vector4 not having virtual functions
+ // (2) the data packed in a 4*sizeof(float) memory block
+ float& operator[] (int i);
+ const float& operator[] (int i) const;
+ operator float* ();
+ operator const float* () const;
+
+ // assignment and comparison
+ Vector4& operator= (const Vector4& rkVector);
+ bool operator== (const Vector4& rkVector) const;
+ bool operator!= (const Vector4& rkVector) const;
+
+ inline void set(float _x, float _y, float _z, float _w) {
+ x = _x;
+ y = _y;
+ z = _z;
+ w = _w;
+ }
+
+ inline void set(const Vector3& v, float _w) {
+ x = v.x;
+ y = v.y;
+ z = v.z;
+ w = _w;
+ }
+
+ inline void set(const Vector2& v, float _z, float _w) {
+ x = v.x;
+ y = v.y;
+ z = _z;
+ w = _w;
+ }
+
+ size_t hashCode() const;
+ bool fuzzyEq(const Vector4& other) const;
+ bool fuzzyNe(const Vector4& other) const;
+
+ inline static const Vector4& inf() { static Vector4 v((float)G3D::inf(), (float)G3D::inf(), (float)G3D::inf(), (float)G3D::inf()); return v; }
+ inline static const Vector4& nan() { static Vector4 v((float)G3D::nan(), (float)G3D::nan(), (float)G3D::nan(), (float)G3D::nan()); return v; }
+
+ /** sqrt(this->dot(*this)) */
+ float length() const;
+ float squaredLength() const;
+
+ inline float sum() const {
+ return x + y + z + w;
+ }
+
+ /** Returns true if this vector has finite length */
+ bool isFinite() const;
+
+ /** Returns true if this vector has length == 0 */
+ bool isZero() const;
+
+ /** Returns true if this vector has length == 1 */
+ bool isUnit() const;
+
+ // arithmetic operations
+ Vector4 operator+ (const Vector4& rkVector) const;
+ Vector4 operator- (const Vector4& rkVector) const;
+
+ inline Vector4 operator*(const Vector4& rkVector) const {
+ return Vector4(x * rkVector.x, y * rkVector.y, z * rkVector.z, w * rkVector.w);
+ }
+
+ inline Vector4 operator/(const Vector4& rkVector) const {
+ return Vector4(x / rkVector.x, y / rkVector.y, z / rkVector.z, w / rkVector.w);
+ }
+
+ Vector4 operator*(const class Matrix4& M) const;
+
+ Vector4 operator* (float fScalar) const;
+ Vector4 operator/ (float fScalar) const;
+ Vector4 operator- () const;
+ friend Vector4 operator* (float, const Vector4& rkVector);
+
+ // arithmetic updates
+ Vector4& operator+= (const Vector4& rkVector);
+ Vector4& operator-= (const Vector4& rkVector);
+ Vector4& operator*= (float fScalar);
+ Vector4& operator/= (float fScalar);
+
+ inline Vector4 clamp(const Vector4& low, const Vector4& high) const {
+ return Vector4(
+ G3D::clamp(x, low.x, high.x),
+ G3D::clamp(y, low.y, high.y),
+ G3D::clamp(z, low.z, high.z),
+ G3D::clamp(w, low.w, high.w));
+ }
+
+ inline Vector4 clamp(float low, float high) const {
+ return Vector4(
+ G3D::clamp(x, low, high),
+ G3D::clamp(y, low, high),
+ G3D::clamp(z, low, high),
+ G3D::clamp(w, low, high));
+ }
+
+ float dot (const Vector4& rkVector) const;
+
+ Vector4 min(const Vector4& v) const;
+ Vector4 max(const Vector4& v) const;
+
+ std::string toString() const;
+
+ /**
+ Linear interpolation
+ */
+ Vector4 lerp(const Vector4& v, float alpha) const;
+
+ // 2-char swizzles
+
+ Vector2 xx() const;
+ Vector2 yx() const;
+ Vector2 zx() const;
+ Vector2 wx() const;
+ Vector2 xy() const;
+ Vector2 yy() const;
+ Vector2 zy() const;
+ Vector2 wy() const;
+ Vector2 xz() const;
+ Vector2 yz() const;
+ Vector2 zz() const;
+ Vector2 wz() const;
+ Vector2 xw() const;
+ Vector2 yw() const;
+ Vector2 zw() const;
+ Vector2 ww() const;
+
+ // 3-char swizzles
+
+ Vector3 xxx() const;
+ Vector3 yxx() const;
+ Vector3 zxx() const;
+ Vector3 wxx() const;
+ Vector3 xyx() const;
+ Vector3 yyx() const;
+ Vector3 zyx() const;
+ Vector3 wyx() const;
+ Vector3 xzx() const;
+ Vector3 yzx() const;
+ Vector3 zzx() const;
+ Vector3 wzx() const;
+ Vector3 xwx() const;
+ Vector3 ywx() const;
+ Vector3 zwx() const;
+ Vector3 wwx() const;
+ Vector3 xxy() const;
+ Vector3 yxy() const;
+ Vector3 zxy() const;
+ Vector3 wxy() const;
+ Vector3 xyy() const;
+ Vector3 yyy() const;
+ Vector3 zyy() const;
+ Vector3 wyy() const;
+ Vector3 xzy() const;
+ Vector3 yzy() const;
+ Vector3 zzy() const;
+ Vector3 wzy() const;
+ Vector3 xwy() const;
+ Vector3 ywy() const;
+ Vector3 zwy() const;
+ Vector3 wwy() const;
+ Vector3 xxz() const;
+ Vector3 yxz() const;
+ Vector3 zxz() const;
+ Vector3 wxz() const;
+ Vector3 xyz() const;
+ Vector3 yyz() const;
+ Vector3 zyz() const;
+ Vector3 wyz() const;
+ Vector3 xzz() const;
+ Vector3 yzz() const;
+ Vector3 zzz() const;
+ Vector3 wzz() const;
+ Vector3 xwz() const;
+ Vector3 ywz() const;
+ Vector3 zwz() const;
+ Vector3 wwz() const;
+ Vector3 xxw() const;
+ Vector3 yxw() const;
+ Vector3 zxw() const;
+ Vector3 wxw() const;
+ Vector3 xyw() const;
+ Vector3 yyw() const;
+ Vector3 zyw() const;
+ Vector3 wyw() const;
+ Vector3 xzw() const;
+ Vector3 yzw() const;
+ Vector3 zzw() const;
+ Vector3 wzw() const;
+ Vector3 xww() const;
+ Vector3 yww() const;
+ Vector3 zww() const;
+ Vector3 www() const;
+
+ // 4-char swizzles
+
+ Vector4 xxxx() const;
+ Vector4 yxxx() const;
+ Vector4 zxxx() const;
+ Vector4 wxxx() const;
+ Vector4 xyxx() const;
+ Vector4 yyxx() const;
+ Vector4 zyxx() const;
+ Vector4 wyxx() const;
+ Vector4 xzxx() const;
+ Vector4 yzxx() const;
+ Vector4 zzxx() const;
+ Vector4 wzxx() const;
+ Vector4 xwxx() const;
+ Vector4 ywxx() const;
+ Vector4 zwxx() const;
+ Vector4 wwxx() const;
+ Vector4 xxyx() const;
+ Vector4 yxyx() const;
+ Vector4 zxyx() const;
+ Vector4 wxyx() const;
+ Vector4 xyyx() const;
+ Vector4 yyyx() const;
+ Vector4 zyyx() const;
+ Vector4 wyyx() const;
+ Vector4 xzyx() const;
+ Vector4 yzyx() const;
+ Vector4 zzyx() const;
+ Vector4 wzyx() const;
+ Vector4 xwyx() const;
+ Vector4 ywyx() const;
+ Vector4 zwyx() const;
+ Vector4 wwyx() const;
+ Vector4 xxzx() const;
+ Vector4 yxzx() const;
+ Vector4 zxzx() const;
+ Vector4 wxzx() const;
+ Vector4 xyzx() const;
+ Vector4 yyzx() const;
+ Vector4 zyzx() const;
+ Vector4 wyzx() const;
+ Vector4 xzzx() const;
+ Vector4 yzzx() const;
+ Vector4 zzzx() const;
+ Vector4 wzzx() const;
+ Vector4 xwzx() const;
+ Vector4 ywzx() const;
+ Vector4 zwzx() const;
+ Vector4 wwzx() const;
+ Vector4 xxwx() const;
+ Vector4 yxwx() const;
+ Vector4 zxwx() const;
+ Vector4 wxwx() const;
+ Vector4 xywx() const;
+ Vector4 yywx() const;
+ Vector4 zywx() const;
+ Vector4 wywx() const;
+ Vector4 xzwx() const;
+ Vector4 yzwx() const;
+ Vector4 zzwx() const;
+ Vector4 wzwx() const;
+ Vector4 xwwx() const;
+ Vector4 ywwx() const;
+ Vector4 zwwx() const;
+ Vector4 wwwx() const;
+ Vector4 xxxy() const;
+ Vector4 yxxy() const;
+ Vector4 zxxy() const;
+ Vector4 wxxy() const;
+ Vector4 xyxy() const;
+ Vector4 yyxy() const;
+ Vector4 zyxy() const;
+ Vector4 wyxy() const;
+ Vector4 xzxy() const;
+ Vector4 yzxy() const;
+ Vector4 zzxy() const;
+ Vector4 wzxy() const;
+ Vector4 xwxy() const;
+ Vector4 ywxy() const;
+ Vector4 zwxy() const;
+ Vector4 wwxy() const;
+ Vector4 xxyy() const;
+ Vector4 yxyy() const;
+ Vector4 zxyy() const;
+ Vector4 wxyy() const;
+ Vector4 xyyy() const;
+ Vector4 yyyy() const;
+ Vector4 zyyy() const;
+ Vector4 wyyy() const;
+ Vector4 xzyy() const;
+ Vector4 yzyy() const;
+ Vector4 zzyy() const;
+ Vector4 wzyy() const;
+ Vector4 xwyy() const;
+ Vector4 ywyy() const;
+ Vector4 zwyy() const;
+ Vector4 wwyy() const;
+ Vector4 xxzy() const;
+ Vector4 yxzy() const;
+ Vector4 zxzy() const;
+ Vector4 wxzy() const;
+ Vector4 xyzy() const;
+ Vector4 yyzy() const;
+ Vector4 zyzy() const;
+ Vector4 wyzy() const;
+ Vector4 xzzy() const;
+ Vector4 yzzy() const;
+ Vector4 zzzy() const;
+ Vector4 wzzy() const;
+ Vector4 xwzy() const;
+ Vector4 ywzy() const;
+ Vector4 zwzy() const;
+ Vector4 wwzy() const;
+ Vector4 xxwy() const;
+ Vector4 yxwy() const;
+ Vector4 zxwy() const;
+ Vector4 wxwy() const;
+ Vector4 xywy() const;
+ Vector4 yywy() const;
+ Vector4 zywy() const;
+ Vector4 wywy() const;
+ Vector4 xzwy() const;
+ Vector4 yzwy() const;
+ Vector4 zzwy() const;
+ Vector4 wzwy() const;
+ Vector4 xwwy() const;
+ Vector4 ywwy() const;
+ Vector4 zwwy() const;
+ Vector4 wwwy() const;
+ Vector4 xxxz() const;
+ Vector4 yxxz() const;
+ Vector4 zxxz() const;
+ Vector4 wxxz() const;
+ Vector4 xyxz() const;
+ Vector4 yyxz() const;
+ Vector4 zyxz() const;
+ Vector4 wyxz() const;
+ Vector4 xzxz() const;
+ Vector4 yzxz() const;
+ Vector4 zzxz() const;
+ Vector4 wzxz() const;
+ Vector4 xwxz() const;
+ Vector4 ywxz() const;
+ Vector4 zwxz() const;
+ Vector4 wwxz() const;
+ Vector4 xxyz() const;
+ Vector4 yxyz() const;
+ Vector4 zxyz() const;
+ Vector4 wxyz() const;
+ Vector4 xyyz() const;
+ Vector4 yyyz() const;
+ Vector4 zyyz() const;
+ Vector4 wyyz() const;
+ Vector4 xzyz() const;
+ Vector4 yzyz() const;
+ Vector4 zzyz() const;
+ Vector4 wzyz() const;
+ Vector4 xwyz() const;
+ Vector4 ywyz() const;
+ Vector4 zwyz() const;
+ Vector4 wwyz() const;
+ Vector4 xxzz() const;
+ Vector4 yxzz() const;
+ Vector4 zxzz() const;
+ Vector4 wxzz() const;
+ Vector4 xyzz() const;
+ Vector4 yyzz() const;
+ Vector4 zyzz() const;
+ Vector4 wyzz() const;
+ Vector4 xzzz() const;
+ Vector4 yzzz() const;
+ Vector4 zzzz() const;
+ Vector4 wzzz() const;
+ Vector4 xwzz() const;
+ Vector4 ywzz() const;
+ Vector4 zwzz() const;
+ Vector4 wwzz() const;
+ Vector4 xxwz() const;
+ Vector4 yxwz() const;
+ Vector4 zxwz() const;
+ Vector4 wxwz() const;
+ Vector4 xywz() const;
+ Vector4 yywz() const;
+ Vector4 zywz() const;
+ Vector4 wywz() const;
+ Vector4 xzwz() const;
+ Vector4 yzwz() const;
+ Vector4 zzwz() const;
+ Vector4 wzwz() const;
+ Vector4 xwwz() const;
+ Vector4 ywwz() const;
+ Vector4 zwwz() const;
+ Vector4 wwwz() const;
+ Vector4 xxxw() const;
+ Vector4 yxxw() const;
+ Vector4 zxxw() const;
+ Vector4 wxxw() const;
+ Vector4 xyxw() const;
+ Vector4 yyxw() const;
+ Vector4 zyxw() const;
+ Vector4 wyxw() const;
+ Vector4 xzxw() const;
+ Vector4 yzxw() const;
+ Vector4 zzxw() const;
+ Vector4 wzxw() const;
+ Vector4 xwxw() const;
+ Vector4 ywxw() const;
+ Vector4 zwxw() const;
+ Vector4 wwxw() const;
+ Vector4 xxyw() const;
+ Vector4 yxyw() const;
+ Vector4 zxyw() const;
+ Vector4 wxyw() const;
+ Vector4 xyyw() const;
+ Vector4 yyyw() const;
+ Vector4 zyyw() const;
+ Vector4 wyyw() const;
+ Vector4 xzyw() const;
+ Vector4 yzyw() const;
+ Vector4 zzyw() const;
+ Vector4 wzyw() const;
+ Vector4 xwyw() const;
+ Vector4 ywyw() const;
+ Vector4 zwyw() const;
+ Vector4 wwyw() const;
+ Vector4 xxzw() const;
+ Vector4 yxzw() const;
+ Vector4 zxzw() const;
+ Vector4 wxzw() const;
+ Vector4 xyzw() const;
+ Vector4 yyzw() const;
+ Vector4 zyzw() const;
+ Vector4 wyzw() const;
+ Vector4 xzzw() const;
+ Vector4 yzzw() const;
+ Vector4 zzzw() const;
+ Vector4 wzzw() const;
+ Vector4 xwzw() const;
+ Vector4 ywzw() const;
+ Vector4 zwzw() const;
+ Vector4 wwzw() const;
+ Vector4 xxww() const;
+ Vector4 yxww() const;
+ Vector4 zxww() const;
+ Vector4 wxww() const;
+ Vector4 xyww() const;
+ Vector4 yyww() const;
+ Vector4 zyww() const;
+ Vector4 wyww() const;
+ Vector4 xzww() const;
+ Vector4 yzww() const;
+ Vector4 zzww() const;
+ Vector4 wzww() const;
+ Vector4 xwww() const;
+ Vector4 ywww() const;
+ Vector4 zwww() const;
+ Vector4 wwww() const;
+
+};
+
+
+//----------------------------------------------------------------------------
+inline Vector4::Vector4() {
+ x = y = z = w = 0;
+}
+
+//----------------------------------------------------------------------------
+
+inline Vector4::Vector4 (float fX, float fY, float fZ, float fW) {
+ x = fX;
+ y = fY;
+ z = fZ;
+ w = fW;
+}
+
+//----------------------------------------------------------------------------
+inline Vector4::Vector4 (float afCoordinate[4]) {
+ x = afCoordinate[0];
+ y = afCoordinate[1];
+ z = afCoordinate[2];
+ w = afCoordinate[3];
+}
+
+//----------------------------------------------------------------------------
+inline Vector4::Vector4(const Vector4& rkVector) {
+ x = rkVector.x;
+ y = rkVector.y;
+ z = rkVector.z;
+ w = rkVector.w;
+}
+//----------------------------------------------------------------------------
+inline Vector4::Vector4(const Vector3& rkVector, float fW) {
+ x = rkVector.x;
+ y = rkVector.y;
+ z = rkVector.z;
+ w = fW;
+}
+
+//----------------------------------------------------------------------------
+inline float& Vector4::operator[] (int i) {
+ return ((float*)this)[i];
+}
+
+//----------------------------------------------------------------------------
+inline const float& Vector4::operator[] (int i) const {
+ return ((float*)this)[i];
+}
+
+//----------------------------------------------------------------------------
+inline Vector4::operator float* () {
+ return (float*)this;
+}
+
+inline Vector4::operator const float* () const {
+ return (float*)this;
+}
+
+//----------------------------------------------------------------------------
+inline Vector4& Vector4::operator= (const Vector4& rkVector) {
+ x = rkVector.x;
+ y = rkVector.y;
+ z = rkVector.z;
+ w = rkVector.w;
+ return *this;
+}
+
+//----------------------------------------------------------------------------
+inline bool Vector4::operator== (const Vector4& rkVector) const {
+ return ( (x == rkVector.x) && (y == rkVector.y) && (z == rkVector.z) && (w == rkVector.w));
+}
+
+//----------------------------------------------------------------------------
+inline bool Vector4::operator!= (const Vector4& rkVector) const {
+ return ( x != rkVector.x || y != rkVector.y || z != rkVector.z || w != rkVector.w);
+}
+
+//----------------------------------------------------------------------------
+inline Vector4 Vector4::operator+ (const Vector4& rkVector) const {
+ return Vector4(x + rkVector.x, y + rkVector.y, z + rkVector.z, w + rkVector.w);
+}
+
+//----------------------------------------------------------------------------
+inline Vector4 Vector4::operator- (const Vector4& rkVector) const {
+ return Vector4(x - rkVector.x, y - rkVector.y, z - rkVector.z, w - rkVector.w);
+}
+
+//----------------------------------------------------------------------------
+inline Vector4 Vector4::operator* (float fScalar) const {
+ return Vector4(fScalar*x, fScalar*y, fScalar*z, fScalar*w);
+}
+
+//----------------------------------------------------------------------------
+inline Vector4 Vector4::operator- () const {
+ return Vector4( -x, -y, -z, -w);
+}
+
+//----------------------------------------------------------------------------
+inline Vector4& Vector4::operator+= (const Vector4& rkVector) {
+ x += rkVector.x;
+ y += rkVector.y;
+ z += rkVector.z;
+ w += rkVector.w;
+ return *this;
+}
+
+//----------------------------------------------------------------------------
+inline Vector4& Vector4::operator-= (const Vector4& rkVector) {
+ x -= rkVector.x;
+ y -= rkVector.y;
+ z -= rkVector.z;
+ w -= rkVector.w;
+ return *this;
+}
+
+//----------------------------------------------------------------------------
+
+inline Vector4 Vector4::lerp(const Vector4& v, float alpha) const {
+ return (*this) + (v - *this) * alpha;
+}
+
+
+//----------------------------------------------------------------------------
+inline Vector4& Vector4::operator*= (float fScalar) {
+ x *= fScalar;
+ y *= fScalar;
+ z *= fScalar;
+ w *= fScalar;
+ return *this;
+}
+
+
+//----------------------------------------------------------------------------
+inline float Vector4::dot(const Vector4& rkVector) const {
+ return x*rkVector.x + y*rkVector.y + z*rkVector.z + w*rkVector.w;
+}
+
+//----------------------------------------------------------------------------
+inline Vector4 Vector4::min(const Vector4 &v) const {
+ return Vector4(G3D::min(v.x, x), G3D::min(v.y, y), G3D::min(v.z, z), G3D::min(v.w, w));
+}
+
+//----------------------------------------------------------------------------
+inline Vector4 Vector4::max(const Vector4 &v) const {
+ return Vector4(G3D::max(v.x, x), G3D::max(v.y, y), G3D::max(v.z, z), G3D::max(v.w, w));
+}
+
+//----------------------------------------------------------------------------
+inline bool Vector4::isZero() const {
+ return (x == 0.0f) && (y == 0.0f) && (z == 0.0f) && (w == 0.0f);
+}
+
+//----------------------------------------------------------------------------
+
+inline bool Vector4::isFinite() const {
+ return G3D::isFinite(x) && G3D::isFinite(y) && G3D::isFinite(z) && G3D::isFinite(w);
+}
+
+//----------------------------------------------------------------------------
+
+inline bool Vector4::isUnit() const {
+ return squaredLength() == 1.0;
+}
+
+//----------------------------------------------------------------------------
+
+inline float Vector4::length() const {
+ return sqrtf(squaredLength());
+}
+
+//----------------------------------------------------------------------------
+
+inline float Vector4::squaredLength() const {
+ return x * x + y * y + z * z + w * w;
+}
+
+}
+
+template <> struct HashTrait<G3D::Vector4> {
+ static size_t hashCode(const G3D::Vector4& key) { return key.hashCode(); }
+};
+
+
+template<> struct PositionTrait<class G3D::Vector4> {
+ static void getPosition(const G3D::Vector4& v, G3D::Vector3& p) { p = v.xyz(); }
+};
+
+inline G3D::Vector4 operator* (float s, const G3D::Vector4& v) {
+ return v * s;
+}
+
+#endif
diff --git a/externals/g3dlite/G3D.lib/include/G3D/Vector4int8.h b/externals/g3dlite/G3D.lib/include/G3D/Vector4int8.h
new file mode 100644
index 00000000000..8476a2d008b
--- /dev/null
+++ b/externals/g3dlite/G3D.lib/include/G3D/Vector4int8.h
@@ -0,0 +1,113 @@
+/**
+ @file Vector4int8.h
+
+ Homogeneous vector class.
+
+ @maintainer Morgan McGuire, matrix@graphics3d.com
+
+ @created 2007-02-09
+ @edited 2007-02-09
+
+ Copyright 2000-2007, Morgan McGuire.
+ All rights reserved.
+ */
+
+#ifndef G3D_VECTOR4INT8_H
+#define G3D_VECTOR4INT8_H
+
+#include "G3D/platform.h"
+#include "G3D/g3dmath.h"
+
+namespace G3D {
+
+class Vector3;
+class Vector4;
+
+/**
+ Homogeneous vector stored efficiently in four signed int8s.
+
+ */
+class Vector4int8 {
+private:
+ // Hidden operators
+ bool operator<(const Vector4int8&) const;
+ bool operator>(const Vector4int8&) const;
+ bool operator<=(const Vector4int8&) const;
+ bool operator>=(const Vector4int8&) const;
+
+
+ /** For fast operations, treat this packed data structure as
+ an int32 */
+ inline uint32& asInt32() {
+ return *reinterpret_cast<uint32*>(this);
+ }
+
+ inline const uint32& asInt32() const {
+ return *reinterpret_cast<const uint32*>(this);
+ }
+
+public:
+ // construction
+ inline Vector4int8() : x(0), y(0), z(0), w(0) {}
+
+ /** Multiplies the source by 127 and clamps to (-128, 127) when converting */
+ Vector4int8(const Vector4& source);
+
+ /** Multiplies the source by 127 and clamps to (-128, 127) when converting */
+ Vector4int8(const Vector3& source, int8 w);
+
+ inline Vector4int8(int8 x, int8 y, int8 z, int8 w) : x(x), y(y), z(z), w(w) {}
+
+ Vector4int8(class BinaryInput& b);
+ void serialize(class BinaryOutput& b) const;
+ void deserialize(class BinaryInput& b);
+
+ // coordinates
+ int8 x, y, z, w;
+
+ inline operator int8* () {
+ return reinterpret_cast<int8*>(this);
+ }
+
+ inline operator const int8* () const {
+ return reinterpret_cast<const int8*>(this);
+ }
+
+ // access vector V as V[0] = V.x, V[1] = V.y, V[2] = V.z, etc.
+ //
+ // WARNING. These member functions rely on
+ // (1) Vector4int8 not having virtual functions
+ // (2) the data packed in a 4*sizeof(int8) memory block
+ inline int8& operator[] (int i) {
+ debugAssert(i >= 0 && i <= 4);
+ return ((int8*)this)[i];
+ }
+
+ const int8& operator[] (int i) const {
+ debugAssert(i >= 0 && i <= 4);
+ return ((const int8*)this)[i];
+ }
+
+ // assignment and comparison
+ Vector4int8& operator= (const Vector4int8& other) {
+ asInt32() = other.asInt32();
+ return *this;
+ }
+
+ inline bool operator== (const Vector4int8& other) const {
+ return asInt32() == other.asInt32();
+ }
+
+ inline bool operator!= (const Vector4int8& other) const {
+ return ! (*this == other);
+ }
+
+ inline unsigned int hashCode() const {
+ return asInt32();
+ }
+};
+
+} // namespace G3D
+
+
+#endif
diff --git a/externals/g3dlite/G3D.lib/include/G3D/WeakCache.h b/externals/g3dlite/G3D.lib/include/G3D/WeakCache.h
new file mode 100644
index 00000000000..87988aaf5cb
--- /dev/null
+++ b/externals/g3dlite/G3D.lib/include/G3D/WeakCache.h
@@ -0,0 +1,90 @@
+/**
+ @file WeakCache.h
+
+ @maintainer Morgan McGuire, graphics3d.com
+
+ @created 2007-05-16
+ @edited 2007-05-16
+
+ Copyright 2000-2007, Morgan McGuire.
+ All rights reserved.
+ */
+#ifndef G3D_WEAKCACHE_H
+#define G3D_WEAKCACHE_H
+
+#include "G3D/ReferenceCount.h"
+#include "G3D/Table.h"
+
+namespace G3D {
+
+/**
+ A cache that does not prevent its members from being garbage collected.
+ Useful to avoid loading or computing an expression twice. Useful
+ for memoization and dynamic programming.
+
+ Maintains a table of weak pointers. Weak pointers do not prevent
+ an object from being garbage collected. If the object is garbage
+ collected, the cache removes its reference.
+
+ There are no "contains" or "iterate" methods because elements can be
+ flushed from the cache at any time if they are garbage collected.
+
+ Example:
+ <pre>
+ WeakCache<std::string, TextureRef> textureCache;
+
+ TextureRef loadTexture(std::string s) {
+ TextureRef t = textureCache[s];
+
+ if (t.isNull()) {
+ t = Texture::fromFile(s);
+ textureCache.set(s, t);
+ }
+
+ return t;
+ }
+
+
+ </pre>
+ */
+template<class Key, class ValueRef>
+class WeakCache {
+ typedef WeakReferenceCountedPointer<typename ValueRef::element_type> ValueWeakRef;
+
+private:
+
+ Table<Key, ValueWeakRef> table;
+
+public:
+ /**
+ Returns NULL if the object is not in the cache
+ */
+ ValueRef operator[](const Key& k) {
+ if (table.containsKey(k)) {
+ ValueWeakRef w = table[k];
+ ValueRef s = w.createStrongPtr();
+ if (s.isNull()) {
+ // This object has been collected; clean out its key
+ table.remove(k);
+ }
+ return s;
+ } else {
+ return NULL;
+ }
+ }
+
+ void set(const Key& k, ValueRef v) {
+ table.set(k, v);
+ }
+
+ /** Removes k from the cache or does nothing if it is not currently in the cache.*/
+ void remove(const Key& k) {
+ if (table.containsKey(k)) {
+ table.remove(k);
+ }
+ }
+};
+
+}
+#endif
+
diff --git a/externals/g3dlite/G3D.lib/include/G3D/WrapMode.h b/externals/g3dlite/G3D.lib/include/G3D/WrapMode.h
new file mode 100644
index 00000000000..f97e68d6910
--- /dev/null
+++ b/externals/g3dlite/G3D.lib/include/G3D/WrapMode.h
@@ -0,0 +1,79 @@
+/**
+ @file WrapMode.h
+
+ @maintainer Morgan McGuire, graphics3d.com
+
+ @created 2007-04-17
+ @edited 2007-04-17
+
+ Copyright 2000-2007, Morgan McGuire.
+ All rights reserved.
+ */
+
+#ifndef G3D_WRAPMODE_H
+#define G3D_WRAPMODE_H
+
+#include "G3D/platform.h"
+#include "G3D/enumclass.h"
+
+#ifdef IGNORE
+# undef IGNORE
+#endif
+#ifdef ZERO
+# undef ZERO
+#endif
+#ifdef ERROR
+# undef ERROR
+#endif
+
+namespace G3D {
+
+/**
+ Describes the behavior of G3D::Texture, G3D::Map2D, G3D::Image3, etc. when accessing an out-of-bounds
+ pixel. Not all classes support all modes.
+
+ Refer to these as scoped enums, e.g., <code>WrapMode m = WrapMode::CLAMP;</code>.
+
+ WrapMode::IGNORE silently discards attempts to write to out
+ of bounds locations and returns an undefined value for reading
+ from out of bounds locations.
+
+ WrapMode::ERROR generates an error when the
+ pixel indices are out of bounds
+
+ WrapMode::CLAMP makes out of bounds pixels equal to the last in-range pixel along that dimension.
+
+ WrapMode::TILE computes out of bounds pixels modulo the dimension
+
+ WrapMode::ZERO treats out of bounds values as the zero value, which varies in definition
+ according to the class used. For example, with a G3D::Texture, ZERO = Color4(0,0,0,0).
+
+ Uses the "Intelligent Enum" design pattern
+ http://www.codeguru.com/cpp/cpp/cpp_mfc/article.php/c4001/
+ */
+class WrapMode {
+public:
+ /** Don't use this enum; use WrapMode instances instead. */
+ enum Value {
+ CLAMP,
+ TILE,
+ ZERO,
+ IGNORE,
+ ERROR
+ };
+
+private:
+
+ Value value;
+
+public:
+
+ G3D_DECLARE_ENUM_CLASS_METHODS(WrapMode);
+
+};
+
+} // namespace G3D
+
+G3D_DECLARE_ENUM_CLASS_HASHCODE(G3D::WrapMode);
+
+#endif
diff --git a/externals/g3dlite/G3D.lib/include/G3D/debug.h b/externals/g3dlite/G3D.lib/include/G3D/debug.h
new file mode 100644
index 00000000000..a96babc6fa0
--- /dev/null
+++ b/externals/g3dlite/G3D.lib/include/G3D/debug.h
@@ -0,0 +1,66 @@
+/**
+ @file debug.h
+
+ @maintainer Morgan McGuire, matrix@graphics3d.com
+
+ @created 2001-08-26
+ @edited 2006-02-16
+
+ Copyright 2000-2006, Morgan McGuire.
+ All rights reserved.
+*/
+
+#ifndef G3D_DEBUG_H
+#define G3D_DEBUG_H
+
+#include "G3D/platform.h"
+#ifdef _MSC_VER
+ #include <crtdbg.h>
+#endif
+
+#include "G3D/debugPrintf.h"
+#include "G3D/debugAssert.h"
+
+namespace G3D {
+
+#ifdef _MSC_VER
+ // Turn off 64-bit warnings
+# pragma warning(push)
+# pragma warning( disable : 4312)
+# pragma warning( disable : 4267)
+# pragma warning( disable : 4311)
+#endif
+
+
+/**
+ Useful for debugging purposes.
+ */
+inline bool isValidHeapPointer(const void* x) {
+ #ifdef _MSC_VER
+ return
+ (x != (void*)0xcccccccc) && (x != (void*)0xdeadbeef) && (x != (void*)0xfeeefeee);
+ #else
+ return x != NULL;
+ #endif
+}
+
+/**
+ Returns true if the pointer is likely to be
+ a valid pointer (instead of an arbitrary number).
+ Useful for debugging purposes.
+ */
+inline bool isValidPointer(const void* x) {
+ #ifdef _MSC_VER
+ return x != ((void*)0xcccccccc) && (x != (void*)0xdeadbeef) && (x != (void*)0xfeeefeee);
+ #else
+ return x != NULL;
+ #endif
+}
+
+#ifdef _MSC_VER
+# pragma warning(pop)
+#endif
+
+}
+
+#endif
diff --git a/externals/g3dlite/G3D.lib/include/G3D/debugAssert.h b/externals/g3dlite/G3D.lib/include/G3D/debugAssert.h
new file mode 100644
index 00000000000..f9b5bea4e9f
--- /dev/null
+++ b/externals/g3dlite/G3D.lib/include/G3D/debugAssert.h
@@ -0,0 +1,236 @@
+/**
+ @file debugAssert.h
+
+ debugAssert(expression);
+ debugAssertM(expression, message);
+
+ @cite
+ John Robbins, Microsoft Systems Journal Bugslayer Column, Feb 1999.
+ <A HREF="http://msdn.microsoft.com/library/periodic/period99/feb99_BUGSLAYE_BUGSLAYE.htm">
+ http://msdn.microsoft.com/library/periodic/period99/feb99_BUGSLAYE_BUGSLAYE.htm</A>
+
+ @cite
+ Douglas Cox, An assert() Replacement, Code of The Day, flipcode, Sept 19, 2000
+ <A HREF="http://www.flipcode.com/cgi-bin/msg.cgi?showThread=COTD-AssertReplace&forum=cotd&id=-1">
+ http://www.flipcode.com/cgi-bin/msg.cgi?showThread=COTD-AssertReplace&forum=cotd&id=-1</A>
+
+ @maintainer Morgan McGuire, matrix@graphics3d.com
+
+ @created 2001-08-26
+ @edited 2006-01-12
+
+ Copyright 2000-2006, Morgan McGuire.
+ All rights reserved.
+ */
+
+#ifndef G3D_DEBUGASSERT_H
+#define G3D_DEBUGASSERT_H
+
+#include <string>
+#include "G3D/platform.h"
+
+#include <cstdlib>
+
+#ifdef _MSC_VER
+// conditional expression is constant
+# pragma warning (disable : 4127)
+#endif
+
+#ifdef G3D_LINUX
+ // Needed so we can define a global display
+ // pointer for debugAssert.
+ #include <X11/Xlib.h>
+ #include <X11/Xutil.h>
+ #include <X11/Xatom.h>
+#endif
+
+#ifdef G3D_OSX
+ // Need this for DebugStr()
+ #import <CoreServices/CoreServices.h>
+#endif
+
+
+/**
+ @def debugBreak()
+
+ Break at the current location (i.e. don't push a procedure stack frame
+ before breaking).
+ */
+
+/**
+ @def debugAssert(exp)
+ Breaks if the expression is false. If G3D_DEBUG_NOGUI is defined, prompts at
+ the console, otherwise pops up a dialog. The user may then break (debug),
+ ignore, ignore always, or halt the program.
+
+ The assertion is also posted to the clipboard under Win32.
+ */
+
+/**
+ @def debugAssertM(exp, msg)
+ Breaks if the expression is false and displays a message. If G3D_DEBUG_NOGUI
+ is defined, prompts at the console, otherwise pops up a dialog. The user may
+ then break (debug), ignore, ignore always, or halt the program.
+
+ The assertion is also posted to the clipboard under Win32.
+ */
+
+namespace G3D {
+typedef bool (*AssertionHook)(
+ const char* _expression,
+ const std::string& message,
+ const char* filename,
+ int lineNumber,
+ bool& ignoreAlways,
+ bool useGuiPrompt);
+
+/**
+ Allows customization of the global function invoked when a debugAssert fails.
+ The initial value is G3D::_internal::_handleDebugAssert_. G3D will invoke
+ rawBreak if the hook returns true. If NULL, assertions are not handled.
+*/
+void setAssertionHook(AssertionHook hook);
+
+AssertionHook assertionHook();
+
+/**
+ Called by alwaysAssertM in case of failure in release mode. If returns
+ true then the program exits with -1 (you can replace this with your own
+ version that throws an exception or has other failure modes).
+ */
+void setFailureHook(AssertionHook hook);
+AssertionHook failureHook();
+
+namespace _internal {
+ extern AssertionHook _debugHook;
+ extern AssertionHook _failureHook;
+} // internal
+} // G3D
+
+/**
+ @def __debugPromptShowDialog__
+ @internal
+ */
+
+#ifdef G3D_DEBUG
+
+# if defined(_MSC_VER)
+# define rawBreak() ::DebugBreak();
+# elif defined(__i386__)
+ // gcc on intel
+# define rawBreak() __asm__ __volatile__ ( "int $3" );
+# else
+ // some other gcc
+# define rawBreak() ::abort()
+# endif
+// old mac code:
+//# define rawBreak() DebugStr((const unsigned char*)("\nG3D: Invoking breakpoint in debugger.")); /* XCode must be set to break on Debugger()/DebugStr() */
+
+
+# define debugBreak() G3D::_internal::_releaseInputGrab_(); rawBreak(); G3D::_internal::_restoreInputGrab_();
+# define debugAssert(exp) debugAssertM(exp, "Debug assertion failure")
+
+ #ifdef G3D_DEBUG_NOGUI
+ #define __debugPromptShowDialog__ false
+ #else
+ #define __debugPromptShowDialog__ true
+ #endif
+
+ #define debugAssertM(exp, message) do { \
+ static bool __debugAssertIgnoreAlways__ = false; \
+ if (!__debugAssertIgnoreAlways__ && !(exp)) { \
+ G3D::_internal::_releaseInputGrab_(); \
+ if ((G3D::_internal::_debugHook != NULL) && \
+ G3D::_internal::_debugHook((const char*)(#exp), message, __FILE__, __LINE__, __debugAssertIgnoreAlways__, __debugPromptShowDialog__)) { \
+ rawBreak(); \
+ } \
+ G3D::_internal::_restoreInputGrab_(); \
+ } \
+ } while (0)
+
+ #define alwaysAssertM debugAssertM
+
+#else // Release
+ #ifdef G3D_DEBUG_NOGUI
+ #define __debugPromptShowDialog__ false
+ #else
+ #define __debugPromptShowDialog__ true
+ #endif
+
+ // In the release build, just define away assertions.
+ #define rawBreak() do {} while (0)
+ #define debugAssert(exp) do {} while (0)
+ #define debugAssertM(exp, message) do {} while (0)
+ #define debugBreak() do {} while (0)
+
+ // But keep the 'always' assertions
+ #define alwaysAssertM(exp, message) { \
+ static bool __alwaysAssertIgnoreAlways__ = false; \
+ if (!__alwaysAssertIgnoreAlways__ && !(exp)) { \
+ G3D::_internal::_releaseInputGrab_(); \
+ if ((G3D::_internal::_failureHook != NULL) && \
+ G3D::_internal::_failureHook(#exp, message, __FILE__, __LINE__, __alwaysAssertIgnoreAlways__, __debugPromptShowDialog__)) { \
+ ::exit(-1); \
+ } \
+ G3D::_internal::_restoreInputGrab_(); \
+ } \
+ }
+
+#endif // if debug
+
+
+
+namespace G3D { namespace _internal {
+
+#ifdef G3D_LINUX
+ /**
+ A pointer to the X11 display. Initially NULL. If set to a
+ non-null value (e.g. by SDLWindow), debugAssert attempts to use
+ this display to release the mouse/input grab when an assertion
+ fails.
+ */
+ extern Display* x11Display;
+
+ /**
+ A pointer to the X11 window. Initially NULL. If set to a
+ non-null value (e.g. by SDLWindow), debugAssert attempts to use
+ this window to release the mouse/input grab when an assertion
+ fails.
+ */
+ extern Window x11Window;
+#endif
+
+/**
+ Pops up an assertion dialog or prints an assertion
+
+ ignoreAlways - return result of pressing the ignore button.
+ useGuiPrompt - if true, shows a dialog
+ */
+bool _handleDebugAssert_(
+ const char* expression,
+ const std::string& message,
+ const char* filename,
+ int lineNumber,
+ bool& ignoreAlways,
+ bool useGuiPrompt);
+
+bool _handleErrorCheck_(
+ const char* expression,
+ const std::string& message,
+ const char* filename,
+ int lineNumber,
+ bool& ignoreAlways,
+ bool useGuiPrompt);
+
+/** Attempts to give the user back their mouse and keyboard if they
+ were locked to the current window.
+ @internal*/
+void _releaseInputGrab_();
+
+/** Attempts to restore the state before _releaseInputGrab_.
+ @internal*/
+void _restoreInputGrab_();
+
+}; }; // namespace
+
+#endif
diff --git a/externals/g3dlite/G3D.lib/include/G3D/debugPrintf.h b/externals/g3dlite/G3D.lib/include/G3D/debugPrintf.h
new file mode 100644
index 00000000000..bc3315c8a6d
--- /dev/null
+++ b/externals/g3dlite/G3D.lib/include/G3D/debugPrintf.h
@@ -0,0 +1,62 @@
+/**
+ @file debugPrintf.h
+
+ @maintainer Morgan McGuire, matrix@graphics3d.com
+
+ @created 2001-08-26
+ @edited 2007-07-20
+
+ Copyright 2000-2007, Morgan McGuire.
+ All rights reserved.
+ */
+
+#ifndef G3D_DEBUGPRINTF_H
+#define G3D_DEBUGPRINTF_H
+
+#include "G3D/platform.h"
+#include <stdio.h>
+#include <cstdarg>
+#include "G3D/format.h"
+#include <string>
+
+namespace G3D {
+
+typedef void (*ConsolePrintHook)(const std::string&);
+
+namespace _internal {
+ extern ConsolePrintHook _consolePrintHook;
+}
+
+/** Called by consolePrintf after the log and terminal have been written to.
+ Used by GConsole to intercept printing routines.*/
+void setConsolePrintHook(ConsolePrintHook h);
+
+ConsolePrintHook consolePrintHook();
+
+/**
+ Sends output to the log and to the last GConsole instantiated.
+
+ Guarantees that the output has been flushed by the time the routine
+ returns.
+ @sa G3D::logPrintf, G3D::screenPrintf
+ @return The string that was printed
+ */
+std::string __cdecl consolePrintf(const char* fmt ...) G3D_CHECK_PRINTF_ARGS;
+std::string consolePrint(const std::string&);
+
+/**
+ Under visual studio, appears in the VS debug pane.
+ On unix-based operating systems the output is sent to stderr.
+
+ Also sends output to the console (G3D::consolePrintf) if there is a consolePrintHook,
+ and log (G3D::logPrintf), and flushes before returning.
+
+ @return The string that was printed
+*/
+std::string __cdecl debugPrintf(const char* fmt ...) G3D_CHECK_PRINTF_ARGS;
+std::string debugPrint(const std::string&);
+
+} // namespace G3D
+
+#endif
+
diff --git a/externals/g3dlite/G3D.lib/include/G3D/enumclass.h b/externals/g3dlite/G3D.lib/include/G3D/enumclass.h
new file mode 100644
index 00000000000..7e7bc3f6ac3
--- /dev/null
+++ b/externals/g3dlite/G3D.lib/include/G3D/enumclass.h
@@ -0,0 +1,141 @@
+/**
+ @file G3D/enumclass.h
+
+ @maintainer Morgan McGuire, morgan@graphics3d.com
+ @created 2007-01-27
+ @edited 2007-07-20
+*/
+#ifndef G3D_ENUMCLASS_H
+#define G3D_ENUMCLASS_H
+
+#include "G3D/Table.h"
+#include "G3D/BinaryInput.h"
+#include "G3D/BinaryOutput.h"
+
+/**
+ Creates a series of methods that turn a class into a scoped enumeration.
+ Uses the "Intelligent Enum" design pattern
+ http://www.codeguru.com/cpp/cpp/cpp_mfc/article.php/c4001/
+
+ Enum classes are initialized to their zero value by default.
+
+ See GLG3D/GKey.h for an example.
+ */
+#define G3D_DECLARE_ENUM_CLASS_METHODS(Classname)\
+ inline Classname(char v) : value((Value)v) {}\
+\
+ inline Classname() : value((Value)0) {}\
+\
+ inline Classname(const Value v) : value(v) {}\
+\
+ explicit inline Classname(int v) : value((Value)v) {}\
+\
+ /** Support cast back to the Value type, which is needed to allow implicit assignment inside unions. */\
+ /*inline operator Value() const {
+ return value;
+ }*/\
+\
+ inline operator int() const {\
+ return (int)value;\
+ }\
+\
+ inline bool operator== (const Classname other) const {\
+ return value == other.value;\
+ }\
+\
+ inline bool operator== (const Classname::Value other) const {\
+ return value == other;\
+ }\
+\
+ inline bool operator!= (const Classname other) const {\
+ return value != other.value;\
+ }\
+\
+ inline bool operator!= (const Classname::Value other) const {\
+ return value != other;\
+ }\
+\
+ inline bool operator< (const Classname other) const {\
+ return value < other.value;\
+ }\
+\
+ inline bool operator> (const Classname other) const {\
+ return value > other.value;\
+ }\
+\
+ inline bool operator>= (const Classname other) const {\
+ return value >= other.value;\
+ }\
+\
+ inline bool operator<= (const Classname other) const {\
+ return value <= other.value;\
+ }\
+\
+ inline bool operator< (const Value other) const {\
+ return value < other;\
+ }\
+\
+ inline bool operator> (const Value other) const {\
+ return value > other;\
+ }\
+\
+ inline bool operator<= (const Value other) const {\
+ return value <= other;\
+ }\
+\
+ inline bool operator>= (const Value other) const {\
+ return value >= other;\
+ }\
+\
+ inline Classname& operator-- () {\
+ value = (Value)((int)value - 1);\
+ return *this;\
+ }\
+\
+ inline Classname& operator++ () {\
+ value = (Value)((int)value + 1);\
+ return *this;\
+ }\
+\
+ inline Classname& operator+= (const int x) {\
+ value = (Value)((int)value + x);\
+ return *this;\
+ }\
+\
+ inline Classname& operator-= (const int x) {\
+ value = (Value)((int)value - x);\
+ return *this;\
+ }\
+\
+ inline Classname operator+ (const int x) const {\
+ return Classname((int)value + x);\
+ }\
+\
+ inline Classname operator- (const int x) const {\
+ return Classname((int)value - x);\
+ }\
+\
+ inline unsigned int hashCode() const {\
+ return (unsigned int)value;\
+ }\
+\
+ void serialize(BinaryOutput& b) const {\
+ b.writeInt32(value);\
+ }\
+\
+ void deserialize(BinaryInput& b) {\
+ value = (Value)b.readInt32();\
+ }
+
+#define G3D_DECLARE_ENUM_CLASS_HASHCODE(Classname)\
+template <> struct HashTrait<Classname::Value> \
+{ \
+ static size_t hashCode(Classname::Value key) { return static_cast<size_t>(key); } \
+}; \
+ \
+template <> struct HashTrait<Classname> \
+{ \
+ static size_t hashCode(Classname key) { return static_cast<size_t>(key.hashCode()); } \
+};
+
+#endif
diff --git a/externals/g3dlite/G3D.lib/include/G3D/fileutils.h b/externals/g3dlite/G3D.lib/include/G3D/fileutils.h
new file mode 100644
index 00000000000..ead20720870
--- /dev/null
+++ b/externals/g3dlite/G3D.lib/include/G3D/fileutils.h
@@ -0,0 +1,246 @@
+/**
+ @file fileutils.h
+
+ @maintainer Morgan McGuire, matrix@graphics3d.com
+
+ @author 2002-06-06
+ @edited 2007-01-18
+
+ Copyright 2000-2007, Morgan McGuire.
+ All rights reserved.
+ */
+
+#ifndef G3D_FILEUTILS_H
+#define G3D_FILEUTILS_H
+
+#include "G3D/platform.h"
+#include <string>
+#include <stdio.h>
+#include "G3D/Array.h"
+#include "G3D/Set.h"
+#include "G3D/g3dmath.h"
+
+#ifdef G3D_WIN32
+// For chdir, mkdir, etc.
+# include <direct.h>
+#endif
+
+namespace G3D {
+
+ namespace _internal {
+ extern Set<std::string> currentFilesUsed;
+ }
+
+/** Returns all the files used by G3D and GLG3D during the current execution. */
+Array<std::string> filesUsed();
+
+std::string readWholeFile(
+ const std::string& filename);
+
+
+/** Reads from a zip file and decompresses the desired contents
+ into memory. Does not support recursive zip calls (i.e. a .zip
+ stored within another .zip)
+
+ @param file the path, of the format C:\...\something.zip\...\desiredfile.ext
+ @param data a pointer to the memory where the file will be stored
+ @param length the size of the file decompressed to memory */
+void zipRead(const std::string& file,
+ void*& data,
+ size_t& length);
+
+
+/** Closes the contents of a zip file that had been decompressed to
+ memory. Must be called in tandem with zipRead() to avoid memory
+ leaks.
+
+ @param data the pointer to the decompressed file in memory */
+void zipClose(void* data);
+
+
+/**
+ @param flush If true (default), the file is ready for reading as soon
+ as the function returns. If false, the function returns immediately and
+ writes the file in the background.
+ */
+void writeWholeFile(
+ const std::string& filename,
+ const std::string& str,
+ bool flush = true);
+
+/**
+ Creates the directory (which may optionally end in a /)
+ and any parents needed to reach it.
+ */
+void createDirectory(
+ const std::string& dir);
+
+/**
+ Fully qualifies a filename. The filename may contain wildcards,
+ in which case the wildcards will be preserved in the returned value.
+ */
+std::string resolveFilename(const std::string& filename);
+
+/**
+ Appends all files matching filespec to the files array. The names
+ will not contain paths unless includePath == true. These may be
+ relative to the current directory unless the filespec is fully qualified
+ (can be done with resolveFilename).
+ Wildcards can only appear to the right of the last slash in filespec.
+ Works with .zip files used as paths, if filespec is passed in the form
+ C:\...\something.zip\* Does not work recursively with zipfiles (a
+ .zip within a .zip will not work)
+ */
+void getFiles(
+ const std::string& filespec,
+ Array<std::string>& files,
+ bool includePath = false);
+
+/**
+ Appends all directories matching filespec to the files array. The names
+ will not contain paths unless includePath == true. These may be
+ relative to the current directory unless the filespec is fully qualified
+ (can be done with resolveFilename).
+ Does not append special directories "." or "..".
+ Works with .zip files used as paths, if filespec is passed in the form
+ C:\...\something.zip\* Does not work recursively with zipfiles (a
+ .zip within a .zip will not work)
+ */
+void getDirs(
+ const std::string& filespec,
+ Array<std::string>& files,
+ bool includePath = false);
+
+
+/** Returns true if the specified path exists and is a directory */
+bool isDirectory(const std::string& filespec);
+
+
+/** Returns true if the specified filename exists and is a zipfile */
+bool isZipfile(const std::string& filename);
+
+
+/** Returns the length of the file. If
+ filename specifies a path that contains a zipfile, but the
+ contents within are specified correctly, returns the
+ uncompressed size of the requested file. Returns -1 if
+ the file does not exist.
+
+ @param filename the path to test, may contain .zip
+*/
+int64 fileLength(const std::string& filename);
+
+/**
+ Copies the file
+ */
+void copyFile(
+ const std::string& source,
+ const std::string& dest);
+
+/** Returns a temporary file that is open for read/write access. This
+ tries harder than the ANSI tmpfile, so it may succeed when that fails. */
+FILE* createTempFile();
+
+/**
+ Returns true if the given file (or directory) exists.
+
+ @param filename the path to test. must not end in a trailing slash.
+ @param lookInZipfiles if the path does not exist, calls zipfileExists()
+ */
+bool fileExists(
+ const std::string& filename,
+ const bool lookInZipfiles = true);
+
+/**
+ Returns true if the given file (or directory) exists
+ within a zipfile. Called if fileExists initially
+ returns false and the lookInZipfiles flag has been set.
+ Must not end in a trailing slash. Does not work for recursive
+ zipfiles (.zips within another .zip)
+
+ @param filename the path to test
+ @param outZipfile the path to the .zip file
+ @param outInternalFile the path (within the .zip) where the desired file is located, if valid
+
+ */
+bool zipfileExists(
+ const std::string& filename,
+ std::string& outZipfile,
+ std::string& outInternalFile
+ );
+
+bool zipfileExists(const std::string& filename);
+
+/**
+ Parses a filename into four useful pieces.
+
+ Examples:
+
+ c:\a\b\d.e
+ root = "c:\"
+ path = "a" "b"
+ base = "d"
+ ext = "e"
+
+ /a/b/d.e
+ root = "/"
+ path = "a" "b"
+ base = "d"
+ ext = "e"
+
+ /a/b
+ root = "/"
+ path = "a"
+ base = "b"
+ ext = "e"
+
+ */
+void parseFilename(
+ const std::string& filename,
+ std::string& drive,
+ Array<std::string>& path,
+ std::string& base,
+ std::string& ext);
+
+
+/**
+ Returns the part of the filename that includes the base and ext from
+ parseFilename (i.e. everything to the right of the path).
+ */
+std::string filenameBaseExt(const std::string& filename);
+
+/**
+ Returns the extension on a filename.
+ */
+std::string filenameExt(const std::string& filename);
+
+
+/** Returns the portion of a filename to the left of the last period
+ and to the right of the last slash or colon.
+ */
+std::string filenameBase(const std::string& filename);
+
+/** Creates a unique filename base in the current directory using the
+ specified prefix.*/
+std::string generateFilenameBase(const std::string& prefix = "");
+
+/**
+ Returns the drive (if Win32) and path from a filename, including
+ a slash if there was one.
+ <CODE>filenamePath(f) + filenameBaseExt(f) == f</CODE>
+ */
+std::string filenamePath(const std::string& filename);
+
+/** Returns true if '*' or '?' appears in the string */
+bool filenameContainsWildcards(const std::string& filename);
+
+/** Returns true if dst does not exist or src is newer than dst. Works on both files and directories. */
+bool fileIsNewer(const std::string& src, const std::string& dst);
+
+/** Appends file onto dirname, ensuring a / if needed. */
+std::string pathConcat(const std::string& dirname, const std::string& file);
+
+} // namespace
+
+#endif
+
diff --git a/externals/g3dlite/G3D.lib/include/G3D/filter.h b/externals/g3dlite/G3D.lib/include/G3D/filter.h
new file mode 100644
index 00000000000..74a32ad01ea
--- /dev/null
+++ b/externals/g3dlite/G3D.lib/include/G3D/filter.h
@@ -0,0 +1,29 @@
+/**
+ @file G3D/filter.h
+
+ @author Morgan McGuire, matrix@graphics3d.com
+ @created 2007-03-01
+ @edited 2007-03-01
+
+ Copyright 2000-2007, Morgan McGuire.
+ All rights reserved.
+ */
+#ifndef G3D_FILTER_H
+#define G3D_FILTER_H
+
+#include "G3D/platform.h"
+#include "G3D/Array.h"
+#include "G3D/g3dmath.h"
+
+namespace G3D {
+/**
+ Generates a set of 1D gaussian filter coefficients of size N. The coefficients
+ are centered on element (N-1)/2 and have standard deviation given by std. The coefficients
+ are normalized such that the sum across coeff is 1.0.
+
+ Matches the results returned by Matlab <code>fspecial('gaussian', [1, N], std)</code>
+ */
+void gaussian1D(Array<float>& coeff, int N = 5, float std = 0.5f);
+}
+
+#endif
diff --git a/externals/g3dlite/G3D.lib/include/G3D/format.h b/externals/g3dlite/G3D.lib/include/G3D/format.h
new file mode 100644
index 00000000000..f993a74038f
--- /dev/null
+++ b/externals/g3dlite/G3D.lib/include/G3D/format.h
@@ -0,0 +1,44 @@
+/**
+ @file format.h
+
+ @maintainer Morgan McGuire, matrix@graphics3d.com
+
+ @author 2000-09-09
+ @edited 2005-11-03
+
+ Copyright 2000-2005, Morgan McGuire.
+ All rights reserved.
+ */
+
+#ifndef G3D_FORMAT_H
+#define G3D_FORMAT_H
+
+#include "G3D/platform.h"
+#include <string>
+#include <stdio.h>
+#include <cstdarg>
+
+namespace G3D {
+
+/**
+ Produces a string from arguments of the style of printf. This avoids
+ problems with buffer overflows when using sprintf and makes it easy
+ to use the result functionally. This function is fast when the resulting
+ string is under 160 characters (not including terminator) and slower
+ when the string is longer.
+ */
+std::string __cdecl format(
+ const char* fmt
+ ...) G3D_CHECK_PRINTF_ARGS;
+
+/**
+ Like format, but can be called with the argument list from a ... function.
+ */
+std::string vformat(
+ const char* fmt,
+ va_list argPtr) G3D_CHECK_VPRINTF_ARGS;
+
+
+} // namespace
+
+#endif
diff --git a/externals/g3dlite/G3D.lib/include/G3D/g3dmath.h b/externals/g3dlite/G3D.lib/include/G3D/g3dmath.h
new file mode 100644
index 00000000000..0a4f01f11c6
--- /dev/null
+++ b/externals/g3dlite/G3D.lib/include/G3D/g3dmath.h
@@ -0,0 +1,810 @@
+/**
+ @file g3dmath.h
+
+ Math util class.
+
+ @maintainer Morgan McGuire, matrix@graphics3d.com
+ @cite highestBit by Jukka Liimatta
+
+ @created 2001-06-02
+ @edited 2006-01-16
+
+ Copyright 2000-2006, Morgan McGuire.
+ All rights reserved.
+ */
+
+#ifndef G3DMATH_H
+#define G3DMATH_H
+
+#ifdef _MSC_VER
+// Disable conditional expression is constant, which occurs incorrectly on inlined functions
+# pragma warning (push)
+# pragma warning (disable : 4127)
+// disable: "C++ exception handler used"
+# pragma warning (disable : 4530)
+#endif
+
+#include "G3D/platform.h"
+#include <ctype.h>
+#include <float.h>
+#include <limits>
+#include <stdlib.h>
+
+#ifdef _MSC_VER
+ // Visual Studio is missing inttypes.h
+# ifndef PRId64
+# define PRId64 "I64d"
+# endif
+#else
+#include <inttypes.h>
+#endif
+
+/*These defines enable functionality introduced with the 1999 ISO C
+**standard. They must be defined before the inclusion of math.h to
+**engage them. If optimisation is enabled, these functions will be
+**inlined. With optimisation switched off, you have to link in the
+**maths library using -lm.
+*/
+
+#define _ISOC9X_SOURCE1
+#define _ISOC99_SOURCE1
+#define __USE_ISOC9X1
+#define __USE_ISOC991
+
+#include <math.h>
+
+#include "G3D/debug.h"
+
+#undef min
+#undef max
+
+namespace G3D {
+
+#ifdef _MSC_VER
+
+/**
+ Win32 implementation of the C99 fast rounding routines.
+
+ @cite routines are
+ Copyright (C) 2001 Erik de Castro Lopo <erikd AT mega-nerd DOT com>
+
+ Permission to use, copy, modify, distribute, and sell this file for any
+ purpose is hereby granted without fee, provided that the above copyright
+ and this permission notice appear in all copies. No representations are
+ made about the suitability of this software for any purpose. It is
+ provided "as is" without express or implied warranty.
+*/
+
+__inline long int lrint (double flt) {
+ int intgr;
+
+ _asm {
+ fld flt
+ fistp intgr
+ };
+
+ return intgr;
+}
+
+__inline long int lrintf(float flt) {
+ int intgr;
+
+ _asm {
+ fld flt
+ fistp intgr
+ };
+
+ return intgr;
+}
+#endif
+
+
+
+const double fuzzyEpsilon = 0.00001;
+
+/** Returns a reference to a static double.
+ This value should not be tested against directly, instead
+ G3D::isNan() and G3D::isFinite() will return reliable results. */
+inline const double& inf() {
+
+ // double is a standard type and should have infinity
+ static const double i = std::numeric_limits<double>::infinity();
+ return i;
+}
+
+/** Returns a reference to a static double.
+ This value should not be tested against directly, instead
+ G3D::isNan() and G3D::isFinite() will return reliable results. */
+inline const double& nan() {
+
+ // double is a standard type and should have quiet NaN
+ static const double n = std::numeric_limits<double>::quiet_NaN();
+ return n;
+}
+
+/** Returns a reference to a static double. Use instead of G3D_PI. */
+inline const double& pi() {
+ static const double p = 3.1415926535898;
+ return p;
+}
+
+/** Returns a reference to a static double. */
+inline const double& halfPi() {
+ static const double p = pi() / 2.0;
+ return p;
+}
+
+/** Returns a reference to a static double. */
+inline const double& twoPi() {
+ static const double p = pi() * 2.0;;
+ return p;
+}
+
+typedef signed char int8;
+typedef unsigned char uint8;
+typedef short int16;
+typedef unsigned short uint16;
+typedef int int32;
+typedef unsigned int uint32;
+
+#ifdef _MSC_EXTENSIONS
+ typedef __int64 int64;
+ typedef unsigned __int64 uint64;
+#elif ! defined(_MSC_VER)
+ typedef int64_t int64;
+ typedef uint64_t uint64;
+#else
+ typedef long long int64;
+ typedef unsigned long long uint64;
+#endif
+
+typedef float float32;
+typedef double float64;
+
+int iAbs(int iValue);
+int iCeil(double fValue);
+
+/**
+ Clamps the value to the range [low, hi] (inclusive)
+ */
+int iClamp(int val, int low, int hi);
+int16 iClamp(int16 val, int16 low, int16 hi);
+double clamp(double val, double low, double hi);
+float clamp(float val, float low, float hi);
+
+/**
+ Returns a + (b - a) * f;
+ */
+inline double lerp(double a, double b, double f) {
+ return a + (b - a) * f;
+}
+
+inline float lerp(float a, float b, float f) {
+ return a + (b - a) * f;
+}
+
+/**
+ Wraps the value to the range [0, hi) (exclusive
+ on the high end). This is like the clock arithmetic
+ produced by % (modulo) except the result is guaranteed
+ to be positive.
+ */
+int iWrap(int val, int hi);
+
+int iFloor(double fValue);
+
+int iSign(int iValue);
+int iSign(double fValue);
+
+inline int iSign(float f) {
+ return iSign((double)f);
+}
+
+
+/**
+ Fast round to integer using the lrint routine.
+ Typically 6x faster than casting to integer.
+ */
+inline int iRound(double fValue) {
+ return lrint(fValue);
+}
+
+/**
+ Fast round to integer using the lrint routine.
+ Typically 6x faster than casting to integer.
+ */
+inline int iRound(float f) {
+ return lrintf(f);
+}
+
+/**
+ Returns a random number uniformly at random between low and hi
+ (inclusive).
+ */
+int iRandom(int low, int hi);
+
+double abs (double fValue);
+double aCos (double fValue);
+double aSin (double fValue);
+double aTan (double fValue);
+double aTan2 (double fY, double fX);
+double sign (double fValue);
+double square (double fValue);
+
+/**
+ Returns true if the argument is a finite real number.
+ */
+bool isFinite(double x);
+
+/**
+ Returns true if the argument is NaN (not a number).
+ You can't use x == nan to test this because all
+ comparisons against nan return false.
+ */
+bool isNaN(double x);
+
+/**
+ Computes x % 3.
+ */
+int iMod3(int x);
+
+/**
+ Uniform random number between low and hi, inclusive. [low, hi]
+ */
+float uniformRandom(float low = 0.0f, float hi = 1.0f);
+
+/**
+ Normally distributed random number.
+ */
+float gaussRandom(float mean = 0.0f, float stdev = 1.0f);
+
+template <class T>
+inline T min(const T& x, const T& y) {
+ return std::min<T>(x, y);
+}
+
+template <class T>
+inline T min(const T& x, const T& y, const T& z) {
+ return std::min<T>(std::min<T>(x, y), z);
+}
+
+template <class T>
+inline T min(const T& x, const T& y, const T& z, const T& w) {
+ return std::min<T>(std::min<T>(x, y), std::min<T>(z, w));
+}
+
+template <class T>
+inline T max(const T& x, const T& y) {
+ return std::max<T>(x, y);
+}
+
+template <class T>
+inline T max(const T& x, const T& y, const T& z) {
+ return std::max<T>(std::max<T>(x, y), z);
+}
+
+template <class T>
+inline T max(const T& x, const T& y, const T& z, const T& w) {
+ return std::max<T>(std::max<T>(x, y), std::max<T>(z, w));
+}
+
+int iMin(int x, int y);
+int iMax(int x, int y);
+
+double square(double x);
+double sumSquares(double x, double y);
+double sumSquares(double x, double y, double z);
+double distance(double x, double y);
+double distance(double x, double y, double z);
+
+/**
+ Returnes the 0-based index of the highest 1 bit from
+ the left. -1 means the number was 0.
+
+ @cite Based on code by jukka@liimatta.org
+ */
+int highestBit(uint32 x);
+
+/**
+ Note that fuzzyEq(a, b) && fuzzyEq(b, c) does not imply
+ fuzzyEq(a, c), although that will be the case on some
+ occasions.
+ */
+bool fuzzyEq(double a, double b);
+
+/** True if a is definitely not equal to b.
+ Guaranteed false if a == b.
+ Possibly false when a != b.*/
+bool fuzzyNe(double a, double b);
+
+/** Is a strictly greater than b? (Guaranteed false if a <= b).
+ (Possibly false if a > b) */
+bool fuzzyGt(double a, double b);
+
+/** Is a near or greater than b? */
+bool fuzzyGe(double a, double b);
+
+/** Is a strictly less than b? (Guaranteed false if a >= b)*/
+bool fuzzyLt(double a, double b);
+
+/** Is a near or less than b? */
+bool fuzzyLe(double a, double b);
+
+/**
+ Computes 1 / sqrt(x).
+ */
+inline float rsq(float x) {
+ return 1.0f / sqrtf(x);
+}
+
+/**
+ Uses SSE to implement rsq.
+ @cite Nick nicolas@capens.net
+ */
+inline float SSErsq(float x) {
+
+ #if defined(SSE) && defined(G3D_WIN32)
+ __asm {
+ movss xmm0, x
+ rsqrtss xmm0, xmm0
+ movss x, xmm0
+ }
+ return x;
+ #else
+ return 1.0f / sqrt(x);
+ #endif
+}
+
+/**
+ Return the next power of 2 higher than the input
+ If the input is already a power of 2, the output will be the same
+ as the input.
+ */
+int ceilPow2(unsigned int in);
+
+/** Returns 2^x */
+inline int pow2(unsigned int x) {
+ return 1 << x;
+}
+
+
+/** Log base 2 */
+inline float log2(float x) {
+ return ::logf(x) / ::logf(2.0f);
+}
+
+/** Log base 2 */
+inline double log2(double x) {
+ return ::log(x) / ::log(2.0);
+}
+
+/** Log base 2 */
+inline double log2(int x) {
+ return log2((double)x);
+}
+
+
+/**
+ * True if num is a power of two.
+ */
+bool isPow2(int num);
+
+bool isOdd(int num);
+bool isEven(int num);
+
+double toRadians(double deg);
+double toDegrees(double rad);
+
+/**
+ Returns true if x is not exactly equal to 0.0f.
+ */
+inline bool any(float x) {
+ return x != 0;
+}
+
+/**
+ Returns true if x is not exactly equal to 0.0f.
+ */
+inline bool all(float x) {
+ return x != 0;
+}
+
+/**
+ v / v (for DirectX/Cg support)
+ */
+inline float normalize(float v) {
+ return v / v;
+}
+
+/**
+ a * b (for DirectX/Cg support)
+ */
+inline float dot(float a, float b) {
+ return a * b;
+}
+
+
+/**
+ a * b (for DirectX/Cg support)
+ */
+inline float mul(float a, float b) {
+ return a * b;
+}
+
+/**
+ 2^x
+ */
+inline double exp2(double x) {
+ return pow(2.0, x);
+}
+
+inline double rsqrt(double x) {
+ return 1.0 / sqrt(x);
+}
+
+
+/**
+ sin(x)/x
+ */
+inline double sinc(double x) {
+ double r = sin(x) / x;
+
+ if (isNaN(r)) {
+ return 1.0;
+ } else {
+ return r;
+ }
+}
+
+/**
+ Computes a floating point modulo; the result is t wrapped to the range [lo, hi).
+ */
+inline float wrap(float t, float lo, float hi) {
+ if ((t >= lo) && (t < hi)) {
+ return t;
+ }
+
+ debugAssert(hi > lo);
+
+ float interval = hi - lo;
+
+ return t - interval * iFloor((t - lo) / interval);
+}
+
+
+inline double wrap(double t, double lo, double hi) {
+ if ((t >= lo) && (t < hi)) {
+ return t;
+ }
+
+ debugAssert(hi > lo);
+
+ double interval = hi - lo;
+
+ return t - interval * iFloor((t - lo) / interval);
+}
+
+inline double wrap(double t, double hi) {
+ return wrap(t, 0.0, hi);
+}
+
+
+inline bool isNaN(double x) {
+ bool b1 = (x < 0.0);
+ bool b2 = (x >= 0.0);
+ bool b3 = !(b1 || b2);
+ return b3;
+}
+
+inline bool isFinite(double x) {
+ return ! isNaN(x) && (x < G3D::inf()) && (x > -G3D::inf());
+}
+
+//----------------------------------------------------------------------------
+inline int iAbs (int iValue) {
+ return ( iValue >= 0 ? iValue : -iValue );
+}
+
+//----------------------------------------------------------------------------
+inline int iCeil (double fValue) {
+ return int(::ceil(fValue));
+}
+
+//----------------------------------------------------------------------------
+
+inline int iClamp(int val, int low, int hi) {
+ debugAssert(low <= hi);
+ if (val <= low) {
+ return low;
+ } else if (val >= hi) {
+ return hi;
+ } else {
+ return val;
+ }
+}
+
+//----------------------------------------------------------------------------
+
+inline int16 iClamp(int16 val, int16 low, int16 hi) {
+ debugAssert(low <= hi);
+ if (val <= low) {
+ return low;
+ } else if (val >= hi) {
+ return hi;
+ } else {
+ return val;
+ }
+}
+
+//----------------------------------------------------------------------------
+
+inline double clamp(double val, double low, double hi) {
+ debugAssert(low <= hi);
+ if (val <= low) {
+ return low;
+ } else if (val >= hi) {
+ return hi;
+ } else {
+ return val;
+ }
+}
+
+inline float clamp(float val, float low, float hi) {
+ debugAssert(low <= hi);
+ if (val <= low) {
+ return low;
+ } else if (val >= hi) {
+ return hi;
+ } else {
+ return val;
+ }
+}
+//----------------------------------------------------------------------------
+
+inline int iWrap(int val, int hi) {
+ if (val < 0) {
+ return ((val % hi) + hi) % hi;
+ } else {
+ return val % hi;
+ }
+}
+
+//----------------------------------------------------------------------------
+inline int iFloor (double fValue) {
+ return int(::floor(fValue));
+}
+
+//----------------------------------------------------------------------------
+inline int iSign (int iValue) {
+ return ( iValue > 0 ? + 1 : ( iValue < 0 ? -1 : 0 ) );
+}
+
+inline int iSign (double fValue) {
+ return ( fValue > 0.0 ? + 1 : ( fValue < 0.0 ? -1 : 0 ) );
+}
+
+//----------------------------------------------------------------------------
+inline double abs (double fValue) {
+ return double(::fabs(fValue));
+}
+
+//----------------------------------------------------------------------------
+inline double aCos (double fValue) {
+ if ( -1.0 < fValue ) {
+ if ( fValue < 1.0 )
+ return double(::acos(fValue));
+ else
+ return 0.0;
+ } else {
+ return pi();
+ }
+}
+
+//----------------------------------------------------------------------------
+inline double aSin (double fValue) {
+ if ( -1.0 < fValue ) {
+ if ( fValue < 1.0 ) {
+ return double(::asin(fValue));
+ } else {
+ return -halfPi();
+ }
+ } else {
+ return halfPi();
+ }
+}
+
+//----------------------------------------------------------------------------
+inline double aTan (double fValue) {
+ return double(::atan(fValue));
+}
+
+//----------------------------------------------------------------------------
+inline double aTan2 (double fY, double fX) {
+ return double(::atan2(fY, fX));
+}
+
+//----------------------------------------------------------------------------
+inline double sign (double fValue) {
+ if (fValue > 0.0) {
+ return 1.0;
+ }
+
+ if (fValue < 0.0) {
+ return -1.0;
+ }
+
+ return 0.0;
+}
+
+inline float sign (float fValue) {
+ if (fValue > 0.0f) {
+ return 1.0f;
+ }
+
+ if (fValue < 0.0f) {
+ return -1.0f;
+ }
+
+ return 0.0f;
+}
+
+
+inline float uniformRandom(float low, float hi) {
+ return (hi - low) * float(::rand()) / float(RAND_MAX) + low;
+}
+
+//----------------------------------------------------------------------------
+inline double square(double x) {
+ return x * x;
+}
+
+//----------------------------------------------------------------------------
+inline double sumSquares(double x, double y) {
+ return x*x + y*y;
+}
+
+//----------------------------------------------------------------------------
+inline double sumSquares(double x, double y, double z) {
+ return x*x + y*y + z*z;
+}
+
+//----------------------------------------------------------------------------
+inline double distance(double x, double y) {
+ return sqrt(sumSquares(x, y));
+}
+
+//----------------------------------------------------------------------------
+inline double distance(double x, double y, double z) {
+ return sqrt(sumSquares(x, y, z));
+}
+
+//----------------------------------------------------------------------------
+
+/** @deprecated use G3D::min */
+inline int iMin(int x, int y) {
+ return (x >= y) ? y : x;
+}
+
+//----------------------------------------------------------------------------
+/** @deprecated use G3D::min */
+inline int iMax(int x, int y) {
+ return (x >= y) ? x : y;
+}
+
+//----------------------------------------------------------------------------
+inline int ceilPow2(unsigned int in) {
+ in -= 1;
+
+ in |= in >> 16;
+ in |= in >> 8;
+ in |= in >> 4;
+ in |= in >> 2;
+ in |= in >> 1;
+
+ return in + 1;
+}
+
+inline bool isPow2(int num) {
+ return ((num & -num) == num);
+}
+
+inline bool isOdd(int num) {
+ return (num & 1) == 1;
+}
+
+inline bool isEven(int num) {
+ return (num & 1) == 0;
+}
+
+inline double toRadians(double deg) {
+ return deg * pi() / 180.0;
+}
+
+inline double toDegrees(double rad) {
+ return rad * 180.0 / pi();
+}
+
+inline float toRadians(float deg) {
+ return deg * (float)pi() / 180.0f;
+}
+
+inline float toDegrees(float rad) {
+ return rad * 180.0f / (float)pi();
+}
+
+inline float toRadians(int deg) {
+ return deg * (float)pi() / 180.0f;
+}
+
+inline float toDegrees(int rad) {
+ return rad * 180.0f / (float)pi();
+}
+/**
+ Computes an appropriate epsilon for comparing a and b.
+ */
+inline double eps(double a, double b) {
+ // For a and b to be nearly equal, they must have nearly
+ // the same magnitude. This means that we can ignore b
+ // since it either has the same magnitude or the comparison
+ // will fail anyway.
+ (void)b;
+ const double aa = abs(a) + 1;
+ if (aa == inf()) {
+ return fuzzyEpsilon;
+ } else {
+ return fuzzyEpsilon * aa;
+ }
+}
+
+inline bool fuzzyEq(double a, double b) {
+ return (a == b) || (abs(a - b) <= eps(a, b));
+}
+
+inline bool fuzzyNe(double a, double b) {
+ return ! fuzzyEq(a, b);
+}
+
+inline bool fuzzyGt(double a, double b) {
+ return a > b + eps(a, b);
+}
+
+inline bool fuzzyGe(double a, double b) {
+ return a > b - eps(a, b);
+}
+
+inline bool fuzzyLt(double a, double b) {
+ return a < b - eps(a, b);
+}
+
+inline bool fuzzyLe(double a, double b) {
+ return a < b + eps(a, b);
+}
+
+inline int iMod3(int x) {
+ return x % 3;
+}
+
+/**
+ Given a 32-bit integer, returns the integer with the bytes in the opposite order.
+ */
+inline uint32 flipEndian32(const uint32 x) {
+ return (x << 24) | ((x & 0xFF00) << 8) |
+ ((x & 0xFF0000) >> 8) | ((x & 0xFF000000) >> 24);
+}
+
+/**
+ Given a 16-bit integer, returns the integer with the bytes in the opposite order.
+ */
+inline uint16 flipEndian16(const uint16 x) {
+ return (x << 8) | ((x & 0xFF00) >> 8);
+}
+
+
+} // namespace
+
+#ifdef _MSC_VER
+# pragma warning (pop)
+#endif
+
+#endif
+
diff --git a/externals/g3dlite/G3D.lib/include/G3D/platform.h b/externals/g3dlite/G3D.lib/include/G3D/platform.h
new file mode 100644
index 00000000000..be9ec4d5123
--- /dev/null
+++ b/externals/g3dlite/G3D.lib/include/G3D/platform.h
@@ -0,0 +1,256 @@
+/**
+ @file platform.h
+
+ #defines for platform specific issues.
+
+ @maintainer Morgan McGuire, matrix@graphics3d.com
+
+ @created 2003-06-09
+ @edited 2007-07-30
+ */
+
+#ifndef G3D_PLATFORM_H
+#define G3D_PLATFORM_H
+
+/**
+ The version number of G3D in the form: MmmBB ->
+ version M.mm [beta BB]
+ */
+#define G3D_VER 70100
+
+#if defined(G3D_RELEASEDEBUG)
+# define G3D_DEBUGRELEASE
+#endif
+
+#if defined(G3D_DEBUGRELEASE) && defined(_DEBUG)
+# undef _DEBUG
+#endif
+
+#if !defined(G3D_DEBUG) && (defined(_DEBUG) || defined(G3D_DEBUGRELEASE))
+# define G3D_DEBUG
+#endif
+
+#ifdef _MSC_VER
+ #define G3D_WIN32
+#elif defined(__FreeBSD__) || defined(__OpenBSD__)
+ #define G3D_FREEBSD
+ #define G3D_LINUX
+#elif defined(__linux__)
+ #define G3D_LINUX
+#elif defined(__APPLE__)
+ #define G3D_OSX
+
+ // Prevent OS X fp.h header from being included; it defines
+ // pi as a constant, which creates a conflict with G3D
+#define __FP__
+#else
+ #error Unknown platform
+#endif
+
+
+// Default to compiling with SSE, but if you want to compile
+// without installing SP5.0 and the Processor Pack on Windows, compile with NO_SSE
+// defined (can be passed to the compiler command line with /D "NO_SSE")
+#if !defined(NO_SSE)
+ #define SSE
+#endif
+
+// On g++, recognize cases where the -msse2 flag was not specified
+#if defined(SSE) && defined(__GNUC__) && ! defined (__SSE__)
+# undef SSE
+#endif
+
+
+// Verify that the supported compilers are being used and that this is a known
+// processor.
+
+#ifdef G3D_LINUX
+# ifndef __GNUC__
+# error G3D only supports the gcc compiler on Linux.
+# endif
+#endif
+
+#ifdef G3D_OSX
+# ifndef __GNUC__
+# error G3D only supports the gcc compiler on OS X.
+# endif
+
+# if defined(__i386__)
+# define G3D_OSX_INTEL
+# elif defined(__PPC__)
+# define G3D_OSX_PPC
+# else
+# define G3D_OSX_UNKNOWN
+# endif
+
+#endif
+
+
+#ifdef _MSC_VER
+// Microsoft Visual C++ 8.0 ("Express") = 1400
+// Microsoft Visual C++ 7.1 ("2003") _MSC_VER = 1310
+// Microsoft Visual C++ 7.0 ("2002") _MSC_VER = 1300
+// Microsoft Visual C++ 6.0 _MSC_VER = 1200
+// Microsoft Visual C++ 5.0 _MSC_VER = 1100
+
+// Turn off warnings about deprecated C routines (TODO: revisit)
+# pragma warning (disable : 4996)
+
+// Turn off "conditional expression is constant" warning; MSVC generates this
+// for debug assertions in inlined methods.
+# pragma warning (disable : 4127)
+
+# define G3D_DEPRECATED __declspec(deprecated)
+
+// Prevent Winsock conflicts by hiding the winsock API
+# ifndef _WINSOCKAPI_
+# define _G3D_INTERNAL_HIDE_WINSOCK_
+# define _WINSOCKAPI_
+# endif
+
+// Disable 'name too long for browse information' warning
+# pragma warning (disable : 4786)
+// TODO: remove
+# pragma warning (disable : 4244)
+
+# define ZLIB_WINAPI
+
+# define restrict
+
+# define G3D_CHECK_PRINTF_ARGS
+# define G3D_CHECK_VPRINTF_ARGS
+# define G3D_CHECK_PRINTF_METHOD_ARGS
+# define G3D_CHECK_VPRINTF_METHOD_ARGS
+
+ // On MSVC, we need to link against the multithreaded DLL version of
+ // the C++ runtime because that is what SDL and ZLIB are compiled
+ // against. This is not the default for MSVC, so we set the following
+ // defines to force correct linking.
+ //
+ // For documentation on compiler options, see:
+ // http://msdn.microsoft.com/library/default.asp?url=/library/en-us/vccore/html/_core_.2f.md.2c_2f.ml.2c_2f.mt.2c_2f.ld.asp
+ // http://msdn.microsoft.com/library/default.asp?url=/library/en-us/vccore98/HTML/_core_Compiler_Reference.asp
+ //
+
+ // DLL runtime
+ #ifndef _DLL
+ #define _DLL
+ #endif
+
+ // Multithreaded runtime
+ #ifndef _MT
+ #define _MT 1
+ #endif
+
+ // Ensure that we aren't forced into the static lib
+ #ifdef _STATIC_CPPLIB
+ #undef _STATIC_CPPLIB
+ #endif
+
+ #ifdef _DEBUG
+ #pragma comment (linker, "/NODEFAULTLIB:LIBCMTD.LIB")
+ #pragma comment (linker, "/NODEFAULTLIB:LIBCPMTD.LIB")
+ #pragma comment (linker, "/NODEFAULTLIB:LIBCPD.LIB")
+ #pragma comment (linker, "/DEFAULTLIB:MSVCPRTD.LIB")
+ #pragma comment(linker, "/NODEFAULTLIB:LIBCD.LIB")
+ #pragma comment(linker, "/DEFAULTLIB:MSVCRTD.LIB")
+ #else
+ #pragma comment(linker, "/NODEFAULTLIB:LIBC.LIB")
+ #pragma comment(linker, "/DEFAULTLIB:MSVCRT.LIB")
+ #pragma comment (linker, "/NODEFAULTLIB:LIBCMT.LIB")
+ #pragma comment (linker, "/NODEFAULTLIB:LIBCPMT.LIB")
+ #pragma comment(linker, "/NODEFAULTLIB:LIBCP.LIB")
+ #pragma comment (linker, "/DEFAULTLIB:MSVCPRT.LIB")
+ #endif
+
+ // Now set up external linking
+
+# ifdef _DEBUG
+ // zlib was linked against the release MSVCRT; force
+ // the debug version.
+# pragma comment(linker, "/NODEFAULTLIB:MSVCRT.LIB")
+# endif
+
+
+# ifndef WIN32_LEAN_AND_MEAN
+# define WIN32_LEAN_AND_MEAN 1
+# endif
+
+
+# define NOMINMAX 1
+# ifndef _WIN32_WINNT
+# define _WIN32_WINNT 0x0500
+# endif
+# include <windows.h>
+# undef WIN32_LEAN_AND_MEAN
+# undef NOMINMAX
+
+# ifdef _G3D_INTERNAL_HIDE_WINSOCK_
+# undef _G3D_INTERNAL_HIDE_WINSOCK_
+# undef _WINSOCKAPI_
+# endif
+
+
+# define G3D_START_AT_MAIN()\
+int WINAPI G3D_WinMain(HINSTANCE hInst, HINSTANCE hPrev, LPSTR szCmdLine, int sw);\
+int WINAPI WinMain(HINSTANCE hInst, HINSTANCE hPrev, LPSTR szCmdLine, int sw) {\
+ return G3D_WinMain(hInst, hPrev, szCmdLine, sw);\
+}
+
+#else
+
+# define G3D_START_AT_MAIN()
+
+#endif // win32
+
+#ifdef __GNUC__
+
+# include <stdint.h>
+
+# if __STDC_VERSION__ < 199901
+# define restrict __restrict__
+# endif
+
+# define G3D_DEPRECATED __attribute__((__deprecated__))
+
+// setup function calling conventions
+# if defined(__i386__) && ! defined(__x86_64__)
+
+# ifndef __cdecl
+# define __cdecl __attribute__((cdecl))
+# endif
+
+# ifndef __stdcall
+# define __stdcall __attribute__((stdcall))
+# endif
+
+# elif defined(__x86_64__) || defined(__powerpc__)
+
+# ifndef __cdecl
+# define __cdecl
+# endif
+
+# ifndef __stdcall
+# define __stdcall
+# endif
+# endif // calling conventions
+
+# define G3D_CHECK_PRINTF_METHOD_ARGS __attribute__((__format__(__printf__, 2, 3)))
+# define G3D_CHECK_VPRINTF_METHOD_ARGS __attribute__((__format__(__printf__, 2, 0)))
+# define G3D_CHECK_PRINTF_ARGS __attribute__((__format__(__printf__, 1, 2)))
+# define G3D_CHECK_VPRINTF_ARGS __attribute__((__format__(__printf__, 1, 0)))
+#endif
+
+
+/**
+ @def STR(expression)
+
+ Creates a string from the expression. Frequently used with G3D::Shader
+ to express shading programs inline.
+
+ <CODE>STR(this becomes a string)<PRE> evaluates the same as <CODE>"this becomes a string"</CODE>
+ */
+#define STR(x) #x
+
+// Header guard
+#endif
diff --git a/externals/g3dlite/G3D.lib/include/G3D/prompt.h b/externals/g3dlite/G3D.lib/include/G3D/prompt.h
new file mode 100644
index 00000000000..e25b955ab28
--- /dev/null
+++ b/externals/g3dlite/G3D.lib/include/G3D/prompt.h
@@ -0,0 +1,67 @@
+/**
+ @file prompt.h
+
+ @maintainer Morgan McGuire, matrix@graphics3d.com
+ @cite Windows GUI code by Max McGuire
+
+ @created 2001-08-26
+ @edited 2006-08-13
+ */
+
+#ifndef G3D_PROMPT_H
+#define G3D_PROMPT_H
+
+#include "platform.h"
+#include <string>
+
+namespace G3D {
+
+/**
+ Prints a prompt to stdout and waits for user input. The return value is
+ the number of the user's choice (the first is 0, if there are no
+ choices, returns 0).
+
+ @param useGui Under Win32, use a GUI, not stdout prompt.
+ @param windowTitle The title for the prompt window
+ @param promptx The text string to prompt the user with
+ @param choice An array of strings that are the choices the user may make
+ @param numChoices The length of choice.
+
+ @cite Windows dialog interface by Max McGuire, mmcguire@ironlore.com
+ @cite Font setting code by Kurt Miller, kurt@flipcode.com
+ */
+int prompt(
+ const char* windowTitle,
+ const char* promptx,
+ const char** choice,
+ int numChoices,
+ bool useGui);
+
+/**
+ Prints a prompt and waits for user input. The return value is
+ the number of the user's choice (the first is 0, if there are no
+ choices, returns 0).
+ <P>Uses GUI under Win32, stdout prompt otherwise.
+ */
+inline int prompt(
+ const char* windowTitle,
+ const char* promptx,
+ const char** choice,
+ int numChoices) {
+
+ return prompt(windowTitle, promptx, choice, numChoices, true);
+}
+
+
+/**
+ Displays a GUI prompt with "Ok" as the only choice.
+ */
+void msgBox(
+ const std::string& message,
+ const std::string& title = "Message");
+
+
+}; // namespace
+
+#endif
+
diff --git a/externals/g3dlite/G3D.lib/include/G3D/serialize.h b/externals/g3dlite/G3D.lib/include/G3D/serialize.h
new file mode 100644
index 00000000000..2382c0ee0fd
--- /dev/null
+++ b/externals/g3dlite/G3D.lib/include/G3D/serialize.h
@@ -0,0 +1,30 @@
+#ifndef G3D_SERIALIZE_H
+#define G3D_SERIALIZE_H
+
+#include "G3D/BinaryInput.h"
+#include "G3D/BinaryOutput.h"
+#include "G3D/Array.h"
+
+namespace G3D {
+
+
+template<typename T>
+void serialize(const Array<T>& array, BinaryOutput& b) {
+ b.writeInt32(array.size());
+ for (int i = 0; i < array.size(); ++i) {
+ serialize(array[i], b);
+ }
+}
+
+template<typename T>
+void deserialize(Array<T>& array, BinaryInput& b) {
+ int N = b.readInt32();
+ array.resize(N);
+ for (int i = 0; i < array.size(); ++i) {
+ deserialize(array[i], b);
+ }
+}
+
+}
+
+#endif
diff --git a/externals/g3dlite/G3D.lib/include/G3D/splinefunc.h b/externals/g3dlite/G3D.lib/include/G3D/splinefunc.h
new file mode 100644
index 00000000000..a9daad9b578
--- /dev/null
+++ b/externals/g3dlite/G3D.lib/include/G3D/splinefunc.h
@@ -0,0 +1,118 @@
+/**
+ @file spline.h
+
+ @maintainer Morgan McGuire, matrix@graphics3d.com
+
+ @created 2004-07-25
+ @edited 2007-05-05
+ */
+
+#ifndef G3D_SPLINEFUNC_H
+#define G3D_SPLINEFUNC_H
+
+namespace G3D {
+
+#include "G3D/platform.h"
+#include "G3D/debug.h"
+#include "G3D/Array.h"
+#include "G3D/g3dmath.h"
+
+/**
+ Interpolates a property according to a piecewise linear spline. This provides
+ C0 continuity but the derivatives are not smooth.
+ <P>
+ Example:
+ <CODE>
+ const double times[] = {MIDNIGHT, SUNRISE - HOUR, SUNRISE, SUNRISE + sunRiseAndSetTime / 4, SUNRISE + sunRiseAndSetTime, SUNSET - sunRiseAndSetTime, SUNSET - sunRiseAndSetTime / 2, SUNSET, SUNSET + HOUR/2, DAY};
+ const Color3 color[] = {Color3(0, .0, .1), Color3(0, .0, .1), Color3::black(), Color3::black(), Color3::white() * .25, Color3::white() * .25, Color3(.5, .2, .2), Color3(.05, .05, .1), Color3(0, .0, .1), Color3(0, .0, .1)};
+ ambient = linearSpline(time, times, color, 10);
+ </CODE>
+
+ See also G3D::Spline
+
+ @param x The spline is a function of x; this is the sample to choose.
+ @param controlX controlX[i], controlY[i] is a control points. It is assumed
+ that controlX are strictly increasing. XType must support
+ the "<" operator and a subtraction operator that returns
+ a number.
+ @param controlY YType must support multiplication and addition.
+ @param numControl The number of control points.
+ */
+template<class XType, class YType>
+YType linearSpline(double x, const XType* controlX, const YType* controlY, int numControl) {
+ debugAssert(numControl >= 1);
+
+ // Off the beginning
+ if ((numControl == 1) || (x < controlX[0])) {
+ return controlY[0];
+ }
+
+ for (int i = 1; i < numControl; ++i) {
+ if (x < controlX[i]) {
+ const double alpha = (double)(controlX[i] - x) / (controlX[i] - controlX[i - 1]);
+ return controlY[i] * (1 - alpha) + controlY[i - 1] * alpha;
+ }
+ }
+
+ // Off the end
+ return controlY[numControl - 1];
+}
+
+
+ /** See also G3D::Spline*/
+template<class YType> YType cyclicCatmullRomSpline(
+ double t,
+ const YType* controlY,
+ int numPoints) {
+
+ debugAssert(numPoints >= 3);
+
+ t = wrap(t, numPoints);
+
+ // Find the indices of adjacent control points
+ int i = iFloor(t);
+
+ // Compute the distance from the control point
+ t = t - i;
+
+ // Shift back one point for correct indexing
+ i += numPoints - 1;
+
+ // Pick up four control points
+ const YType& P0 = controlY[(i + 0) % numPoints];
+ const YType& P1 = controlY[(i + 1) % numPoints];
+ const YType& P2 = controlY[(i + 2) % numPoints];
+ const YType& P3 = controlY[(i + 3) % numPoints];
+
+ return 0.5 * ((2 * P1) +
+ (-P0 + P2) * t +
+ (2*P0 - 5*P1 + 4*P2 - P3) * t*t +
+ (-P0 + 3*P1- 3*P2 + P3) * t*t*t);
+}
+
+/**
+ A cubic spline with regularly spaced
+ control points. The spline interpolates
+ the control points. The spline
+ will wrap from the last point back to the first.
+
+ The t parameter is on the range [0, controlY.size()],
+ where integers correspond to control points exactly.
+
+ See also G3D::Spline
+
+ @cite http://www.mvps.org/directx/articles/catmull/
+*/
+template<class YType> YType cyclicCatmullRomSpline(
+ double t,
+ const Array<YType>& controlY) {
+
+ int numPoints = controlY.size();
+ return cyclicCatmullRomSpline(t, controlY.getCArray(), numPoints);
+}
+
+}
+
+#endif
+
+
diff --git a/externals/g3dlite/G3D.lib/include/G3D/stringutils.h b/externals/g3dlite/G3D.lib/include/G3D/stringutils.h
new file mode 100644
index 00000000000..20a2ff6e795
--- /dev/null
+++ b/externals/g3dlite/G3D.lib/include/G3D/stringutils.h
@@ -0,0 +1,130 @@
+/**
+ @file stringutils.h
+
+ @maintainer Morgan McGuire, matrix@graphics3d.com
+
+ @author 2000-09-09
+ @edited 2008-08-05
+ */
+
+#ifndef G3D_STRINGUTILS_H
+#define G3D_STRINGUTILS_H
+
+#include "G3D/platform.h"
+#include "G3D/Array.h"
+#include <cstring>
+
+namespace G3D {
+
+extern const char* NEWLINE;
+
+/**
+ Returns true if the test string begins with the pattern string.
+ */
+bool beginsWith(
+ const std::string& test,
+ const std::string& pattern);
+
+/**
+ Returns true if the test string ends with the pattern string.
+ */
+bool endsWith(
+ const std::string& test,
+ const std::string& pattern);
+
+/**
+ Produces a new string that is the input string
+ wrapped at a certain number of columns (where
+ the line is broken at the latest space before the
+ column limit.) Platform specific NEWLINEs
+ are inserted to wrap.
+ */
+std::string wordWrap(
+ const std::string& input,
+ int numCols);
+
+/**
+ A comparison function for passing to Array::sort.
+ */
+int stringCompare(
+ const std::string& s1,
+ const std::string& s2);
+
+int stringPtrCompare(
+ const std::string* s1,
+ const std::string* s2);
+
+/**
+ Returns a new string that is an uppercase version of x.
+ */
+std::string toUpper(
+ const std::string& x);
+
+std::string toLower(
+ const std::string& x);
+
+/**
+ Splits x at each occurance of splitChar.
+ */
+G3D::Array<std::string> stringSplit(
+ const std::string& x,
+ char splitChar);
+
+/**
+ joinChar is not inserted at the beginning or end, just in between
+ elements.
+ */
+std::string stringJoin(
+ const G3D::Array<std::string>& a,
+ char joinChar);
+
+std::string stringJoin(
+ const G3D::Array<std::string>& a,
+ const std::string& joinStr);
+
+/**
+ Strips whitespace from both ends of the string.
+ */
+std::string trimWhitespace(
+ const std::string& s);
+
+/** These standard C functions are renamed for clarity/naming
+ conventions and to return bool, not int.
+ */
+inline bool isWhiteSpace(const unsigned char c) {
+ return isspace(c) != 0;
+}
+
+/** These standard C functions are renamed for clarity/naming
+ conventions and to return bool, not int.
+ */
+inline bool isNewline(const unsigned char c) {
+ return (c == '\n') || (c == '\r');
+}
+
+/** These standard C functions are renamed for clarity/naming
+ conventions and to return bool, not int.
+ */
+inline bool isDigit(const unsigned char c) {
+ return isdigit(c) != 0;
+}
+
+/** These standard C functions are renamed for clarity/naming
+ conventions and to return bool, not int.
+ */
+inline bool isLetter(const unsigned char c) {
+ return isalpha(c) != 0;
+}
+
+inline bool isSlash(const unsigned char c) {
+ return (c == '\\') || (c == '/');
+}
+
+inline bool isQuote(const unsigned char c) {
+ return (c == '\'') || (c == '\"');
+}
+
+}; // namespace
+
+#endif
+
diff --git a/externals/g3dlite/G3D.lib/include/G3D/uint128.h b/externals/g3dlite/G3D.lib/include/G3D/uint128.h
new file mode 100644
index 00000000000..3e970cb2a7e
--- /dev/null
+++ b/externals/g3dlite/G3D.lib/include/G3D/uint128.h
@@ -0,0 +1,51 @@
+/**
+ @file uint128.h
+
+ @maintainer Morgan McGuire, matrix@graphics3d.com
+ @author Kyle Whitson
+
+ @created 2008-07-17
+ @edited 2008-07-17
+ */
+
+#ifndef G3D_UINT128_H
+#define G3D_UINT128_H
+
+#include "G3D/g3dmath.h"
+
+namespace G3D {
+
+/** Limited functionality 128-bit unsigned integer. This is primarily to support FNV hashing and other
+ cryptography applications. See the GMP library for high-precision C++ math support. */
+class uint128 {
+public:
+
+ G3D::uint64 hi;
+ G3D::uint64 lo;
+
+ uint128(const uint64& lo);
+
+ uint128(const uint64& hi, const uint64& lo);
+
+ uint128& operator+=(const uint128& x);
+
+ uint128& operator*=(const uint128& x);
+
+ uint128& operator^=(const uint128& x);
+
+ uint128& operator&=(const uint128& x);
+
+ uint128& operator|=(const uint128& x);
+
+ bool operator==(const uint128& x);
+
+ uint128& operator>>=(const int x);
+
+ uint128& operator<<=(const int x);
+
+ uint128 operator&(const uint128& x);
+
+};
+}
+
+#endif
diff --git a/externals/g3dlite/G3D.lib/include/G3D/vectorMath.h b/externals/g3dlite/G3D.lib/include/G3D/vectorMath.h
new file mode 100644
index 00000000000..aedc731e7f8
--- /dev/null
+++ b/externals/g3dlite/G3D.lib/include/G3D/vectorMath.h
@@ -0,0 +1,235 @@
+/**
+ @file vectorMath.h
+
+ Function aliases for popular vector methods.
+
+ @maintainer Morgan McGuire, matrix@graphics3d.com
+
+ @created: 2001-06-02
+ @edited: 2004-02-02
+ Copyright 2000-2004, Morgan McGuire.
+ All rights reserved.
+ */
+
+#ifndef G3D_VECTORMATH_H
+#define G3D_VECTORMATH_H
+
+#include "G3D/platform.h"
+#include "G3D/g3dmath.h"
+#include "G3D/Vector2.h"
+#include "G3D/Vector3.h"
+#include "G3D/Vector4.h"
+#include "G3D/Matrix3.h"
+#include "G3D/Matrix4.h"
+#include "G3D/Color1.h"
+#include "G3D/Color3.h"
+#include "G3D/Color4.h"
+
+
+namespace G3D {
+
+
+inline Matrix4 mul(const Matrix4& a, const Matrix4& b) {
+ return a * b;
+}
+
+inline Vector4 mul(const Matrix4& m, const Vector4& v) {
+ return m * v;
+}
+
+inline Vector3 mul(const Matrix3& m, const Vector3& v) {
+ return m * v;
+}
+
+inline Matrix3 mul(const Matrix3& a, const Matrix3& b) {
+ return a * b;
+}
+
+inline float dot(const Vector2& a, const Vector2& b) {
+ return a.dot(b);
+}
+
+inline float dot(const Vector3& a, const Vector3& b) {
+ return a.dot(b);
+}
+
+inline float dot(const Vector4& a, const Vector4& b) {
+ return a.dot(b);
+}
+
+inline Vector2 normalize(const Vector2& v) {
+ return v / v.length();
+}
+
+inline Vector3 normalize(const Vector3& v) {
+ return v / v.magnitude();
+}
+
+inline Vector4 normalize(const Vector4& v) {
+ return v / v.length();
+}
+
+inline Vector2 abs(const Vector2& v) {
+ return Vector2(::fabsf(v.x), ::fabsf(v.y));
+}
+
+inline Vector3 abs(const Vector3& v) {
+ return Vector3(::fabsf(v.x), ::fabsf(v.y), ::fabsf(v.z));
+}
+
+inline Vector4 abs(const Vector4& v) {
+ return Vector4(::fabsf(v.x), ::fabsf(v.y), ::fabsf(v.z), ::fabsf(v.w));
+}
+
+inline bool all(const Vector2& v) {
+ return (v.x != 0) && (v.y != 0);
+}
+
+inline bool all(const Vector3& v) {
+ return (v.x != 0) && (v.y != 0) && (v.z != 0);
+}
+
+inline bool all(const Vector4& v) {
+ return (v.x != 0) && (v.y != 0) && (v.z != 0) && (v.w != 0);
+}
+
+inline bool any(const Vector2& v) {
+ return (v.x != 0) || (v.y != 0);
+}
+
+inline bool any(const Vector3& v) {
+ return (v.x != 0) || (v.y != 0) || (v.z != 0);
+}
+
+inline bool any(const Vector4& v) {
+ return (v.x != 0) || (v.y != 0) || (v.z != 0) || (v.w != 0);
+}
+
+inline Vector2 clamp(const Vector2& v, const Vector2& a, const Vector2& b) {
+ return v.clamp(a, b);
+}
+
+inline Vector3 clamp(const Vector3& v, const Vector3& a, const Vector3& b) {
+ return v.clamp(a, b);
+}
+
+inline Vector4 clamp(const Vector4& v, const Vector4& a, const Vector4& b) {
+ return v.clamp(a, b);
+}
+
+inline Vector2 lerp(const Vector2& v1, const Vector2& v2, float f) {
+ return v1.lerp(v2, f);
+}
+
+inline Vector3 lerp(const Vector3& v1, const Vector3& v2, float f) {
+ return v1.lerp(v2, f);
+}
+
+inline Vector4 lerp(const Vector4& v1, const Vector4& v2, float f) {
+ return v1.lerp(v2, f);
+}
+
+inline Color1 lerp(const Color1& v1, const Color1& v2, float f) {
+ return v1.lerp(v2, f);
+}
+
+inline Color3 lerp(const Color3& v1, const Color3& v2, float f) {
+ return v1.lerp(v2, f);
+}
+
+inline Color4 lerp(const Color4& v1, const Color4& v2, float f) {
+ return v1.lerp(v2, f);
+}
+
+inline Vector3 cross(const Vector3& v1, const Vector3& v2) {
+ return v1.cross(v2);
+}
+
+inline double determinant(const Matrix3& m) {
+ return m.determinant();
+}
+
+inline double determinant(const Matrix4& m) {
+ return m.determinant();
+}
+
+inline Vector2 min(const Vector2& v1, const Vector2& v2) {
+ return v1.min(v2);
+}
+
+inline Vector3 min(const Vector3& v1, const Vector3& v2) {
+ return v1.min(v2);
+}
+
+inline Vector4 min(const Vector4& v1, const Vector4& v2) {
+ return v1.min(v2);
+}
+
+inline Color3 min(const Color3& v1, const Color3& v2) {
+ return v1.min(v2);
+}
+
+inline Color4 min(const Color4& v1, const Color4& v2) {
+ return v1.min(v2);
+}
+
+inline Vector2 max(const Vector2& v1, const Vector2& v2) {
+ return v1.max(v2);
+}
+
+inline Vector3 max(const Vector3& v1, const Vector3& v2) {
+ return v1.max(v2);
+}
+
+inline Vector4 max(const Vector4& v1, const Vector4& v2) {
+ return v1.max(v2);
+}
+
+inline Color3 max(const Color3& v1, const Color3& v2) {
+ return v1.max(v2);
+}
+
+inline Color4 max(const Color4& v1, const Color4& v2) {
+ return v1.max(v2);
+}
+
+inline Vector2 sign(const Vector2& v) {
+ return Vector2((float)sign(v.x), (float)sign(v.y));
+}
+
+inline Vector3 sign(const Vector3& v) {
+ return Vector3((float)sign(v.x), (float)sign(v.y), (float)sign(v.z));
+}
+
+inline Vector4 sign(const Vector4& v) {
+ return Vector4((float)sign(v.x), (float)sign(v.y), (float)sign(v.z), (float)sign(v.w));
+}
+
+inline float length(float v) {
+ return ::fabsf(v);
+}
+
+inline float length(const Vector2& v) {
+ return v.length();
+}
+
+inline float length(const Vector3& v) {
+ return v.magnitude();
+}
+
+inline float length(const Vector4& v) {
+ return v.length();
+}
+
+/**
+ Computes the log of each component. Useful for
+ inverting the monitor gamma function or simulating
+ perceptual response.
+ */
+inline Color3 log(const Color3& c) {
+ return Color3(::logf(c.r), ::logf(c.g), ::logf(c.b));
+}
+
+}
+
+#endif
diff --git a/externals/g3dlite/G3D.lib/source/AABox.cpp b/externals/g3dlite/G3D.lib/source/AABox.cpp
new file mode 100644
index 00000000000..9e871aa8af7
--- /dev/null
+++ b/externals/g3dlite/G3D.lib/source/AABox.cpp
@@ -0,0 +1,342 @@
+/**
+ @file AABox.cpp
+
+ @maintainer Morgan McGuire, matrix@graphics3d.com
+
+ @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 {
+
+
+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::inf()) &&
+ (abs(hi.x) < G3D::inf()) &&
+ (abs(lo.y) < G3D::inf()) &&
+ (abs(hi.y) < G3D::inf()) &&
+ (abs(lo.z) < G3D::inf()) &&
+ (abs(hi.z) < G3D::inf());
+
+ // 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::inf()) &&
+ (abs(hi.x) < G3D::inf()) &&
+ (abs(lo.y) < G3D::inf()) &&
+ (abs(hi.y) < G3D::inf()) &&
+ (abs(lo.z) < G3D::inf()) &&
+ (abs(hi.z) < G3D::inf());
+
+ // 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/G3D.lib/source/AnyVal.cpp b/externals/g3dlite/G3D.lib/source/AnyVal.cpp
new file mode 100644
index 00000000000..45fb41df534
--- /dev/null
+++ b/externals/g3dlite/G3D.lib/source/AnyVal.cpp
@@ -0,0 +1,1381 @@
+/**
+ @file AnyVal.cpp
+ @author Morgan McGuire
+ @maintainer Morgan McGuire
+ @created 2006-06-11
+ @edited 2008-07-14
+ */
+
+#include "G3D/AnyVal.h"
+#include "G3D/Array.h"
+#include "G3D/stringutils.h"
+#include "G3D/Table.h"
+#include "G3D/Vector2.h"
+#include "G3D/Vector3.h"
+#include "G3D/Vector4.h"
+#include "G3D/Color1.h"
+#include "G3D/Color3.h"
+#include "G3D/Color4.h"
+#include "G3D/Matrix2.h"
+#include "G3D/Matrix3.h"
+#include "G3D/Matrix4.h"
+#include "G3D/Rect2D.h"
+#include "G3D/AABox.h"
+#include "G3D/CoordinateFrame.h"
+#include "G3D/Quat.h"
+#include "G3D/TextInput.h"
+#include "G3D/TextOutput.h"
+#include "G3D/BinaryInput.h"
+#include "G3D/BinaryOutput.h"
+
+namespace G3D {
+
+AnyVal AnyVal::fromFile(const std::string& filename) {
+ TextInput t(filename);
+ return AnyVal(t);
+}
+
+
+void AnyVal::load(const std::string& filename) {
+ *this = fromFile(filename);
+}
+
+
+void AnyVal::save(const std::string& filename) const {
+ TextOutput t(filename);
+ serialize(t);
+ t.commit();
+}
+
+
+AnyVal::AnyVal() : m_type(NIL), m_value(NULL), m_referenceCount(NULL) {
+}
+
+
+AnyVal::AnyVal(bool b) : m_type(BOOLEAN), m_value(new bool(b)), m_referenceCount(NULL) {
+}
+
+
+AnyVal::AnyVal(G3D::TextInput& t) : m_type(NIL), m_value(NULL), m_referenceCount(NULL) {
+ deserialize(t);
+}
+
+
+/*AnyVal::AnyVal(G3D::BinaryInput& b) {
+ deserialize(b);
+}
+*/
+
+AnyVal::AnyVal(double v) : m_type(NUMBER), m_referenceCount(NULL) {
+ m_value = new double(v);
+}
+
+
+AnyVal::AnyVal(int v) : m_type(NUMBER), m_referenceCount(NULL) {
+ m_value = new double(v);
+}
+
+
+AnyVal::AnyVal(const Rect2D& v) : m_type(RECT2D), m_referenceCount(NULL) {
+ m_value = new Rect2D(v);
+}
+
+
+AnyVal::AnyVal(const AABox& v) : m_type(AABOX), m_referenceCount(NULL) {
+ m_value = new AABox(v);
+}
+
+
+AnyVal::AnyVal(const Vector2& v) : m_type(VECTOR2), m_referenceCount(NULL) {
+ m_value = new Vector2(v);
+}
+
+
+AnyVal::AnyVal(const Vector3& v) : m_type(VECTOR3), m_referenceCount(NULL) {
+ m_value = new Vector3(v);
+}
+
+
+AnyVal::AnyVal(const Vector4& v) : m_type(VECTOR4), m_referenceCount(NULL) {
+ m_value = new Vector4(v);
+}
+
+
+AnyVal::AnyVal(const Color1& v) : m_type(COLOR1), m_referenceCount(NULL) {
+ m_value = new Color1(v);
+}
+
+
+AnyVal::AnyVal(const Color3& v) : m_type(COLOR3), m_referenceCount(NULL) {
+ m_value = new Color3(v);
+}
+
+
+AnyVal::AnyVal(const Color4& v) : m_type(COLOR4), m_referenceCount(NULL) {
+ m_value = new Color4(v);
+}
+
+
+AnyVal::AnyVal(const std::string& v) : m_type(STRING), m_referenceCount(NULL) {
+ m_value = new std::string(v);
+}
+
+
+AnyVal::AnyVal(const char* v) : m_type(STRING), m_referenceCount(NULL) {
+ m_value = new std::string(v);
+}
+
+
+AnyVal::AnyVal(const Quat& v) : m_type(QUAT), m_referenceCount(NULL) {
+ m_value = new Quat(v);
+}
+
+
+AnyVal::AnyVal(const CoordinateFrame& v) : m_type(COORDINATEFRAME), m_referenceCount(NULL) {
+ m_value = new CoordinateFrame(v);
+}
+
+
+AnyVal::AnyVal(const Matrix2& v) : m_type(MATRIX2), m_referenceCount(NULL) {
+ m_value = new Matrix2(v);
+}
+
+AnyVal::AnyVal(const Matrix3& v) : m_type(MATRIX3), m_referenceCount(NULL) {
+ m_value = new Matrix3(v);
+}
+
+
+AnyVal::AnyVal(const Matrix4& v) : m_type(MATRIX4), m_referenceCount(NULL) {
+ m_value = new Matrix4(v);
+}
+
+
+AnyVal::AnyVal(const AnyVal& c) : m_type(NIL), m_value(NULL), m_referenceCount(NULL) {
+ *this = c;
+}
+
+
+AnyVal::AnyVal(Type arrayOrTable) : m_type(NIL), m_value(NULL), m_referenceCount(new int(1)) {
+ // TODO: make AnyVal::createArray()
+ switch (arrayOrTable) {
+ case ARRAY:
+ m_type = ARRAY;
+ m_value = new Array<AnyVal>();
+ break;
+
+ case TABLE:
+ m_type = TABLE;
+ m_value = new Table<std::string, AnyVal>();
+ break;
+
+ default:
+ debugAssertM(false, "Cannot construct AnyVal from constants except ARRAY or TABLE.");
+ }
+}
+
+
+AnyVal::~AnyVal() {
+ deleteValue();
+}
+
+
+void AnyVal::deleteValue() {
+ if (m_referenceCount) {
+ --(*m_referenceCount);
+ if (*m_referenceCount <= 0) {
+ delete m_referenceCount;
+ m_referenceCount = NULL;
+ // Pass through and delete the real object now
+ } else {
+ // Someone else is holding a reference, so we can't delete
+ // the object.
+ m_referenceCount = NULL;
+ return;
+ }
+ }
+
+ switch (m_type) {
+ case NIL:
+ // Nothing to do
+ break;
+
+ case NUMBER:
+ delete (double*)m_value;
+ break;
+
+ case BOOLEAN:
+ delete (bool*)m_value;
+ break;
+
+ case STRING:
+ delete (std::string*)m_value;
+ break;
+
+ case RECT2D:
+ delete (Rect2D*)m_value;
+ break;
+
+ case AABOX:
+ delete (AABox*)m_value;
+ break;
+
+ case VECTOR2:
+ delete (Vector2*)m_value;
+ break;
+
+ case VECTOR3:
+ delete (Vector3*)m_value;
+ break;
+
+ case VECTOR4:
+ delete (Vector4*)m_value;
+ break;
+
+ case MATRIX2:
+ delete (Matrix2*)m_value;
+ break;
+
+ case MATRIX3:
+ delete (Matrix3*)m_value;
+ break;
+
+ case MATRIX4:
+ delete (Matrix4*)m_value;
+ break;
+
+ case QUAT:
+ delete (Quat*)m_value;
+ break;
+
+ case COORDINATEFRAME:
+ delete (CoordinateFrame*)m_value;
+ break;
+
+ case COLOR1:
+ delete (Color1*)m_value;
+ break;
+
+ case COLOR3:
+ delete (Color3*)m_value;
+ break;
+
+ case COLOR4:
+ delete (Color4*)m_value;
+ break;
+
+ case ARRAY:
+ delete (Array<AnyVal>*)m_value;
+ break;
+
+ case TABLE:
+ delete (Table<std::string, AnyVal>*)m_value;
+ break;
+
+ default:
+ debugAssertM(false, "Internal error: no destructor for this type.");
+ }
+
+ m_value = NULL;
+}
+
+
+AnyVal& AnyVal::operator=(const AnyVal& v) {
+ deleteValue();
+
+ m_type = v.m_type;
+
+ m_referenceCount = v.m_referenceCount;
+
+ if (isSharedType()) {
+ ++(*m_referenceCount);
+ m_value = v.m_value;
+ } else {
+ m_value = v.copyValue();
+ }
+
+ return *this;
+}
+
+
+void* AnyVal::copyValue() const {
+ switch (m_type) {
+ case NIL:
+ return NULL;
+
+ case NUMBER:
+ return new double(*(double*)m_value);
+
+ case BOOLEAN:
+ return new bool(*(bool*)m_value);
+
+ case STRING:
+ return new std::string(*(std::string*)m_value);
+
+ case RECT2D:
+ return new Rect2D(*(Rect2D*)m_value);
+
+ case AABOX:
+ return new AABox(*(AABox*)m_value);
+
+ case VECTOR2:
+ return new Vector2(*(Vector2*)m_value);
+
+ case VECTOR3:
+ return new Vector3(*(Vector3*)m_value);
+
+ case VECTOR4:
+ return new Vector4(*(Vector4*)m_value);
+
+ case MATRIX2:
+ return new Matrix2(*(Matrix2*)m_value);
+
+ case MATRIX3:
+ return new Matrix3(*(Matrix3*)m_value);
+
+ case MATRIX4:
+ return new Matrix4(*(Matrix4*)m_value);
+
+ case QUAT:
+ return new Quat(*(Quat*)m_value);
+
+ case COORDINATEFRAME:
+ return new CoordinateFrame(*(CoordinateFrame*)m_value);
+
+ case COLOR1:
+ return new Color1(*(Color1*)m_value);
+
+ case COLOR3:
+ return new Color3(*(Color3*)m_value);
+
+ case COLOR4:
+ return new Color4(*(Color4*)m_value);
+
+ case ARRAY:
+ return new Array<AnyVal>(*(Array<AnyVal>*)m_value);
+
+ case TABLE:
+ return new Table<std::string, AnyVal>(*(Table<std::string, AnyVal>*)m_value);
+
+ default:
+ debugAssertM(false, "Internal error: no assignment operator for this type.");
+ return NULL;
+ }
+}
+
+AnyVal::Type AnyVal::type() const {
+ return m_type;
+}
+
+
+static bool legalIdentifier(const std::string& s) {
+ if (s.size() == 0) {
+ return false;
+ }
+
+ if (! isLetter(s[0]) || (s[0] == '_')) {
+ return false;
+ }
+
+ bool ok = true;
+
+ for (unsigned int i = 1; i < s.size(); ++i) {
+ ok &= isDigit(s[i]) || isLetter(s[i]) || (s[i] == '_');
+ }
+
+ return ok;
+}
+
+
+void AnyVal::serialize(G3D::TextOutput& t) const {
+ switch (m_type) {
+ case NIL:
+ t.writeSymbol("Nil");
+ break;
+
+ case NUMBER:
+ t.printf("%g", *(double*)m_value);
+ break;
+
+ case BOOLEAN:
+ t.writeBoolean(*(bool*)m_value);
+ break;
+
+ case STRING:
+ t.writeString(*(std::string*)m_value);
+ break;
+
+ case RECT2D:
+ t.printf("R(%g, %g, %g, %g)", ((Rect2D*)m_value)->x0(), ((Rect2D*)m_value)->y0(),
+ ((Rect2D*)m_value)->width(), ((Rect2D*)m_value)->height());
+ break;
+
+ case AABOX:
+ t.printf("AAB(V3(%g, %g, %g), V3(%g, %g, %g))",
+ aabox().low().x,
+ aabox().low().y,
+ aabox().low().z,
+ aabox().high().x,
+ aabox().high().y,
+ aabox().high().z);
+ break;
+
+ case VECTOR2:
+ t.printf("V2(%g, %g)", ((Vector2*)m_value)->x, ((Vector2*)m_value)->y);
+ break;
+
+ case VECTOR3:
+ t.printf("V3(%g, %g, %g)", ((Vector3*)m_value)->x, ((Vector3*)m_value)->y, ((Vector3*)m_value)->z);
+ break;
+
+ case VECTOR4:
+ t.printf("V4(%g, %g, %g, %g)", ((Vector4*)m_value)->x, ((Vector4*)m_value)->y, ((Vector4*)m_value)->z, ((Vector4*)m_value)->w);
+ break;
+
+ case MATRIX2:
+ {
+ const Matrix2& m = *(Matrix2*)m_value;
+ t.printf("M2(\n");
+ t.pushIndent();
+ t.printf("%10.5f, %10.5f,\n%10.5f, %10.5f)",
+ m[0][0], m[0][1],
+ m[1][0], m[1][1]);
+ t.popIndent();
+ }
+ break;
+
+ case MATRIX3:
+ {
+ const Matrix3& m = *(Matrix3*)m_value;
+ t.printf("M3(\n");
+ t.pushIndent();
+ t.printf("%10.5f, %10.5f, %10.5f,\n%10.5f, %10.5f, %10.5f,\n%10.5f, %10.5f, %10.5f)",
+ m[0][0], m[0][1], m[0][2],
+ m[1][0], m[1][1], m[1][2],
+ m[2][0], m[2][1], m[2][2]);
+ t.popIndent();
+ }
+ break;
+
+ case MATRIX4:
+ {
+ const Matrix4& m = *(Matrix4*)m_value;
+ t.printf("M4(\n");
+ t.pushIndent();
+ t.printf(
+ "%10.5f, %10.5f, %10.5f, %10.5f,\n"
+ "%10.5f, %10.5f, %10.5f, %10.5f,\n"
+ "%10.5f, %10.5f, %10.5f, %10.5f,\n"
+ "%10.5f, %10.5f, %10.5f, %10.5f)",
+ m[0][0], m[0][1], m[0][2], m[0][3],
+ m[1][0], m[1][1], m[1][2], m[1][3],
+ m[2][0], m[2][1], m[2][2], m[2][3],
+ m[3][0], m[3][1], m[3][2], m[3][3]);
+ t.popIndent();
+ }
+ break;
+
+ case QUAT:
+ t.printf("Q(%g, %g, %g, %g)", ((Quat*)m_value)->x, ((Quat*)m_value)->y, ((Quat*)m_value)->z, ((Quat*)m_value)->w);
+ break;
+
+ case COORDINATEFRAME:
+ {
+ const CoordinateFrame& c = *(CoordinateFrame*)m_value;
+ float x,y,z,yaw,pitch,roll;
+ c.getXYZYPRDegrees(x,y,z,yaw,pitch,roll);
+ t.printf("CF(V3(%g,%g,%g), %g, %g, %g)", x, y, z, yaw, pitch, roll);
+ /*
+ t.pushIndent();
+ t.printf(
+ "CF(\n%10.5f, %10.5f, %10.5f, %10.5f,\n"
+ "%10.5f, %10.5f, %10.5f, %10.5f,\n"
+ "%10.5f, %10.5f, %10.5f, %10.5f)",
+ c.rotation[0][0], c.rotation[0][1], c.rotation[0][2], c.translation.x,
+ c.rotation[1][0], c.rotation[1][1], c.rotation[1][2], c.translation.y,
+ c.rotation[2][0], c.rotation[2][1], c.rotation[2][2], c.translation.z);
+ t.popIndent();
+ */
+ }
+ break;
+
+ case COLOR1:
+ t.printf("C1(%g)", ((Color1*)m_value)->value);
+ break;
+
+ case COLOR3:
+ t.printf("C3(%g, %g, %g)", ((Color3*)m_value)->r, ((Color3*)m_value)->g, ((Color3*)m_value)->b);
+ break;
+
+ case COLOR4:
+ t.printf("C4(%g, %g, %g, %g)", ((Color4*)m_value)->r, ((Color4*)m_value)->g, ((Color4*)m_value)->b, ((Color4*)m_value)->a);
+ break;
+
+ case ARRAY:
+ {
+ const Array<AnyVal>& a = *(Array<AnyVal>*)m_value;
+ t.printf("[\n");
+ t.pushIndent();
+ for (int i = 0; i < a.size(); ++i) {
+ a[i].serialize(t);
+ if (i != a.size() - 1) {
+ t.printf(", \n");
+ }
+ }
+ t.printf("]");
+ t.popIndent();
+ }
+ break;
+
+ case TABLE:
+ {
+ const Table<std::string, AnyVal>& a = *(Table<std::string, AnyVal>*)m_value;
+ t.printf("{\n");
+ t.pushIndent();
+ Table<std::string, AnyVal>::Iterator i = a.begin();
+ const Table<std::string, AnyVal>::Iterator end = a.end();
+ while (i != end) {
+ // Quote names that are not legal C++ identifiers
+ if (! legalIdentifier(i->key)) {
+ t.printf("'%s' ", i->key.c_str());
+ } else {
+ t.writeSymbol(i->key);
+ }
+ t.printf("= ");
+
+ i->value.serialize(t);
+
+ if (i != end) {
+ t.printf("\n");
+ }
+ ++i;
+ }
+ t.popIndent();
+ t.printf("}");
+ }
+ break;
+
+ default:
+ debugAssertM(false, "Internal error: no serialize method for this type.");
+ }
+}
+
+
+std::string AnyVal::toString() const {
+ TextOutput t;
+ serialize(t);
+ std::string s;
+ t.commitString(s);
+ return s;
+}
+
+/*
+void AnyVal::serialize(G3D::BinaryOutput& t) const {
+ alwaysAssertM(false, "TODO");
+}
+*/
+
+void AnyVal::deserialize(G3D::TextInput& t) {
+ deleteValue();
+ m_type = NIL;
+ m_value = NULL;
+
+ if (! t.hasMore()) {
+ return;
+ }
+
+ switch (t.peek().type()) {
+ case Token::END:
+ // should never get here because of the hasMore check above
+ return;
+ break;
+
+ case Token::NUMBER:
+ m_type = NUMBER;
+ m_value = new double(t.readNumber());
+ break;
+
+ case Token::STRING:
+ m_type = STRING;
+ m_value = new std::string(t.readString());
+ break;
+
+ case Token::BOOLEAN:
+ m_type = BOOLEAN;
+ m_value = new bool(t.readBoolean());
+ break;
+
+ case Token::SYMBOL:
+ {
+ std::string s = t.readSymbol();
+ if (s == "NIL") {
+ break;
+
+ } else if (s == "true") {
+
+ m_type = BOOLEAN;
+ m_value = new bool(true);
+
+ } else if (s == "false") {
+
+ m_type = BOOLEAN;
+ m_value = new bool(false);
+
+ } else if (s == "R") {
+
+ m_type = RECT2D;
+ t.readSymbol("(");
+ float x,y,w,h;
+ x = (float)t.readNumber();
+ t.readSymbol(",");
+ y = (float)t.readNumber();
+ t.readSymbol(",");
+ w = (float)t.readNumber();
+ t.readSymbol(",");
+ h = (float)t.readNumber();
+ t.readSymbol(")");
+ m_value = new Rect2D(Rect2D::xywh(x, y, w, h));
+
+ } else if (s == "AAB") {
+
+ m_type = AABOX;
+ Vector3 v[2];
+ t.readSymbol("(");
+ for (int i = 0; i < 2; ++i) {
+ t.readSymbols("V3", "(");
+ v[i].x = (float)t.readNumber();
+ t.readSymbol(",");
+ v[i].y = (float)t.readNumber();
+ t.readSymbol(",");
+ v[i].z = (float)t.readNumber();
+ t.readSymbol(",");
+ if (i == 0) {
+ t.readSymbol(",");
+ }
+ }
+ t.readSymbol(")");
+ m_value = new AABox(v[0], v[1]);
+
+ } else if (s == "V2") {
+
+ t.readSymbol("(");
+ Vector2 v;
+ v.x = (float)t.readNumber();
+ t.readSymbol(",");
+ v.y = (float)t.readNumber();
+ t.readSymbol(")");
+ m_value = new Vector2(v);
+ m_type = VECTOR2;
+
+ } else if (s == "V3") {
+
+ t.readSymbol("(");
+ Vector3 v;
+ v.x = (float)t.readNumber();
+ t.readSymbol(",");
+ v.y = (float)t.readNumber();
+ t.readSymbol(",");
+ v.z = (float)t.readNumber();
+ t.readSymbol(")");
+ m_value = new Vector3(v);
+ m_type = VECTOR3;
+
+ } else if (s == "V4") {
+
+ t.readSymbol("(");
+ Vector4 v;
+ v.x = (float)t.readNumber();
+ t.readSymbol(",");
+ v.y = (float)t.readNumber();
+ t.readSymbol(",");
+ v.z = (float)t.readNumber();
+ t.readSymbol(",");
+ v.w = (float)t.readNumber();
+ t.readSymbol(")");
+ m_value = new Vector4(v);
+ m_type = VECTOR4;
+
+ } else if (s == "M2") {
+
+ t.readSymbol("(");
+ Matrix2 m;
+ for (int r = 0; r < 2; ++r) {
+ for (int c = 0; c < 2; ++c) {
+ m[r][c] = (float)t.readNumber();
+ if ((c != 1) || (r != 1)) {
+ t.readSymbol(",");
+ }
+ }
+ }
+ t.readSymbol(")");
+ m_value = new Matrix2(m);
+ m_type = MATRIX2;
+
+ } else if (s == "M3") {
+
+ t.readSymbol("(");
+ Matrix3 m;
+ for (int r = 0; r < 3; ++r) {
+ for (int c = 0; c < 3; ++c) {
+ m[r][c] = (float)t.readNumber();
+ if ((c != 2) || (r != 2)) {
+ t.readSymbol(",");
+ }
+ }
+ }
+ t.readSymbol(")");
+ m_value = new Matrix3(m);
+ m_type = MATRIX3;
+
+ } else if (s == "M4") {
+
+ t.readSymbol("(");
+ Matrix4 m;
+ for (int r = 0; r < 4; ++r) {
+ for (int c = 0; c < 4; ++c) {
+ m[r][c] = (float)t.readNumber();
+ if ((c != 3) || (r != 3)) {
+ t.readSymbol(",");
+ }
+ }
+ }
+ t.readSymbol(")");
+ m_value = new Matrix4(m);
+ m_type = MATRIX4;
+
+ } else if (s == "Q") {
+
+ t.readSymbol("(");
+ Quat q;
+ q.x = (float)t.readNumber();
+ t.readSymbol(",");
+ q.y = (float)t.readNumber();
+ t.readSymbol(",");
+ q.z = (float)t.readNumber();
+ t.readSymbol(",");
+ q.w = (float)t.readNumber();
+ t.readSymbol(")");
+ m_value = new Quat(q);
+ m_type = QUAT;
+
+ } else if (s == "CF") {
+
+ t.readSymbol("(");
+ CoordinateFrame m;
+ if (t.peek().type() == Token::SYMBOL) {
+ // Angle format
+ float x, y, z, yaw, roll, pitch;
+ t.readSymbols("V3", "(");
+ x = (float)t.readNumber();
+ t.readSymbol(",");
+ y = (float)t.readNumber();
+ t.readSymbol(",");
+ z = (float)t.readNumber();
+ t.readSymbols(")", ",");
+ yaw = (float)t.readNumber();
+ t.readSymbol(",");
+ pitch = (float)t.readNumber();
+ roll = 0;
+ if (t.peek().string() == ",") {
+ t.readSymbol(",");
+ roll = (float)t.readNumber();
+ }
+ m = CoordinateFrame::fromXYZYPRDegrees(x, y, z, yaw, pitch, roll);
+ } else {
+ // Matrix format
+ for (int r = 0; r < 3; ++r) {
+ for (int c = 0; c < 3; ++c) {
+ m.rotation[r][c] = (float)t.readNumber();
+ }
+ m.translation[r] = (float)t.readNumber();
+ if (r != 2) {
+ t.readSymbol(",");
+ }
+ }
+ }
+ t.readSymbol(")");
+ m_value = new CoordinateFrame(m);
+ m_type = COORDINATEFRAME;
+
+ } else if (s == "C1") {
+
+ t.readSymbol("(");
+ float v = (float)t.readNumber();
+ t.readSymbol(")");
+ m_value = new Color1(v);
+ m_type = COLOR1;
+
+ } else if (s == "C3") {
+
+ t.readSymbol("(");
+ Color3 c;
+ c.r = (float)t.readNumber();
+ t.readSymbol(",");
+ c.g = (float)t.readNumber();
+ t.readSymbol(",");
+ c.b = (float)t.readNumber();
+ t.readSymbol(")");
+ m_value = new Color3(c);
+ m_type = COLOR3;
+
+ } else if (s == "C4") {
+
+ t.readSymbol("(");
+ Color4 c;
+ c.r = (float)t.readNumber();
+ t.readSymbol(",");
+ c.g = (float)t.readNumber();
+ t.readSymbol(",");
+ c.b = (float)t.readNumber();
+ t.readSymbol(",");
+ c.a = (float)t.readNumber();
+ t.readSymbol(")");
+ m_value = new Color4(c);
+ m_type = COLOR4;
+
+ } else if (s == "[") {
+
+ // Array
+ m_type = ARRAY;
+ m_value = new Array<AnyVal>();
+ m_referenceCount = new int(1);
+ Array<AnyVal>& a = *(Array<AnyVal>*)m_value;
+
+ Token peek = t.peek();
+ while ((peek.type() != Token::SYMBOL) || (peek.string() != "]")) {
+ // Avoid copying large objects
+ a.next().deserialize(t);
+
+ peek = t.peek();
+ if (peek.type() != Token::SYMBOL) {
+ throw CorruptText("Expected ',' or ']'", peek);
+ } else if (peek.string() == ",") {
+ t.readSymbol(",");
+ } else if (peek.string() != "]") {
+ throw CorruptText("Missing ']'", peek);
+ }
+ }
+ t.readSymbol("]");
+
+ } else if (s == "{") {
+
+ // Table
+ m_type = TABLE;
+ m_value = new Table<std::string, AnyVal>();
+ m_referenceCount = new int(1);
+ Table<std::string, AnyVal>& a = *(Table<std::string, AnyVal>*)m_value;
+
+ Token peek = t.peek();
+ while ((peek.type() != Token::SYMBOL) || (peek.string() != "}")) {
+
+ std::string key;
+ // Get the name
+ if (peek.type() == Token::SYMBOL) {
+ key = t.readSymbol();
+ } else if (peek.extendedType() == Token::SINGLE_QUOTED_TYPE) {
+ key = t.readString();
+ } else {
+ throw CorruptText("Expected name inside table", peek);
+ }
+
+ t.readSymbol("=");
+
+ // Avoid copying large values
+ a.set(key, AnyVal());
+ a[key].deserialize(t);
+
+ peek = t.peek();
+ if ((peek.type() != Token::SYMBOL) && (peek.extendedType() == Token::SINGLE_QUOTED_TYPE)) {
+ throw CorruptText("Missing expected name or '}'", peek);
+ }
+ }
+ t.readSymbol("}");
+
+ } else {
+ throw CorruptText("Invalid value type.", t.peek());
+ } // dispatch on symbol type
+ } // scope
+ break;
+ }
+}
+
+/*
+void AnyVal::deserialize(G3D::BinaryInput& t) {
+ alwaysAssertM(false, "TODO");
+}
+*/
+
+
+AnyVal& AnyVal::operator[](const char* key) {
+ return this->operator[]((std::string)key);
+}
+
+
+const AnyVal& AnyVal::operator[](const char* key) const {
+ return this->operator[]((std::string)key);
+}
+
+
+AnyVal& AnyVal::operator[](const std::string& key) {
+ if (m_type != TABLE) {
+ throw WrongType(TABLE, m_type);
+ }
+
+ makeMutable();
+
+ Table<std::string, AnyVal>& t = *(Table<std::string, AnyVal>*)m_value;
+
+ if (! t.containsKey(key)) {
+ t.set(key, AnyVal());
+ }
+
+ return t[key];
+}
+
+
+const AnyVal& AnyVal::operator[](const std::string& key) const {
+ if (m_type != TABLE) {
+ throw WrongType(TABLE, m_type);
+ }
+
+ const Table<std::string, AnyVal>& t = *(const Table<std::string, AnyVal>*)m_value;
+
+ if (! t.containsKey(key)) {
+ throw KeyNotFound(key);
+ }
+
+ return t[key];
+}
+
+
+void AnyVal::append(const AnyVal& v) {
+ if (m_type != ARRAY) {
+ throw WrongType(ARRAY, m_type);
+ }
+ makeMutable();
+
+ Array<AnyVal>& a = *(Array<AnyVal>*)m_value;
+ a.append(v);
+}
+
+
+void AnyVal::getKeys(Array<std::string>& keys) const {
+ if (m_type != TABLE) {
+ throw WrongType(TABLE, m_type);
+ }
+
+ const Table<std::string, AnyVal>& t = *(const Table<std::string, AnyVal>*)m_value;
+ t.getKeys(keys);
+}
+
+
+int AnyVal::size() const {
+ switch (m_type) {
+ case TABLE:
+ {
+ const Table<std::string, AnyVal>& t = *(const Table<std::string, AnyVal>*)m_value;
+ return t.size();
+ }
+
+ case ARRAY:
+ {
+ const Array<AnyVal>& a = *(Array<AnyVal>*)m_value;
+ return a.size();
+ }
+
+ default:
+ throw WrongType(ARRAY, m_type);
+ }
+}
+
+
+AnyVal& AnyVal::operator[](int i) {
+ if (m_type != ARRAY) {
+ throw WrongType(ARRAY, m_type);
+ }
+ makeMutable();
+
+ Array<AnyVal>& a = *(Array<AnyVal>*)m_value;
+
+ if (i < 0) {
+ throw IndexOutOfBounds(i, a.size());
+ }
+
+ if (a.size() <= i) {
+ a.resize(i + 1);
+ }
+
+ return a[i];
+}
+
+
+const AnyVal& AnyVal::operator[](int i) const {
+ if (m_type != ARRAY) {
+ throw WrongType(ARRAY, m_type);
+ }
+
+ const Array<AnyVal>& a = *(Array<AnyVal>*)m_value;
+
+ if (a.size() <= i || i < 0) {
+ throw IndexOutOfBounds(i, a.size());
+ }
+
+ return a[i];
+}
+
+
+void AnyVal::makeMutable() {
+ if (*m_referenceCount > 1) {
+ // This is a shared instance
+ --(*m_referenceCount);
+ m_referenceCount = new int(1);
+ m_value = copyValue();
+ }
+}
+
+bool AnyVal::boolean() const {
+ if (m_type != BOOLEAN) {
+ throw WrongType(BOOLEAN, m_type);
+ }
+
+ return *(bool*)m_value;
+}
+
+
+bool AnyVal::boolean(bool defaultVal) const {
+ if (m_type != BOOLEAN) {
+ return defaultVal;
+ }
+
+ return *(bool*)m_value;
+}
+
+
+const std::string& AnyVal::string() const {
+ if (m_type != STRING) {
+ throw WrongType(STRING, m_type);
+ }
+
+ return *(std::string*)m_value;
+}
+
+
+const std::string& AnyVal::string(const std::string& defaultVal) const {
+ if (m_type != STRING) {
+ return defaultVal;
+ } else {
+ return *(std::string*)m_value;
+ }
+}
+
+
+double AnyVal::number() const {
+ if (m_type != NUMBER) {
+ throw WrongType(NUMBER, m_type);
+ }
+
+ return *(double*)m_value;
+}
+
+
+double AnyVal::number(double defaultVal) const {
+ if (m_type != NUMBER) {
+ return defaultVal;
+ } else {
+ return *(double*)m_value;
+ }
+}
+
+
+const Rect2D& AnyVal::rect2D() const {
+ if (m_type != RECT2D) {
+ throw WrongType(RECT2D, m_type);
+ }
+
+ return *(Rect2D*)m_value;
+}
+
+
+const Rect2D& AnyVal::rect2D(const Rect2D& defaultVal) const {
+ if (m_type != RECT2D) {
+ return defaultVal;
+ } else {
+ return *(Rect2D*)m_value;
+ }
+}
+
+
+const AABox& AnyVal::aabox() const {
+ if (m_type != AABOX) {
+ throw WrongType(AABOX, m_type);
+ }
+
+ return *(AABox*)m_value;
+}
+
+
+const AABox& AnyVal::aabox(const AABox& defaultVal) const {
+ if (m_type != AABOX) {
+ return defaultVal;
+ } else {
+ return *(AABox*)m_value;
+ }
+}
+
+
+const Color1& AnyVal::color1() const {
+ if (m_type != COLOR1) {
+ throw WrongType(COLOR1, m_type);
+ }
+
+ return *(Color1*)m_value;
+}
+
+
+const Color1& AnyVal::color1(const Color1& defaultVal) const {
+ if (m_type != COLOR1) {
+ return defaultVal;
+ } else {
+ return *(Color1*)m_value;
+ }
+}
+
+
+const Color3& AnyVal::color3() const {
+ if (m_type != COLOR3) {
+ throw WrongType(COLOR3, m_type);
+ }
+
+ return *(Color3*)m_value;
+}
+
+
+const Color3& AnyVal::color3(const Color3& defaultVal) const {
+ if (m_type != COLOR3) {
+ return defaultVal;
+ } else {
+ return *(Color3*)m_value;
+ }
+}
+
+
+const Color4& AnyVal::color4() const {
+ if (m_type != COLOR4) {
+ throw WrongType(COLOR4, m_type);
+ }
+
+ return *(Color4*)m_value;
+}
+
+
+const Color4& AnyVal::color4(const Color4& defaultVal) const {
+ if (m_type != COLOR4) {
+ return defaultVal;
+ } else {
+ return *(Color4*)m_value;
+ }
+}
+
+
+const Vector2& AnyVal::vector2() const {
+ if (m_type != VECTOR2) {
+ throw WrongType(VECTOR2, m_type);
+ }
+
+ return *(Vector2*)m_value;
+}
+
+
+const Vector2& AnyVal::vector2(const Vector2& defaultVal) const {
+ if (m_type != VECTOR2) {
+ return defaultVal;
+ } else {
+ return *(Vector2*)m_value;
+ }
+}
+
+
+const Vector3& AnyVal::vector3() const {
+ if (m_type != VECTOR3) {
+ throw WrongType(VECTOR3, m_type);
+ }
+
+ return *(Vector3*)m_value;
+}
+
+
+const Vector3& AnyVal::vector3(const Vector3& defaultVal) const {
+ if (m_type != VECTOR3) {
+ return defaultVal;
+ } else {
+ return *(Vector3*)m_value;
+ }
+}
+
+
+const Vector4& AnyVal::vector4() const {
+ if (m_type != VECTOR4) {
+ throw WrongType(VECTOR4, m_type);
+ }
+
+ return *(Vector4*)m_value;
+}
+
+
+const Vector4& AnyVal::vector4(const Vector4& defaultVal) const {
+ if (m_type != VECTOR4) {
+ return defaultVal;
+ } else {
+ return *(Vector4*)m_value;
+ }
+}
+
+
+const CoordinateFrame& AnyVal::coordinateFrame() const {
+ if (m_type != COORDINATEFRAME) {
+ throw WrongType(COORDINATEFRAME, m_type);
+ }
+
+ return *(CoordinateFrame*)m_value;
+}
+
+
+const CoordinateFrame& AnyVal::coordinateFrame(const CoordinateFrame& defaultVal) const {
+ if (m_type != COORDINATEFRAME) {
+ return defaultVal;
+ } else {
+ return *(CoordinateFrame*)m_value;
+ }
+}
+
+const Matrix2& AnyVal::matrix2(const Matrix2& defaultVal) const {
+ if (m_type != MATRIX2) {
+ return defaultVal;
+ } else {
+ return *(Matrix2*)m_value;
+ }
+}
+
+
+const Matrix2& AnyVal::matrix2() const {
+ if (m_type != MATRIX2) {
+ throw WrongType(MATRIX2, m_type);
+ }
+
+ return *(Matrix2*)m_value;
+}
+
+
+const Matrix3& AnyVal::matrix3(const Matrix3& defaultVal) const {
+ if (m_type != MATRIX3) {
+ return defaultVal;
+ } else {
+ return *(Matrix3*)m_value;
+ }
+}
+
+
+const Matrix3& AnyVal::matrix3() const {
+ if (m_type != MATRIX3) {
+ throw WrongType(MATRIX3, m_type);
+ }
+
+ return *(Matrix3*)m_value;
+}
+
+
+const Matrix4& AnyVal::matrix4(const Matrix4& defaultVal) const {
+ if (m_type != MATRIX4) {
+ return defaultVal;
+ } else {
+ return *(Matrix4*)m_value;
+ }
+}
+
+
+const Matrix4& AnyVal::matrix4() const {
+ if (m_type != MATRIX4) {
+ throw WrongType(MATRIX4, m_type);
+ }
+
+ return *(Matrix4*)m_value;
+}
+
+
+const Quat& AnyVal::quat(const Quat& defaultVal) const {
+ if (m_type != QUAT) {
+ return defaultVal;
+ } else {
+ return *(Quat*)m_value;
+ }
+}
+
+
+const Quat& AnyVal::quat() const {
+ if (m_type != QUAT) {
+ throw WrongType(QUAT, m_type);
+ }
+
+ return *(Quat*)m_value;
+}
+
+
+const AnyVal& AnyVal::get(const std::string& key, const AnyVal& defaultVal) const {
+ if (m_type != TABLE) {
+ return defaultVal;
+ }
+
+ const Table<std::string, AnyVal>& t = *(const Table<std::string, AnyVal>*)m_value;
+
+ if (t.containsKey(key)) {
+ return t[key];
+ } else {
+ return defaultVal;
+ }
+}
+
+
+const AnyVal& AnyVal::get(const std::string& key) const {
+ if (m_type != TABLE) {
+ throw WrongType(TABLE, m_type);
+ }
+
+ const Table<std::string, AnyVal>& t = *(const Table<std::string, AnyVal>*)m_value;
+
+ if (t.containsKey(key)) {
+ return t[key];
+ } else {
+ throw KeyNotFound(key);
+ }
+}
+
+
+const AnyVal& AnyVal::get(int i, const AnyVal& defaultVal) const {
+ if (m_type != ARRAY) {
+ return defaultVal;
+ }
+
+ const Array<AnyVal>& a = *(const Array<AnyVal>*)m_value;
+
+ if ((i >= 0) && (i < a.size())) {
+ return a[i];
+ } else {
+ return defaultVal;
+ }
+}
+
+
+const AnyVal& AnyVal::get(int i) const {
+ if (m_type != ARRAY) {
+ throw WrongType(ARRAY, m_type);
+ }
+
+ const Array<AnyVal>& a = *(const Array<AnyVal>*)m_value;
+
+ if ((i >= 0) && (i < a.size())) {
+ return a[i];
+ } else {
+ throw IndexOutOfBounds(i, a.size());
+ }
+}
+
+}
diff --git a/externals/g3dlite/G3D.lib/source/BinaryFormat.cpp b/externals/g3dlite/G3D.lib/source/BinaryFormat.cpp
new file mode 100644
index 00000000000..a30ec0644ea
--- /dev/null
+++ b/externals/g3dlite/G3D.lib/source/BinaryFormat.cpp
@@ -0,0 +1,81 @@
+/**
+ @file BinaryFormat.cpp
+ @maintainer Morgan McGuire, matrix@graphics3d.com
+
+ @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/G3D.lib/source/BinaryInput.cpp b/externals/g3dlite/G3D.lib/source/BinaryInput.cpp
new file mode 100644
index 00000000000..65a9976fe04
--- /dev/null
+++ b/externals/g3dlite/G3D.lib/source/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/G3D.lib/source/BinaryOutput.cpp b/externals/g3dlite/G3D.lib/source/BinaryOutput.cpp
new file mode 100644
index 00000000000..8fcc30e548f
--- /dev/null
+++ b/externals/g3dlite/G3D.lib/source/BinaryOutput.cpp
@@ -0,0 +1,518 @@
+/**
+ @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 + "'");
+
+ m_alreadyWritten += m_bufferLen;
+
+ fwrite(m_buffer, m_bufferLen, 1, file);
+ 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/G3D.lib/source/Box.cpp b/externals/g3dlite/G3D.lib/source/Box.cpp
new file mode 100644
index 00000000000..4b6c56388ec
--- /dev/null
+++ b/externals/g3dlite/G3D.lib/source/Box.cpp
@@ -0,0 +1,393 @@
+/**
+ @file Box.cpp
+ Box class
+
+ @maintainer Morgan McGuire, matrix@graphics3d.com
+
+ @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::inf();
+ }
+
+ debugAssert(! isNaN(_extent.x));
+
+ _area = 2 *
+ (_extent.x * _extent.y +
+ _extent.y * _extent.z +
+ _extent.z * _extent.x);
+
+ _center = (max + min) / 2;
+
+ // 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/G3D.lib/source/Capsule.cpp b/externals/g3dlite/G3D.lib/source/Capsule.cpp
new file mode 100644
index 00000000000..fbcb56fe97b
--- /dev/null
+++ b/externals/g3dlite/G3D.lib/source/Capsule.cpp
@@ -0,0 +1,179 @@
+/**
+ @file Capsule.cpp
+
+ @maintainer Morgan McGuire, matrix@graphics3d.com
+
+ @created 2003-02-07
+ @edited 2005-08-18
+
+ Copyright 2000-2005, 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/G3D.lib/source/CollisionDetection.cpp b/externals/g3dlite/G3D.lib/source/CollisionDetection.cpp
new file mode 100644
index 00000000000..16650bc1a96
--- /dev/null
+++ b/externals/g3dlite/G3D.lib/source/CollisionDetection.cpp
@@ -0,0 +1,2152 @@
+/**
+ @file CollisionDetection.cpp
+
+ @maintainer Morgan McGuire, matrix@graphics3d.com
+
+ @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-10-10
+ */
+
+#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"
+
+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 = -(float)G3D::inf();
+ 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 = (float)inf();
+ 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 (float)inf();
+ }
+
+ float t = -(pdotN + d) / vdotN;
+ if (t < 0) {
+ location = Vector3::inf();
+ return (float)inf();
+ } else {
+ location = point + velocity * t;
+ outNormal = normal;
+ return t;
+ }
+}
+
+
+float CollisionDetection::collisionTimeForMovingPointFixedSphere(
+ const Vector3& point,
+ const Vector3& velocity,
+ const Sphere& sphere,
+ Vector3& location,
+ Vector3& outNormal) {
+
+ double speed = velocity.magnitude();
+ Vector3 direction = velocity / speed;
+
+ Vector3 L = sphere.center - point;
+ double d = L.dot(direction);
+
+ double L2 = L.dot(L);
+ double R2 = sphere.radius * sphere.radius;
+ double D2 = d * d;
+
+ if ((d < 0) && (L2 > R2)) {
+ location = Vector3::inf();
+ return inf();
+ }
+
+ double M2 = L2 - D2;
+
+ if (M2 > R2) {
+ location = Vector3::inf();
+ return inf();
+ }
+
+ double q = sqrt(R2 - M2);
+ double 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) {
+
+ double time = collisionTimeForMovingPointFixedSphere(movingSphere.center, velocity, Sphere(fixedSphere.center, fixedSphere.radius + movingSphere.radius), location, outNormal);
+
+ if (time < inf()) {
+ // 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 == inf()) {
+ // No collision with the plane of the triangle.
+ return inf();
+ }
+
+ 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 inf();
+ }
+}*/
+
+/*
+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 inf();
+ }
+
+ // 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 inf();
+ }
+
+ // 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 inf();
+ }
+
+ // 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 inf();
+ }
+
+ #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)inf();
+ }
+}
+
+
+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 == inf()) {
+ // 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 inf();
+ }
+}
+
+/** 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 inf();
+ }
+}
+
+
+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)inf();
+ }
+
+ 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 == inf()) {
+ // 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 < inf()) {
+ 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 == inf()) {
+ // 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 < inf()) {
+ // 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));
+}
+
+
+} // namespace
+#ifdef _MSC_VER
+#pragma warning (pop)
+#endif
diff --git a/externals/g3dlite/G3D.lib/source/Color1.cpp b/externals/g3dlite/G3D.lib/source/Color1.cpp
new file mode 100644
index 00000000000..7e058a27d1a
--- /dev/null
+++ b/externals/g3dlite/G3D.lib/source/Color1.cpp
@@ -0,0 +1,40 @@
+/**
+ @file Color1.cpp
+
+ Color class.
+
+ @author Morgan McGuire, matrix@graphics3d.com
+
+ @created 2007-01-30
+ @edited 2007-01-30
+ */
+
+#include "G3D/platform.h"
+#include "G3D/Color1.h"
+#include "G3D/Color1uint8.h"
+#include "G3D/BinaryInput.h"
+#include "G3D/BinaryOutput.h"
+
+namespace G3D {
+
+Color1::Color1(BinaryInput& bi) {
+ deserialize(bi);
+}
+
+
+void Color1::deserialize(BinaryInput& bi) {
+ value = bi.readFloat32();
+}
+
+
+void Color1::serialize(BinaryOutput& bo) const {
+ bo.writeFloat32(value);
+}
+
+
+Color1::Color1(const class Color1uint8& other) {
+ value = other.value / 255.0f;
+}
+
+} // namespace G3D
+
diff --git a/externals/g3dlite/G3D.lib/source/Color1uint8.cpp b/externals/g3dlite/G3D.lib/source/Color1uint8.cpp
new file mode 100644
index 00000000000..0fd00693d4c
--- /dev/null
+++ b/externals/g3dlite/G3D.lib/source/Color1uint8.cpp
@@ -0,0 +1,38 @@
+/**
+ @file Color1uint8.cpp
+
+ @author Morgan McGuire, matrix@graphics3d.com
+
+ @created 2007-01-30
+ @edited 2007-01-30
+ */
+
+#include "G3D/platform.h"
+#include "G3D/g3dmath.h"
+#include "G3D/Color1uint8.h"
+#include "G3D/Color1.h"
+#include "G3D/BinaryInput.h"
+#include "G3D/BinaryOutput.h"
+
+namespace G3D {
+
+Color1uint8::Color1uint8(const class Color1& c) : value(iClamp(iFloor(c.value * 256), 0, 255)) {
+}
+
+
+Color1uint8::Color1uint8(class BinaryInput& bi) {
+ deserialize(bi);
+}
+
+
+void Color1uint8::serialize(class BinaryOutput& bo) const {
+ bo.writeUInt8(value);
+}
+
+
+void Color1uint8::deserialize(class BinaryInput& bi) {
+ value = bi.readUInt8();
+}
+
+
+}
diff --git a/externals/g3dlite/G3D.lib/source/Color3.cpp b/externals/g3dlite/G3D.lib/source/Color3.cpp
new file mode 100644
index 00000000000..8183d8a0f62
--- /dev/null
+++ b/externals/g3dlite/G3D.lib/source/Color3.cpp
@@ -0,0 +1,321 @@
+/**
+ @file Color3.cpp
+
+ Color class.
+
+ @author Morgan McGuire, matrix@graphics3d.com
+ @cite Portions based on Dave Eberly's Magic Software Library at http://www.magic-software.com
+
+
+ @created 2001-06-02
+ @edited 2006-01-13
+ */
+
+#include "G3D/platform.h"
+#include <stdlib.h>
+#include "G3D/Color3.h"
+#include "G3D/Vector3.h"
+#include "G3D/format.h"
+#include "G3D/BinaryInput.h"
+#include "G3D/BinaryOutput.h"
+#include "G3D/Color3uint8.h"
+
+namespace G3D {
+
+const Color3& Color3::red() {
+ static Color3 c(1.0f, 0.0f, 0.0f);
+ return c;
+}
+
+
+const Color3& Color3::green() {
+ static Color3 c(0.0f, 1.0f, 0.0f);
+ return c;
+}
+
+
+const Color3& Color3::blue() {
+ static Color3 c(0.0f, 0.0f, 1.0f);
+ return c;
+}
+
+
+const Color3& Color3::purple() {
+ static Color3 c(0.7f, 0.0f, 1.0f);
+ return c;
+}
+
+
+const Color3& Color3::cyan() {
+ static Color3 c(0.0f, 0.7f, 1.0f);
+ return c;
+}
+
+
+const Color3& Color3::yellow() {
+ static Color3 c(1.0f, 1.0f, 0.0f);
+ return c;
+}
+
+
+const Color3& Color3::brown() {
+ static Color3 c(0.5f, 0.5f, 0.0f);
+ return c;
+}
+
+
+const Color3& Color3::orange() {
+ static Color3 c(1.0f, 0.5f, 0.0f);
+ return c;
+}
+
+
+const Color3& Color3::black() {
+ static Color3 c(0.0f, 0.0f, 0.0f);
+ return c;
+}
+
+const Color3& Color3::zero() {
+ static Color3 c(0.0f, 0.0f, 0.0f);
+ return c;
+}
+
+
+const Color3& Color3::one() {
+ static Color3 c(1.0f, 1.0f, 1.0f);
+ return c;
+}
+
+
+const Color3& Color3::gray() {
+ static Color3 c(0.7f, 0.7f, 0.7f);
+ return c;
+}
+
+
+const Color3& Color3::white() {
+ static Color3 c(1, 1, 1);
+ return c;
+}
+
+
+Color3::Color3(BinaryInput& bi) {
+ deserialize(bi);
+}
+
+
+void Color3::deserialize(BinaryInput& bi) {
+ r = bi.readFloat32();
+ g = bi.readFloat32();
+ b = bi.readFloat32();
+}
+
+
+void Color3::serialize(BinaryOutput& bo) const {
+ bo.writeFloat32(r);
+ bo.writeFloat32(g);
+ bo.writeFloat32(b);
+}
+
+
+const Color3& Color3::wheelRandom() {
+ static const Color3 colorArray[8] =
+ {Color3::blue(), Color3::red(), Color3::green(),
+ Color3::orange(), Color3::yellow(),
+ Color3::cyan(), Color3::purple(), Color3::brown()};
+
+ return colorArray[iRandom(0, 7)];
+}
+
+
+size_t Color3::hashCode() const {
+ unsigned int rhash = (*(int*)(void*)(&r));
+ unsigned int ghash = (*(int*)(void*)(&g));
+ unsigned int bhash = (*(int*)(void*)(&b));
+
+ return rhash + (ghash * 37) + (bhash * 101);
+}
+
+
+Color3::Color3(const Vector3& v) {
+ r = v.x;
+ g = v.y;
+ b = v.z;
+}
+
+
+Color3::Color3(const class Color3uint8& other) {
+ r = other.r / 255.0f;
+ g = other.g / 255.0f;
+ b = other.b / 255.0f;
+}
+
+
+Color3 Color3::fromARGB(uint32 x) {
+ return Color3((float)((x >> 16) & 0xFF), (float)((x >> 8) & 0xFF), (float)(x & 0xFF)) / 255.0f;
+}
+
+//----------------------------------------------------------------------------
+
+
+Color3 Color3::random() {
+ return Color3(uniformRandom(),
+ uniformRandom(),
+ uniformRandom()).direction();
+}
+
+//----------------------------------------------------------------------------
+Color3 Color3::operator/ (float fScalar) const {
+ Color3 kQuot;
+
+ if (fScalar != 0.0f) {
+ float fInvScalar = 1.0f / fScalar;
+ kQuot.r = fInvScalar * r;
+ kQuot.g = fInvScalar * g;
+ kQuot.b = fInvScalar * b;
+ return kQuot;
+
+ } else {
+
+ return Color3((float)G3D::inf(), (float)G3D::inf(), (float)G3D::inf());
+ }
+}
+
+//----------------------------------------------------------------------------
+Color3& Color3::operator/= (float fScalar) {
+ if (fScalar != 0.0f) {
+ float fInvScalar = 1.0f / fScalar;
+ r *= fInvScalar;
+ g *= fInvScalar;
+ b *= fInvScalar;
+ } else {
+ r = (float)G3D::inf();
+ g = (float)G3D::inf();
+ b = (float)G3D::inf();
+ }
+
+ return *this;
+}
+
+//----------------------------------------------------------------------------
+float Color3::unitize (float fTolerance) {
+ float fLength = length();
+
+ if ( fLength > fTolerance ) {
+ float fInvLength = 1.0f / fLength;
+ r *= fInvLength;
+ g *= fInvLength;
+ b *= fInvLength;
+ } else {
+ fLength = 0.0f;
+ }
+
+ return fLength;
+}
+
+//----------------------------------------------------------------------------
+Color3 Color3::fromHSV(const Vector3& _hsv) {
+ debugAssertM((_hsv.x <= 1.0f && _hsv.x >= 0.0f)
+ && (_hsv.y <= 1.0f && _hsv.y >= 0.0f)
+ && ( _hsv.z <= 1.0f && _hsv.z >= 0.0f), "H,S,V must be between [0,1]");
+ const int i = G3D::iFloor(6.0*_hsv.x);
+ const float f = 6.0f * _hsv.x - i;
+ const float m = _hsv.z * (1.0f - (_hsv.y));
+ const float n = _hsv.z * (1.0f - (_hsv.y * f));
+ const float k = _hsv.z * (1.0f - (_hsv.y * (1 - f)));
+ switch(i) {
+ case 0:
+ return Color3(_hsv.z, k, m);
+
+ case 1:
+ return Color3(n, _hsv.z, m);
+
+ case 2:
+ return Color3(m, _hsv.z, k);
+
+ case 3:
+ return Color3(m, n, _hsv.z);
+
+ case 4:
+ return Color3(k, m, _hsv.z);
+
+ case 5:
+ return Color3(_hsv.z, m, n);
+
+ default:
+ debugAssertM(false, "fell through switch..");
+ }
+ return Color3::black();
+}
+
+
+Vector3 Color3::toHSV(const Color3& _rgb) {
+ debugAssertM((_rgb.r <= 1.0f && _rgb.r >= 0.0f)
+ && (_rgb.g <= 1.0f && _rgb.g >= 0.0f)
+ && (_rgb.b <= 1.0f && _rgb.b >= 0.0f), "R,G,B must be between [0,1]");
+ Vector3 hsv = Vector3::zero();
+ hsv.z = G3D::max(G3D::max(_rgb.r, _rgb.g), _rgb.b);
+ if (G3D::fuzzyEq(hsv.z, 0.0f)) {
+ return hsv;
+ }
+
+ const float x = G3D::min(G3D::min(_rgb.r, _rgb.g), _rgb.b);
+ hsv.y = (hsv.z - x) / hsv.z;
+
+ if (G3D::fuzzyEq(hsv.y, 0.0f)) {
+ return hsv;
+ }
+
+ Vector3 rgbN;
+ rgbN.x = (hsv.z - _rgb.r) / (hsv.z - x);
+ rgbN.y = (hsv.z - _rgb.g) / (hsv.z - x);
+ rgbN.z = (hsv.z - _rgb.b) / (hsv.z - x);
+
+ if (_rgb.r == hsv.z) { // note from the max we know that it exactly equals one of the three.
+ hsv.x = (_rgb.g == x)? 5.0f + rgbN.z : 1.0f - rgbN.y;
+ } else if (_rgb.g == hsv.z) {
+ hsv.x = (_rgb.b == x)? 1.0f + rgbN.x : 3.0f - rgbN.z;
+ } else {
+ hsv.x = (_rgb.r == x)? 3.0f + rgbN.y : 5.0f - rgbN.x;
+ }
+
+ hsv.x /= 6.0f;
+
+ return hsv;
+}
+
+Color3 Color3::jetColorMap(const float& val) {
+ debugAssertM(val <= 1.0f && val >= 0.0f , "value should be in [0,1]");
+
+ //truncated triangles where sides have slope 4
+ Color3 jet;
+
+ jet.r = G3D::min(4.0f * val - 1.5f,-4.0f * val + 4.5f) ;
+ jet.g = G3D::min(4.0f * val - 0.5f,-4.0f * val + 3.5f) ;
+ jet.b = G3D::min(4.0f * val + 0.5f,-4.0f * val + 2.5f) ;
+
+
+ jet.r = G3D::clamp(jet.r, 0.0f, 1.0f);
+ jet.g = G3D::clamp(jet.g, 0.0f, 1.0f);
+ jet.b = G3D::clamp(jet.b, 0.0f, 1.0f);
+
+ return jet;
+}
+
+
+
+
+
+std::string Color3::toString() const {
+ return G3D::format("(%g, %g, %g)", r, g, b);
+}
+
+//----------------------------------------------------------------------------
+
+Color3 Color3::rainbowColorMap(float hue) {
+ return fromHSV(Vector3(hue, 1.0f, 1.0f));
+}
+
+
+}; // namespace
+
diff --git a/externals/g3dlite/G3D.lib/source/Color3uint8.cpp b/externals/g3dlite/G3D.lib/source/Color3uint8.cpp
new file mode 100644
index 00000000000..837bf1b2c8b
--- /dev/null
+++ b/externals/g3dlite/G3D.lib/source/Color3uint8.cpp
@@ -0,0 +1,45 @@
+/**
+ @file Color3uint8.cpp
+
+ @author Morgan McGuire, matrix@graphics3d.com
+
+ @created 2003-04-07
+ @edited 2006-01-07
+ */
+
+#include "G3D/platform.h"
+#include "G3D/g3dmath.h"
+#include "G3D/Color3uint8.h"
+#include "G3D/Color3.h"
+#include "G3D/BinaryInput.h"
+#include "G3D/BinaryOutput.h"
+
+namespace G3D {
+
+Color3uint8::Color3uint8(const class Color3& c) {
+ r = iMin(255, iFloor(c.r * 256));
+ g = iMin(255, iFloor(c.g * 256));
+ b = iMin(255, iFloor(c.b * 256));
+}
+
+
+Color3uint8::Color3uint8(class BinaryInput& bi) {
+ deserialize(bi);
+}
+
+
+void Color3uint8::serialize(class BinaryOutput& bo) const {
+ bo.writeUInt8(r);
+ bo.writeUInt8(g);
+ bo.writeUInt8(b);
+}
+
+
+void Color3uint8::deserialize(class BinaryInput& bi) {
+ r = bi.readUInt8();
+ g = bi.readUInt8();
+ b = bi.readUInt8();
+}
+
+
+}
diff --git a/externals/g3dlite/G3D.lib/source/Color4.cpp b/externals/g3dlite/G3D.lib/source/Color4.cpp
new file mode 100644
index 00000000000..ed2e91291a1
--- /dev/null
+++ b/externals/g3dlite/G3D.lib/source/Color4.cpp
@@ -0,0 +1,140 @@
+/**
+ @file Color4.cpp
+
+ Color class.
+
+ @author Morgan McGuire, matrix@graphics3d.com
+ @cite Portions by Laura Wollstadt, graphics3d.com
+ @cite Portions based on Dave Eberly's Magic Software Library at http://www.magic-software.com
+
+
+ @created 2002-06-25
+ @edited 2006-01-10
+ */
+
+#include <stdlib.h>
+#include "G3D/Color4.h"
+#include "G3D/Color4uint8.h"
+#include "G3D/Vector4.h"
+#include "G3D/format.h"
+#include "G3D/BinaryInput.h"
+#include "G3D/BinaryOutput.h"
+
+namespace G3D {
+
+const Color4& Color4::zero() {
+ static Color4 c(0.0f, 0.0f, 0.0f, 0.0f);
+ return c;
+}
+
+
+const Color4& Color4::inf() {
+ static Color4 c((float)G3D::inf(), (float)G3D::inf(), (float)G3D::inf(), (float)G3D::inf());
+ return c;
+}
+
+
+const Color4& Color4::clear() {
+ return Color4::zero();
+}
+
+
+Color4::Color4(const Vector4& v) {
+ r = v.x;
+ g = v.y;
+ b = v.z;
+ a = v.w;
+}
+
+
+Color4::Color4(const Color4uint8& c) : r(c.r), g(c.g), b(c.b), a(c.a) {
+ *this /= 255.0f;
+}
+
+size_t Color4::hashCode() const {
+ unsigned int rhash = (*(int*)(void*)(&r));
+ unsigned int ghash = (*(int*)(void*)(&g));
+ unsigned int bhash = (*(int*)(void*)(&b));
+ unsigned int ahash = (*(int*)(void*)(&a));
+
+ return rhash + (ghash * 37) + (bhash * 101) + (ahash * 241);
+}
+
+Color4 Color4::fromARGB(uint32 x) {
+ return Color4(
+ (float)((x >> 16) & 0xFF),
+ (float)((x >> 8) & 0xFF),
+ (float)(x & 0xFF),
+ (float)((x >> 24) & 0xFF)) / 255.0;
+}
+
+
+Color4::Color4(BinaryInput& bi) {
+ deserialize(bi);
+}
+
+
+void Color4::deserialize(BinaryInput& bi) {
+ r = bi.readFloat32();
+ g = bi.readFloat32();
+ b = bi.readFloat32();
+ a = bi.readFloat32();
+}
+
+
+void Color4::serialize(BinaryOutput& bo) const {
+ bo.writeFloat32(r);
+ bo.writeFloat32(g);
+ bo.writeFloat32(b);
+ bo.writeFloat32(a);
+}
+
+
+//----------------------------------------------------------------------------
+
+Color4 Color4::operator/ (float fScalar) const {
+ Color4 kQuot;
+
+ if (fScalar != 0.0f) {
+ float fInvScalar = 1.0f / fScalar;
+ kQuot.r = fInvScalar * r;
+ kQuot.g = fInvScalar * g;
+ kQuot.b = fInvScalar * b;
+ kQuot.a = fInvScalar * a;
+ return kQuot;
+
+ } else {
+
+ return Color4::inf();
+ }
+}
+
+//----------------------------------------------------------------------------
+
+Color4& Color4::operator/= (float fScalar) {
+ if (fScalar != 0.0f) {
+ float fInvScalar = 1.0f / fScalar;
+ r *= fInvScalar;
+ g *= fInvScalar;
+ b *= fInvScalar;
+ a *= fInvScalar;
+ } else {
+ r = (float)G3D::inf();
+ g = (float)G3D::inf();
+ b = (float)G3D::inf();
+ a = (float)G3D::inf();
+ }
+
+ return *this;
+}
+
+//----------------------------------------------------------------------------
+
+std::string Color4::toString() const {
+ return G3D::format("(%g, %g, %g, %g)", r, g, b, a);
+}
+
+//----------------------------------------------------------------------------
+
+}; // namespace
+
diff --git a/externals/g3dlite/G3D.lib/source/Color4uint8.cpp b/externals/g3dlite/G3D.lib/source/Color4uint8.cpp
new file mode 100644
index 00000000000..8c8636a742e
--- /dev/null
+++ b/externals/g3dlite/G3D.lib/source/Color4uint8.cpp
@@ -0,0 +1,47 @@
+/**
+ @file Color4uint8.cpp
+
+ @author Morgan McGuire, matrix@graphics3d.com
+
+ @created 2003-04-07
+ @edited 2006-01-07
+ */
+#include "G3D/platform.h"
+#include "G3D/g3dmath.h"
+#include "G3D/Color4uint8.h"
+#include "G3D/Color4.h"
+#include "G3D/BinaryInput.h"
+#include "G3D/BinaryOutput.h"
+
+namespace G3D {
+
+Color4uint8::Color4uint8(const class Color4& c) {
+ r = iMin(255, iFloor(c.r * 256));
+ g = iMin(255, iFloor(c.g * 256));
+ b = iMin(255, iFloor(c.b * 256));
+ a = iMin(255, iFloor(c.a * 256));
+}
+
+
+Color4uint8::Color4uint8(class BinaryInput& bi) {
+ deserialize(bi);
+}
+
+
+void Color4uint8::serialize(class BinaryOutput& bo) const {
+ bo.writeUInt8(r);
+ bo.writeUInt8(g);
+ bo.writeUInt8(b);
+ bo.writeUInt8(a);
+}
+
+
+void Color4uint8::deserialize(class BinaryInput& bi) {
+ r = bi.readUInt8();
+ g = bi.readUInt8();
+ b = bi.readUInt8();
+ a = bi.readUInt8();
+}
+
+
+}
diff --git a/externals/g3dlite/G3D.lib/source/Cone.cpp b/externals/g3dlite/G3D.lib/source/Cone.cpp
new file mode 100644
index 00000000000..99b29b5b0af
--- /dev/null
+++ b/externals/g3dlite/G3D.lib/source/Cone.cpp
@@ -0,0 +1,79 @@
+/**
+ @file Cone.cpp
+
+ Cone class
+
+ @maintainer Morgan McGuire, matrix@graphics3d.com
+
+ @created 2001-07-09
+ @edited 2006-01-29
+*/
+
+#include "G3D/platform.h"
+#include "G3D/Cone.h"
+#include "G3D/Line.h"
+#include "G3D/Sphere.h"
+#include "G3D/Box.h"
+
+namespace G3D {
+
+Cone::Cone(const Vector3 &tip, const Vector3 &direction, float angle) {
+ this->tip = tip;
+ this->direction = direction.direction();
+ this->angle = angle;
+
+ debugAssert(angle >= 0);
+ debugAssert(angle <= pi());
+}
+
+/**
+ Forms the smallest cone that contains the box. Undefined if
+ the tip is inside or on the box.
+ */
+Cone::Cone(const Vector3& tip, const Box& box) {
+ this->tip = tip;
+ this->direction = (box.center() - tip).direction();
+
+ // Find the biggest angle
+ float smallestDotProduct = direction.dot((box.corner(0) - tip).direction());
+
+ for (int i = 1; i < 8; ++i) {
+ float dp = direction.dot((box.corner(i) - tip).direction());
+
+ debugAssert(dp > 0);
+
+ if (dp < smallestDotProduct) {
+ smallestDotProduct = dp;
+ }
+ }
+
+ angle = acosf(smallestDotProduct);
+}
+
+
+bool Cone::intersects(const Sphere& b) const {
+ // If the bounding sphere contains the tip, then
+ // they definitely touch.
+ if (b.contains(this->tip)) {
+ return true;
+ }
+
+ // Move the tip backwards, effectively making the cone bigger
+ // to account for the radius of the sphere.
+
+ Vector3 tip = this->tip - direction * b.radius / sinf(angle);
+
+ return Cone(tip, direction, angle).contains(b.center);
+}
+
+
+bool Cone::contains(const Vector3& v) const {
+
+ Vector3 d = (v - tip).direction();
+
+ float x = d.dot(direction);
+
+ return (x > 0) && (x >= cosf(angle));
+}
+
+}; // namespace
diff --git a/externals/g3dlite/G3D.lib/source/ConvexPolyhedron.cpp b/externals/g3dlite/G3D.lib/source/ConvexPolyhedron.cpp
new file mode 100644
index 00000000000..76dbe21a7c4
--- /dev/null
+++ b/externals/g3dlite/G3D.lib/source/ConvexPolyhedron.cpp
@@ -0,0 +1,449 @@
+/**
+ @file ConvexPolyhedron.cpp
+
+ @author Morgan McGuire, morgan@graphics3d.com
+
+ @created 2001-11-11
+ @edited 2006-01-10
+
+ Copyright 2000-2006, Morgan McGuire.
+ All rights reserved.
+ */
+
+#include "G3D/platform.h"
+#include "G3D/ConvexPolyhedron.h"
+#include "G3D/debug.h"
+
+namespace G3D {
+
+ConvexPolygon::ConvexPolygon(const Array<Vector3>& __vertex) : _vertex(__vertex) {
+ // Intentionally empty
+}
+
+
+bool ConvexPolygon::isEmpty() const {
+ return (_vertex.length() == 0) || (getArea() <= fuzzyEpsilon);
+}
+
+
+float ConvexPolygon::getArea() const {
+
+ if (_vertex.length() < 3) {
+ return 0;
+ }
+
+ float sum = 0;
+
+ int length = _vertex.length();
+ // Split into triangle fan, compute individual area
+ for (int v = 2; v < length; v++) {
+ int i0 = 0;
+ int i1 = v - 1;
+ int i2 = v;
+
+ sum += (_vertex[i1] - _vertex[i0]).cross(_vertex[i2] - _vertex[i0]).magnitude() / 2;
+ }
+
+ return sum;
+}
+
+void ConvexPolygon::cut(const Plane& plane, ConvexPolygon &above, ConvexPolygon &below) {
+ DirectedEdge edge;
+ cut(plane, above, below, edge);
+}
+
+void ConvexPolygon::cut(const Plane& plane, ConvexPolygon &above, ConvexPolygon &below, DirectedEdge &newEdge) {
+ above._vertex.resize(0);
+ below._vertex.resize(0);
+
+ if (isEmpty()) {
+ //debugPrintf("Empty\n");
+ return;
+ }
+
+ int v = 0;
+ int length = _vertex.length();
+
+
+ Vector3 polyNormal = normal();
+ Vector3 planeNormal= plane.normal();
+
+ // See if the polygon is *in* the plane.
+ if (planeNormal.fuzzyEq(polyNormal) || planeNormal.fuzzyEq(-polyNormal)) {
+ // Polygon is parallel to the plane. It must be either above,
+ // below, or in the plane.
+
+ double a, b, c, d;
+ Vector3 pt = _vertex[0];
+
+ plane.getEquation(a,b,c,d);
+ float r = (float)(a * pt.x + b * pt.y + c * pt.z + d);
+
+ if (fuzzyGe(r, 0)) {
+ // The polygon is entirely in the plane.
+ //debugPrintf("Entirely above\n");
+ above = *this;
+ return;
+ } else {
+ //debugPrintf("Entirely below (1)\n");
+ below = *this;
+ return;
+ }
+ }
+
+
+ // Number of edges crossing the plane. Used for
+ // debug assertions.
+ int count = 0;
+
+ // True when the last _vertex we looked at was above the plane
+ bool lastAbove = plane.halfSpaceContains(_vertex[v]);
+
+ if (lastAbove) {
+ above._vertex.append(_vertex[v]);
+ } else {
+ below._vertex.append(_vertex[v]);
+ }
+
+ for (v = 1; v < length; v++) {
+ bool isAbove = plane.halfSpaceContains(_vertex[v]);
+
+ if (lastAbove ^ isAbove) {
+ // Switched sides.
+ // Create an interpolated point that lies
+ // in the plane, between the two points.
+ Line line = Line::fromTwoPoints(_vertex[v - 1], _vertex[v]);
+ Vector3 interp = line.intersection(plane);
+
+ if (! interp.isFinite()) {
+
+ // Since the polygon is not in the plane (we checked above),
+ // it must be the case that this edge (and only this edge)
+ // is in the plane. This only happens when the polygon is
+ // entirely below the plane except for one edge. This edge
+ // forms a degenerate polygon, so just treat the whole polygon
+ // as below the plane.
+ below = *this;
+ above._vertex.resize(0);
+ //debugPrintf("Entirely below\n");
+ return;
+ }
+
+ above._vertex.append(interp);
+ below._vertex.append(interp);
+ if (lastAbove) {
+ newEdge.stop = interp;
+ } else {
+ newEdge.start = interp;
+ }
+ count++;
+ }
+
+ lastAbove = isAbove;
+ if (lastAbove) {
+ above._vertex.append(_vertex[v]);
+ } else {
+ below._vertex.append(_vertex[v]);
+ }
+ }
+
+ // Loop back to the first point, seeing if an interpolated point is
+ // needed.
+ bool isAbove = plane.halfSpaceContains(_vertex[0]);
+ if (lastAbove ^ isAbove) {
+ Line line = Line::fromTwoPoints(_vertex[length - 1], _vertex[0]);
+ Vector3 interp = line.intersection(plane);
+ if (! interp.isFinite()) {
+ // Since the polygon is not in the plane (we checked above),
+ // it must be the case that this edge (and only this edge)
+ // is in the plane. This only happens when the polygon is
+ // entirely below the plane except for one edge. This edge
+ // forms a degenerate polygon, so just treat the whole polygon
+ // as below the plane.
+ below = *this;
+ above._vertex.resize(0);
+ //debugPrintf("Entirely below\n");
+ return;
+ }
+
+ above._vertex.append(interp);
+ below._vertex.append(interp);
+ debugAssertM(count < 2, "Convex polygons may only intersect planes at two edges.");
+ if (lastAbove) {
+ newEdge.stop = interp;
+ } else {
+ newEdge.start = interp;
+ }
+ count++;
+ }
+
+ debugAssertM((count == 2) || (count == 0), "Convex polygons may only intersect planes at two edges.");
+}
+
+ConvexPolygon ConvexPolygon::inverse() const {
+ ConvexPolygon result;
+ int length = _vertex.length();
+ result._vertex.resize(length);
+
+ for (int v = 0; v < length; v++) {
+ result._vertex[v] = _vertex[length - v - 1];
+ }
+
+ return result;
+}
+
+void ConvexPolygon::removeDuplicateVertices(){
+ // Any valid polygon should have 3 or more vertices, but why take chances?
+ if(_vertex.size() >= 2){
+
+ // Remove duplicate vertices.
+ for(int i=0;i<_vertex.size()-1;++i){
+ if(_vertex[i].fuzzyEq(_vertex[i+1])){
+ _vertex.remove(i+1);
+ --i; // Don't move forward.
+ }
+ }
+
+ // Check the last vertex against the first.
+ if(_vertex[_vertex.size()-1].fuzzyEq(_vertex[0])){
+ _vertex.pop();
+ }
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////////
+
+ConvexPolyhedron::ConvexPolyhedron(const Array<ConvexPolygon>& _face) : face(_face) {
+ // Intentionally empty
+}
+
+float ConvexPolyhedron::getVolume() const {
+
+ if (face.length() < 4) {
+ return 0;
+ }
+
+ // The volume of any pyramid is 1/3 * h * base area.
+ // Discussion at: http://nrich.maths.org/mathsf/journalf/oct01/art1/
+
+ float sum = 0;
+
+ // Choose the first _vertex of the first face as the origin.
+ // This lets us skip one face, too, and avoids negative heights.
+ Vector3 v0 = face[0]._vertex[0];
+ for (int f = 1; f < face.length(); f++) {
+ const ConvexPolygon& poly = face[f];
+
+ float height = (poly._vertex[0] - v0).dot(poly.normal());
+ float base = poly.getArea();
+
+ sum += height * base;
+ }
+
+ return sum / 3;
+}
+
+bool ConvexPolyhedron::isEmpty() const {
+ return (face.length() == 0) || (getVolume() <= fuzzyEpsilon);
+}
+
+void ConvexPolyhedron::cut(const Plane& plane, ConvexPolyhedron &above, ConvexPolyhedron &below) {
+ above.face.resize(0);
+ below.face.resize(0);
+
+ Array<DirectedEdge> edge;
+
+ int f;
+
+ // See if the plane cuts this polyhedron at all. Detect when
+ // the polyhedron is entirely to one side or the other.
+ //{
+ int numAbove = 0, numIn = 0, numBelow = 0;
+ bool ruledOut = false;
+ double d;
+ Vector3 abc;
+ plane.getEquation(abc, d);
+
+ // This number has to be fairly large to prevent precision problems down
+ // the road.
+ const float eps = 0.005f;
+ for (f = face.length() - 1; (f >= 0) && (!ruledOut); f--) {
+ const ConvexPolygon& poly = face[f];
+ for (int v = poly._vertex.length() - 1; (v >= 0) && (!ruledOut); v--) {
+ double r = abc.dot(poly._vertex[v]) + d;
+ if (r > eps) {
+ numAbove++;
+ } else if (r < -eps) {
+ numBelow++;
+ } else {
+ numIn++;
+ }
+
+ ruledOut = (numAbove != 0) && (numBelow !=0);
+ }
+ }
+
+ if (numBelow == 0) {
+ above = *this;
+ return;
+ } else if (numAbove == 0) {
+ below = *this;
+ return;
+ }
+ //}
+
+ // Clip each polygon, collecting split edges.
+ for (f = face.length() - 1; f >= 0; f--) {
+ ConvexPolygon a, b;
+ DirectedEdge e;
+ face[f].cut(plane, a, b, e);
+
+ bool aEmpty = a.isEmpty();
+ bool bEmpty = b.isEmpty();
+
+ //debugPrintf("\n");
+ if (! aEmpty) {
+ //debugPrintf(" Above %f\n", a.getArea());
+ above.face.append(a);
+ }
+
+ if (! bEmpty) {
+ //debugPrintf(" Below %f\n", b.getArea());
+ below.face.append(b);
+ }
+
+ if (! aEmpty && ! bEmpty) {
+ //debugPrintf(" == Split\n");
+ edge.append(e);
+ } else {
+ // Might be the case that the polygon is entirely on
+ // one side of the plane yet there is an edge we need
+ // because it touches the plane.
+ //
+ // Extract the non-empty _vertex list and examine it.
+ // If we find exactly one edge in the plane, add that edge.
+ const Array<Vector3>& _vertex = (aEmpty ? b._vertex : a._vertex);
+ int L = _vertex.length();
+ int count = 0;
+ for (int v = 0; v < L; v++) {
+ if (plane.fuzzyContains(_vertex[v]) && plane.fuzzyContains(_vertex[(v + 1) % L])) {
+ e.start = _vertex[v];
+ e.stop = _vertex[(v + 1) % L];
+ count++;
+ }
+ }
+
+ if (count == 1) {
+ edge.append(e);
+ }
+ }
+ }
+
+ if (above.face.length() == 1) {
+ // Only one face above means that this entire
+ // polyhedron is below the plane. Move that face over.
+ below.face.append(above.face[0]);
+ above.face.resize(0);
+ } else if (below.face.length() == 1) {
+ // This shouldn't happen, but it arises in practice
+ // from numerical imprecision.
+ above.face.append(below.face[0]);
+ below.face.resize(0);
+ }
+
+ if ((above.face.length() > 0) && (below.face.length() > 0)) {
+ // The polyhedron was actually cut; create a cap polygon
+ ConvexPolygon cap;
+
+ // Collect the final polgyon by sorting the edges
+ int numVertices = edge.length();
+/*debugPrintf("\n");
+for (int xx=0; xx < numVertices; xx++) {
+ std::string s1 = edge[xx].start.toString();
+ std::string s2 = edge[xx].stop.toString();
+ debugPrintf("%s -> %s\n", s1.c_str(), s2.c_str());
+}
+*/
+
+ // Need at least three points to make a polygon
+ debugAssert(numVertices >= 3);
+
+ Vector3 last_vertex = edge.last().stop;
+ cap._vertex.append(last_vertex);
+
+ // Search for the next _vertex. Because of accumulating
+ // numerical error, we have to find the closest match, not
+ // just the one we expect.
+ for (int v = numVertices - 1; v >= 0; v--) {
+ // matching edge index
+ int index = 0;
+ int num = edge.length();
+ double distance = (edge[index].start - last_vertex).squaredMagnitude();
+ for (int e = 1; e < num; e++) {
+ double d = (edge[e].start - last_vertex).squaredMagnitude();
+
+ if (d < distance) {
+ // This is the new closest one
+ index = e;
+ distance = d;
+ }
+ }
+
+ // Don't tolerate ridiculous error.
+ debugAssertM(distance < 0.02, "Edge missing while closing polygon.");
+
+ last_vertex = edge[index].stop;
+ cap._vertex.append(last_vertex);
+ }
+
+ //debugPrintf("\n");
+ //debugPrintf("Cap (both) %f\n", cap.getArea());
+ above.face.append(cap);
+ below.face.append(cap.inverse());
+ }
+
+ // Make sure we put enough faces on each polyhedra
+ debugAssert((above.face.length() == 0) || (above.face.length() >= 4));
+ debugAssert((below.face.length() == 0) || (below.face.length() >= 4));
+}
+
+///////////////////////////////////////////////
+
+ConvexPolygon2D::ConvexPolygon2D(const Array<Vector2>& pts, bool reverse) : m_vertex(pts) {
+ if (reverse) {
+ m_vertex.reverse();
+ }
+}
+
+
+bool ConvexPolygon2D::contains(const Vector2& p, bool reverse) const {
+ // Compute the signed area of each polygon from p to an edge.
+ // If the area is non-negative for all polygons then p is inside
+ // the polygon. (To adapt this algorithm for a concave polygon,
+ // the *sum* of the areas must be non-negative).
+
+ float r = reverse ? -1 : 1;
+
+ for (int i0 = 0; i0 < m_vertex.size(); ++i0) {
+ int i1 = (i0 + 1) % m_vertex.size();
+ const Vector2& v0 = m_vertex[i0];
+ const Vector2& v1 = m_vertex[i1];
+
+ Vector2 e0 = v0 - p;
+ Vector2 e1 = v1 - p;
+
+ // Area = (1/2) cross product, negated to be ccw in
+ // a 2D space; we neglect the 1/2
+ float area = -(e0.x * e1.y - e0.y * e1.x);
+
+ if (area * r < 0) {
+ return false;
+ }
+ }
+
+ return true;
+}
+
+
+}
+
diff --git a/externals/g3dlite/G3D.lib/source/CoordinateFrame.cpp b/externals/g3dlite/G3D.lib/source/CoordinateFrame.cpp
new file mode 100644
index 00000000000..b6e94fe5857
--- /dev/null
+++ b/externals/g3dlite/G3D.lib/source/CoordinateFrame.cpp
@@ -0,0 +1,381 @@
+/**
+ @file CoordinateFrame.cpp
+
+ Coordinate frame class
+
+ @maintainer Morgan McGuire, morgan@cs.williams.edu
+
+ @created 2001-06-02
+ @edited 2008-07-13
+*/
+
+#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"
+
+namespace G3D {
+
+CoordinateFrame::CoordinateFrame(const class UprightFrame& f) {
+ *this = f.toCoordinateFrame();
+}
+
+
+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 {
+ return toWorldSpace(Box(b));
+}
+
+
+Box CoordinateFrame::toWorldSpace(const Box& b) const {
+ Box out(b);
+
+ for (int i = 0; i < 8; ++i) {
+ out._corner[i] = pointToWorldSpace(out._corner[i]);
+ debugAssert(! isNaN(out._corner[i].x));
+ }
+
+ for (int i = 0; i < 3; ++i) {
+ out._axis[i] = vectorToWorldSpace(out._axis[i]);
+ }
+
+ out._center = pointToWorldSpace(out._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/G3D.lib/source/Crypto.cpp b/externals/g3dlite/G3D.lib/source/Crypto.cpp
new file mode 100644
index 00000000000..09aee2c265d
--- /dev/null
+++ b/externals/g3dlite/G3D.lib/source/Crypto.cpp
@@ -0,0 +1,70 @@
+/**
+ @file Crypto.cpp
+
+ @author Morgan McGuire, matrix@graphics3d.com
+
+
+ @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/G3D.lib/source/Crypto_md5.cpp b/externals/g3dlite/G3D.lib/source/Crypto_md5.cpp
new file mode 100644
index 00000000000..4aa24c39f9c
--- /dev/null
+++ b/externals/g3dlite/G3D.lib/source/Crypto_md5.cpp
@@ -0,0 +1,471 @@
+/**
+ @file Crypto_md5.cpp
+
+ @author Morgan McGuire, matrix@graphics3d.com
+ Copyright 2006-2007, Morgan McGuire. All rights reserved.
+
+ @created 2006-03-28
+ @edited 2006-04-06
+ */
+
+#include "G3D/platform.h"
+#include "G3D/Crypto.h"
+#include "G3D/BinaryInput.h"
+#include "G3D/BinaryOutput.h"
+
+#include <cstring>
+
+namespace G3D {
+
+
+MD5Hash::MD5Hash(class BinaryInput& b) {
+ deserialize(b);
+}
+
+
+void MD5Hash::deserialize(class BinaryInput& b) {
+ b.readBytes(value, 16);
+}
+
+
+void MD5Hash::serialize(class BinaryOutput& b) const {
+ b.writeBytes(value, 16);
+}
+
+
+typedef unsigned char md5_byte_t; /* 8-bit byte */
+typedef unsigned int md5_word_t; /* 32-bit word */
+
+/* Define the state of the MD5 Algorithm. */
+typedef struct md5_state_s {
+ md5_word_t count[2]; /* message length in bits, lsw first */
+ md5_word_t abcd[4]; /* digest buffer */
+ md5_byte_t buf[64]; /* accumulate block */
+} md5_state_t;
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+/* Initialize the algorithm. */
+static void md5_init(md5_state_t *pms);
+
+/* Append a string to the message. */
+static void md5_append(md5_state_t *pms, const md5_byte_t *data, int nbytes);
+
+/* Finish the message and return the digest. */
+void md5_finish(md5_state_t *pms, md5_byte_t digest[16]);
+#ifdef __cplusplus
+}
+#endif
+
+
+
+MD5Hash Crypto::md5(const void* data, size_t n) {
+ md5_state_t state;
+ md5_init(&state);
+ md5_append(&state, (const uint8*)data, (int)n);
+
+ MD5Hash h;
+ md5_finish(&state, &(h[0]));
+ return h;
+}
+
+/*
+ Copyright (C) 1999, 2000, 2002 Aladdin Enterprises. All rights reserved.
+
+ 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.
+
+ L. Peter Deutsch
+ ghost@aladdin.com
+
+ */
+/*
+ Independent implementation of MD5 (RFC 1321).
+
+ This code implements the MD5 Algorithm defined in RFC 1321, whose
+ text is available at
+ http://www.ietf.org/rfc/rfc1321.txt
+ The code is derived from the text of the RFC, including the test suite
+ (section A.5) but excluding the rest of Appendix A. It does not include
+ any code or documentation that is identified in the RFC as being
+ copyrighted.
+
+ The original and principal author of md5.c is L. Peter Deutsch
+ <ghost@aladdin.com>. Other authors are noted in the change history
+ that follows (in reverse chronological order):
+
+ 2002-04-13 lpd Clarified derivation from RFC 1321; now handles byte order
+ either statically or dynamically; added missing #include <string.h>
+ in library.
+ 2002-03-11 lpd Corrected argument list for main(), and added int return
+ type, in test program and T value program.
+ 2002-02-21 lpd Added missing #include <stdio.h> in test program.
+ 2000-07-03 lpd Patched to eliminate warnings about "constant is
+ unsigned in ANSI C, signed in traditional"; made test program
+ self-checking.
+ 1999-11-04 lpd Edited comments slightly for automatic TOC extraction.
+ 1999-10-18 lpd Fixed typo in header comment (ansi2knr rather than md5).
+ 1999-05-03 lpd Original version.
+ */
+
+/*
+ * This package supports both compile-time and run-time determination of CPU
+ * byte order. If ARCH_IS_BIG_ENDIAN is defined as 0, the code will be
+ * compiled to run only on little-endian CPUs; if ARCH_IS_BIG_ENDIAN is
+ * defined as non-zero, the code will be compiled to run only on big-endian
+ * CPUs; if ARCH_IS_BIG_ENDIAN is not defined, the code will be compiled to
+ * run on either big- or little-endian CPUs, but will run slightly less
+ * efficiently on either one than if ARCH_IS_BIG_ENDIAN is defined.
+ */
+
+
+#if defined(G3D_LINUX) || defined(G3D_OSX)
+# if defined(G3D_OSX_PPC)
+# include <ppc/endian.h>
+# elif defined(G3D_OSX_INTEL)
+# include <i386/endian.h>
+# elif defined(__linux__)
+# include <endian.h>
+# elif defined(__FreeBSD__)
+# include <sys/endian.h>
+# endif
+#else
+# define BYTE_ORDER 0
+#endif
+
+#define T_MASK ((md5_word_t)~0)
+#define T1 /* 0xd76aa478 */ (T_MASK ^ 0x28955b87)
+#define T2 /* 0xe8c7b756 */ (T_MASK ^ 0x173848a9)
+#define T3 0x242070db
+#define T4 /* 0xc1bdceee */ (T_MASK ^ 0x3e423111)
+#define T5 /* 0xf57c0faf */ (T_MASK ^ 0x0a83f050)
+#define T6 0x4787c62a
+#define T7 /* 0xa8304613 */ (T_MASK ^ 0x57cfb9ec)
+#define T8 /* 0xfd469501 */ (T_MASK ^ 0x02b96afe)
+#define T9 0x698098d8
+#define T10 /* 0x8b44f7af */ (T_MASK ^ 0x74bb0850)
+#define T11 /* 0xffff5bb1 */ (T_MASK ^ 0x0000a44e)
+#define T12 /* 0x895cd7be */ (T_MASK ^ 0x76a32841)
+#define T13 0x6b901122
+#define T14 /* 0xfd987193 */ (T_MASK ^ 0x02678e6c)
+#define T15 /* 0xa679438e */ (T_MASK ^ 0x5986bc71)
+#define T16 0x49b40821
+#define T17 /* 0xf61e2562 */ (T_MASK ^ 0x09e1da9d)
+#define T18 /* 0xc040b340 */ (T_MASK ^ 0x3fbf4cbf)
+#define T19 0x265e5a51
+#define T20 /* 0xe9b6c7aa */ (T_MASK ^ 0x16493855)
+#define T21 /* 0xd62f105d */ (T_MASK ^ 0x29d0efa2)
+#define T22 0x02441453
+#define T23 /* 0xd8a1e681 */ (T_MASK ^ 0x275e197e)
+#define T24 /* 0xe7d3fbc8 */ (T_MASK ^ 0x182c0437)
+#define T25 0x21e1cde6
+#define T26 /* 0xc33707d6 */ (T_MASK ^ 0x3cc8f829)
+#define T27 /* 0xf4d50d87 */ (T_MASK ^ 0x0b2af278)
+#define T28 0x455a14ed
+#define T29 /* 0xa9e3e905 */ (T_MASK ^ 0x561c16fa)
+#define T30 /* 0xfcefa3f8 */ (T_MASK ^ 0x03105c07)
+#define T31 0x676f02d9
+#define T32 /* 0x8d2a4c8a */ (T_MASK ^ 0x72d5b375)
+#define T33 /* 0xfffa3942 */ (T_MASK ^ 0x0005c6bd)
+#define T34 /* 0x8771f681 */ (T_MASK ^ 0x788e097e)
+#define T35 0x6d9d6122
+#define T36 /* 0xfde5380c */ (T_MASK ^ 0x021ac7f3)
+#define T37 /* 0xa4beea44 */ (T_MASK ^ 0x5b4115bb)
+#define T38 0x4bdecfa9
+#define T39 /* 0xf6bb4b60 */ (T_MASK ^ 0x0944b49f)
+#define T40 /* 0xbebfbc70 */ (T_MASK ^ 0x4140438f)
+#define T41 0x289b7ec6
+#define T42 /* 0xeaa127fa */ (T_MASK ^ 0x155ed805)
+#define T43 /* 0xd4ef3085 */ (T_MASK ^ 0x2b10cf7a)
+#define T44 0x04881d05
+#define T45 /* 0xd9d4d039 */ (T_MASK ^ 0x262b2fc6)
+#define T46 /* 0xe6db99e5 */ (T_MASK ^ 0x1924661a)
+#define T47 0x1fa27cf8
+#define T48 /* 0xc4ac5665 */ (T_MASK ^ 0x3b53a99a)
+#define T49 /* 0xf4292244 */ (T_MASK ^ 0x0bd6ddbb)
+#define T50 0x432aff97
+#define T51 /* 0xab9423a7 */ (T_MASK ^ 0x546bdc58)
+#define T52 /* 0xfc93a039 */ (T_MASK ^ 0x036c5fc6)
+#define T53 0x655b59c3
+#define T54 /* 0x8f0ccc92 */ (T_MASK ^ 0x70f3336d)
+#define T55 /* 0xffeff47d */ (T_MASK ^ 0x00100b82)
+#define T56 /* 0x85845dd1 */ (T_MASK ^ 0x7a7ba22e)
+#define T57 0x6fa87e4f
+#define T58 /* 0xfe2ce6e0 */ (T_MASK ^ 0x01d3191f)
+#define T59 /* 0xa3014314 */ (T_MASK ^ 0x5cfebceb)
+#define T60 0x4e0811a1
+#define T61 /* 0xf7537e82 */ (T_MASK ^ 0x08ac817d)
+#define T62 /* 0xbd3af235 */ (T_MASK ^ 0x42c50dca)
+#define T63 0x2ad7d2bb
+#define T64 /* 0xeb86d391 */ (T_MASK ^ 0x14792c6e)
+
+
+static void
+md5_process(md5_state_t *pms, const md5_byte_t *data /*[64]*/)
+{
+ md5_word_t
+ a = pms->abcd[0], b = pms->abcd[1],
+ c = pms->abcd[2], d = pms->abcd[3];
+ md5_word_t t;
+#if BYTE_ORDER > 0
+ /* Define storage only for big-endian CPUs. */
+ md5_word_t X[16];
+#else
+ /* Define storage for little-endian or both types of CPUs. */
+ md5_word_t xbuf[16];
+ const md5_word_t *X;
+#endif
+
+ {
+#if BYTE_ORDER == 0
+ /*
+ * Determine dynamically whether this is a big-endian or
+ * little-endian machine, since we can use a more efficient
+ * algorithm on the latter.
+ */
+ static const int w = 1;
+
+ if (*((const md5_byte_t *)&w)) /* dynamic little-endian */
+#endif
+#if BYTE_ORDER <= 0 /* little-endian */
+ {
+ /*
+ * On little-endian machines, we can process properly aligned
+ * data without copying it.
+ */
+ if (!((data - (const md5_byte_t *)0) & 3)) {
+ /* data are properly aligned */
+ X = (const md5_word_t *)data;
+ } else {
+ /* not aligned */
+ memcpy(xbuf, data, 64);
+ X = xbuf;
+ }
+ }
+#endif
+#if BYTE_ORDER == 0
+ else /* dynamic big-endian */
+#endif
+#if BYTE_ORDER >= 0 /* big-endian */
+ {
+ /*
+ * On big-endian machines, we must arrange the bytes in the
+ * right order.
+ */
+ const md5_byte_t *xp = data;
+ int i;
+
+# if BYTE_ORDER == 0
+ X = xbuf; /* (dynamic only) */
+# else
+# define xbuf X /* (static only) */
+# endif
+ for (i = 0; i < 16; ++i, xp += 4)
+ xbuf[i] = xp[0] + (xp[1] << 8) + (xp[2] << 16) + (xp[3] << 24);
+ }
+#endif
+ }
+
+#define ROTATE_LEFT(x, n) (((x) << (n)) | ((x) >> (32 - (n))))
+
+ /* Round 1. */
+ /* Let [abcd k s i] denote the operation
+ a = b + ((a + F(b,c,d) + X[k] + T[i]) <<< s). */
+#define F(x, y, z) (((x) & (y)) | (~(x) & (z)))
+#define SET(a, b, c, d, k, s, Ti)\
+ t = a + F(b,c,d) + X[k] + Ti;\
+ a = ROTATE_LEFT(t, s) + b
+ /* Do the following 16 operations. */
+ SET(a, b, c, d, 0, 7, T1);
+ SET(d, a, b, c, 1, 12, T2);
+ SET(c, d, a, b, 2, 17, T3);
+ SET(b, c, d, a, 3, 22, T4);
+ SET(a, b, c, d, 4, 7, T5);
+ SET(d, a, b, c, 5, 12, T6);
+ SET(c, d, a, b, 6, 17, T7);
+ SET(b, c, d, a, 7, 22, T8);
+ SET(a, b, c, d, 8, 7, T9);
+ SET(d, a, b, c, 9, 12, T10);
+ SET(c, d, a, b, 10, 17, T11);
+ SET(b, c, d, a, 11, 22, T12);
+ SET(a, b, c, d, 12, 7, T13);
+ SET(d, a, b, c, 13, 12, T14);
+ SET(c, d, a, b, 14, 17, T15);
+ SET(b, c, d, a, 15, 22, T16);
+#undef SET
+
+ /* Round 2. */
+ /* Let [abcd k s i] denote the operation
+ a = b + ((a + G(b,c,d) + X[k] + T[i]) <<< s). */
+#define G(x, y, z) (((x) & (z)) | ((y) & ~(z)))
+#define SET(a, b, c, d, k, s, Ti)\
+ t = a + G(b,c,d) + X[k] + Ti;\
+ a = ROTATE_LEFT(t, s) + b
+ /* Do the following 16 operations. */
+ SET(a, b, c, d, 1, 5, T17);
+ SET(d, a, b, c, 6, 9, T18);
+ SET(c, d, a, b, 11, 14, T19);
+ SET(b, c, d, a, 0, 20, T20);
+ SET(a, b, c, d, 5, 5, T21);
+ SET(d, a, b, c, 10, 9, T22);
+ SET(c, d, a, b, 15, 14, T23);
+ SET(b, c, d, a, 4, 20, T24);
+ SET(a, b, c, d, 9, 5, T25);
+ SET(d, a, b, c, 14, 9, T26);
+ SET(c, d, a, b, 3, 14, T27);
+ SET(b, c, d, a, 8, 20, T28);
+ SET(a, b, c, d, 13, 5, T29);
+ SET(d, a, b, c, 2, 9, T30);
+ SET(c, d, a, b, 7, 14, T31);
+ SET(b, c, d, a, 12, 20, T32);
+#undef SET
+
+ /* Round 3. */
+ /* Let [abcd k s t] denote the operation
+ a = b + ((a + H(b,c,d) + X[k] + T[i]) <<< s). */
+#define H(x, y, z) ((x) ^ (y) ^ (z))
+#define SET(a, b, c, d, k, s, Ti)\
+ t = a + H(b,c,d) + X[k] + Ti;\
+ a = ROTATE_LEFT(t, s) + b
+ /* Do the following 16 operations. */
+ SET(a, b, c, d, 5, 4, T33);
+ SET(d, a, b, c, 8, 11, T34);
+ SET(c, d, a, b, 11, 16, T35);
+ SET(b, c, d, a, 14, 23, T36);
+ SET(a, b, c, d, 1, 4, T37);
+ SET(d, a, b, c, 4, 11, T38);
+ SET(c, d, a, b, 7, 16, T39);
+ SET(b, c, d, a, 10, 23, T40);
+ SET(a, b, c, d, 13, 4, T41);
+ SET(d, a, b, c, 0, 11, T42);
+ SET(c, d, a, b, 3, 16, T43);
+ SET(b, c, d, a, 6, 23, T44);
+ SET(a, b, c, d, 9, 4, T45);
+ SET(d, a, b, c, 12, 11, T46);
+ SET(c, d, a, b, 15, 16, T47);
+ SET(b, c, d, a, 2, 23, T48);
+#undef SET
+
+ /* Round 4. */
+ /* Let [abcd k s t] denote the operation
+ a = b + ((a + I(b,c,d) + X[k] + T[i]) <<< s). */
+#define I(x, y, z) ((y) ^ ((x) | ~(z)))
+#define SET(a, b, c, d, k, s, Ti)\
+ t = a + I(b,c,d) + X[k] + Ti;\
+ a = ROTATE_LEFT(t, s) + b
+ /* Do the following 16 operations. */
+ SET(a, b, c, d, 0, 6, T49);
+ SET(d, a, b, c, 7, 10, T50);
+ SET(c, d, a, b, 14, 15, T51);
+ SET(b, c, d, a, 5, 21, T52);
+ SET(a, b, c, d, 12, 6, T53);
+ SET(d, a, b, c, 3, 10, T54);
+ SET(c, d, a, b, 10, 15, T55);
+ SET(b, c, d, a, 1, 21, T56);
+ SET(a, b, c, d, 8, 6, T57);
+ SET(d, a, b, c, 15, 10, T58);
+ SET(c, d, a, b, 6, 15, T59);
+ SET(b, c, d, a, 13, 21, T60);
+ SET(a, b, c, d, 4, 6, T61);
+ SET(d, a, b, c, 11, 10, T62);
+ SET(c, d, a, b, 2, 15, T63);
+ SET(b, c, d, a, 9, 21, T64);
+#undef SET
+
+ /* Then perform the following additions. (That is increment each
+ of the four registers by the value it had before this block
+ was started.) */
+ pms->abcd[0] += a;
+ pms->abcd[1] += b;
+ pms->abcd[2] += c;
+ pms->abcd[3] += d;
+}
+
+void
+md5_init(md5_state_t *pms)
+{
+ pms->count[0] = pms->count[1] = 0;
+ pms->abcd[0] = 0x67452301;
+ pms->abcd[1] = /*0xefcdab89*/ T_MASK ^ 0x10325476;
+ pms->abcd[2] = /*0x98badcfe*/ T_MASK ^ 0x67452301;
+ pms->abcd[3] = 0x10325476;
+}
+
+void
+md5_append(md5_state_t *pms, const md5_byte_t *data, int nbytes)
+{
+ const md5_byte_t *p = data;
+ int left = nbytes;
+ int offset = (pms->count[0] >> 3) & 63;
+ md5_word_t nbits = (md5_word_t)(nbytes << 3);
+
+ if (nbytes <= 0)
+ return;
+
+ /* Update the message length. */
+ pms->count[1] += nbytes >> 29;
+ pms->count[0] += nbits;
+ if (pms->count[0] < nbits)
+ pms->count[1]++;
+
+ /* Process an initial partial block. */
+ if (offset) {
+ int copy = (offset + nbytes > 64 ? 64 - offset : nbytes);
+
+ memcpy(pms->buf + offset, p, copy);
+ if (offset + copy < 64)
+ return;
+ p += copy;
+ left -= copy;
+ md5_process(pms, pms->buf);
+ }
+
+ /* Process full blocks. */
+ for (; left >= 64; p += 64, left -= 64)
+ md5_process(pms, p);
+
+ /* Process a final partial block. */
+ if (left)
+ memcpy(pms->buf, p, left);
+}
+
+void
+md5_finish(md5_state_t *pms, md5_byte_t digest[16])
+{
+ static const md5_byte_t pad[64] = {
+ 0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
+ };
+ md5_byte_t data[8];
+ int i;
+
+ /* Save the length before padding. */
+ for (i = 0; i < 8; ++i)
+ data[i] = (md5_byte_t)(pms->count[i >> 2] >> ((i & 3) << 3));
+ /* Pad to 56 bytes mod 64. */
+ md5_append(pms, pad, ((55 - (pms->count[0] >> 3)) & 63) + 1);
+ /* Append the length. */
+ md5_append(pms, data, 8);
+ for (i = 0; i < 16; ++i)
+ digest[i] = (md5_byte_t)(pms->abcd[i >> 2] >> ((i & 3) << 3));
+}
+
+}
diff --git a/externals/g3dlite/G3D.lib/source/Cylinder.cpp b/externals/g3dlite/G3D.lib/source/Cylinder.cpp
new file mode 100644
index 00000000000..bdc7d56be23
--- /dev/null
+++ b/externals/g3dlite/G3D.lib/source/Cylinder.cpp
@@ -0,0 +1,176 @@
+/**
+ @file Cylinder.cpp
+
+ @maintainer Morgan McGuire, matrix@graphics3d.com
+
+ @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/G3D.lib/source/Discovery.cpp b/externals/g3dlite/G3D.lib/source/Discovery.cpp
new file mode 100644
index 00000000000..2c27c5bf2b9
--- /dev/null
+++ b/externals/g3dlite/G3D.lib/source/Discovery.cpp
@@ -0,0 +1,170 @@
+/**
+ @file Discovery.cpp
+
+ @maintainer Morgan McGuire, matrix@graphics3d.com
+
+ @created 2003-06-26
+ @edited 2005-02-24
+ */
+
+#include "G3D/Discovery.h"
+#include "G3D/BinaryInput.h"
+#include "G3D/BinaryOutput.h"
+
+#include <cstring>
+
+namespace G3D {
+
+///////////////////////////////////////////////////////////////////////////////////////////
+
+void DiscoveryAdvertisement::serialize(BinaryOutput& b) const {
+ address.serialize(b);
+}
+
+void DiscoveryAdvertisement::deserialize(BinaryInput& b) {
+ address.deserialize(b);
+ lastUpdateTime = System::time();
+}
+
+
+///////////////////////////////////////////////////////////////////////////////////////////
+
+void DiscoveryServerAddressMessage::serialize(BinaryOutput& b) const {
+ b.writeString(G3D_DISCOVERY_PROTOCOL_NAME);
+ b.writeInt32(G3D_DISCOVERY_PROTOCOL_VERSION);
+ b.writeString(settings->appProtocolName);
+ b.writeInt32(settings->appProtocolVersion);
+
+ // Send addresses
+ b.writeInt32(address.size());
+ for (int i = 0; i < address.size(); ++i) {
+ address[i].serialize(b);
+ }
+}
+
+
+void DiscoveryServerAddressMessage::deserialize(BinaryInput& b) {
+ address.clear();
+ correctProtocol = false;
+ serverProtocolVersion[0] = 0;
+ serverProtocolVersion[1] = 0;
+
+ // Verify that we are on the same protocol
+ if (b.readString(strlen(G3D_DISCOVERY_PROTOCOL_NAME) + 1) != G3D_DISCOVERY_PROTOCOL_NAME) {
+ return;
+ }
+
+ serverProtocolVersion[0] = b.readInt32();
+ if (serverProtocolVersion[0] != G3D_DISCOVERY_PROTOCOL_VERSION) {
+ return;
+ }
+
+ if (b.readString() != settings->appProtocolName) {
+ return;
+ }
+
+ serverProtocolVersion[1] = b.readInt32();
+ if (serverProtocolVersion[1] != settings->appProtocolVersion) {
+ return;
+ }
+
+ correctProtocol = true;
+
+ address.resize(b.readInt32());
+ for (int i = 0; i < address.size(); ++i) {
+ address[i].deserialize(b);
+ }
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////
+
+void DiscoveryServer::sendAnnouncement() const {
+ NetAddress broadcast(NetworkDevice::instance()->broadcastAddressArray()[0], settings->serverBroadcastPort);
+
+ net->send(broadcast, SERVER_BROADCAST_MESSAGE, addressMessage);
+
+ const_cast<DiscoveryServer*>(this)->lastBroadcast = System::time();
+}
+
+
+void DiscoveryServer::sendShutDown() const {
+ NetAddress broadcast(NetworkDevice::instance()->broadcastAddressArray()[0], settings->serverBroadcastPort);
+ ShutdownMessage s;
+ net->send(broadcast, SERVER_SHUTDOWN_MESSAGE, s);
+}
+
+
+bool DiscoveryServer::ok() const {
+ return listener->ok() && net->ok();
+}
+
+
+void DiscoveryServer::init(
+ const DiscoverySettings* _settings,
+ DiscoveryAdvertisement* _advertisement) {
+
+ Discovery::init(_settings);
+
+ advertisement = _advertisement;
+ addressMessage.settings = settings;
+ NetworkDevice::instance()->localHostAddresses(addressMessage.address);
+
+ // Set the port number
+ for (int i = 0; i < addressMessage.address.size(); ++i) {
+ addressMessage.address[i] =
+ NetAddress(addressMessage.address[i].ip(),
+ settings->serverAdvertisementPort);
+ }
+
+ net = LightweightConduit::create(settings->clientBroadcastPort, true, true);
+
+ listener = NetListener::create(settings->serverAdvertisementPort);
+
+ // Send initial announcement
+ sendAnnouncement();
+}
+
+
+void DiscoveryServer::doNetwork() {
+ const RealTime UNSOLICITED_BROADCAST_PERIOD = 60;
+
+ // Check for client broadcast requests
+
+ if (net->messageWaiting()) {
+ // Some client broadcast that it is looking for servers.
+ // Respond by sending out our announcement to everyone
+ // (avoids having to figure out if the message return address
+ // is correct).
+ NetAddress dummy;
+ net->receive(dummy);
+ sendAnnouncement();
+ } else if (System::time() > lastBroadcast + UNSOLICITED_BROADCAST_PERIOD) {
+ sendAnnouncement();
+ }
+
+ // Handle incoming connections
+
+ if (listener->clientWaiting()) {
+ // Respond to this client
+ ReliableConduitRef client = listener->waitForConnection();
+ client->send(SERVER_BROADCAST_MESSAGE, *advertisement);
+ }
+}
+
+
+void DiscoveryServer::cleanup() {
+ sendShutDown();
+}
+
+//////////////////////////////////////////////////////////////////////////////////
+
+std::string IncompatibleServerDescription::toString() const {
+ return std::string("Incompatible server at ") + address.toString() +
+ format(", version %d.%d", protocolVersion[0], protocolVersion[1]);
+}
+
+//////////////////////////////////////////////////////////////////////////////////
+
+
+}
+
diff --git a/externals/g3dlite/G3D.lib/source/GCamera.cpp b/externals/g3dlite/G3D.lib/source/GCamera.cpp
new file mode 100644
index 00000000000..e70188377e6
--- /dev/null
+++ b/externals/g3dlite/G3D.lib/source/GCamera.cpp
@@ -0,0 +1,399 @@
+/**
+ @file GCamera.cpp
+
+ @author Morgan McGuire, matrix@graphics3d.com
+ @author Jeff Marsceill, 08jcm@williams.edu
+
+ @created 2005-07-20
+ @edited 2007-07-24
+*/
+#include "G3D/GCamera.h"
+#include "G3D/platform.h"
+#include "G3D/Rect2D.h"
+#include "G3D/BinaryInput.h"
+#include "G3D/BinaryOutput.h"
+#include "G3D/Ray.h"
+#include "G3D/Matrix4.h"
+
+namespace G3D {
+
+GCamera::GCamera() {
+ setNearPlaneZ(-0.1f);
+ setFarPlaneZ(-(float)inf());
+ setFieldOfView((float)toRadians(55.0f), VERTICAL);
+}
+
+
+GCamera::~GCamera() {
+}
+
+void GCamera::getCoordinateFrame(CoordinateFrame& c) const {
+ c = m_cframe;
+}
+
+void GCamera::setCoordinateFrame(const CoordinateFrame& c) {
+ m_cframe = c;
+}
+
+void GCamera::setFieldOfView(float angle, FOVDirection dir) {
+ debugAssert((angle < pi()) && (angle > 0));
+
+ m_fieldOfView = angle;
+ m_direction = dir;
+}
+
+float GCamera::imagePlaneDepth() const{
+ return -m_nearPlaneZ;
+}
+
+float GCamera::viewportWidth(const Rect2D& viewport) const {
+ // Compute the side of a square at the near plane based on our field of view
+ float s = 2.0f * -m_nearPlaneZ * tan(m_fieldOfView * 0.5f);
+
+ if (m_direction == VERTICAL) {
+ s *= viewport.width() / viewport.height();
+ }
+
+ return s;
+}
+
+float GCamera::viewportHeight(const Rect2D& viewport) const {
+ // Compute the side of a square at the near plane based on our field of view
+ float s = 2.0f * -m_nearPlaneZ * tan(m_fieldOfView * 0.5f);
+
+ if (m_direction == HORIZONTAL) {
+ s *= viewport.height() / viewport.width();
+ }
+
+ return s;
+}
+
+Ray GCamera::worldRay(float x, float y, const Rect2D& viewport) const {
+
+ int screenWidth = iFloor(viewport.width());
+ int screenHeight = iFloor(viewport.height());
+
+ Ray out;
+ out.origin = m_cframe.translation;
+
+ float cx = screenWidth / 2.0f;
+ float cy = screenHeight / 2.0f;
+
+ out.direction = Vector3( (x - cx) * viewportWidth(viewport) / screenWidth,
+ -(y - cy) * viewportHeight(viewport) / screenHeight,
+ (m_nearPlaneZ) );
+
+ out.direction = m_cframe.vectorToWorldSpace(out.direction);
+
+ // Normalize the direction (we didn't do it before)
+ out.direction = out.direction.direction();
+
+ return out;
+}
+
+/**
+This is the matrix that a RenderDevice (or OpenGL) uses as the projection matrix.
+@sa RenderDevice::setProjectionAndCameraMatrix, RenderDevice::setProjectionMatrix, Matrix4::perspectiveProjection
+*/
+void GCamera::getProjectUnitMatrix(const Rect2D& viewport, Matrix4& P) const{
+
+ float screenWidth = viewport.width();
+ float screenHeight = viewport.height();
+
+ float r, l, t, b, n, f, x, y;
+
+ if(m_direction == VERTICAL){
+ y = -m_nearPlaneZ * tan(m_fieldOfView / 2);
+ x = y * (screenWidth / screenHeight);
+ }
+ else{ //m_direction == HORIZONTAL
+ x = -m_nearPlaneZ * tan(m_fieldOfView / 2);
+ y = x * (screenHeight / screenWidth);
+ }
+
+ n = -m_nearPlaneZ;
+ f = -m_farPlaneZ;
+ r = x;
+ l = -x;
+ t = y;
+ b = -y;
+
+ P = Matrix4::perspectiveProjection(l, r, b, t, n, f);
+}
+
+Vector3 GCamera::projectUnit(const Vector3& point, const Rect2D& viewport) const {
+ Matrix4 M;
+ getProjectUnitMatrix(viewport, M);
+
+ Vector4 cameraSpacePoint(coordinateFrame().pointToObjectSpace(point), 1.0f);
+ const Vector4& screenSpacePoint = M * cameraSpacePoint;
+
+ return Vector3(screenSpacePoint.xyz() / screenSpacePoint.w);
+}
+
+Vector3 GCamera::project(const Vector3& point,
+ const Rect2D& viewport) const {
+
+ // Find the point in the homogeneous cube
+ const Vector3& cube = projectUnit(point, viewport);
+
+ return convertFromUnitToNormal(cube, viewport);
+}
+
+Vector3 GCamera::unprojectUnit(const Vector3& v, const Rect2D& viewport) const {
+
+ const Vector3& projectedPoint = convertFromUnitToNormal(v, viewport);
+
+ return unproject(projectedPoint, viewport);
+}
+
+
+Vector3 GCamera::unproject(const Vector3& v, const Rect2D& viewport) const {
+
+ const float n = m_nearPlaneZ;
+ const float f = m_farPlaneZ;
+
+ float z;
+
+ if (-f >= inf()) {
+ // Infinite far plane
+ z = 1.0f / (((-1.0f / n) * v.z) + 1.0f / n);
+ } else {
+ z = 1.0f / ((((1.0f / f) - (1.0f / n)) * v.z) + 1.0f / n);
+ }
+
+ const Ray& ray = worldRay(v.x, v.y, viewport);
+
+ // Find out where the ray reaches the specified depth.
+ const Vector3& out = ray.origin + ray.direction * -z / (ray.direction.dot(m_cframe.lookVector()));
+
+ return out;
+}
+
+float GCamera::worldToScreenSpaceArea(float area, float z, const Rect2D& viewport) const {
+ if (z >= 0) {
+ return (float)inf();
+ }
+ return area * (float)square(imagePlaneDepth() / z);
+}
+
+
+void GCamera::getClipPlanes(
+ const Rect2D& viewport,
+ Array<Plane>& clip) const {
+
+ Frustum fr;
+ frustum(viewport, fr);
+ clip.resize(fr.faceArray.size(), DONT_SHRINK_UNDERLYING_ARRAY);
+ for (int f = 0; f < clip.size(); ++f) {
+ clip[f] = fr.faceArray[f].plane;
+ }
+}
+
+GCamera::Frustum GCamera::frustum(const Rect2D& viewport) const {
+ Frustum f;
+ frustum(viewport, f);
+ return f;
+}
+
+void GCamera::frustum(const Rect2D& viewport, Frustum& fr) const {
+
+ // The volume is the convex hull of the vertices definining the view
+ // frustum and the light source point at infinity.
+
+ const float x = viewportWidth(viewport) / 2;
+ const float y = viewportHeight(viewport) / 2;
+ const float z = m_nearPlaneZ;
+ const float w = z / -m_farPlaneZ;
+ float fovx;
+
+ fovx = m_fieldOfView;
+ if (m_direction == VERTICAL) {
+ fovx *= x / y;
+ }
+
+ // Near face (ccw from UR)
+ fr.vertexPos.append(
+ Vector4( x, y, z, 1),
+ Vector4(-x, y, z, 1),
+ Vector4(-x, -y, z, 1),
+ Vector4( x, -y, z, 1));
+
+ // Far face (ccw from UR, from origin)
+ fr.vertexPos.append(
+ Vector4( x, y, z, w),
+ Vector4(-x, y, z, w),
+ Vector4(-x, -y, z, w),
+ Vector4( x, -y, z, w));
+
+ Frustum::Face face;
+
+ // Near plane (wind backwards so normal faces into frustum)
+ // Recall that nearPlane, farPlane are positive numbers, so
+ // we need to negate them to produce actual z values.
+ face.plane = Plane(Vector3(0,0,-1), Vector3(0,0,m_nearPlaneZ));
+ face.vertexIndex[0] = 3;
+ face.vertexIndex[1] = 2;
+ face.vertexIndex[2] = 1;
+ face.vertexIndex[3] = 0;
+ fr.faceArray.append(face);
+
+ // Right plane
+ face.plane = Plane(Vector3(-cosf(fovx/2), 0, -sinf(fovx/2)), Vector3::zero());
+ face.vertexIndex[0] = 0;
+ face.vertexIndex[1] = 4;
+ face.vertexIndex[2] = 7;
+ face.vertexIndex[3] = 3;
+ fr.faceArray.append(face);
+
+ // Left plane
+ face.plane = Plane(Vector3(-fr.faceArray.last().plane.normal().x, 0, fr.faceArray.last().plane.normal().z), Vector3::zero());
+ face.vertexIndex[0] = 5;
+ face.vertexIndex[1] = 1;
+ face.vertexIndex[2] = 2;
+ face.vertexIndex[3] = 6;
+ fr.faceArray.append(face);
+
+ // Top plane
+ face.plane = Plane(Vector3(0, -cosf(m_fieldOfView/2.0f), -sinf(m_fieldOfView/2.0f)), Vector3::zero());
+ face.vertexIndex[0] = 1;
+ face.vertexIndex[1] = 5;
+ face.vertexIndex[2] = 4;
+ face.vertexIndex[3] = 0;
+ fr.faceArray.append(face);
+
+ // Bottom plane
+ face.plane = Plane(Vector3(0, -fr.faceArray.last().plane.normal().y, fr.faceArray.last().plane.normal().z), Vector3::zero());
+ face.vertexIndex[0] = 2;
+ face.vertexIndex[1] = 3;
+ face.vertexIndex[2] = 7;
+ face.vertexIndex[3] = 6;
+ fr.faceArray.append(face);
+
+ // Far plane
+ if (-m_farPlaneZ < inf()) {
+ face.plane = Plane(Vector3(0, 0, 1), Vector3(0, 0, m_farPlaneZ));
+ face.vertexIndex[0] = 4;
+ face.vertexIndex[1] = 5;
+ face.vertexIndex[2] = 6;
+ face.vertexIndex[3] = 7;
+ fr.faceArray.append(face);
+ }
+
+ // Transform vertices to world space
+ for (int v = 0; v < fr.vertexPos.size(); ++v) {
+ fr.vertexPos[v] = m_cframe.toWorldSpace(fr.vertexPos[v]);
+ }
+
+ // Transform planes to world space
+ for (int p = 0; p < fr.faceArray.size(); ++p) {
+ // Since there is no scale factor, we don't have to
+ // worry about the inverse transpose of the normal.
+ Vector3 normal;
+ float d;
+
+ fr.faceArray[p].plane.getEquation(normal, d);
+
+ Vector3 newNormal = m_cframe.rotation * normal;
+
+ if (isFinite(d)) {
+ d = (newNormal * -d + m_cframe.translation).dot(newNormal);
+ fr.faceArray[p].plane = Plane(newNormal, newNormal * d);
+ } else {
+ // When d is infinite, we can't multiply 0's by it without
+ // generating NaNs.
+ fr.faceArray[p].plane = Plane::fromEquation(newNormal.x, newNormal.y, newNormal.z, d);
+ }
+ }
+}
+
+void GCamera::getNearViewportCorners(
+ const Rect2D& viewport,
+ Vector3& outUR,
+ Vector3& outUL,
+ Vector3& outLL,
+ Vector3& outLR) const {
+
+ // Must be kept in sync with getFrustum()
+ const float w = viewportWidth(viewport) / 2.0f;
+ const float h = viewportHeight(viewport) / 2.0f;
+ const float z = nearPlaneZ();
+
+ // Compute the points
+ outUR = Vector3( w, h, z);
+ outUL = Vector3(-w, h, z);
+ outLL = Vector3(-w, -h, z);
+ outLR = Vector3( w, -h, z);
+
+ // Take to world space
+ outUR = m_cframe.pointToWorldSpace(outUR);
+ outUL = m_cframe.pointToWorldSpace(outUL);
+ outLR = m_cframe.pointToWorldSpace(outLR);
+ outLL = m_cframe.pointToWorldSpace(outLL);
+}
+
+void GCamera::getFarViewportCorners(
+ const Rect2D& viewport,
+ Vector3& outUR,
+ Vector3& outUL,
+ Vector3& outLL,
+ Vector3& outLR) const {
+
+ // Must be kept in sync with getFrustum()
+ const float w = viewportWidth(viewport) * m_farPlaneZ / m_nearPlaneZ;
+ const float h = viewportHeight(viewport) * m_farPlaneZ / m_nearPlaneZ;
+ const float z = m_farPlaneZ;
+
+ // Compute the points
+ outUR = Vector3( w, h, z);
+ outUL = Vector3(-w, h, z);
+ outLL = Vector3(-w, -h, z);
+ outLR = Vector3( w, -h, z);
+
+ // Take to world space
+ outUR = m_cframe.pointToWorldSpace(outUR);
+ outUL = m_cframe.pointToWorldSpace(outUL);
+ outLR = m_cframe.pointToWorldSpace(outLR);
+ outLL = m_cframe.pointToWorldSpace(outLL);
+}
+
+
+
+void GCamera::setPosition(const Vector3& t) {
+ m_cframe.translation = t;
+}
+
+
+void GCamera::lookAt(const Vector3& position, const Vector3& up) {
+ m_cframe.lookAt(position, up);
+}
+
+
+void GCamera::serialize(BinaryOutput& bo) const {
+ bo.writeFloat32(m_fieldOfView);
+ bo.writeFloat32(imagePlaneDepth());
+ debugAssert(nearPlaneZ() < 0.0f);
+ bo.writeFloat32(nearPlaneZ());
+ debugAssert(farPlaneZ() < 0.0f);
+ bo.writeFloat32(farPlaneZ());
+ m_cframe.serialize(bo);
+ bo.writeInt8(m_direction);
+}
+
+
+void GCamera::deserialize(BinaryInput& bi) {
+ m_fieldOfView = bi.readFloat32();
+ m_nearPlaneZ = bi.readFloat32();
+ debugAssert(m_nearPlaneZ < 0.0f);
+ m_farPlaneZ = bi.readFloat32();
+ debugAssert(m_farPlaneZ < 0.0f);
+ m_cframe.deserialize(bi);
+ m_direction = (FOVDirection)bi.readInt8();
+}
+
+
+Vector3 GCamera::convertFromUnitToNormal(const Vector3& in, const Rect2D& viewport) const{
+ return (in + Vector3(1,1,1)) * 0.5 * Vector3(viewport.width(), -viewport.height(), 1) +
+ Vector3(viewport.x0(), viewport.y1(), 0);
+}
+} // namespace
diff --git a/externals/g3dlite/G3D.lib/source/GImage.cpp b/externals/g3dlite/G3D.lib/source/GImage.cpp
new file mode 100644
index 00000000000..c7abf982553
--- /dev/null
+++ b/externals/g3dlite/G3D.lib/source/GImage.cpp
@@ -0,0 +1,1065 @@
+/**
+ @file GImage.cpp
+ @author Morgan McGuire, morgan@graphics3d.com
+ Copyright 2002-2007, Morgan McGuire
+
+ @created 2002-05-27
+ @edited 2006-10-10
+ */
+#include "G3D/platform.h"
+#include "G3D/GImage.h"
+#include "G3D/debug.h"
+#include "G3D/stringutils.h"
+#include "G3D/TextInput.h"
+#include "G3D/TextOutput.h"
+#include "G3D/BinaryInput.h"
+#include "G3D/BinaryOutput.h"
+#include "G3D/Log.h"
+#include <png.h>
+#include <sys/stat.h>
+#include <assert.h>
+#include <sys/types.h>
+
+//////////////////////////////////////////////////////////////////////////////////////////////
+
+namespace G3D {
+
+void GImage::RGBtoRGBA(
+ const uint8* in,
+ uint8* out,
+ int numPixels) {
+
+ for (int i = 0; i < numPixels; ++i) {
+ int i3 = i * 3;
+ int i4 = i3 + i;
+
+ out[i4 + 0] = in[i3 + 0];
+ out[i4 + 1] = in[i3 + 1];
+ out[i4 + 2] = in[i3 + 2];
+ out[i4 + 3] = 255;
+ }
+}
+
+
+void GImage::RGBAtoRGB(
+ const uint8* in,
+ uint8* out,
+ int numPixels) {
+
+ for (int i = 0; i < numPixels; ++i) {
+ int i3 = i * 3;
+ int i4 = i3 + i;
+
+ out[i3 + 0] = in[i4 + 0];
+ out[i3 + 1] = in[i4 + 1];
+ out[i3 + 2] = in[i4 + 2];
+ }
+}
+
+
+void GImage::RGBtoBGRA(
+ const uint8* in,
+ uint8* out,
+ int numPixels) {
+
+ for (int i = 0; i < numPixels; ++i) {
+ int i3 = i * 3;
+ int i4 = i3 + i;
+
+ out[i4 + 2] = in[i3 + 0];
+ out[i4 + 1] = in[i3 + 1];
+ out[i4 + 0] = in[i3 + 2];
+ out[i4 + 3] = 255;
+ }
+}
+
+
+
+void GImage::RGBtoBGR(
+ const uint8* in,
+ uint8* out,
+ int numPixels) {
+
+ for (int i = 0; i < numPixels; ++i) {
+ int i3 = i * 3;
+
+ int r = in[i3 + 0];
+ int g = in[i3 + 1];
+ int b = in[i3 + 2];
+
+ out[i3 + 2] = r;
+ out[i3 + 1] = g;
+ out[i3 + 0] = b;
+ }
+}
+
+
+void GImage::RGBxRGBtoRGBA(
+ const uint8* colorRGB,
+ const uint8* alphaRGB,
+ uint8* out,
+ int numPixels) {
+
+ for (int i = numPixels - 1; i >= 0; --i) {
+ int i3 = i * 3;
+ int i4 = i3 + i;
+
+ out[i4 + 0] = colorRGB[i3 + 0];
+ out[i4 + 1] = colorRGB[i3 + 1];
+ out[i4 + 2] = colorRGB[i3 + 2];
+ out[i4 + 3] = alphaRGB[i3 + 0];
+ }
+}
+
+
+void GImage::RGBtoARGB(
+ const uint8* in,
+ uint8* out,
+ int numPixels) {
+
+ for (int i = 0; i < numPixels; ++i) {
+ int i3 = i * 3;
+ int i4 = i3 + i;
+
+ out[i4 + 0] = 255;
+ out[i4 + 1] = in[i3 + 0];
+ out[i4 + 2] = in[i3 + 1];
+ out[i4 + 3] = in[i3 + 2];
+ }
+}
+
+
+void GImage::flipRGBVertical(
+ const uint8* in,
+ uint8* out,
+ int width,
+ int height) {
+
+
+ // Allocate a temp row so the operation
+ // is still safe if in == out
+ uint8* temp = (uint8*)System::malloc(width * 3);
+ alwaysAssertM(temp != NULL, "Out of memory");
+
+ int oneRow = width * 3;
+ int N = height / 2;
+
+ // if height is an odd value, don't swap odd middle row
+ for (int i = 0; i < N; ++i) {
+ int topOff = i * oneRow;
+ int botOff = (height - i - 1) * oneRow;
+ System::memcpy(temp, in + topOff, oneRow);
+ System::memcpy(out + topOff, in + botOff, oneRow);
+ System::memcpy(out + botOff, temp, oneRow);
+ }
+
+ System::free(temp);
+}
+
+
+void GImage::flipRGBAVertical(
+ const uint8* in,
+ uint8* out,
+ int width,
+ int height) {
+
+
+ // Allocate a temp row so the operation
+ // is still safe if in == out
+ uint8* temp = (uint8*)System::malloc(width * 4);
+ alwaysAssertM(temp != NULL, "Out of memory");
+
+ int oneRow = width * 4;
+
+ // if height is an odd value, don't swap odd middle row
+ for (int i = 0; i < height / 2; ++i) {
+ int topOff = i * oneRow;
+ int botOff = (height - i - 1) * oneRow;
+ System::memcpy(temp, in + topOff, oneRow);
+ System::memcpy(out + topOff, in + botOff, oneRow);
+ System::memcpy(out + botOff, temp, oneRow);
+ }
+
+ System::free(temp);
+}
+
+////////////////////////////////////////////////////////////////////////////////////////
+
+void GImage::decode(
+ BinaryInput& input,
+ Format format) {
+
+ switch (format) {
+ case PPM_ASCII:
+ decodePPMASCII(input);
+ break;
+
+ case PPM:
+ decodePPM(input);
+ break;
+
+ case PNG:
+ decodePNG(input);
+ break;
+
+ case JPEG:
+ decodeJPEG(input);
+ break;
+
+ case TGA:
+ decodeTGA(input);
+ break;
+
+ case BMP:
+ decodeBMP(input);
+ break;
+
+ case ICO:
+ decodeICO(input);
+ break;
+
+ case PCX:
+ decodePCX(input);
+ break;
+
+ default:
+ debugAssert(false);
+ }
+
+ debugAssert(width >= 0);
+ debugAssert(height >= 0);
+ debugAssert(channels == 1 || channels == 3 || channels == 4);
+ debugAssert(_byte != NULL);
+}
+
+
+void GImage::decodePCX(
+ BinaryInput& input) {
+
+ uint8 manufacturer = input.readUInt8();
+ uint8 version = input.readUInt8();
+ uint8 encoding = input.readUInt8();
+ uint8 bitsPerPixel = input.readUInt8();
+
+ uint16 xmin = input.readUInt16();
+ uint16 ymin = input.readUInt16();
+ uint16 xmax = input.readUInt16();
+ uint16 ymax = input.readUInt16();
+
+ uint16 horizDPI = input.readUInt16();
+ uint16 vertDPI = input.readUInt16();
+
+ Color3uint8 colorMap[16];
+ input.readBytes(colorMap, 48);
+
+ input.skip(1);
+
+ uint8 planes = input.readUInt8();
+ uint16 bytesPerLine = input.readUInt16();
+ uint16 paletteType = input.readUInt16();
+ input.skip(4 + 54);
+
+ (void)bytesPerLine;
+
+ width = xmax - xmin + 1;
+ height = ymax - ymin + 1;
+ channels = 3;
+
+ if ((manufacturer != 0x0A) || (encoding != 0x01)) {
+ throw GImage::Error("PCX file is corrupted", input.getFilename());
+ }
+
+ (void)version;
+ (void)vertDPI;
+ (void)horizDPI;
+
+ if ((bitsPerPixel != 8) || ((planes != 1) && (planes != 3))) {
+ throw GImage::Error("Only 8-bit paletted and 24-bit PCX files supported.", input.getFilename());
+ }
+
+ // Prepare the pointer object for the pixel data
+ _byte = (uint8*)System::malloc(width * height * 3);
+
+ if ((paletteType == 1) && (planes == 3)) {
+
+ Color3uint8* pixel = pixel3();
+
+ // Iterate over each scan line
+ for (int row = 0; row < height; ++row) {
+ // Read each scan line once per plane
+ for (int plane = 0; plane < planes; ++plane) {
+ int p = row * width;
+ int p1 = p + width;
+ while (p < p1) {
+ uint8 value = input.readUInt8();
+ int length = 1;
+
+ if (value >= 192) {
+ // This is the length, not the value. Mask off
+ // the two high bits and read the true index.
+ length = value & 0x3F;
+ value = input.readUInt8();
+ }
+
+ // Set the whole run
+ for (int i = length - 1; i >= 0; --i, ++p) {
+ debugAssert(p < width * height);
+ pixel[p][plane] = value;
+ }
+ }
+ }
+ }
+
+ } else if (planes == 1) {
+
+ Color3uint8 palette[256];
+
+ int imageBeginning = input.getPosition();
+ int paletteBeginning = input.getLength() - 769;
+
+ input.setPosition(paletteBeginning);
+
+ uint8 dummy = input.readUInt8();
+
+ if (dummy != 12) {
+ Log::common()->println("\n*********************");
+ Log::common()->printf("Warning: Corrupted PCX file (palette marker byte was missing) \"%s\"\nLoading anyway\n\n", input.getFilename().c_str());
+ }
+
+ input.readBytes(palette, sizeof(palette));
+ input.setPosition(imageBeginning);
+
+ Color3uint8* pixel = pixel3();
+
+ // The palette indices are run length encoded.
+ int p = 0;
+ while (p < width * height) {
+ uint8 index = input.readUInt8();
+ uint8 length = 1;
+
+ if (index >= 192) {
+ // This is the length, not the index. Mask off
+ // the two high bits and read the true index.
+ length = index & 0x3F;
+ index = input.readUInt8();
+ }
+
+ Color3uint8 color = palette[index];
+
+ // Set the whole run
+ for (int i = length - 1; i >= 0; --i, ++p) {
+ if (p > width * height) {
+ break;
+ }
+ pixel[p] = color;
+ }
+
+ }
+
+ } else {
+ throw GImage::Error("Unsupported PCX file type.", input.getFilename());
+ }
+}
+
+
+GImage::Format GImage::resolveFormat(const std::string& filename) {
+ BinaryInput b(filename, G3D_LITTLE_ENDIAN);
+ if (b.size() <= 0) {
+ throw Error("File not found.", filename);
+ }
+
+ return resolveFormat(filename, b.getCArray(), b.size(), AUTODETECT);
+}
+
+
+GImage::Format GImage::resolveFormat(
+ const std::string& filename,
+ const uint8* data,
+ int dataLen,
+ Format maybeFormat) {
+
+ // Return the provided format if it is specified.
+ if (maybeFormat != AUTODETECT) {
+ return maybeFormat;
+ }
+
+ std::string extension;
+
+ // Try to detect from the filename's extension
+ if (filename.size() >= 5) {
+ int n = iMax(filename.size() - 1, 5);
+ // Search backwards for the "."
+ for (int i = 1; i <= n; ++i) {
+ if (filename[filename.size() - i] == '.') {
+ // Upper case
+ extension = toUpper(filename.substr(filename.size() - i + 1));
+ break;
+ }
+ }
+ }
+
+ if (extension == "PPM") {
+ // There are two PPM formats; we handle them differently
+ if (dataLen > 3) {
+ if (!memcmp(data, "P6", 2)) {
+ return PPM;
+ } else {
+ return PPM_ASCII;
+ }
+ }
+ }
+
+ Format tmp = stringToFormat(extension);
+ if ((tmp != AUTODETECT) && (tmp != UNKNOWN)) {
+ return tmp;
+ }
+
+ // Try and autodetect from the file itself by looking at the first
+ // character.
+
+ // We can't look at the character if it is null.
+ debugAssert(data != NULL);
+
+ if ((dataLen > 3) && (!memcmp(data, "P3", 2) || !memcmp(data, "P2", 2) || !memcmp(data, "P1", 2))) {
+ return PPM_ASCII;
+ }
+
+ if ((dataLen > 3) && !memcmp(data, "P6", 2)) {
+ return PPM;
+ }
+
+ if (dataLen > 8) {
+ if (!png_sig_cmp((png_bytep)data, 0, 8))
+ return PNG;
+ }
+
+ if ((dataLen > 0) && (data[0] == 'B')) {
+ return BMP;
+ }
+
+ if (dataLen > 10) {
+ if ((dataLen > 11) && (data[0] == 0xFF) &&
+ (memcmp(&data[6], "JFIF", 4) == 0)) {
+ return JPEG;
+ }
+ }
+
+ if (dataLen > 40) {
+ if (memcmp(&data[dataLen - 18], "TRUEVISION-XFILE", 16) == 0) {
+ return TGA;
+ }
+ }
+
+ if ((dataLen > 4) && (data[0] == 0) && (data[1] == 0) && (data[2] == 0) && (data[3] == 1)) {
+ return ICO;
+ }
+
+ if ((dataLen > 0) && (data[0] == 10)) {
+ return PCX;
+ }
+
+ return UNKNOWN;
+}
+
+
+GImage::GImage(
+ const std::string& filename,
+ Format format) :
+ _byte(NULL),
+ width(0),
+ height(0),
+ channels(0){
+
+ load(filename, format);
+}
+
+
+void GImage::load(
+ const std::string& filename,
+ Format format) {
+
+ clear();
+
+ try {
+ BinaryInput b(filename, G3D_LITTLE_ENDIAN);
+ if (b.size() <= 0) {
+ throw Error("File not found.", filename);
+ }
+
+ alwaysAssertM(this != NULL, "Corrupt GImage");
+ decode(b, resolveFormat(filename, b.getCArray(), b.size(), format));
+ } catch (const std::string& error) {
+ throw Error(error, filename);
+ }
+}
+
+
+GImage::GImage(
+ const uint8* data,
+ int length,
+ Format format) {
+
+ BinaryInput b(data, length, G3D_LITTLE_ENDIAN);
+ // It is safe to cast away the const because we
+ // know we don't corrupt the data.
+
+ decode(b, resolveFormat("", data, length, format));
+}
+
+
+GImage::GImage(
+ int width,
+ int height,
+ int channels) {
+
+ _byte = NULL;
+ resize(width, height, channels);
+}
+
+
+void GImage::resize(
+ int width,
+ int height,
+ int channels) {
+ debugAssert(width >= 0);
+ debugAssert(height >= 0);
+ debugAssert(channels >= 1);
+
+ clear();
+
+ this->width = width;
+ this->height = height;
+ this->channels = channels;
+ size_t sz = width * height * channels;
+
+ _byte = (uint8*)System::calloc(sz, sizeof(uint8));
+ debugAssert(isValidHeapPointer(_byte));
+}
+
+
+void GImage::_copy(
+ const GImage& other) {
+
+ clear();
+
+ width = other.width;
+ height = other.height;
+ channels = other.channels;
+ int s = width * height * channels * sizeof(uint8);
+ _byte = (uint8*)System::malloc(s);
+ debugAssert(isValidHeapPointer(_byte));
+ memcpy(_byte, other._byte, s);
+}
+
+
+GImage::GImage(
+ const GImage& other) : _byte(NULL) {
+
+ _copy(other);
+}
+
+
+GImage::~GImage() {
+ clear();
+}
+
+
+void GImage::clear() {
+ width = 0;
+ height = 0;
+ System::free(_byte);
+ _byte = NULL;
+}
+
+
+GImage& GImage::operator=(const GImage& other) {
+ _copy(other);
+ return *this;
+}
+
+
+bool GImage::copySubImage(
+ GImage & dest, const GImage & src,
+ int srcX, int srcY, int srcWidth, int srcHeight) {
+ if ((src.width < srcX + srcWidth) ||
+ (src.height < srcY + srcHeight) ||
+ (srcY < 0) ||
+ (srcX < 0)) {
+
+ return false;
+ }
+
+ dest.resize(srcWidth, srcHeight, src.channels);
+
+ bool ret;
+ ret = pasteSubImage(dest, src, 0, 0, srcX, srcY, srcWidth, srcHeight);
+ debugAssert(ret);
+
+ return true;
+}
+
+
+bool GImage::pasteSubImage(
+ GImage & dest, const GImage & src,
+ int destX, int destY,
+ int srcX, int srcY, int srcWidth, int srcHeight) {
+ if ((src.width < srcX + srcWidth) ||
+ (src.height < srcY + srcHeight) ||
+ (dest.width < destX + srcWidth) ||
+ (dest.height < destY + srcHeight) ||
+ (srcY < 0) ||
+ (srcX < 0) ||
+ (destY < 0) ||
+ (destX < 0) ||
+ (src.channels != dest.channels)) {
+
+ return false;
+ }
+
+ for (int i = 0; i < srcHeight; i++) {
+ const uint8* srcRow = src.byte() +
+ ((i + srcY) * src.width + srcX) * src.channels;
+ uint8* destRow = dest.byte() +
+ ((i + destY) * dest.width + destX) * dest.channels;
+ memcpy(destRow, srcRow, srcWidth * src.channels);
+ }
+
+ return true;
+}
+
+
+bool GImage::supportedFormat(
+ const std::string& format) {
+
+ return (stringToFormat(format) != UNKNOWN);
+}
+
+
+GImage::Format GImage::stringToFormat(
+ const std::string& format) {
+
+ std::string extension = toUpper(format);
+
+ if ((extension == "JPG") || (extension == "JPEG")) {
+ return JPEG;
+ } else if (extension == "TGA") {
+ return TGA;
+ } else if (extension == "BMP") {
+ return BMP;
+ } else if (extension == "PCX") {
+ return PCX;
+ } else if (extension == "ICO") {
+ return ICO;
+ } else if (extension == "PNG") {
+ return PNG;
+ } else if (extension == "PPM") {
+ return PPM;
+ } else {
+ return UNKNOWN;
+ }
+}
+
+
+void GImage::save(
+ const std::string& filename,
+ Format format) const {
+
+ BinaryOutput b(filename, G3D_LITTLE_ENDIAN);
+ encode(resolveFormat(filename, NULL, 0, format), b);
+ b.commit(false);
+}
+
+
+void GImage::encode(
+ Format format,
+ uint8*& outData,
+ int& outLength) const {
+
+ BinaryOutput out;
+
+ encode(format, out);
+
+ outData = (uint8*)System::malloc(out.size());
+ debugAssert(outData);
+ outLength = out.size();
+
+ out.commit(outData);
+}
+
+
+void GImage::encode(
+ Format format,
+ BinaryOutput& out) const {
+
+ switch (format) {
+ case PPM_ASCII:
+ encodePPMASCII(out);
+ break;
+
+ case PPM:
+ encodePPM(out);
+ break;
+
+ case PNG:
+ encodePNG(out);
+ break;
+
+ case JPEG:
+ encodeJPEG(out);
+ break;
+
+ case BMP:
+ encodeBMP(out);
+ break;
+
+ case TGA:
+ encodeTGA(out);
+ break;
+
+ default:
+ debugAssert(false);
+ }
+}
+
+void GImage::insertRedAsAlpha(const GImage& alpha, GImage& output) const {
+ debugAssert(alpha.width == width);
+ debugAssert(alpha.height == height);
+
+ // make sure output GImage is valid
+ if (output.width != width || output.height != height || output.channels != 4) {
+ output.resize(width, height, 4);
+ }
+
+ for (int i = 0; i < width * height; ++i) {
+ output.byte()[i * 4 + 0] = byte()[i * channels + 0];
+ output.byte()[i * 4 + 1] = byte()[i * channels + 1];
+ output.byte()[i * 4 + 2] = byte()[i * channels + 2];
+ output.byte()[i * 4 + 3] = alpha.byte()[i * alpha.channels];
+ }
+}
+
+GImage GImage::insertRedAsAlpha(const GImage& alpha) const {
+ debugAssert(alpha.width == width);
+ debugAssert(alpha.height == height);
+
+ GImage out(width, height, 4);
+
+ insertRedAsAlpha(alpha, out);
+
+ return out;
+}
+
+
+void GImage::stripAlpha(GImage& output) const {
+
+ if (output.width != width || output.height != height || output.channels != 3)
+ {
+ output.resize(width, height, 3);
+ }
+
+ for (int i = 0; i < width * height; ++i) {
+ output.byte()[i * 3 + 0] = byte()[i * channels + 0];
+ output.byte()[i * 3 + 1] = byte()[i * channels + 1];
+ output.byte()[i * 3 + 2] = byte()[i * channels + 2];
+ }
+}
+
+GImage GImage::stripAlpha() const {
+ GImage out(width, height, 3);
+
+ stripAlpha(out);
+
+ return out;
+}
+
+
+int GImage::sizeInMemory() const {
+ return sizeof(GImage) + width * height * channels;
+}
+
+
+void GImage::computeNormalMap(
+ const GImage& bump,
+ GImage& normal,
+ float whiteHeightInPixels,
+ bool lowPassBump,
+ bool scaleHeightByNz) {
+ computeNormalMap(bump.width, bump.height, bump.channels, bump.byte(), normal, whiteHeightInPixels, lowPassBump, scaleHeightByNz);
+}
+
+void GImage::computeNormalMap(
+ int width,
+ int height,
+ int channels,
+ const uint8* src,
+ GImage& normal,
+ float whiteHeightInPixels,
+ bool lowPassBump,
+ bool scaleHeightByNz) {
+
+ if (whiteHeightInPixels == -1.0f) {
+ // Default setting scales so that a gradient ramp
+ // over the whole image becomes a 45-degree angle
+
+ // Account for potentially non-square aspect ratios
+ whiteHeightInPixels = max(width, height);
+ }
+
+ debugAssert(whiteHeightInPixels >= 0);
+
+ const int w = width;
+ const int h = height;
+ const int stride = channels;
+
+ normal.resize(w, h, 4);
+
+ const uint8* const B = src;
+ Color4uint8* const N = normal.pixel4();
+
+ for (int y = 0; y < h; ++y) {
+ for (int x = 0; x < w; ++x) {
+ // Index into normal map pixel
+ int i = x + y * w;
+
+ // Index into bump map *byte*
+ int j = stride * i;
+
+ Vector3 delta;
+
+ // Get a value from B (with wrapping lookup) relative to (x, y)
+ // and divide by 255
+ #define height(DX, DY) ((int)B[(((DX + x + w) % w) + \
+ ((DY + y + h) % h) * w) * stride])
+
+
+ // Sobel filter to compute the normal.
+ //
+ // Y Filter (X filter is the transpose)
+ // [ -1 -2 -1 ]
+ // [ 0 0 0 ]
+ // [ 1 2 1 ]
+
+ // Write the Y value directly into the x-component so we don't have
+ // to explicitly compute a cross product at the end.
+ delta.y = -(height(-1, -1) * 1 + height( 0, -1) * 2 + height( 1, -1) * 1 +
+ height(-1, 1) * -1 + height( 0, 1) * -2 + height( 1, 1) * -1);
+
+ delta.x = -(height(-1, -1) * -1 + height( 1, -1) * 1 +
+ height(-1, 0) * -2 + height( 1, 0) * 2 +
+ height(-1, 1) * -1 + height( 1, 1) * 1);
+
+ // The scale of each filter row is 4, the filter width is two pixels,
+ // and the "normal" range is 0-255.
+ delta.z = 4 * 2 * (whiteHeightInPixels / 255.0f);
+
+ // Delta is now scaled in pixels; normalize
+ delta = delta.direction();
+
+ // Copy over the bump value into the alpha channel.
+ float H = B[j] / 255.0;
+
+ if (lowPassBump) {
+ H = (height(-1, -1) + height( 0, -1) + height(1, -1) +
+ height(-1, 0) + height( 0, 0) + height(1, 0) +
+ height(-1, 1) + height( 0, 1) + height(1, 1)) / (255.0f * 9.0f);
+ }
+ #undef height
+
+ if (scaleHeightByNz) {
+ // delta.z can't possibly be negative, so we avoid actually
+ // computing the absolute value.
+ H *= delta.z;
+ }
+
+ N[i].a = iRound(H * 255.0);
+
+ // Pack into byte range
+ delta = delta * 127.5 + Vector3(127.5, 127.5, 127.5);
+ N[i].r = iClamp(iRound(delta.x), 0, 255);
+ N[i].g = iClamp(iRound(delta.y), 0, 255);
+ N[i].b = iClamp(iRound(delta.z), 0, 255);
+ }
+ }
+}
+
+
+/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+void GImage::convertToL8() {
+ switch(channels) {
+ case 1:
+ return;
+
+ case 3:
+ {
+ // Average
+ Color3uint8* src = (Color3uint8*)_byte;
+ _byte = NULL;
+ resize(width, height, 1);
+ for (int i = width * height - 1; i >= 0; --i) {
+ const Color3uint8 s = src[i];
+ uint8& d = _byte[i];
+ d = ((int)s.r + (int)s.g + (int)s.b) / 3;
+ }
+ System::free(src);
+ }
+ break;
+
+ case 4:
+ {
+ // Average
+ Color4uint8* src = (Color4uint8*)_byte;
+ _byte = NULL;
+ resize(width, height, 1);
+ for (int i = width * height - 1; i >= 0; --i) {
+ const Color4uint8 s = src[i];
+ uint8& d = _byte[i];
+ d = ((int)s.r + (int)s.g + (int)s.b) / 3;
+ }
+ System::free(src);
+ }
+ return;
+
+ default:
+ alwaysAssertM(false, "Bad number of channels in input image");
+ }
+}
+
+
+void GImage::convertToRGBA() {
+ switch(channels) {
+ case 1:
+ {
+ // Spread
+ uint8* old = _byte;
+ _byte = NULL;
+ resize(width, height, 4);
+ for (int i = width * height - 1; i >= 0; --i) {
+ const uint8 s = old[i];
+ Color4uint8& d = ((Color4uint8*)_byte)[i];
+ d.r = d.g = d.b = s;
+ d.a = 255;
+ }
+ System::free(_byte);
+ }
+ break;
+
+ case 3:
+ {
+ // Add alpha
+ Color3uint8* old = (Color3uint8*)_byte;
+ _byte = NULL;
+ resize(width, height, 4);
+ for (int i = width * height - 1; i >= 0; --i) {
+ const Color3uint8 s = old[i];
+ Color4uint8& d = ((Color4uint8*)_byte)[i];
+ d.r = s.r;
+ d.g = s.g;
+ d.b = s.b;
+ d.a = 255;
+ }
+ System::free(old);
+ }
+ break;
+
+ case 4:
+ // Already RGBA
+ return;
+
+ default:
+ alwaysAssertM(false, "Bad number of channels in input image");
+ }
+}
+
+
+void GImage::convertToRGB() {
+ switch(channels) {
+ case 1:
+ {
+ // Spread
+ uint8* old = _byte;
+ _byte = NULL;
+ resize(width, height, 3);
+ for (int i = width * height - 1; i >= 0; --i) {
+ const uint8 s = old[i];
+ Color3uint8& d = ((Color3uint8*)_byte)[i];
+ d.r = d.g = d.b = s;
+ }
+ System::free(old);
+ }
+ break;
+
+ case 3:
+ return;
+
+ case 4:
+ // Strip alpha
+ {
+ Color4uint8* old = (Color4uint8*)_byte;
+ _byte = NULL;
+ resize(width, height, 3);
+ for (int i = width * height - 1; i >= 0; --i) {
+ const Color4uint8 s = old[i];
+ Color3uint8& d = ((Color3uint8*)_byte)[i];
+ d.r = s.r;
+ d.g = s.g;
+ d.b = s.b;
+ }
+ System::free(old);
+ }
+ break;
+
+ default:
+ alwaysAssertM(false, "Bad number of channels in input image");
+ }
+}
+
+
+void GImage::R8G8B8_to_Y8U8V8(int width, int height, const uint8* _in, uint8* _out) {
+ const Color3uint8* in = reinterpret_cast<const Color3uint8*>(_in);
+ Color3uint8* out = reinterpret_cast<Color3uint8*>(_out);
+
+ Color3uint8 p;
+ for (int i = width * height - 1; i >= 0; --i) {
+ p.r = iClamp(iRound(in->r * 0.229 + in->g * 0.587 + in->b * 0.114), 0, 255);
+ p.g = iClamp(iRound(in->r * -0.147 + in->g * -0.289 + in->b * 0.436) + 127, 0, 255);
+ p.b = iClamp(iRound(in->r * 0.615 + in->g * -0.515 + in->b * -0.100) + 127, 0, 255);
+ *out = p;
+ ++in;
+ ++out;
+ }
+}
+
+
+
+void GImage::Y8U8V8_to_R8G8B8(int width, int height, const uint8* _in, uint8* _out) {
+ const Color3uint8* in = reinterpret_cast<const Color3uint8*>(_in);
+ Color3uint8* out = reinterpret_cast<Color3uint8*>(_out);
+
+ Color3uint8 p;
+ for (int i = width * height - 1; i >= 0; --i) {
+ p.r = iClamp(iRound(in->r * 1.0753 + (in->b - 127) * 1.2256), 0, 255);
+ p.g = iClamp(iRound(in->r * 1.0753 + (in->g - 127) * -0.3946 + (in->b - 127) * -0.4947), 0, 255);
+ p.b = iClamp(iRound(in->r * 1.0753 + (in->g - 127) * 2.0320 + (in->b - 127) * 0.0853), 0, 255);
+ *out = p;
+ ++in;
+ ++out;
+ }
+}
+
+
+void GImage::makeCheckerboard(GImage& im, int checkerSize, const Color4uint8& A, const Color4uint8& B) {
+ for (int y = 0; y < im.height; ++y) {
+ for (int x = 0; x < im.width; ++x) {
+ bool checker = isOdd((x / checkerSize) + (y / checkerSize));
+ const Color4uint8& color = checker ? A : B;
+ for (int c = 0; c < im.channels; ++c) {
+ uint8* v = im.byte() + (x + y * im.width) * im.channels + c;
+ *v = color[c];
+ }
+ }
+ }
+}
+
+}
+
diff --git a/externals/g3dlite/G3D.lib/source/GImage_bayer.cpp b/externals/g3dlite/G3D.lib/source/GImage_bayer.cpp
new file mode 100644
index 00000000000..16499af15f6
--- /dev/null
+++ b/externals/g3dlite/G3D.lib/source/GImage_bayer.cpp
@@ -0,0 +1,298 @@
+/**
+ @file GImage_bayer.cpp
+ @author Morgan McGuire, morgan@graphics3d.com
+ @created 2002-05-27
+ @edited 2006-05-10
+ */
+#include "G3D/platform.h"
+#include "G3D/GImage.h"
+
+namespace G3D {
+
+void GImage::BAYER_G8B8_R8G8_to_Quarter_R8G8B8(int width, int height, const uint8* in, uint8* out) {
+ debugAssert(in != out);
+
+ int halfHeight = height / 2;
+ int halfWidth = width / 2;
+
+ int dst_off = 0;
+ for (int y = 0; y < halfHeight; ++y) {
+ for (int x = 0; x < halfWidth; ++x) {
+ // GBRG
+ int src_off = x*2 + y*2*width;
+ out[dst_off] = in[src_off+width]; // red
+ out[dst_off+1] = ((int)in[src_off] + (int)in[src_off+width+1])/2; // green
+ out[dst_off+2] = in[src_off+1]; // blue
+
+ dst_off = dst_off + 3;
+ }
+ }
+}
+
+
+void GImage::Quarter_R8G8B8_to_BAYER_G8B8_R8G8(int inWidth, int inHeight, const uint8* in, uint8* out) {
+ // Undo quarter-size Bayer as best we can. This code isn't very efficient, but it
+ // also isn't used very frequently.
+
+ debugAssert(out != in);
+
+ int outWidth = 2 * inWidth;
+ int outHeight = 2 * inHeight;
+
+ for (int y = 0; y < outHeight; ++y) {
+ for (int x = 0; x < outWidth; ++x) {
+ const Color3uint8* inp = ((const Color3uint8*)in) + ((x/2) + (y/2)* inWidth);
+ uint8* outp = out + x + y * outWidth;
+
+ if (isEven(y)) {
+ // GB row
+ if (isEven(x)) {
+ // Green
+ *outp = inp->g;
+ } else {
+ // Blue
+ *outp = inp->b;
+ }
+ } else {
+ // RG row
+ if (isEven(x)) {
+ // Red
+ *outp = inp->r;
+ } else {
+ // Green
+ *outp = inp->g;
+ }
+ }
+ }
+ }
+}
+
+
+/** Applies a 5x5 filter to monochrome image I (wrapping at the boundaries) */
+static uint8 applyFilter(
+ const uint8* I,
+ int x,
+ int y,
+ int w,
+ int h,
+ const float filter[5][5]) {
+
+ debugAssert(isEven(w));
+ debugAssert(isEven(h));
+
+ float sum = 0.0f;
+ float denom = 0.0f;
+
+ for (int dy = 0; dy < 5; ++dy) {
+ int offset = ((y + dy + h - 2) % h) * w;
+
+ for (int dx = 0; dx < 5; ++dx) {
+ float f = filter[dy][dx];
+ sum += f * I[((x + dx + w - 2) % w) + offset];
+ denom += f;
+ }
+ }
+
+ return (uint8)iClamp(iRound(sum / denom), 0, 255);
+}
+
+////////////////////////////////////////////////////////////////////////////////////////////////
+//
+// Bayer conversions
+//
+
+// There are two kinds of rows (GR and BG).
+// In each row, there are two kinds of pixels (G/R, B/G).
+// We express the four kinds of INPUT pixels as:
+// GRG, GRG, BGB, BGG
+//
+// There are three kinds of OUTPUT pixels: R, G, B.
+// Thus there are nominally 12 different I/O combinations,
+// but several are impulses because needed output at that
+// location *is* the input (e.g., G_GRG and G_BGG).
+//
+// The following 5x5 row-major filters are named as output_input.
+
+// Green
+static const float G_GRR[5][5] =
+{{ 0.0f, 0.0f, -1.0f, 0.0f, 0.0f},
+{ 0.0f, 0.0f, 2.0f, 0.0f, 0.0f},
+{ -1.0f, 2.0f, 4.0f, 2.0f, -1.0f},
+{ 0.0f, 0.0f, 2.0f, 0.0f, 0.0f},
+{ 0.0f, 0.0f, -1.0f, 0.0f, 0.0f}};
+
+static const float G_BGB[5][5] =
+{{ 0.0f, 0.0f, -1.0f, 0.0f, 0.0f},
+{ 0.0f, 0.0f, 2.0f, 0.0f, 0.0f},
+{ -1.0f, 2.0f, 4.0f, 2.0f, -1.0f},
+{ 0.0f, 0.0f, 2.0f, 0.0f, 0.0f},
+{ 0.0f, 0.0f, -1.0f, 0.0f, 0.0f}};
+
+// Red
+//(the caption in the paper is wrong for this case:
+// "R row B column really means R row G column"
+static const float R_GRG[5][5] =
+{{ 0.0f, 0.0f, 0.5f, 0.0f, 0.0f},
+{ 0.0f, -1.0f, 0.0f, -1.0f, 0.0f},
+{ -1.0f, 4.0f, 5.0f, 4.0f, -1.0f},
+{ 0.0f, -1.0f, 0.0f, -1.0f, 0.0f},
+{ 0.0f, 0.0f, 0.5f, 0.0f, 0.0f}};
+
+static const float R_BGG[5][5] =
+{{ 0.0f, 0.0f, -1.0f, 0.0f, 0.0f},
+{ 0.0f, -1.0f, 4.0f, -1.0f, 0.0f},
+{ 0.5f, 0.0f, 5.0f, 0.0f, 0.5f},
+{ 0.0f, -1.0f, 4.0f, -1.0f, 0.0f},
+{ 0.0f, 0.0f, -1.0f, 0.0f, 0.0f}};
+
+static const float R_BGB[5][5] =
+{{ 0.0f, 0.0f, -3.0f/2.0f, 0.0f, 0.0f},
+{ 0.0f, 2.0f, 0.0f, 2.0f, 0.0f},
+{-3.0f/2.0f, 0.0f, 6.0f, 0.0f, -3.0f/2.0f},
+{ 0.0f, 2.0f, 0.0f, 2.0f, 0.0f},
+{ 0.0f, 0.0f, -3.0f/2.0f, 0.0f, 0.0f}};
+
+
+// Blue
+//(the caption in the paper is wrong for this case:
+// "B row R column really means B row G column")
+#define B_BGG R_GRG
+#define B_GRG R_BGG
+#define B_GRR R_BGB
+
+
+void GImage::BAYER_R8G8_G8B8_to_R8G8B8_MHC(int w, int h, const uint8* in, uint8* _out) {
+ debugAssert(in != _out);
+
+ Color3uint8* out = (Color3uint8*)_out;
+
+ for (int y = 0; y < h; ++y) {
+
+ // Row beginning in the input array.
+ int offset = y * w;
+
+ // RG row
+ for (int x = 0; x < w; ++x, ++out) {
+ // R pixel
+ {
+ out->r = in[x + offset];
+ out->g = applyFilter(in, x, y, w, h, G_GRR);
+ out->b = applyFilter(in, x, y, w, h, B_GRR);
+ }
+ ++x; ++out;
+
+ // G pixel
+ {
+ out->r = applyFilter(in, x, y, w, h, R_GRG);
+ out->g = in[x + offset];
+ out->b = applyFilter(in, x, y, w, h, B_GRG);
+ }
+ }
+
+ ++y;
+ offset += w;
+
+ // GB row
+ for (int x = 0; x < w; ++x, ++out) {
+ // G pixel
+ {
+ out->r = applyFilter(in, x, y, w, h, R_BGG);
+ out->g = in[x + offset];
+ out->b = applyFilter(in, x, y, w, h, B_BGG);
+ }
+ ++x; ++out;
+
+ // B pixel
+ {
+ out->r = applyFilter(in, x, y, w, h, R_BGB);
+ out->g = applyFilter(in, x, y, w, h, G_BGB);
+ out->b = in[x + offset];
+ }
+ }
+ }
+}
+
+static void swapRedAndBlue(int N, Color3uint8* out) {
+ for (int i = N - 1; i >= 0; --i) {
+ uint8 tmp = out[i].r;
+ out[i].r = out[i].b;
+ out[i].b = tmp;
+ }
+}
+
+void GImage::BAYER_G8R8_B8G8_to_R8G8B8_MHC(int w, int h, const uint8* in, uint8* _out) {
+ // Run the equivalent function for red
+ BAYER_G8B8_R8G8_to_R8G8B8_MHC(w, h, in, _out);
+
+ // Now swap red and blue
+ swapRedAndBlue(w * h, (Color3uint8*)_out);
+}
+
+
+void GImage::BAYER_B8G8_G8R8_to_R8G8B8_MHC(int w, int h, const uint8* in, uint8* _out) {
+ // Run the equivalent function for red
+ BAYER_R8G8_G8B8_to_R8G8B8_MHC(w, h, in, _out);
+
+ // Now swap red and blue
+ swapRedAndBlue(w * h, (Color3uint8*)_out);
+}
+
+
+void GImage::BAYER_G8B8_R8G8_to_R8G8B8_MHC(int w, int h, const uint8* in, uint8* _out) {
+
+ debugAssert(in != _out);
+
+ Color3uint8* out = (Color3uint8*)_out;
+
+ for (int y = 0; y < h; ++y) {
+
+ // Row beginning in the input array.
+ int offset = y * w;
+
+ // GB row
+ for (int x = 0; x < w; ++x, ++out) {
+ // G pixel
+ {
+ out->r = applyFilter(in, x, y, w, h, R_BGG);
+ out->g = in[x + offset];
+ out->b = applyFilter(in, x, y, w, h, B_BGG);
+ }
+ ++x; ++out;
+
+ // B pixel
+ {
+ out->r = applyFilter(in, x, y, w, h, R_BGB);
+ out->g = applyFilter(in, x, y, w, h, G_BGB);
+ out->b = in[x + offset];
+ }
+ }
+
+ ++y;
+ offset += w;
+
+ // RG row
+ for (int x = 0; x < w; ++x, ++out) {
+ // R pixel
+ {
+ out->r = in[x + offset];
+ out->g = applyFilter(in, x, y, w, h, G_GRR);
+ out->b = applyFilter(in, x, y, w, h, B_GRR);
+ }
+ ++x; ++out;
+
+ // G pixel
+ {
+ out->r = applyFilter(in, x, y, w, h, R_GRG);
+ out->g = in[x + offset];
+ out->b = applyFilter(in, x, y, w, h, B_GRG);
+ }
+ }
+ }
+
+}
+
+#undef B_BGG
+#undef B_GRG
+#undef B_GRR
+
+}
diff --git a/externals/g3dlite/G3D.lib/source/GImage_bmp.cpp b/externals/g3dlite/G3D.lib/source/GImage_bmp.cpp
new file mode 100644
index 00000000000..598ba730d0b
--- /dev/null
+++ b/externals/g3dlite/G3D.lib/source/GImage_bmp.cpp
@@ -0,0 +1,716 @@
+/**
+ @file GImage_bmp.cpp
+ @author Morgan McGuire, morgan@graphics3d.com
+ @created 2002-05-27
+ @edited 2006-05-10
+ */
+#include "G3D/platform.h"
+#include "G3D/GImage.h"
+#include "G3D/BinaryInput.h"
+#include "G3D/BinaryOutput.h"
+#include "G3D/Log.h"
+
+namespace G3D {
+
+#ifndef G3D_WIN32
+/**
+ This is used by the Windows bitmap I/O.
+ */
+static const int BI_RGB = 0;
+#endif
+
+void GImage::encodeBMP(
+ BinaryOutput& out) const {
+
+ debugAssert(channels == 1 || channels == 3);
+ out.setEndian(G3D_LITTLE_ENDIAN);
+
+ uint8 red;
+ uint8 green;
+ uint8 blue;
+ int pixelBufferSize = width * height * 3;
+ int fileHeaderSize = 14;
+ int infoHeaderSize = 40;
+ int BMScanWidth;
+ int BMPadding;
+
+ // First write the BITMAPFILEHEADER
+ //
+ // WORD bfType;
+ // DWORD bfSize;
+ // WORD bfReserved1;
+ // WORD bfReserved2;
+ // DWORD bfOffBits;
+
+ // Type
+ out.writeUInt8('B');
+ out.writeUInt8('M');
+
+ // File size
+ out.writeUInt32(fileHeaderSize + infoHeaderSize + pixelBufferSize);
+
+ // Two reserved fields set to zero
+ out.writeUInt16(0);
+ out.writeUInt16(0);
+
+ // The offset, in bytes, from the BITMAPFILEHEADER structure
+ // to the bitmap bits.
+ out.writeUInt32(infoHeaderSize + fileHeaderSize);
+
+ // Now the BITMAPINFOHEADER
+ //
+ // DWORD biSize;
+ // LONG biWidth;
+ // LONG biHeight;
+ // WORD biPlanes;
+ // WORD biBitCount
+ // DWORD biCompression;
+ // DWORD biSizeImage;
+ // LONG biXPelsPerMeter;
+ // LONG biYPelsPerMeter;
+ // DWORD biClrUsed;
+ // DWORD biClrImportant;
+
+ // Size of the info header
+ out.writeUInt32(infoHeaderSize);
+
+ // Width and height of the image
+ out.writeUInt32(width);
+ out.writeUInt32(height);
+
+ // Planes ("must be set to 1")
+ out.writeUInt16(1);
+
+ // BitCount and CompressionType
+ out.writeUInt16(24);
+ out.writeUInt32(BI_RGB);
+
+ // Image size ("may be zero for BI_RGB bitmaps")
+ out.writeUInt32(0);
+
+ // biXPelsPerMeter
+ out.writeUInt32(0);
+ // biYPelsPerMeter
+ out.writeUInt32(0);
+
+ // biClrUsed
+ out.writeUInt32(0);
+
+ // biClrImportant
+ out.writeUInt32(0);
+
+ BMScanWidth = width * 3;
+
+ if (BMScanWidth & 3) {
+ BMPadding = 4 - (BMScanWidth & 3);
+ } else {
+ BMPadding = 0;
+ }
+
+ int hStart = height - 1;
+ int hEnd = -1;
+ int hDir = -1;
+ int dest;
+
+ // Write the pixel data
+ for (int h = hStart; h != hEnd; h += hDir) {
+ dest = channels * h * width;
+ for (int w = 0; w < width; ++w) {
+
+ if (channels == 3) {
+ red = _byte[dest];
+ green = _byte[dest + 1];
+ blue = _byte[dest + 2];
+ } else {
+ red = _byte[dest];
+ green = _byte[dest];
+ blue = _byte[dest];
+ }
+
+ out.writeUInt8(blue);
+ out.writeUInt8(green);
+ out.writeUInt8(red);
+
+ dest += channels;
+ }
+
+ if (BMPadding > 0) {
+ out.skip(BMPadding);
+ }
+ }
+}
+
+
+void GImage::decodeBMP(
+ BinaryInput& input) {
+
+ // The BMP decoding uses these flags.
+ static const uint16 PICTURE_NONE = 0x0000;
+ static const uint16 PICTURE_BITMAP = 0x1000;
+
+ // Compression Flags
+ static const uint16 PICTURE_UNCOMPRESSED = 0x0100;
+ static const uint16 PICTURE_MONOCHROME = 0x0001;
+ static const uint16 PICTURE_4BIT = 0x0002;
+ static const uint16 PICTURE_8BIT = 0x0004;
+ static const uint16 PICTURE_16BIT = 0x0008;
+ static const uint16 PICTURE_24BIT = 0x0010;
+ static const uint16 PICTURE_32BIT = 0x0020;
+
+ (void)PICTURE_16BIT;
+ (void)PICTURE_32BIT;
+
+ // This is a simple BMP loader that can handle uncompressed BMP files.
+ // Verify this is a BMP file by looking for the BM tag.
+ input.reset();
+ std::string tag = input.readString(2);
+ if (tag != "BM") {
+ throw Error("Not a BMP file", input.getFilename());
+ }
+
+ channels = 3;
+ // Skip to the BITMAPINFOHEADER's width and height
+ input.skip(16);
+
+ width = input.readUInt32();
+ height = input.readUInt32();
+
+ // Skip to the bit count and compression type
+ input.skip(2);
+
+ uint16 bitCount = input.readUInt16();
+ uint32 compressionType = input.readUInt32();
+
+ uint8 red;
+ uint8 green;
+ uint8 blue;
+ uint8 blank;
+
+ // Only uncompressed bitmaps are supported by this code
+ if ((int32)compressionType != BI_RGB) {
+ throw Error("BMP images must be uncompressed", input.getFilename());
+ }
+
+ uint8* palette = NULL;
+
+ // Create the palette if needed
+ if (bitCount <= 8) {
+
+ // Skip to the palette color count in the header
+ input.skip(12);
+
+ int numColors = input.readUInt32();
+
+ palette = (uint8*)System::malloc(numColors * 3);
+ debugAssert(palette);
+
+ // Skip past the end of the header to the palette info
+ input.skip(4);
+
+ int c;
+ for(c = 0; c < numColors * 3; c += 3) {
+ // Palette information in bitmaps is stored in BGR_ format.
+ // That means it's blue-green-red-blank, for each entry.
+ blue = input.readUInt8();
+ green = input.readUInt8();
+ red = input.readUInt8();
+ blank = input.readUInt8();
+
+ palette[c] = red;
+ palette[c + 1] = green;
+ palette[c + 2] = blue;
+ }
+ }
+
+ int hStart = 0;
+ int hEnd = 0;
+ int hDir = 0;
+
+ if (height < 0) {
+ height = -height;
+ hStart = 0;
+ hEnd = height;
+ hDir = 1;
+ } else {
+ //height = height;
+ hStart = height - 1;
+ hEnd = -1;
+ hDir = -1;
+ }
+
+ _byte = (uint8*)System::malloc(width * height * 3);
+ debugAssert(_byte);
+
+ int BMScanWidth;
+ int BMPadding;
+ uint8 BMGroup;
+ uint8 BMPixel8;
+ int currPixel;
+ int dest;
+ int flags = PICTURE_NONE;
+
+ if (bitCount == 1) {
+ // Note that this file is not necessarily grayscale, since it's possible
+ // the palette is blue-and-white, or whatever. But of course most image
+ // programs only write 1-bit images if they're black-and-white.
+ flags = PICTURE_BITMAP | PICTURE_UNCOMPRESSED | PICTURE_MONOCHROME;
+
+ // For bitmaps, each scanline is dword-aligned.
+ BMScanWidth = (width + 7) >> 3;
+ if (BMScanWidth & 3) {
+ BMScanWidth += 4 - (BMScanWidth & 3);
+ }
+
+ // Powers of 2
+ int pow2[8] = {0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80};
+
+ for (int h = hStart; h != hEnd; h += hDir) {
+
+ currPixel = 0;
+ dest = 3 * h * width;
+
+ for (int w = 0; w < BMScanWidth; ++w) {
+
+ BMGroup = input.readUInt8();
+
+ // Now we read the pixels. Usually there are eight pixels per byte,
+ // since each pixel is represented by one bit, but if the width
+ // is not a multiple of eight, the last byte will have some bits
+ // set, with the others just being extra. Plus there's the
+ // dword-alignment padding. So we keep checking to see if we've
+ // already read "width" number of pixels.
+ for (int i = 7; i >= 0; --i) {
+ if (currPixel < width) {
+ int src = 3 * ((BMGroup & pow2[i]) >> i);
+
+ _byte[dest] = palette[src];
+ _byte[dest + 1] = palette[src + 1];
+ _byte[dest + 2] = palette[src + 2];
+
+ ++currPixel;
+ dest += 3;
+ }
+ }
+ }
+ }
+
+ } else if (bitCount == 4) {
+
+ flags = PICTURE_BITMAP | PICTURE_UNCOMPRESSED | PICTURE_4BIT;
+
+ // For bitmaps, each scanline is dword-aligned.
+ int BMScanWidth = (width+1) >> 1;
+ if (BMScanWidth & 3) {
+ BMScanWidth += 4 - (BMScanWidth & 3);
+ }
+
+ for (int h = hStart; h != hEnd; h += hDir) {
+
+ currPixel = 0;
+ dest = 3 * h * width;
+
+ for (int w = 0; w < BMScanWidth; w++) {
+
+ BMGroup = input.readUInt8();
+ int src[2];
+ src[0] = 3 * ((BMGroup & 0xF0) >> 4);
+ src[1] = 3 * (BMGroup & 0x0F);
+
+ // Now we read the pixels. Usually there are two pixels per byte,
+ // since each pixel is represented by four bits, but if the width
+ // is not a multiple of two, the last byte will have only four bits
+ // set, with the others just being extra. Plus there's the
+ // dword-alignment padding. So we keep checking to see if we've
+ // already read "Width" number of pixels.
+
+ for (int i = 0; i < 2; ++i) {
+ if (currPixel < width) {
+ int tsrc = src[i];
+
+ _byte[dest] = palette[tsrc];
+ _byte[dest + 1] = palette[tsrc + 1];
+ _byte[dest + 2] = palette[tsrc + 2];
+
+ ++currPixel;
+ dest += 3;
+ }
+ }
+ }
+ }
+
+ } else if (bitCount == 8) {
+
+ flags = PICTURE_BITMAP | PICTURE_UNCOMPRESSED | PICTURE_8BIT;
+
+ // For bitmaps, each scanline is dword-aligned.
+ BMScanWidth = width;
+ if (BMScanWidth & 3) {
+ BMScanWidth += 4 - (BMScanWidth & 3);
+ }
+
+ for (int h = hStart; h != hEnd; h += hDir) {
+
+ currPixel = 0;
+
+ for (int w = 0; w < BMScanWidth; ++w) {
+
+ BMPixel8 = input.readUInt8();
+
+ if (currPixel < width) {
+ dest = 3 * ((h * width) + currPixel);
+ int src = 3 * BMPixel8;
+
+ _byte[dest] = palette[src];
+ _byte[dest + 1] = palette[src + 1];
+ _byte[dest + 2] = palette[src + 2];
+
+ ++currPixel;
+ }
+ }
+ }
+
+ } else if (bitCount == 16) {
+
+ System::free(_byte);
+ _byte = NULL;
+ System::free(palette);
+ palette = NULL;
+ throw Error("16-bit bitmaps not supported", input.getFilename());
+
+ } else if (bitCount == 24) {
+ input.skip(20);
+
+ flags = PICTURE_BITMAP | PICTURE_UNCOMPRESSED | PICTURE_24BIT;
+
+ // For bitmaps, each scanline is dword-aligned.
+ BMScanWidth = width * 3;
+
+ if (BMScanWidth & 3) {
+ BMPadding = 4 - (BMScanWidth & 3);
+ } else {
+ BMPadding = 0;
+ }
+
+ for (int h = hStart; h != hEnd; h += hDir) {
+ dest = 3 * h * width;
+ for (int w = 0; w < width; ++w) {
+
+ blue = input.readUInt8();
+ green = input.readUInt8();
+ red = input.readUInt8();
+
+ _byte[dest] = red;
+ _byte[dest + 1] = green;
+ _byte[dest + 2] = blue;
+
+ dest += 3;
+ }
+
+ if (BMPadding) {
+ input.skip(2);
+ }
+ }
+
+ } else if (bitCount == 32) {
+
+ System::free(_byte);
+ _byte = NULL;
+ System::free(palette);
+ palette = NULL;
+ throw Error("32 bit bitmaps not supported", input.getFilename());
+
+ } else {
+ // We support all possible bit depths, so if the
+ // code gets here, it's not even a real bitmap.
+ System::free(_byte);
+ _byte = NULL;
+ throw Error("Not a bitmap!", input.getFilename());
+ }
+
+ System::free(palette);
+ palette = NULL;
+}
+
+
+void GImage::decodeICO(
+ BinaryInput& input) {
+
+ // Header
+ uint16 r = input.readUInt16();
+ debugAssert(r == 0);
+ r = input.readUInt16();
+ debugAssert(r == 1);
+
+ // Read the number of icons, although we'll only load the
+ // first one.
+ int count = input.readUInt16();
+
+ channels = 4;
+
+ debugAssert(count > 0);
+
+ const uint8* headerBuffer = input.getCArray() + input.getPosition();
+ int maxWidth = 0, maxHeight = 0;
+ int maxHeaderNum = 0;
+ for (int currentHeader = 0; currentHeader < count; ++currentHeader) {
+
+ const uint8* curHeaderBuffer = headerBuffer + (currentHeader * 16);
+ int tmpWidth = curHeaderBuffer[0];
+ int tmpHeight = curHeaderBuffer[1];
+ // Just in case there is a non-square icon, checking area
+ if ((tmpWidth * tmpHeight) > (maxWidth * maxHeight)) {
+ maxWidth = tmpWidth;
+ maxHeight = tmpHeight;
+ maxHeaderNum = currentHeader;
+ }
+ }
+
+ input.skip(maxHeaderNum * 16);
+
+ width = input.readUInt8();
+ height = input.readUInt8();
+ int numColors = input.readUInt8();
+
+ _byte = (uint8*)System::malloc(width * height * channels);
+ debugAssert(_byte);
+
+ // Bit mask for packed bits
+ int mask = 0;
+
+ int bitsPerPixel = 8;
+
+ switch (numColors) {
+ case 2:
+ mask = 0x01;
+ bitsPerPixel = 1;
+ break;
+
+ case 16:
+ mask = 0x0F;
+ bitsPerPixel = 4;
+ break;
+
+ case 0:
+ numColors = 256;
+ mask = 0xFF;
+ bitsPerPixel = 8;
+ break;
+ default:
+ throw Error("Unsupported ICO color count.", input.getFilename());
+ }
+
+ input.skip(5);
+ // Skip 'size' unused
+ input.skip(4);
+
+ int offset = input.readUInt32();
+
+ // Skip over any other icon descriptions
+ input.setPosition(offset);
+
+ // Skip over bitmap header; it is redundant
+ input.skip(40);
+
+ Array<Color4uint8> palette;
+ palette.resize(numColors, true);
+ for (int c = 0; c < numColors; ++c) {
+ palette[c].b = input.readUInt8();
+ palette[c].g = input.readUInt8();
+ palette[c].r = input.readUInt8();
+ palette[c].a = input.readUInt8();
+ }
+
+ // The actual image and mask follow
+
+ // The XOR Bitmap is stored as 1-bit, 4-bit or 8-bit uncompressed Bitmap
+ // using the same encoding as BMP files. The AND Bitmap is stored in as
+ // 1-bit uncompressed Bitmap.
+ //
+ // Pixels are stored bottom-up, left-to-right. Pixel lines are padded
+ // with zeros to end on a 32bit (4byte) boundary. Every line will have the
+ // same number of bytes. Color indices are zero based, meaning a pixel color
+ // of 0 represents the first color table entry, a pixel color of 255 (if there
+ // are that many) represents the 256th entry.
+/*
+ int bitsPerRow = width * bitsPerPixel;
+ int bytesPerRow = iCeil((double)bitsPerRow / 8);
+ // Rows are padded to 32-bit boundaries
+ bytesPerRow += bytesPerRow % 4;
+
+ // Read the XOR values into the color channel
+ for (int y = height - 1; y >= 0; --y) {
+ int x = 0;
+ // Read the row
+ for (int i = 0; i < bytesPerRow; ++i) {
+ uint8 byte = input.readUInt8();
+ for (int j = 0; (j < 8) && (x < width); ++x, j += bitsPerPixel) {
+ int bit = ((byte << j) >> (8 - bitsPerPixel)) & mask;
+ pixel4(x, y) = colorTable[bit];
+ }
+ }
+ }
+*/
+ int hStart = 0;
+ int hEnd = 0;
+ int hDir = 0;
+
+ if (height < 0) {
+ height = -height;
+ hStart = 0;
+ hEnd = height;
+ hDir = 1;
+ } else {
+ //height = height;
+ hStart = height - 1;
+ hEnd = -1;
+ hDir = -1;
+ }
+
+ int BMScanWidth;
+ uint8 BMGroup;
+ uint8 BMPixel8;
+ int currPixel;
+ int dest;
+
+ if (bitsPerPixel == 1) {
+ // Note that this file is not necessarily grayscale, since it's possible
+ // the palette is blue-and-white, or whatever. But of course most image
+ // programs only write 1-bit images if they're black-and-white.
+
+ // For bitmaps, each scanline is dword-aligned.
+ BMScanWidth = (width + 7) >> 3;
+ if (BMScanWidth & 3) {
+ BMScanWidth += 4 - (BMScanWidth & 3);
+ }
+
+ // Powers of 2
+ int pow2[8] = {0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80};
+
+ for (int h = hStart; h != hEnd; h += hDir) {
+
+ currPixel = 0;
+ dest = 3 * h * width;
+
+ for (int w = 0; w < BMScanWidth; ++w) {
+
+ BMGroup = input.readUInt8();
+
+ // Now we read the pixels. Usually there are eight pixels per byte,
+ // since each pixel is represented by one bit, but if the width
+ // is not a multiple of eight, the last byte will have some bits
+ // set, with the others just being extra. Plus there's the
+ // dword-alignment padding. So we keep checking to see if we've
+ // already read "width" number of pixels.
+ for (int i = 7; i >= 0; --i) {
+ if (currPixel < width) {
+ int src = ((BMGroup & pow2[i]) >> i);
+
+ _byte[dest] = palette[src].r;
+ _byte[dest + 1] = palette[src].g;
+ _byte[dest + 2] = palette[src].b;
+
+ ++currPixel;
+ dest += 4;
+ }
+ }
+ }
+ }
+
+ } else if (bitsPerPixel == 4) {
+
+ // For bitmaps, each scanline is dword-aligned.
+ int BMScanWidth = (width+1) >> 1;
+ if (BMScanWidth & 3) {
+ BMScanWidth += 4 - (BMScanWidth & 3);
+ }
+
+ for (int h = hStart; h != hEnd; h += hDir) {
+
+ currPixel = 0;
+ dest = 4 * h * width;
+
+ for (int w = 0; w < BMScanWidth; w++) {
+
+ BMGroup = input.readUInt8();
+ int src[2];
+ src[0] = ((BMGroup & 0xF0) >> 4);
+ src[1] = (BMGroup & 0x0F);
+
+ // Now we read the pixels. Usually there are two pixels per byte,
+ // since each pixel is represented by four bits, but if the width
+ // is not a multiple of two, the last byte will have only four bits
+ // set, with the others just being extra. Plus there's the
+ // dword-alignment padding. So we keep checking to see if we've
+ // already read "Width" number of pixels.
+
+ for (int i = 0; i < 2; ++i) {
+ if (currPixel < width) {
+ int tsrc = src[i];
+
+ _byte[dest] = palette[tsrc].r;
+ _byte[dest + 1] = palette[tsrc].g;
+ _byte[dest + 2] = palette[tsrc].b;
+
+ ++currPixel;
+ dest += 4;
+ }
+ }
+ }
+ }
+
+ } else if (bitsPerPixel == 8) {
+
+ // For bitmaps, each scanline is dword-aligned.
+ BMScanWidth = width;
+ if (BMScanWidth & 3) {
+ BMScanWidth += 4 - (BMScanWidth & 3);
+ }
+
+ for (int h = hStart; h != hEnd; h += hDir) {
+
+ currPixel = 0;
+
+ for (int w = 0; w < BMScanWidth; ++w) {
+
+ BMPixel8 = input.readUInt8();
+
+ if (currPixel < width) {
+ dest = 4 * ((h * width) + currPixel);
+ int src = BMPixel8;
+
+ _byte[dest] = palette[src].r;
+ _byte[dest + 1] = palette[src].g;
+ _byte[dest + 2] = palette[src].b;
+
+ ++currPixel;
+ }
+ }
+ }
+ }
+
+ // Read the mask into the alpha channel
+ int bitsPerRow = width;
+ int bytesPerRow = iCeil((double)bitsPerRow / 8);
+
+ // For bitmaps, each scanline is dword-aligned.
+ //BMScanWidth = (width + 1) >> 1;
+ if (bytesPerRow & 3) {
+ bytesPerRow += 4 - (bytesPerRow & 3);
+ }
+
+ for (int y = height - 1; y >= 0; --y) {
+ int x = 0;
+ // Read the row
+ for (int i = 0; i < bytesPerRow; ++i) {
+ uint8 byte = input.readUInt8();
+ for (int j = 0; (j < 8) && (x < width); ++x, ++j) {
+ int bit = (byte >> (7 - j)) & 0x01;
+ pixel4(x, y).a = (1 - bit) * 0xFF;
+ }
+ }
+ }
+
+}
+
+
+}
diff --git a/externals/g3dlite/G3D.lib/source/GImage_jpeg.cpp b/externals/g3dlite/G3D.lib/source/GImage_jpeg.cpp
new file mode 100644
index 00000000000..fe2812cf3b2
--- /dev/null
+++ b/externals/g3dlite/G3D.lib/source/GImage_jpeg.cpp
@@ -0,0 +1,445 @@
+/**
+ @file GImage_jpeg.cpp
+ @author Morgan McGuire, morgan@graphics3d.com
+ @created 2002-05-27
+ @edited 2006-10-10
+ */
+#include "G3D/platform.h"
+#include "G3D/GImage.h"
+#include "G3D/BinaryInput.h"
+#include "G3D/BinaryOutput.h"
+
+#include <cstring>
+
+/**
+ Pick up libjpeg headers locally on Windows, but from the system on all other platforms.
+*/
+extern "C" {
+#include <jconfig.h>
+#include <jpeglib.h>
+}
+
+namespace G3D {
+
+
+const int jpegQuality = 96;
+
+/**
+ The IJG library needs special setup for compress/decompressing
+ from memory. These classes provide them.
+
+ The format of this class is defined by the IJG library; do not
+ change it.
+ */
+class memory_destination_mgr {
+public:
+ struct jpeg_destination_mgr pub;
+ JOCTET* buffer;
+ int size;
+ int count;
+};
+
+typedef memory_destination_mgr* mem_dest_ptr;
+
+/**
+ Signature dictated by IJG.
+ */
+static void init_destination (
+ j_compress_ptr cinfo) {
+
+ mem_dest_ptr dest = (mem_dest_ptr) cinfo->dest;
+
+ dest->pub.next_output_byte = dest->buffer;
+ dest->pub.free_in_buffer = dest->size;
+ dest->count=0;
+}
+
+/**
+ Signature dictated by IJG.
+ */
+static boolean empty_output_buffer (
+ j_compress_ptr cinfo) {
+
+ mem_dest_ptr dest = (mem_dest_ptr) cinfo->dest;
+
+ dest->pub.next_output_byte = dest->buffer;
+ dest->pub.free_in_buffer = dest->size;
+
+ return TRUE;
+}
+
+/**
+ Signature dictated by IJG.
+ */
+static void term_destination (
+ j_compress_ptr cinfo) {
+
+ mem_dest_ptr dest = (mem_dest_ptr) cinfo->dest;
+ dest->count = dest->size - dest->pub.free_in_buffer;
+}
+
+/**
+ Signature dictated by IJG.
+ */
+static void jpeg_memory_dest (
+ j_compress_ptr cinfo,
+ JOCTET* buffer,
+ int size) {
+
+ mem_dest_ptr dest;
+
+ if (cinfo->dest == NULL) {
+ // First time for this JPEG object; call the
+ // IJG allocator to get space.
+ cinfo->dest = (struct jpeg_destination_mgr*)
+ (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo,
+ JPOOL_PERMANENT,
+ sizeof(memory_destination_mgr));
+ }
+
+ dest = (mem_dest_ptr) cinfo->dest;
+ dest->size = size;
+ dest->buffer = buffer;
+ dest->pub.init_destination = init_destination;
+ dest->pub.empty_output_buffer = empty_output_buffer;
+ dest->pub.term_destination = term_destination;
+}
+
+////////////////////////////////////////////////////////////////////////////////////////
+
+#define INPUT_BUF_SIZE 4096
+
+/**
+ Structure dictated by IJG.
+ */
+class memory_source_mgr {
+public:
+ struct jpeg_source_mgr pub;
+ int source_size;
+ unsigned char* source_data;
+ boolean start_of_data;
+ JOCTET* buffer;
+};
+
+
+typedef memory_source_mgr* mem_src_ptr;
+
+
+/**
+ Signature dictated by IJG.
+ */
+static void init_source(
+ j_decompress_ptr cinfo) {
+
+ mem_src_ptr src = (mem_src_ptr) cinfo->src;
+
+ src->start_of_data = TRUE;
+}
+
+
+/**
+ Signature dictated by IJG.
+ */
+static boolean fill_input_buffer(
+ j_decompress_ptr cinfo) {
+
+ mem_src_ptr src = (mem_src_ptr) cinfo->src;
+
+ size_t bytes_read = 0;
+
+ if (src->source_size > INPUT_BUF_SIZE)
+ bytes_read = INPUT_BUF_SIZE;
+ else
+ bytes_read = src->source_size;
+
+ memcpy (src->buffer, src->source_data, bytes_read);
+
+ src->source_data += bytes_read;
+ src->source_size -= bytes_read;
+
+ src->pub.next_input_byte = src->buffer;
+ src->pub.bytes_in_buffer = bytes_read;
+ src->start_of_data = FALSE;
+
+
+ return TRUE;
+}
+
+
+/**
+ Signature dictated by IJG.
+ */
+static void skip_input_data(
+ j_decompress_ptr cinfo,
+ long num_bytes) {
+
+ mem_src_ptr src = (mem_src_ptr)cinfo->src;
+
+ if (num_bytes > 0) {
+ while (num_bytes > (long) src->pub.bytes_in_buffer) {
+ num_bytes -= (long) src->pub.bytes_in_buffer;
+ boolean s = fill_input_buffer(cinfo);
+ debugAssert(s); (void)s;
+ }
+
+ src->pub.next_input_byte += (size_t) num_bytes;
+ src->pub.bytes_in_buffer -= (size_t) num_bytes;
+ }
+}
+
+
+/**
+ Signature dictated by IJG.
+ */
+static void term_source (
+ j_decompress_ptr cinfo) {
+ (void)cinfo;
+ // Intentionally empty
+}
+
+
+/**
+ Signature dictated by IJG.
+ */
+static void jpeg_memory_src (
+ j_decompress_ptr cinfo,
+ JOCTET* buffer,
+ int size) {
+
+ mem_src_ptr src;
+
+ if (cinfo->src == NULL) {
+ // First time for this JPEG object
+ cinfo->src = (struct jpeg_source_mgr*)
+ (*cinfo->mem->alloc_small)(
+ (j_common_ptr) cinfo,
+ JPOOL_PERMANENT,
+ sizeof(memory_source_mgr));
+
+ src = (mem_src_ptr)cinfo->src;
+
+ src->buffer = (JOCTET*)
+ (*cinfo->mem->alloc_small)(
+ (j_common_ptr) cinfo,
+ JPOOL_PERMANENT,
+ INPUT_BUF_SIZE * sizeof(JOCTET));
+ }
+
+ src = (mem_src_ptr)cinfo->src;
+ src->pub.init_source = init_source;
+ src->pub.fill_input_buffer = fill_input_buffer;
+ src->pub.skip_input_data = skip_input_data;
+
+ // use default method
+ src->pub.resync_to_restart = jpeg_resync_to_restart;
+ src->pub.term_source = term_source;
+ src->source_data = buffer;
+ src->source_size = size;
+
+ // forces fill_input_buffer on first read
+ src->pub.bytes_in_buffer = 0;
+
+ // until buffer loaded
+ src->pub.next_input_byte = NULL;
+}
+
+
+void GImage::encodeJPEG(
+ BinaryOutput& out) const {
+
+ if (channels != 3) {
+ // Convert to three channel
+ GImage tmp = *this;
+ tmp.convertToRGB();
+ tmp.encodeJPEG(out);
+ return;
+ }
+
+ debugAssert(channels == 3);
+ out.setEndian(G3D_LITTLE_ENDIAN);
+
+ // Allocate and initialize a compression object
+ jpeg_compress_struct cinfo;
+ jpeg_error_mgr jerr;
+
+ cinfo.err = jpeg_std_error(&jerr);
+ jpeg_create_compress(&cinfo);
+
+ // Specify the destination for the compressed data.
+ // (Overestimate the size)
+ int buffer_size = width * height * 3 + 200;
+ JOCTET* compressed_data = (JOCTET*)System::malloc(buffer_size);
+ jpeg_memory_dest(&cinfo, compressed_data, buffer_size);
+
+
+ cinfo.image_width = width;
+ cinfo.image_height = height;
+
+ // # of color components per pixel
+ cinfo.input_components = 3;
+
+ // colorspace of input image
+ cinfo.in_color_space = JCS_RGB;
+ cinfo.input_gamma = 1.0;
+
+ // Set parameters for compression, including image size & colorspace
+ jpeg_set_defaults(&cinfo);
+ jpeg_set_quality(&cinfo, jpegQuality, false);
+ cinfo.smoothing_factor = 0;
+ cinfo.optimize_coding = TRUE;
+// cinfo.dct_method = JDCT_FLOAT;
+ cinfo.dct_method = JDCT_ISLOW;
+ cinfo.jpeg_color_space = JCS_YCbCr;
+
+ // Initialize the compressor
+ jpeg_start_compress(&cinfo, TRUE);
+
+ // Iterate over all scanlines from top to bottom
+ // pointer to a single row
+ JSAMPROW row_pointer[1];
+
+ // JSAMPLEs per row in image_buffer
+ int row_stride = cinfo.image_width * 3;
+ while (cinfo.next_scanline < cinfo.image_height) {
+ row_pointer[0] = &(_byte[cinfo.next_scanline * row_stride]);
+ jpeg_write_scanlines(&cinfo, row_pointer, 1);
+ }
+
+ // Shut down the compressor
+ jpeg_finish_compress(&cinfo);
+
+ // Figure out how big the result was.
+ int outLength = ((mem_dest_ptr)cinfo.dest)->count;
+
+ // Release the JPEG compression object
+ jpeg_destroy_compress(&cinfo);
+
+ // Copy into an appropriately sized output buffer.
+ out.writeBytes(compressed_data, outLength);
+
+ // Free the conservative buffer.
+ System::free(compressed_data);
+ compressed_data = NULL;
+}
+
+
+
+void GImage::decodeJPEG(
+ BinaryInput& input) {
+
+ struct jpeg_decompress_struct cinfo;
+ struct jpeg_error_mgr jerr;
+ int loc = 0;
+
+ channels = 3;
+ // We have to set up the error handler, in case initialization fails.
+ cinfo.err = jpeg_std_error(&jerr);
+
+ // Initialize the JPEG decompression object.
+ jpeg_create_decompress(&cinfo);
+
+ // Specify data source (eg, a file, for us, memory)
+ jpeg_memory_src(&cinfo, const_cast<uint8*>(input.getCArray()), input.size());
+
+ // Read the parameters with jpeg_read_header()
+ jpeg_read_header(&cinfo, TRUE);
+
+ // Set parameters for decompression
+ // (We do nothing here since the defaults are fine)
+
+ // Start decompressor
+ jpeg_start_decompress(&cinfo);
+
+ // Get and set the values of interest to this object
+ this->width = cinfo.output_width;
+ this->height = cinfo.output_height;
+
+ // Prepare the pointer object for the pixel data
+ _byte = (uint8*)System::malloc(width * height * 3);
+
+ // JSAMPLEs per row in output buffer
+ int bpp = cinfo.output_components;
+ int row_stride = cinfo.output_width * bpp;
+
+ // Make a one-row-high sample array that will go away when done with image
+ JSAMPARRAY temp = (*cinfo.mem->alloc_sarray)
+ ((j_common_ptr) &cinfo, JPOOL_IMAGE, row_stride, 1);
+
+ // Read data on a scanline by scanline basis
+ while (cinfo.output_scanline < cinfo.output_height) {
+
+ // We may need to adjust the output based on the
+ // number of channels it has.
+ switch (bpp) {
+ case 1:
+ // Grayscale; decompress to temp.
+ jpeg_read_scanlines(&cinfo, temp, 1);
+
+ // Expand to three channels
+ {
+ uint8* scan = &(_byte[loc * 3]);
+ uint8* endScan = scan + (width * 3);
+ uint8* t = *temp;
+
+ while (scan < endScan) {
+ uint8 value = t[0];
+
+ // Spread the value 3x.
+ scan[0] = value;
+ scan[1] = value;
+ scan[2] = value;
+
+ scan += 3;
+ t += 1;
+ }
+ }
+ break;
+
+ case 3:
+ // Read directly into the array
+ {
+ // Need one extra level of indirection.
+ uint8* scan = _byte + loc;
+ JSAMPARRAY ptr = &scan;
+ jpeg_read_scanlines(&cinfo, ptr, 1);
+ }
+ break;
+
+ case 4:
+ // RGBA; decompress to temp.
+ jpeg_read_scanlines(&cinfo, temp, 1);
+
+ // Drop the 3rd channel
+ {
+ uint8* scan = &(_byte[loc * 3]);
+ uint8* endScan = scan + width * 3;
+ uint8* t = *temp;
+
+ while (scan < endScan) {
+ scan[0] = t[0];
+ scan[1] = t[1];
+ scan[2] = t[2];
+
+ scan += 3;
+ t += 4;
+ }
+ }
+ break;
+
+ default:
+ throw Error("Unexpected number of channels.", input.getFilename());
+ }
+
+ loc += row_stride;
+ }
+
+ // Finish decompression
+ jpeg_finish_decompress(&cinfo);
+
+ alwaysAssertM(this, "Corrupt GImage");
+ // Release JPEG decompression object
+ jpeg_destroy_decompress(&cinfo);
+}
+
+
+}
diff --git a/externals/g3dlite/G3D.lib/source/GImage_png.cpp b/externals/g3dlite/G3D.lib/source/GImage_png.cpp
new file mode 100644
index 00000000000..9fd6a743e8e
--- /dev/null
+++ b/externals/g3dlite/G3D.lib/source/GImage_png.cpp
@@ -0,0 +1,245 @@
+/**
+ @file GImage_png.cpp
+ @author Morgan McGuire, morgan@graphics3d.com
+ @created 2002-05-27
+ @edited 2006-05-10
+ */
+#include "G3D/platform.h"
+#include "G3D/GImage.h"
+#include "G3D/BinaryInput.h"
+#include "G3D/BinaryOutput.h"
+#include "G3D/Log.h"
+#include <png.h>
+
+namespace G3D {
+
+
+//libpng required function signature
+static void png_read_data(
+ png_structp png_ptr,
+ png_bytep data,
+ png_size_t length) {
+
+
+ debugAssert( png_ptr->io_ptr != NULL );
+ debugAssert( length >= 0 );
+ debugAssert( data != NULL );
+
+ ((BinaryInput*)png_ptr->io_ptr)->readBytes(data, length);
+}
+
+//libpng required function signature
+static void png_write_data(png_structp png_ptr,
+ png_bytep data,
+ png_size_t length) {
+
+ debugAssert( png_ptr->io_ptr != NULL );
+ debugAssert( data != NULL );
+
+ ((BinaryOutput*)png_ptr->io_ptr)->writeBytes(data, length);
+}
+
+//libpng required function signature
+static void png_flush_data(
+ png_structp png_ptr) {
+ (void)png_ptr;
+ //Do nothing.
+}
+
+//libpng required function signature
+static void png_error(
+ png_structp png_ptr,
+ png_const_charp error_msg) {
+
+ (void)png_ptr;
+ debugAssert( error_msg != NULL );
+ throw GImage::Error(error_msg, "PNG");
+}
+
+//libpng required function signature
+void png_warning(
+ png_structp png_ptr,
+ png_const_charp warning_msg) {
+
+ (void)png_ptr;
+ debugAssert( warning_msg != NULL );
+ Log::common()->println(warning_msg);
+}
+
+void GImage::encodePNG(
+ BinaryOutput& out) const {
+
+ debugAssert( channels == 1 || channels == 3 || channels == 4 );
+
+ if (this->height > (int)(PNG_UINT_32_MAX/png_sizeof(png_bytep)))
+ throw GImage::Error("Unsupported PNG height.", out.getFilename());
+
+ out.setEndian(G3D_LITTLE_ENDIAN);
+
+ png_structp png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, png_error, png_warning);
+ if (!png_ptr)
+ throw GImage::Error("Unable to initialize PNG encoder.", out.getFilename());
+
+ png_infop info_ptr = png_create_info_struct(png_ptr);
+ if (!info_ptr) {
+ png_destroy_write_struct(&png_ptr, &info_ptr);
+ throw GImage::Error("Unable to initialize PNG encoder.", out.getFilename());
+ }
+
+ //setup libpng write handler so can use BinaryOutput
+ png_set_write_fn(png_ptr, (void*)&out, png_write_data, png_flush_data);
+
+ if (channels == 3) {
+ png_set_IHDR(png_ptr, info_ptr, this->width, this->height, 8, PNG_COLOR_TYPE_RGB,
+ PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_BASE, PNG_FILTER_TYPE_BASE);
+ }
+ else if (channels == 4) {
+ png_set_IHDR(png_ptr, info_ptr, this->width, this->height, 8, PNG_COLOR_TYPE_RGBA,
+ PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_BASE, PNG_FILTER_TYPE_BASE);
+ }
+ else if (channels == 1) {
+ png_set_IHDR(png_ptr, info_ptr, this->width, this->height, 8, PNG_COLOR_TYPE_GRAY,
+ PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_BASE, PNG_FILTER_TYPE_BASE);
+ }
+ else {
+ png_destroy_write_struct(&png_ptr, &info_ptr);
+ throw GImage::Error("Unsupported number of channels for PNG.", out.getFilename());
+ }
+
+ png_color_8_struct sig_bit;
+ sig_bit.red = 8;
+ sig_bit.green = 8;
+ sig_bit.blue = 8;
+ if (channels == 4)
+ sig_bit.alpha = 8;
+ else
+ sig_bit.alpha = 0;
+ png_set_sBIT(png_ptr, info_ptr, &sig_bit);
+
+ //write the png header
+ png_write_info(png_ptr, info_ptr);
+
+ png_bytepp row_pointers = new png_bytep[this->height];
+
+ for (int i=0; i < this->height; ++i) {
+ row_pointers[i] = (png_bytep)&this->_byte[(this->width * this->channels * i)];
+ }
+
+ png_write_image(png_ptr, row_pointers);
+
+ png_write_end(png_ptr, info_ptr);
+
+ delete[] row_pointers;
+
+ png_destroy_write_struct(&png_ptr, &info_ptr);
+}
+
+
+void GImage::decodePNG(
+ BinaryInput& input) {
+
+ png_structp png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, png_error, png_warning);
+ if (!png_ptr)
+ throw GImage::Error("Unable to initialize PNG decoder.", input.getFilename());
+
+ png_infop info_ptr = png_create_info_struct(png_ptr);
+ if (!info_ptr) {
+ png_destroy_read_struct(&png_ptr, (png_infopp)NULL, (png_infopp)NULL);
+ throw GImage::Error("Unable to initialize PNG decoder.", input.getFilename());
+ }
+
+ png_infop end_info = png_create_info_struct(png_ptr);
+ if (!end_info) {
+ png_destroy_read_struct(&png_ptr, &info_ptr, (png_infopp)NULL);
+ throw GImage::Error("Unable to initialize PNG decoder.", input.getFilename());
+ }
+
+ //now that the libpng structures are setup, change the error handlers and read routines
+ //to use G3D functions so that BinaryInput can be used.
+
+ png_set_read_fn(png_ptr, (png_voidp)&input, png_read_data);
+
+ //read in sequentially so that three copies of the file are not in memory at once
+ png_read_info(png_ptr, info_ptr);
+
+ png_uint_32 png_width, png_height;
+ int bit_depth, color_type, interlace_type;
+ //this will validate the data it extracts from info_ptr
+ png_get_IHDR(png_ptr, info_ptr, &png_width, &png_height, &bit_depth, &color_type,
+ &interlace_type, int_p_NULL, int_p_NULL);
+
+ if (color_type == PNG_COLOR_TYPE_GRAY_ALPHA) {
+ png_destroy_read_struct(&png_ptr, &info_ptr, &end_info);
+ throw GImage::Error("Unsupported PNG color type - PNG_COLOR_TYPE_GRAY_ALPHA.", input.getFilename());
+ }
+
+ this->width = static_cast<uint32>(png_width);
+ this->height = static_cast<uint32>(png_height);
+
+ //swap bytes of 16 bit files to least significant byte first
+ png_set_swap(png_ptr);
+
+ png_set_strip_16(png_ptr);
+
+ //Expand paletted colors into true RGB triplets
+ if (color_type == PNG_COLOR_TYPE_PALETTE) {
+ png_set_palette_to_rgb(png_ptr);
+ }
+
+ //Expand grayscale images to the full 8 bits from 1, 2, or 4 bits/pixel
+ if (color_type == PNG_COLOR_TYPE_GRAY && bit_depth < 8) {
+ png_set_gray_1_2_4_to_8(png_ptr);
+ }
+
+ //Expand paletted or RGB images with transparency to full alpha channels
+ //so the data will be available as RGBA quartets.
+ if (png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS)) {
+ png_set_tRNS_to_alpha(png_ptr);
+ }
+
+ // Fix sub-8 bit_depth to 8bit
+ if (bit_depth < 8) {
+ png_set_packing(png_ptr);
+ }
+
+ if ((color_type == PNG_COLOR_TYPE_RGBA) ||
+ ((color_type == PNG_COLOR_TYPE_PALETTE) && (png_ptr->num_trans > 0)) ) {
+
+ this->channels = 4;
+ this->_byte = (uint8*)System::malloc(width * height * 4);
+
+ } else if ((color_type == PNG_COLOR_TYPE_RGB) ||
+ (color_type == PNG_COLOR_TYPE_PALETTE)) {
+
+ this->channels = 3;
+ this->_byte = (uint8*)System::malloc(width * height * 3);
+
+ } else if (color_type == PNG_COLOR_TYPE_GRAY) {
+
+ this->channels = 1;
+ this->_byte = (uint8*)System::malloc(width * height);
+
+ } else {
+ throw GImage::Error("Unsupported PNG bit-depth or type.", input.getFilename());
+ }
+
+ //since we are reading row by row, required to handle interlacing
+ uint32 number_passes = png_set_interlace_handling(png_ptr);
+
+ png_read_update_info(png_ptr, info_ptr);
+
+ for (uint32 pass = 0; pass < number_passes; ++pass) {
+ for (uint32 y = 0; y < (uint32)height; ++y) {
+ png_bytep rowPointer = &this->_byte[width * this->channels * y];
+ png_read_rows(png_ptr, &rowPointer, png_bytepp_NULL, 1);
+ }
+ }
+
+// png_read_image(png_ptr, &_byte);
+ png_read_end(png_ptr, info_ptr);
+
+ png_destroy_read_struct(&png_ptr, &info_ptr, &end_info);
+}
+
+
+}
diff --git a/externals/g3dlite/G3D.lib/source/GImage_ppm.cpp b/externals/g3dlite/G3D.lib/source/GImage_ppm.cpp
new file mode 100644
index 00000000000..bfe5c8305e8
--- /dev/null
+++ b/externals/g3dlite/G3D.lib/source/GImage_ppm.cpp
@@ -0,0 +1,185 @@
+/**
+ @file GImage_ppm.cpp
+ @author Morgan McGuire, morgan@graphics3d.com
+ @created 2002-05-27
+ @edited 2006-05-10
+ */
+#include "G3D/platform.h"
+#include "G3D/GImage.h"
+#include "G3D/BinaryInput.h"
+#include "G3D/BinaryOutput.h"
+#include "G3D/TextInput.h"
+#include "G3D/TextOutput.h"
+#include "G3D/Log.h"
+
+namespace G3D {
+
+void GImage::encodePPMASCII(
+ BinaryOutput& out) const {
+
+ debugAssert(channels == 3);
+
+ TextOutput::Settings ppmOptions;
+ ppmOptions.convertNewlines = false;
+ ppmOptions.numColumns = 70;
+ ppmOptions.wordWrap = TextOutput::Settings::WRAP_WITHOUT_BREAKING;
+ TextOutput ppm(ppmOptions);
+ // Always write out a full-color ppm
+ ppm.printf("P3\n%d %d\n255\n", width, height);
+
+ const Color3uint8* c = this->pixel3();
+ for (uint32 i = 0; i < (uint32)(width * height); ++i) {
+ ppm.printf("%d %d %d%c", c[i].r, c[i].g, c[i].b,
+ ((i % ((width * 3) - 1)) == 0) ?
+ '\n' : ' ');
+ }
+
+ out.writeString(ppm.commitString());
+}
+
+
+void GImage::encodePPM(
+ BinaryOutput& out) const {
+
+ // http://netpbm.sourceforge.net/doc/ppm.html
+ debugAssert(channels == 3);
+
+ std::string header = format("P6 %d %d 255 ", width, height);
+
+ out.writeBytes(header.c_str(), header.size());
+
+ out.writeBytes(this->pixel3(), width * height * 3);
+}
+
+void GImage::decodePPMASCII(
+ BinaryInput& input) {
+
+ int ppmWidth;
+ int ppmHeight;
+
+ double maxColor;
+
+ // Create a TextInput object to parse ascii format
+ // Mixed binary/ascii formats will require more
+
+ const std::string inputStr = input.readString();
+
+ TextInput::Settings ppmOptions;
+ ppmOptions.cppComments = false;
+ ppmOptions.otherCommentCharacter = '#';
+ ppmOptions.signedNumbers = true;
+ ppmOptions.singleQuotedStrings = false;
+
+ TextInput ppmInput(TextInput::FROM_STRING, inputStr, ppmOptions);
+
+ //Skip first line in header P#
+ std::string ppmType = ppmInput.readSymbol();
+
+ ppmWidth = (int)ppmInput.readNumber();
+ ppmHeight = (int)ppmInput.readNumber();
+
+ // Everything but a PBM will have a max color value
+ if (ppmType != "P2") {
+ maxColor = ppmInput.readNumber();
+ } else {
+ maxColor = 255;
+ }
+
+ if ((ppmWidth < 0) ||
+ (ppmHeight < 0) ||
+ (maxColor <= 0)) {
+ throw GImage::Error("Invalid PPM Header.", input.getFilename());
+ }
+
+ // I don't think it's proper to scale values less than 255
+ if (maxColor <= 255.0) {
+ maxColor = 255.0;
+ }
+
+ this->width = ppmWidth;
+ this->height = ppmHeight;
+ this->channels = 3;
+ // always scale down to 1 byte per channel
+ this->_byte = (uint8*)System::malloc(width * height * 3);
+
+ // Read in the image data. I am not validating if the values match the maxColor
+ // requirements. I only scale if needed to fit within the byte available.
+ for (uint32 i = 0; i < (uint32)(width * height); ++i) {
+ // read in color and scale to max pixel defined in header
+ // A max color less than 255 might need to be left alone and not scaled.
+ Color3uint8& curPixel = *(this->pixel3() + i);
+
+ if (ppmType == "P3") {
+ curPixel.r = (uint8)(ppmInput.readNumber() * (255.0 / maxColor));
+ curPixel.g = (uint8)(ppmInput.readNumber() * (255.0 / maxColor));
+ curPixel.b = (uint8)(ppmInput.readNumber() * (255.0 / maxColor));
+ } else if (ppmType == "P2") {
+ uint8 pixel = (uint8)(ppmInput.readNumber() * (255.0 / maxColor));
+ curPixel.r = pixel;
+ curPixel.g = pixel;
+ curPixel.b = pixel;
+ } else if (ppmType == "P1") {
+ int pixel = (uint8)(ppmInput.readNumber() * maxColor);
+ curPixel.r = pixel;
+ curPixel.g = pixel;
+ curPixel.b = pixel;
+ }
+ }
+}
+
+/** Consumes whitespace up to and including a number, but not the following character */
+static int scanUInt(BinaryInput& input) {
+ char c = input.readUInt8();
+ while (isWhiteSpace(c)) {
+ c = input.readUInt8();
+ }
+
+ std::string s;
+ s += c;
+ c = input.readUInt8();
+ while (!isWhiteSpace(c)) {
+ s += c;
+ c = input.readUInt8();
+ }
+
+ // Back up one to avoid consuming the last character
+ input.setPosition(input.getPosition() - 1);
+
+ int x;
+ sscanf(s.c_str(), "%d", &x);
+ return x;
+}
+
+void GImage::decodePPM(
+ BinaryInput& input) {
+
+ char head[2];
+ int w, h;
+
+ input.readBytes(head, 2);
+ if (head[0] != 'P' || head[1] != '6') {
+ throw GImage::Error("Invalid PPM Header.", input.getFilename());
+ }
+
+ w = scanUInt(input);
+ h = scanUInt(input);
+
+ // Skip the max color specifier
+ scanUInt(input);
+
+ if ((w < 0) ||
+ (h < 0) ||
+ (w > 100000) ||
+ (h > 100000)) {
+ throw GImage::Error("Invalid PPM size in header.", input.getFilename());
+ }
+
+ // Trailing whitespace
+ input.readUInt8();
+
+ resize(w, h, 3);
+
+ input.readBytes(_byte, width * height * 3);
+}
+
+}
diff --git a/externals/g3dlite/G3D.lib/source/GImage_tga.cpp b/externals/g3dlite/G3D.lib/source/GImage_tga.cpp
new file mode 100644
index 00000000000..d84feedecc9
--- /dev/null
+++ b/externals/g3dlite/G3D.lib/source/GImage_tga.cpp
@@ -0,0 +1,179 @@
+/**
+ @file GImage_tga.cpp
+ @author Morgan McGuire, morgan@graphics3d.com
+ @created 2002-05-27
+ @edited 2006-05-10
+ */
+#include "G3D/platform.h"
+#include "G3D/GImage.h"
+#include "G3D/BinaryInput.h"
+#include "G3D/BinaryOutput.h"
+#include "G3D/Log.h"
+
+namespace G3D {
+
+void GImage::encodeTGA(
+ BinaryOutput& out) const {
+
+ out.setEndian(G3D_LITTLE_ENDIAN);
+
+ // ID length
+ out.writeUInt8(0);
+
+ // Color map Type
+ out.writeUInt8(0);
+
+ // Type
+ out.writeUInt8(2);
+
+ // Color map
+ out.skip(5);
+
+ // x, y offsets
+ out.writeUInt16(0);
+ out.writeUInt16(0);
+
+ // Width & height
+ out.writeUInt16(width);
+ out.writeUInt16(height);
+
+ // Color depth
+ out.writeUInt8(8 * channels);
+
+ // Image descriptor
+ if (channels == 3) {
+ // 0 alpha bits
+ out.writeUInt8(0);
+ }
+ else {
+ // 8 alpha bits
+ out.writeUInt8(8);
+ }
+
+ // Image ID (zero length)
+
+ if (channels == 3) {
+ // Pixels are upside down in BGR format.
+ for (int y = height - 1; y >= 0; y--) {
+ for (int x = 0; x < width; x++) {
+ uint8* p = &(_byte[3 * (y * width + x)]);
+ out.writeUInt8(p[2]);
+ out.writeUInt8(p[1]);
+ out.writeUInt8(p[0]);
+ }
+ }
+ } else {
+ // Pixels are upside down in BGRA format.
+ for (int y = height - 1; y >= 0; y--) {
+ for (int x = 0; x < width; x++) {
+ uint8* p = &(_byte[4 * (y * width + x)]);
+ out.writeUInt8(p[2]);
+ out.writeUInt8(p[1]);
+ out.writeUInt8(p[0]);
+ out.writeUInt8(p[3]);
+ }
+ }
+ }
+
+ // Write "TRUEVISION-XFILE " 18 bytes from the end
+ // (with null termination)
+ out.writeString("TRUEVISION-XFILE ");
+}
+
+
+void GImage::decodeTGA(
+ BinaryInput& input) {
+
+ // This is a simple TGA loader that can handle uncompressed
+ // truecolor TGA files (TGA type 2).
+ // Verify this is a TGA file by looking for the TRUEVISION tag.
+ int pos = input.getPosition();
+ input.setPosition(input.size() - 18);
+ std::string tag = input.readString(16);
+ if (tag != "TRUEVISION-XFILE") {
+ throw Error("Not a TGA file", input.getFilename());
+ }
+
+ input.setPosition(pos);
+
+ int IDLength = input.readUInt8();
+ int colorMapType = input.readUInt8();
+ int imageType = input.readUInt8();
+
+ (void)colorMapType;
+
+ // 2 is the type supported by this routine.
+ if (imageType != 2) {
+ throw Error("TGA images must be type 2 (Uncompressed truecolor)", input.getFilename());
+ }
+
+ // Color map specification
+ input.skip(5);
+
+ // Image specification
+
+ // Skip x and y offsets
+ input.skip(4);
+
+ width = input.readInt16();
+ height = input.readInt16();
+
+ int colorDepth = input.readUInt8();
+
+ if ((colorDepth != 24) && (colorDepth != 32)) {
+ throw Error("TGA files must be 24 or 32 bit.", input.getFilename());
+ }
+
+ if (colorDepth == 32) {
+ channels = 4;
+ } else {
+ channels = 3;
+ }
+
+ // Image descriptor contains overlay data as well
+ // as data indicating where the origin is
+ int imageDescriptor = input.readUInt8();
+ (void)imageDescriptor;
+
+ // Image ID
+ input.skip(IDLength);
+
+ _byte = (uint8*)System::malloc(width * height * channels);
+ debugAssert(_byte);
+
+ // Pixel data
+ int x;
+ int y;
+
+ if (channels == 3) {
+ for (y = height - 1; y >= 0; y--) {
+ for (x = 0; x < width; x++) {
+ int b = input.readUInt8();
+ int g = input.readUInt8();
+ int r = input.readUInt8();
+
+ int i = (x + y * width) * 3;
+ _byte[i + 0] = r;
+ _byte[i + 1] = g;
+ _byte[i + 2] = b;
+ }
+ }
+ } else {
+ for (y = height - 1; y >= 0; y--) {
+ for (x = 0; x < width; x++) {
+ int b = input.readUInt8();
+ int g = input.readUInt8();
+ int r = input.readUInt8();
+ int a = input.readUInt8();
+
+ int i = (x + y * width) * 4;
+ _byte[i + 0] = r;
+ _byte[i + 1] = g;
+ _byte[i + 2] = b;
+ _byte[i + 3] = a;
+ }
+ }
+ }
+}
+
+}
diff --git a/externals/g3dlite/G3D.lib/source/GLight.cpp b/externals/g3dlite/G3D.lib/source/GLight.cpp
new file mode 100644
index 00000000000..8acb066ef54
--- /dev/null
+++ b/externals/g3dlite/G3D.lib/source/GLight.cpp
@@ -0,0 +1,143 @@
+/**
+ @file GLight.cpp
+
+ @maintainer Morgan McGuire, matrix@graphics3d.com
+
+ @created 2003-11-12
+ @edited 2007-10-22
+*/
+
+#include "G3D/GLight.h"
+#include "G3D/Sphere.h"
+
+namespace G3D {
+
+GLight::GLight() {
+ position = Vector4(0, 0, 0, 0);
+ color = Color3::white();
+ spotDirection = Vector3(0, 0, -1);
+ spotCutoff = 180;
+ enabled = false;
+ attenuation[0] = 1.0;
+ attenuation[1] = 0.0;
+ attenuation[2] = 0.0;
+ specular = true;
+ diffuse = true;
+}
+
+
+GLight GLight::directional(const Vector3& toLight, const Color3& color, bool s, bool d) {
+ GLight L;
+ L.position = Vector4(toLight.direction(), 0);
+ L.color = color;
+ L.specular = s;
+ L.diffuse = d;
+ return L;
+}
+
+
+GLight GLight::point(const Vector3& pos, const Color3& color, float constAtt, float linAtt, float quadAtt, bool s, bool d) {
+ GLight L;
+ L.position = Vector4(pos, 1);
+ L.color = color;
+ L.attenuation[0] = constAtt;
+ L.attenuation[1] = linAtt;
+ L.attenuation[2] = quadAtt;
+ L.specular = s;
+ L.diffuse = d;
+ return L;
+}
+
+
+GLight GLight::spot(const Vector3& pos, const Vector3& pointDirection, float cutOffAngleDegrees, const Color3& color, float constAtt, float linAtt, float quadAtt, bool s, bool d) {
+ GLight L;
+ L.position = Vector4(pos, 1.0f);
+ L.spotDirection = pointDirection.direction();
+ debugAssert(cutOffAngleDegrees <= 90);
+ L.spotCutoff = cutOffAngleDegrees;
+ L.color = color;
+ L.attenuation[0] = constAtt;
+ L.attenuation[1] = linAtt;
+ L.attenuation[2] = quadAtt;
+ L.specular = s;
+ L.diffuse = d;
+ return L;
+}
+
+
+bool GLight::operator==(const GLight& other) const {
+ return (position == other.position) &&
+ (spotDirection == other.spotDirection) &&
+ (spotCutoff == other.spotCutoff) &&
+ (attenuation[0] == other.attenuation[0]) &&
+ (attenuation[1] == other.attenuation[1]) &&
+ (attenuation[2] == other.attenuation[2]) &&
+ (color == other.color) &&
+ (enabled == other.enabled) &&
+ (specular == other.specular) &&
+ (diffuse == other.diffuse);
+}
+
+bool GLight::operator!=(const GLight& other) const {
+ return !(*this == other);
+}
+
+
+Sphere GLight::effectSphere(float cutoff) const {
+ if (position.w == 0) {
+ // Directional light
+ return Sphere(Vector3::zero(), (float)inf());
+ } else {
+ // Avoid divide by zero
+ cutoff = max(cutoff, 0.0001f);
+ float maxIntensity = max(color.r, max(color.g, color.b));
+
+ float radius = (float)inf();
+
+ if (attenuation[2] != 0) {
+
+ // Solve I / attenuation.dot(1, r, r^2) < cutoff for r
+ //
+ // a[0] + a[1] r + a[2] r^2 > I/cutoff
+ //
+
+ float a = attenuation[2];
+ float b = attenuation[1];
+ float c = attenuation[0] - maxIntensity / cutoff;
+
+ float discrim = square(b) - 4 * a * c;
+
+ if (discrim >= 0) {
+ discrim = sqrt(discrim);
+
+ float r1 = (-b + discrim) / (2 * a);
+ float r2 = (-b - discrim) / (2 * a);
+
+ if (r1 < 0) {
+ if (r2 > 0) {
+ radius = r2;
+ }
+ } else if (r2 > 0) {
+ radius = min(r1, r2);
+ } else {
+ radius = r1;
+ }
+ }
+
+ } else if (attenuation[1] != 0) {
+
+ // Solve I / attenuation.dot(1, r) < cutoff for r
+ //
+ // r * a[1] + a[0] = I / cutoff
+ // r = (I / cutoff - a[0]) / a[1]
+
+ float radius = (maxIntensity / cutoff - attenuation[0]) / attenuation[1];
+ radius = max(radius, 0.0f);
+ }
+
+ return Sphere(position.xyz(), radius);
+
+ }
+}
+
+}
diff --git a/externals/g3dlite/G3D.lib/source/GThread.cpp b/externals/g3dlite/G3D.lib/source/GThread.cpp
new file mode 100644
index 00000000000..090370436c4
--- /dev/null
+++ b/externals/g3dlite/G3D.lib/source/GThread.cpp
@@ -0,0 +1,203 @@
+/**
+ @file GThread.cpp
+
+ GThread class.
+
+ @created 2005-09-24
+ @edited 2005-10-22
+ */
+
+#include "G3D/GThread.h"
+#include "G3D/System.h"
+#include "G3D/debugAssert.h"
+
+
+namespace G3D {
+
+namespace _internal {
+
+class BasicThread: public GThread {
+public:
+ BasicThread(const std::string& name, void (*proc)(void*), void* param):
+ GThread(name), m_wrapperProc(proc), m_param(param) { }
+protected:
+ virtual void threadMain() {
+ m_wrapperProc(m_param);
+ }
+
+private:
+ void (*m_wrapperProc)(void*);
+
+ void* m_param;
+};
+
+} // namespace _internal
+
+
+GThread::GThread(const std::string& name):
+ m_status(STATUS_CREATED),
+ m_name(name) {
+
+#ifdef G3D_WIN32
+ m_event = NULL;
+#endif
+
+ // system-independent clear of handle
+ System::memset(&m_handle, 0, sizeof(m_handle));
+}
+
+GThread::~GThread() {
+#ifdef _MSC_VER
+# pragma warning( push )
+# pragma warning( disable : 4127 )
+#endif
+ alwaysAssertM(m_status != STATUS_RUNNING, "Deleting thread while running.");
+#ifdef _MSC_VER
+# pragma warning( pop )
+#endif
+
+#ifdef G3D_WIN32
+ if (m_event) {
+ ::CloseHandle(m_event);
+ }
+#endif
+}
+
+GThreadRef GThread::create(const std::string& name, void (*proc)(void*), void* param) {
+ return new _internal::BasicThread(name, proc, param);
+}
+
+
+bool GThread::started() const {
+ return m_status != STATUS_CREATED;
+}
+
+bool GThread::start() {
+
+ debugAssertM(! started(), "Thread has already executed.");
+ if (started()) {
+ return false;
+ }
+
+ m_status = STATUS_STARTED;
+
+# ifdef G3D_WIN32
+ DWORD threadId;
+
+ m_event = ::CreateEvent(NULL, TRUE, FALSE, NULL);
+ debugAssert(m_event);
+
+ m_handle = ::CreateThread(NULL, 0, &internalThreadProc, this, 0, &threadId);
+
+ if (m_handle == NULL) {
+ ::CloseHandle(m_event);
+ m_event = NULL;
+ }
+
+ return (m_handle != NULL);
+# else
+ if (!pthread_create(&m_handle, NULL, &internalThreadProc, this)) {
+ return true;
+ } else {
+ // system-independent clear of handle
+ System::memset(&m_handle, 0, sizeof(m_handle));
+
+ return false;
+ }
+# endif
+}
+
+void GThread::terminate() {
+ if (m_handle) {
+# ifdef G3D_WIN32
+ ::TerminateThread(m_handle, 0);
+# else
+ pthread_kill(m_handle, SIGSTOP);
+# endif
+ // system-independent clear of handle
+ System::memset(&m_handle, 0, sizeof(m_handle));
+ }
+}
+
+bool GThread::running() const{
+ return (m_status == STATUS_RUNNING);
+}
+
+bool GThread::completed() const {
+ return (m_status == STATUS_COMPLETED);
+}
+
+void GThread::waitForCompletion() {
+# ifdef G3D_WIN32
+ debugAssert(m_event);
+ ::WaitForSingleObject(m_event, INFINITE);
+# else
+ debugAssert(m_handle);
+ pthread_join(m_handle, NULL);
+# endif
+}
+
+#ifdef G3D_WIN32
+DWORD WINAPI GThread::internalThreadProc(LPVOID param) {
+ GThread* current = reinterpret_cast<GThread*>(param);
+ debugAssert(current->m_event);
+ current->m_status = STATUS_RUNNING;
+ current->threadMain();
+ current->m_status = STATUS_COMPLETED;
+ ::SetEvent(current->m_event);
+ return 0;
+}
+#else
+void* GThread::internalThreadProc(void* param) {
+ GThread* current = reinterpret_cast<GThread*>(param);
+ current->m_status = STATUS_RUNNING;
+ current->threadMain();
+ current->m_status = STATUS_COMPLETED;
+ return (void*)NULL;
+}
+#endif
+
+
+
+GMutex::GMutex() {
+# ifdef G3D_WIN32
+ ::InitializeCriticalSection(&m_handle);
+# else
+ pthread_mutex_init(&m_handle, NULL);
+# endif
+}
+
+GMutex::~GMutex() {
+ //TODO: Debug check for locked
+# ifdef G3D_WIN32
+ ::DeleteCriticalSection(&m_handle);
+# else
+ pthread_mutex_destroy(&m_handle);
+# endif
+}
+
+//bool GMutex::tryLock() {
+//# ifdef G3D_WIN32
+// return ::TryEnterCriticalSection(&m_handle);
+//# else
+// return pthread_mutex_trylock(&m_handle);
+//# endif
+//}
+
+void GMutex::lock() {
+# ifdef G3D_WIN32
+ ::EnterCriticalSection(&m_handle);
+# else
+ pthread_mutex_lock(&m_handle);
+# endif
+}
+
+void GMutex::unlock() {
+# ifdef G3D_WIN32
+ ::LeaveCriticalSection(&m_handle);
+# else
+ pthread_mutex_unlock(&m_handle);
+# endif
+}
+
+} // namespace G3D
diff --git a/externals/g3dlite/G3D.lib/source/GUniqueID.cpp b/externals/g3dlite/G3D.lib/source/GUniqueID.cpp
new file mode 100644
index 00000000000..25c757b70e4
--- /dev/null
+++ b/externals/g3dlite/G3D.lib/source/GUniqueID.cpp
@@ -0,0 +1,78 @@
+/**
+ @file GUniqueID.cpp
+ @author Morgan McGuire, morgan@cs.williams.edu
+ */
+#include "G3D/GUniqueID.h"
+#include "G3D/BinaryInput.h"
+#include "G3D/TextInput.h"
+#include "G3D/BinaryOutput.h"
+#include "G3D/TextOutput.h"
+#include "G3D/NetworkDevice.h"
+
+namespace G3D {
+
+void GUniqueID::serialize(BinaryOutput& b) const {
+ b.writeUInt64(id);
+}
+
+
+void GUniqueID::deserialize(BinaryInput& b) {
+ id = b.readUInt64();
+}
+
+void GUniqueID::serialize(TextOutput& t) const {
+ t.writeSymbol("(");
+ t.writeNumber((double)(id >> 32));
+ t.writeNumber((double)(id & 0xFFFFFFFF));
+ t.writeSymbol(")");
+}
+
+void GUniqueID::deserialize(TextInput& t) {
+ t.readSymbol("(");
+ id = (((uint64)t.readNumber()) << 32) + (uint64)t.readNumber();
+ t.readSymbol(")");
+}
+
+
+GUniqueID GUniqueID::create(uint16 tag) {
+ static uint64 counter = 0;
+ static uint64 systemID = 0;
+
+ if (systemID == 0) {
+ // Create a unique ID for this machine/program instance
+
+ // TODO: see ioctl(skfd, SIOCGIFHWADDR, &if_hwaddr)
+ Array<NetAddress> addr;
+ NetworkDevice::instance()->localHostAddresses(addr);
+ if (addr.size() > 0) {
+ systemID |= addr[0].ip();
+ }
+
+ union {
+ float64 ft;
+ uint64 ut;
+ };
+ ft = System::time();
+ systemID = ut << 22;
+ systemID ^= ((uint64)iRandom(0, 32768)) << 8;
+
+ systemID &= ~((uint64)1023 << 54);
+
+ // Ensure that the systemID is non-zero (vanishingly small probability)
+ if (systemID == 0) {
+ systemID = 1;
+ }
+ }
+
+ // No need for modulo; we'll all be dead before this counter
+ // overflows 54 bits
+ ++counter;
+
+ GUniqueID i;
+
+ i.id = (((uint64)(tag & 1023)) << 54) | (counter ^ systemID);
+
+ return i;
+}
+
+} // G3D
diff --git a/externals/g3dlite/G3D.lib/source/Image1.cpp b/externals/g3dlite/G3D.lib/source/Image1.cpp
new file mode 100644
index 00000000000..30841d01c6b
--- /dev/null
+++ b/externals/g3dlite/G3D.lib/source/Image1.cpp
@@ -0,0 +1,224 @@
+/**
+ @file Image1.cpp
+
+ @maintainer Morgan McGuire, matrix@graphics3d.com
+
+ @created 2007-01-31
+ @edited 2007-01-31
+*/
+
+
+#include "G3D/Image1.h"
+#include "G3D/Image1uint8.h"
+#include "G3D/GImage.h"
+#include "G3D/Color4.h"
+#include "G3D/Color4uint8.h"
+#include "G3D/Color1.h"
+#include "G3D/Color1uint8.h"
+#include "G3D/ImageFormat.h"
+
+namespace G3D {
+
+Image1::Image1(int w, int h, WrapMode wrap) : Map2D<Color1, Color1>(w, h, wrap) {
+ setAll(Color1(0.0f));
+}
+
+
+Image1::Ref Image1::fromGImage(const GImage& im, WrapMode wrap) {
+ switch (im.channels) {
+ case 1:
+ return fromArray(im.pixel1(), im.width, im.height, wrap);
+
+ case 3:
+ return fromArray(im.pixel3(), im.width, im.height, wrap);
+
+ case 4:
+ return fromArray(im.pixel4(), im.width, im.height, wrap);
+
+ default:
+ debugAssertM(false, "Input GImage must have 1, 3, or 4 channels.");
+ return NULL;
+ }
+}
+
+
+Image1::Ref Image1::fromImage1uint8(const ReferenceCountedPointer<Image1uint8>& im) {
+ Ref out = createEmpty(static_cast<WrapMode>(im->wrapMode()));
+ out->resize(im->width(), im->height());
+
+ int N = im->width() * im->height();
+ const Color1uint8* src = reinterpret_cast<Color1uint8*>(im->getCArray());
+ for (int i = 0; i < N; ++i) {
+ out->data[i] = Color1(src[i]);
+ }
+
+ return out;
+}
+
+
+Image1::Ref Image1::createEmpty(int width, int height, WrapMode wrap) {
+ return new Type(width, height, wrap);
+}
+
+
+Image1::Ref Image1::createEmpty(WrapMode wrap) {
+ return createEmpty(0, 0, wrap);
+}
+
+
+Image1::Ref Image1::fromFile(const std::string& filename, WrapMode wrap, GImage::Format fmt) {
+ Ref out = createEmpty(wrap);
+ out->load(filename);
+ return out;
+}
+
+
+void Image1::load(const std::string& filename, GImage::Format fmt) {
+ copyGImage(GImage(filename, fmt));
+ setChanged(true);
+}
+
+
+Image1::Ref Image1::fromArray(const class Color3uint8* ptr, int w, int h, WrapMode wrap) {
+ Ref out = createEmpty(wrap);
+ out->copyArray(ptr, w, h);
+ return out;
+}
+
+
+Image1::Ref Image1::fromArray(const class Color1* ptr, int w, int h, WrapMode wrap) {
+ Ref out = createEmpty(wrap);
+ out->copyArray(ptr, w, h);
+ return out;
+}
+
+
+Image1::Ref Image1::fromArray(const class Color1uint8* ptr, int w, int h, WrapMode wrap) {
+ Ref out = createEmpty(wrap);
+ out->copyArray(ptr, w, h);
+ return out;
+}
+
+
+Image1::Ref Image1::fromArray(const class Color3* ptr, int w, int h, WrapMode wrap) {
+ Ref out = createEmpty(wrap);
+ out->copyArray(ptr, w, h);
+ return out;
+}
+
+
+Image1::Ref Image1::fromArray(const class Color4uint8* ptr, int w, int h, WrapMode wrap) {
+ Ref out = createEmpty(wrap);
+ out->copyArray(ptr, w, h);
+ return out;
+}
+
+
+Image1::Ref Image1::fromArray(const class Color4* ptr, int w, int h, WrapMode wrap) {
+ Ref out = createEmpty(wrap);
+ out->copyArray(ptr, w, h);
+ return out;
+}
+
+void Image1::copyGImage(const GImage& im) {
+ switch (im.channels) {
+ case 1:
+ copyArray(im.pixel1(), im.width, im.height);
+ break;
+
+ case 3:
+ copyArray(im.pixel3(), im.width, im.height);
+ break;
+
+ case 4:
+ copyArray(im.pixel4(), im.width, im.height);
+ break;
+ }
+}
+
+
+void Image1::copyArray(const Color3uint8* src, int w, int h) {
+ resize(w, h);
+
+ int N = w * h;
+ Color1* dst = data.getCArray();
+ // Convert int8 -> float
+ for (int i = 0; i < N; ++i) {
+ dst[i] = Color1(Color3(src[i]).average());
+ }
+}
+
+
+void Image1::copyArray(const Color4uint8* src, int w, int h) {
+ resize(w, h);
+
+ int N = w * h;
+ Color1* dst = data.getCArray();
+
+ // Strip alpha and convert
+ for (int i = 0; i < N; ++i) {
+ dst[i] = Color1(Color3(src[i].rgb()).average());
+ }
+}
+
+
+void Image1::copyArray(const Color1* src, int w, int h) {
+ resize(w, h);
+ System::memcpy(getCArray(), src, w * h * sizeof(Color1));
+}
+
+
+void Image1::copyArray(const Color4* src, int w, int h) {
+ resize(w, h);
+
+ int N = w * h;
+ Color1* dst = data.getCArray();
+
+ // Strip alpha
+ for (int i = 0; i < N; ++i) {
+ dst[i] = Color1(src[i].rgb().average());
+ }
+}
+
+
+void Image1::copyArray(const Color1uint8* src, int w, int h) {
+ resize(w, h);
+ int N = w * h;
+
+ Color1* dst = getCArray();
+ for (int i = 0; i < N; ++i) {
+ dst[i]= Color1(src[i]);
+ }
+}
+
+
+void Image1::copyArray(const Color3* src, int w, int h) {
+ resize(w, h);
+ int N = w * h;
+
+ Color1* dst = getCArray();
+ for (int i = 0; i < N; ++i) {
+ dst[i] = Color1(src[i].average());
+ }
+}
+
+
+/** Saves in any of the formats supported by G3D::GImage. */
+void Image1::save(const std::string& filename, GImage::Format fmt) {
+ GImage im(width(), height(), 1);
+
+ int N = im.width * im.height;
+ Color1uint8* dst = im.pixel1();
+ for (int i = 0; i < N; ++i) {
+ dst[i] = Color1uint8(data[i]);
+ }
+
+ im.save(filename, fmt);
+}
+
+
+const ImageFormat* Image1::format() const {
+ return ImageFormat::L32F();
+}
+
+} // G3D
diff --git a/externals/g3dlite/G3D.lib/source/Image1uint8.cpp b/externals/g3dlite/G3D.lib/source/Image1uint8.cpp
new file mode 100644
index 00000000000..c43e7194d7d
--- /dev/null
+++ b/externals/g3dlite/G3D.lib/source/Image1uint8.cpp
@@ -0,0 +1,212 @@
+/**
+ @file Image1uint8.cpp
+
+ @maintainer Morgan McGuire, matrix@graphics3d.com
+
+ @created 2007-01-31
+ @edited 2008-01-13
+*/
+
+#include "G3D/Image1uint8.h"
+#include "G3D/Image3uint8.h"
+#include "G3D/Image1.h"
+#include "G3D/GImage.h"
+#include "G3D/Color1.h"
+#include "G3D/Color1uint8.h"
+#include "G3D/Color4.h"
+#include "G3D/Color4uint8.h"
+#include "G3D/ImageFormat.h"
+
+namespace G3D {
+
+Image1uint8::Image1uint8(int w, int h, WrapMode wrap) : Map2D<Color1uint8, Color1>(w, h, wrap) {
+ setAll(Color1uint8(0));
+}
+
+
+Image1uint8::Ref Image1uint8::fromImage3uint8(const ReferenceCountedPointer<class Image3uint8>& im) {
+ return fromArray(im->getCArray(), im->width(), im->height(), im->wrapMode());
+}
+
+
+Image1uint8::Ref Image1uint8::fromGImage(const GImage& im, WrapMode wrap) {
+ switch (im.channels) {
+ case 1:
+ return fromArray(im.pixel1(), im.width, im.height, wrap);
+
+ case 3:
+ return fromArray(im.pixel3(), im.width, im.height, wrap);
+
+ case 4:
+ return fromArray(im.pixel4(), im.width, im.height, wrap);
+
+ default:
+ debugAssertM(false, "Input GImage must have 1, 3, or 4 channels.");
+ return NULL;
+ }
+}
+
+
+Image1uint8::Ref Image1uint8::fromImage1(const ReferenceCountedPointer<Image1>& im) {
+ Ref out = createEmpty(static_cast<WrapMode>(im->wrapMode()));
+ out->copyArray(im->getCArray(), im->width(), im->height());
+
+ return out;
+}
+
+
+Image1uint8::Ref Image1uint8::createEmpty(int width, int height, WrapMode wrap) {
+ return new Type(width, height, wrap);
+}
+
+
+Image1uint8::Ref Image1uint8::createEmpty(WrapMode wrap) {
+ return createEmpty(0, 0, wrap);
+}
+
+
+Image1uint8::Ref Image1uint8::fromFile(const std::string& filename, WrapMode wrap, GImage::Format fmt) {
+ Ref out = createEmpty(wrap);
+ out->load(filename);
+ return out;
+}
+
+
+Image1uint8::Ref Image1uint8::fromArray(const class Color3uint8* ptr, int w, int h, WrapMode wrap) {
+ Ref out = createEmpty(wrap);
+ out->copyArray(ptr, w, h);
+ return out;
+}
+
+
+Image1uint8::Ref Image1uint8::fromArray(const class Color1* ptr, int w, int h, WrapMode wrap) {
+ Ref out = createEmpty(wrap);
+ out->copyArray(ptr, w, h);
+ return out;
+}
+
+
+Image1uint8::Ref Image1uint8::fromArray(const class Color1uint8* ptr, int w, int h, WrapMode wrap) {
+ Ref out = createEmpty(wrap);
+ out->copyArray(ptr, w, h);
+ return out;
+}
+
+
+Image1uint8::Ref Image1uint8::fromArray(const class Color3* ptr, int w, int h, WrapMode wrap) {
+ Ref out = createEmpty(wrap);
+ out->copyArray(ptr, w, h);
+ return out;
+}
+
+
+Image1uint8::Ref Image1uint8::fromArray(const class Color4uint8* ptr, int w, int h, WrapMode wrap) {
+ Ref out = createEmpty(wrap);
+ out->copyArray(ptr, w, h);
+ return out;
+}
+
+
+Image1uint8::Ref Image1uint8::fromArray(const class Color4* ptr, int w, int h, WrapMode wrap) {
+ Ref out = createEmpty(wrap);
+ out->copyArray(ptr, w, h);
+ return out;
+}
+
+
+void Image1uint8::load(const std::string& filename, GImage::Format fmt) {
+ copyGImage(GImage(filename, fmt));
+ setChanged(true);
+}
+
+
+void Image1uint8::copyGImage(const GImage& im) {
+ switch (im.channels) {
+ case 1:
+ copyArray(im.pixel1(), im.width, im.height);
+ break;
+
+ case 3:
+ copyArray(im.pixel3(), im.width, im.height);
+ break;
+
+ case 4:
+ copyArray(im.pixel4(), im.width, im.height);
+ break;
+ }
+}
+
+
+void Image1uint8::copyArray(const Color3uint8* src, int w, int h) {
+ resize(w, h);
+ int N = w * h;
+
+ Color1uint8* dst = getCArray();
+ for (int i = 0; i < N; ++i) {
+ dst[i].value = (src[i].r + src[i].g + src[i].b) / 3;
+ }
+}
+
+void Image1uint8::copyArray(const Color3* src, int w, int h) {
+ resize(w, h);
+ int N = w * h;
+
+ Color1uint8* dst = getCArray();
+ for (int i = 0; i < N; ++i) {
+ dst[i] = Color1uint8(Color1(src[i].average()));
+ }
+}
+
+
+void Image1uint8::copyArray(const Color1uint8* ptr, int w, int h) {
+ resize(w, h);
+ System::memcpy(getCArray(), ptr, w * h);
+}
+
+
+void Image1uint8::copyArray(const Color1* src, int w, int h) {
+ resize(w, h);
+ int N = w * h;
+
+ Color1uint8* dst = getCArray();
+ for (int i = 0; i < N; ++i) {
+ dst[i] = Color1uint8(src[i]);
+ }
+}
+
+
+void Image1uint8::copyArray(const Color4uint8* ptr, int w, int h) {
+ resize(w, h);
+ int N = w * h;
+
+ Color1uint8* dst = getCArray();
+ for (int i = 0; i < N; ++i) {
+ dst[i].value = (ptr[i].r + ptr[i].g + ptr[i].b) / 3;
+ }
+}
+
+
+void Image1uint8::copyArray(const Color4* src, int w, int h) {
+ resize(w, h);
+ int N = w * h;
+
+ Color1uint8* dst = getCArray();
+ for (int i = 0; i < N; ++i) {
+ dst[i] = Color1uint8(Color1(src[i].rgb().average()));
+ }
+}
+
+
+/** Saves in any of the formats supported by G3D::GImage. */
+void Image1uint8::save(const std::string& filename, GImage::Format fmt) {
+ GImage im(width(), height(), 1);
+ System::memcpy(im.byte(), getCArray(), width() * height());
+ im.save(filename, fmt);
+}
+
+
+const ImageFormat* Image1uint8::format() const {
+ return ImageFormat::L8();
+}
+
+} // G3D
diff --git a/externals/g3dlite/G3D.lib/source/Image3.cpp b/externals/g3dlite/G3D.lib/source/Image3.cpp
new file mode 100644
index 00000000000..aa2ac6098dc
--- /dev/null
+++ b/externals/g3dlite/G3D.lib/source/Image3.cpp
@@ -0,0 +1,224 @@
+/**
+ @file Image3.cpp
+
+ @maintainer Morgan McGuire, matrix@graphics3d.com
+
+ @created 2007-01-31
+ @edited 2007-01-31
+*/
+
+
+#include "G3D/Image3.h"
+#include "G3D/Image3uint8.h"
+#include "G3D/GImage.h"
+#include "G3D/Color4.h"
+#include "G3D/Color4uint8.h"
+#include "G3D/Color1.h"
+#include "G3D/Color1uint8.h"
+#include "G3D/ImageFormat.h"
+
+namespace G3D {
+
+Image3::Image3(int w, int h, WrapMode wrap) : Map2D<Color3, Color3>(w, h, wrap) {
+ setAll(Color3::black());
+}
+
+
+Image3::Ref Image3::fromGImage(const GImage& im, WrapMode wrap) {
+ switch (im.channels) {
+ case 1:
+ return fromArray(im.pixel1(), im.width, im.height, wrap);
+
+ case 3:
+ return fromArray(im.pixel3(), im.width, im.height, wrap);
+
+ case 4:
+ return fromArray(im.pixel4(), im.width, im.height, wrap);
+
+ default:
+ debugAssertM(false, "Input GImage must have 1, 3, or 4 channels.");
+ return NULL;
+ }
+}
+
+
+Image3::Ref Image3::fromImage3uint8(const ReferenceCountedPointer<Image3uint8>& im) {
+ Ref out = createEmpty(im->wrapMode());
+ out->resize(im->width(), im->height());
+
+ int N = im->width() * im->height();
+ const Color3uint8* src = reinterpret_cast<Color3uint8*>(im->getCArray());
+ for (int i = 0; i < N; ++i) {
+ out->data[i] = Color3(src[i]);
+ }
+
+ return out;
+}
+
+
+Image3::Ref Image3::createEmpty(int width, int height, WrapMode wrap) {
+ return new Image3(width, height, wrap);
+}
+
+
+Image3::Ref Image3::createEmpty(WrapMode wrap) {
+ return createEmpty(0, 0, wrap);
+}
+
+
+Image3::Ref Image3::fromFile(const std::string& filename, WrapMode wrap, GImage::Format fmt) {
+ Ref out = createEmpty(wrap);
+ out->load(filename);
+ return out;
+}
+
+
+void Image3::load(const std::string& filename, GImage::Format fmt) {
+ copyGImage(GImage(filename, fmt));
+ setChanged(true);
+}
+
+
+Image3::Ref Image3::fromArray(const class Color3uint8* ptr, int w, int h, WrapMode wrap) {
+ Ref out = createEmpty(wrap);
+ out->copyArray(ptr, w, h);
+ return out;
+}
+
+
+Image3::Ref Image3::fromArray(const class Color1* ptr, int w, int h, WrapMode wrap) {
+ Ref out = createEmpty(wrap);
+ out->copyArray(ptr, w, h);
+ return out;
+}
+
+
+Image3::Ref Image3::fromArray(const class Color1uint8* ptr, int w, int h, WrapMode wrap) {
+ Ref out = createEmpty(wrap);
+ out->copyArray(ptr, w, h);
+ return out;
+}
+
+
+Image3::Ref Image3::fromArray(const class Color3* ptr, int w, int h, WrapMode wrap) {
+ Ref out = createEmpty(wrap);
+ out->copyArray(ptr, w, h);
+ return out;
+}
+
+
+Image3::Ref Image3::fromArray(const class Color4uint8* ptr, int w, int h, WrapMode wrap) {
+ Ref out = createEmpty(wrap);
+ out->copyArray(ptr, w, h);
+ return out;
+}
+
+
+Image3::Ref Image3::fromArray(const class Color4* ptr, int w, int h, WrapMode wrap) {
+ Ref out = createEmpty(wrap);
+ out->copyArray(ptr, w, h);
+ return out;
+}
+
+void Image3::copyGImage(const GImage& im) {
+ switch (im.channels) {
+ case 1:
+ copyArray(im.pixel1(), im.width, im.height);
+ break;
+
+ case 3:
+ copyArray(im.pixel3(), im.width, im.height);
+ break;
+
+ case 4:
+ copyArray(im.pixel4(), im.width, im.height);
+ break;
+ }
+}
+
+
+void Image3::copyArray(const Color3uint8* src, int w, int h) {
+ resize(w, h);
+
+ int N = w * h;
+ Color3* dst = data.getCArray();
+ // Convert int8 -> float
+ for (int i = 0; i < N; ++i) {
+ dst[i] = Color3(src[i]);
+ }
+}
+
+
+void Image3::copyArray(const Color4uint8* src, int w, int h) {
+ resize(w, h);
+
+ int N = w * h;
+ Color3* dst = data.getCArray();
+
+ // Strip alpha and convert
+ for (int i = 0; i < N; ++i) {
+ dst[i] = Color3(src[i].rgb());
+ }
+}
+
+
+void Image3::copyArray(const Color3* src, int w, int h) {
+ resize(w, h);
+ System::memcpy(getCArray(), src, w * h * sizeof(Color3));
+}
+
+
+void Image3::copyArray(const Color4* src, int w, int h) {
+ resize(w, h);
+
+ int N = w * h;
+ Color3* dst = data.getCArray();
+
+ // Strip alpha
+ for (int i = 0; i < N; ++i) {
+ dst[i] = src[i].rgb();
+ }
+}
+
+
+void Image3::copyArray(const Color1uint8* src, int w, int h) {
+ resize(w, h);
+ int N = w * h;
+
+ Color3* dst = getCArray();
+ for (int i = 0; i < N; ++i) {
+ dst[i].r = dst[i].g = dst[i].b = Color1(src[i]).value;
+ }
+}
+
+
+void Image3::copyArray(const Color1* src, int w, int h) {
+ resize(w, h);
+ int N = w * h;
+
+ Color3* dst = getCArray();
+ for (int i = 0; i < N; ++i) {
+ dst[i].r = dst[i].g = dst[i].b = src[i].value;
+ }
+}
+
+
+/** Saves in any of the formats supported by G3D::GImage. */
+void Image3::save(const std::string& filename, GImage::Format fmt) {
+ GImage im(width(), height(), 3);
+
+ int N = im.width * im.height;
+ Color3uint8* dst = im.pixel3();
+ for (int i = 0; i < N; ++i) {
+ dst[i] = Color3uint8(data[i]);
+ }
+
+ im.save(filename, fmt);
+}
+
+
+const ImageFormat* Image3::format() const {
+ return ImageFormat::RGB32F();
+}
+
+} // G3D
diff --git a/externals/g3dlite/G3D.lib/source/Image3uint8.cpp b/externals/g3dlite/G3D.lib/source/Image3uint8.cpp
new file mode 100644
index 00000000000..2de32b6009e
--- /dev/null
+++ b/externals/g3dlite/G3D.lib/source/Image3uint8.cpp
@@ -0,0 +1,225 @@
+/**
+ @file Image3uint8.cpp
+
+ @maintainer Morgan McGuire, matrix@graphics3d.com
+
+ @created 2007-01-31
+ @edited 2008-01-08
+*/
+
+#include "G3D/Image1uint8.h"
+#include "G3D/Image3uint8.h"
+#include "G3D/Image3.h"
+#include "G3D/GImage.h"
+#include "G3D/Color1.h"
+#include "G3D/Color1uint8.h"
+#include "G3D/Color4.h"
+#include "G3D/Color4uint8.h"
+#include "G3D/ImageFormat.h"
+
+namespace G3D {
+
+Image3uint8::Ref Image3uint8::fromImage1uint8(const ReferenceCountedPointer<class Image1uint8>& im) {
+ return fromArray(im->getCArray(), im->width(), im->height(), im->wrapMode());
+}
+
+
+Image3uint8::Image3uint8(int w, int h, WrapMode wrap) : Map2D<Color3uint8>(w, h, wrap) {
+ setAll(Color3::black());
+}
+
+
+Image3uint8::Ref Image3uint8::fromGImage(const GImage& im, WrapMode wrap) {
+ switch (im.channels) {
+ case 1:
+ return fromArray(im.pixel1(), im.width, im.height, wrap);
+
+ case 3:
+ return fromArray(im.pixel3(), im.width, im.height, wrap);
+
+ case 4:
+ return fromArray(im.pixel4(), im.width, im.height, wrap);
+
+ default:
+ debugAssertM(false, "Input GImage must have 1, 3, or 4 channels.");
+ return NULL;
+ }
+}
+
+
+Image3uint8::Ref Image3uint8::fromImage3(const ReferenceCountedPointer<Image3>& im) {
+ Ref out = createEmpty(static_cast<WrapMode>(im->wrapMode()));
+ out->copyArray(im->getCArray(), im->width(), im->height());
+
+ return out;
+}
+
+
+Image3uint8::Ref Image3uint8::createEmpty(int width, int height, WrapMode wrap) {
+ return new Type(width, height, wrap);
+}
+
+
+Image3uint8::Ref Image3uint8::createEmpty(WrapMode wrap) {
+ return createEmpty(0, 0, wrap);
+}
+
+
+Image3uint8::Ref Image3uint8::fromFile(const std::string& filename, WrapMode wrap, GImage::Format fmt) {
+ Ref out = createEmpty(wrap);
+ out->load(filename);
+ return out;
+}
+
+
+Image3uint8::Ref Image3uint8::fromArray(const class Color3uint8* ptr, int w, int h, WrapMode wrap) {
+ Ref out = createEmpty(wrap);
+ out->copyArray(ptr, w, h);
+ return out;
+}
+
+
+Image3uint8::Ref Image3uint8::fromArray(const class Color1* ptr, int w, int h, WrapMode wrap) {
+ Ref out = createEmpty(wrap);
+ out->copyArray(ptr, w, h);
+ return out;
+}
+
+
+Image3uint8::Ref Image3uint8::fromArray(const class Color1uint8* ptr, int w, int h, WrapMode wrap) {
+ Ref out = createEmpty(wrap);
+ out->copyArray(ptr, w, h);
+ return out;
+}
+
+
+Image3uint8::Ref Image3uint8::fromArray(const class Color3* ptr, int w, int h, WrapMode wrap) {
+ Ref out = createEmpty(wrap);
+ out->copyArray(ptr, w, h);
+ return out;
+}
+
+
+Image3uint8::Ref Image3uint8::fromArray(const class Color4uint8* ptr, int w, int h, WrapMode wrap) {
+ Ref out = createEmpty(wrap);
+ out->copyArray(ptr, w, h);
+ return out;
+}
+
+
+Image3uint8::Ref Image3uint8::fromArray(const class Color4* ptr, int w, int h, WrapMode wrap) {
+ Ref out = createEmpty(wrap);
+ out->copyArray(ptr, w, h);
+ return out;
+}
+
+
+void Image3uint8::load(const std::string& filename, GImage::Format fmt) {
+ copyGImage(GImage(filename, fmt));
+ setChanged(true);
+}
+
+
+void Image3uint8::copyGImage(const GImage& im) {
+ switch (im.channels) {
+ case 1:
+ copyArray(im.pixel1(), im.width, im.height);
+ break;
+
+ case 3:
+ copyArray(im.pixel3(), im.width, im.height);
+ break;
+
+ case 4:
+ copyArray(im.pixel4(), im.width, im.height);
+ break;
+ }
+}
+
+
+void Image3uint8::copyArray(const Color1uint8* src, int w, int h) {
+ resize(w, h);
+ int N = w * h;
+
+ Color3uint8* dst = getCArray();
+ for (int i = 0; i < N; ++i) {
+ dst[i].r = dst[i].g = dst[i].b = src[i].value;
+ }
+}
+
+void Image3uint8::copyArray(const Color1* src, int w, int h) {
+ resize(w, h);
+ int N = w * h;
+
+ Color3uint8* dst = getCArray();
+ for (int i = 0; i < N; ++i) {
+ dst[i].r = dst[i].g = dst[i].b = Color1uint8(src[i]).value;
+ }
+}
+
+
+void Image3uint8::copyArray(const Color3uint8* ptr, int w, int h) {
+ resize(w, h);
+ System::memcpy(getCArray(), ptr, w * h * 3);
+}
+
+
+void Image3uint8::copyArray(const Color3* src, int w, int h) {
+ resize(w, h);
+ int N = w * h;
+
+ Color3uint8* dst = getCArray();
+ for (int i = 0; i < N; ++i) {
+ dst[i] = Color3uint8(src[i]);
+ }
+}
+
+
+void Image3uint8::copyArray(const Color4uint8* ptr, int w, int h) {
+ resize(w, h);
+
+ // Copy 3/4 bytes
+ GImage::RGBAtoRGB((const uint8*)ptr, (uint8*)getCArray(), w * h);
+}
+
+
+void Image3uint8::copyArray(const Color4* src, int w, int h) {
+ resize(w, h);
+ int N = w * h;
+
+ Color3uint8* dst = getCArray();
+ for (int i = 0; i < N; ++i) {
+ dst[i] = Color3uint8(src[i].rgb());
+ }
+}
+
+
+/** Saves in any of the formats supported by G3D::GImage. */
+void Image3uint8::save(const std::string& filename, GImage::Format fmt) {
+ GImage im(width(), height(), 3);
+ System::memcpy(im.byte(), getCArray(), width() * height() * 3);
+ im.save(filename, fmt);
+}
+
+
+ReferenceCountedPointer<class Image1uint8> Image3uint8::getChannel(int c) const {
+ debugAssert(c >= 0 && c <= 2);
+
+ Image1uint8Ref dst = Image1uint8::createEmpty(width(), height(), wrapMode());
+ const Color3uint8* srcArray = getCArray();
+ Color1uint8* dstArray = dst->getCArray();
+
+ const int N = width() * height();
+ for (int i = 0; i < N; ++i) {
+ dstArray[i] = Color1uint8(srcArray[i][c]);
+ }
+
+ return dst;
+}
+
+
+const ImageFormat* Image3uint8::format() const {
+ return ImageFormat::RGB8();
+}
+
+} // G3D
diff --git a/externals/g3dlite/G3D.lib/source/Image4.cpp b/externals/g3dlite/G3D.lib/source/Image4.cpp
new file mode 100644
index 00000000000..84a1cd650b6
--- /dev/null
+++ b/externals/g3dlite/G3D.lib/source/Image4.cpp
@@ -0,0 +1,226 @@
+/**
+ @file Image4.cpp
+
+ @maintainer Morgan McGuire, matrix@graphics3d.com
+
+ @created 2007-01-31
+ @edited 2008-07-27
+*/
+
+
+#include "G3D/Image4.h"
+#include "G3D/Image4uint8.h"
+#include "G3D/GImage.h"
+#include "G3D/Color3.h"
+#include "G3D/Color3uint8.h"
+#include "G3D/Color1.h"
+#include "G3D/Color1uint8.h"
+#include "G3D/ImageFormat.h"
+
+namespace G3D {
+
+Image4::Image4(int w, int h, WrapMode wrap) : Map2D<Color4, Color4>(w, h, wrap) {
+ setAll(Color4::zero());
+}
+
+
+Image4::Ref Image4::fromGImage(const GImage& im, WrapMode wrap) {
+ switch (im.channels) {
+ case 1:
+ return fromArray(im.pixel1(), im.width, im.height, wrap);
+
+ case 3:
+ return fromArray(im.pixel3(), im.width, im.height, wrap);
+
+ case 4:
+ return fromArray(im.pixel4(), im.width, im.height, wrap);
+
+ default:
+ debugAssertM(false, "Input GImage must have 1, 3, or 4 channels.");
+ return NULL;
+ }
+}
+
+
+Image4::Ref Image4::fromImage4uint8(const ReferenceCountedPointer<Image4uint8>& im) {
+ Ref out = createEmpty(static_cast<WrapMode>(im->wrapMode()));
+ out->resize(im->width(), im->height());
+
+ int N = im->width() * im->height();
+ const Color4uint8* src = reinterpret_cast<Color4uint8*>(im->getCArray());
+ for (int i = 0; i < N; ++i) {
+ out->data[i] = Color4(src[i]);
+ }
+
+ return out;
+}
+
+
+Image4::Ref Image4::createEmpty(int width, int height, WrapMode wrap) {
+ return new Type(width, height, WrapMode::ERROR);
+}
+
+
+Image4::Ref Image4::createEmpty(WrapMode wrap) {
+ return createEmpty(0, 0, wrap);
+}
+
+
+Image4::Ref Image4::fromFile(const std::string& filename, WrapMode wrap, GImage::Format fmt) {
+ Ref out = createEmpty(wrap);
+ out->load(filename);
+ return out;
+}
+
+
+void Image4::load(const std::string& filename, GImage::Format fmt) {
+ copyGImage(GImage(filename, fmt));
+ setChanged(true);
+}
+
+
+Image4::Ref Image4::fromArray(const class Color3uint8* ptr, int w, int h, WrapMode wrap) {
+ Ref out = createEmpty(wrap);
+ out->copyArray(ptr, w, h);
+ return out;
+}
+
+
+Image4::Ref Image4::fromArray(const class Color1* ptr, int w, int h, WrapMode wrap) {
+ Ref out = createEmpty(wrap);
+ out->copyArray(ptr, w, h);
+ return out;
+}
+
+
+Image4::Ref Image4::fromArray(const class Color1uint8* ptr, int w, int h, WrapMode wrap) {
+ Ref out = createEmpty(wrap);
+ out->copyArray(ptr, w, h);
+ return out;
+}
+
+
+Image4::Ref Image4::fromArray(const class Color3* ptr, int w, int h, WrapMode wrap) {
+ Ref out = createEmpty(wrap);
+ out->copyArray(ptr, w, h);
+ return out;
+}
+
+
+Image4::Ref Image4::fromArray(const class Color4uint8* ptr, int w, int h, WrapMode wrap) {
+ Ref out = createEmpty(wrap);
+ out->copyArray(ptr, w, h);
+ return out;
+}
+
+
+Image4::Ref Image4::fromArray(const class Color4* ptr, int w, int h, WrapMode wrap) {
+ Ref out = createEmpty(wrap);
+ out->copyArray(ptr, w, h);
+ return out;
+}
+
+
+void Image4::copyGImage(const GImage& im) {
+ switch (im.channels) {
+ case 1:
+ copyArray(im.pixel1(), im.width, im.height);
+ break;
+
+ case 3:
+ copyArray(im.pixel3(), im.width, im.height);
+ break;
+
+ case 4:
+ copyArray(im.pixel4(), im.width, im.height);
+ break;
+ }
+}
+
+
+void Image4::copyArray(const Color4uint8* src, int w, int h) {
+ resize(w, h);
+
+ int N = w * h;
+ Color4* dst = data.getCArray();
+ // Convert int8 -> float
+ for (int i = 0; i < N; ++i) {
+ dst[i] = Color4(src[i]);
+ }
+}
+
+
+void Image4::copyArray(const Color3uint8* src, int w, int h) {
+ resize(w, h);
+
+ int N = w * h;
+ Color4* dst = data.getCArray();
+
+ // Add alpha and convert
+ for (int i = 0; i < N; ++i) {
+ dst[i] = Color4(Color3(src[i]), 1.0f);
+ }
+}
+
+
+void Image4::copyArray(const Color4* src, int w, int h) {
+ resize(w, h);
+ System::memcpy(getCArray(), src, w * h * sizeof(Color4));
+}
+
+
+void Image4::copyArray(const Color3* src, int w, int h) {
+ resize(w, h);
+
+ int N = w * h;
+ Color4* dst = data.getCArray();
+
+ // Add alpha
+ for (int i = 0; i < N; ++i) {
+ dst[i] = Color4(src[i], 1.0f);
+ }
+}
+
+
+void Image4::copyArray(const Color1uint8* src, int w, int h) {
+ resize(w, h);
+ int N = w * h;
+
+ Color4* dst = getCArray();
+ for (int i = 0; i < N; ++i) {
+ dst[i].r = dst[i].g = dst[i].b = Color1(src[i]).value;
+ dst[i].a = 1.0f;
+ }
+}
+
+
+void Image4::copyArray(const Color1* src, int w, int h) {
+ resize(w, h);
+ int N = w * h;
+
+ Color4* dst = getCArray();
+ for (int i = 0; i < N; ++i) {
+ dst[i].r = dst[i].g = dst[i].b = src[i].value;
+ dst[i].a = 1.0f;
+ }
+}
+
+
+/** Saves in any of the formats supported by G3D::GImage. */
+void Image4::save(const std::string& filename, GImage::Format fmt) {
+ GImage im(width(), height(), 4);
+
+ int N = im.width * im.height;
+ Color4uint8* dst = im.pixel4();
+ for (int i = 0; i < N; ++i) {
+ dst[i] = Color4uint8(data[i]);
+ }
+
+ im.save(filename, fmt);
+}
+
+const ImageFormat* Image4::format() const {
+ return ImageFormat::RGBA32F();
+}
+
+} // G3D
diff --git a/externals/g3dlite/G3D.lib/source/Image4uint8.cpp b/externals/g3dlite/G3D.lib/source/Image4uint8.cpp
new file mode 100644
index 00000000000..dee1cba14ba
--- /dev/null
+++ b/externals/g3dlite/G3D.lib/source/Image4uint8.cpp
@@ -0,0 +1,222 @@
+/**
+ @file Image4uint8.cpp
+
+ @maintainer Morgan McGuire, matrix@graphics3d.com
+
+ @created 2007-01-31
+ @edited 2008-07-31
+*/
+
+#include "G3D/Image4uint8.h"
+#include "G3D/Image4.h"
+#include "G3D/Image3uint8.h"
+#include "G3D/Image3.h"
+#include "G3D/GImage.h"
+#include "G3D/Color1.h"
+#include "G3D/Color1uint8.h"
+#include "G3D/Color4.h"
+#include "G3D/Color4uint8.h"
+#include "G3D/ImageFormat.h"
+
+namespace G3D {
+
+Image4uint8::Image4uint8(int w, int h, WrapMode wrap) : Map2D<Color4uint8, Color4>(w, h, wrap) {
+ setAll(Color4::zero());
+}
+
+
+Image4uint8::Ref Image4uint8::fromGImage(const GImage& im, WrapMode wrap) {
+ switch (im.channels) {
+ case 1:
+ return fromArray(im.pixel1(), im.width, im.height, wrap);
+
+ case 3:
+ return fromArray(im.pixel3(), im.width, im.height, wrap);
+
+ case 4:
+ return fromArray(im.pixel4(), im.width, im.height, wrap);
+
+ default:
+ debugAssertM(false, "Input GImage must have 1, 3, or 4 channels.");
+ return NULL;
+ }
+}
+
+
+Image4uint8::Ref Image4uint8::fromImage4(const ReferenceCountedPointer<Image4>& im) {
+ Ref out = createEmpty(static_cast<WrapMode>(im->wrapMode()));
+ out->copyArray(im->getCArray(), im->width(), im->height());
+
+ return out;
+}
+
+
+Image4uint8::Ref Image4uint8::createEmpty(int width, int height, WrapMode wrap) {
+ return new Type(width, height, wrap);
+}
+
+
+Image4uint8::Ref Image4uint8::createEmpty(WrapMode wrap) {
+ return createEmpty(0, 0, wrap);
+}
+
+
+Image4uint8::Ref Image4uint8::fromFile(const std::string& filename, WrapMode wrap, GImage::Format fmt) {
+ Ref out = createEmpty(wrap);
+ out->load(filename);
+ return out;
+}
+
+
+Image4uint8::Ref Image4uint8::fromArray(const class Color3uint8* ptr, int w, int h, WrapMode wrap) {
+ Ref out = createEmpty(wrap);
+ out->copyArray(ptr, w, h);
+ return out;
+}
+
+
+Image4uint8::Ref Image4uint8::fromArray(const class Color1* ptr, int w, int h, WrapMode wrap) {
+ Ref out = createEmpty(wrap);
+ out->copyArray(ptr, w, h);
+ return out;
+}
+
+
+Image4uint8::Ref Image4uint8::fromArray(const class Color1uint8* ptr, int w, int h, WrapMode wrap) {
+ Ref out = createEmpty(wrap);
+ out->copyArray(ptr, w, h);
+ return out;
+}
+
+
+Image4uint8::Ref Image4uint8::fromArray(const class Color3* ptr, int w, int h, WrapMode wrap) {
+ Ref out = createEmpty(wrap);
+ out->copyArray(ptr, w, h);
+ return out;
+}
+
+
+Image4uint8::Ref Image4uint8::fromArray(const class Color4uint8* ptr, int w, int h, WrapMode wrap) {
+ Ref out = createEmpty(wrap);
+ out->copyArray(ptr, w, h);
+ return out;
+}
+
+
+Image4uint8::Ref Image4uint8::fromArray(const class Color4* ptr, int w, int h, WrapMode wrap) {
+ Ref out = createEmpty(wrap);
+ out->copyArray(ptr, w, h);
+ return out;
+}
+
+
+void Image4uint8::load(const std::string& filename, GImage::Format fmt) {
+ copyGImage(GImage(filename, fmt));
+ setChanged(true);
+}
+
+
+void Image4uint8::copyGImage(const GImage& im) {
+ switch (im.channels) {
+ case 1:
+ copyArray(im.pixel1(), im.width, im.height);
+ break;
+
+ case 3:
+ copyArray(im.pixel3(), im.width, im.height);
+ break;
+
+ case 4:
+ copyArray(im.pixel4(), im.width, im.height);
+ break;
+ }
+}
+
+
+void Image4uint8::copyArray(const Color1uint8* src, int w, int h) {
+ resize(w, h);
+ int N = w * h;
+
+ Color4uint8* dst = getCArray();
+ for (int i = 0; i < N; ++i) {
+ dst[i].r = dst[i].g = dst[i].b = src[i].value;
+ dst[i].a = 255;
+ }
+}
+
+void Image4uint8::copyArray(const Color1* src, int w, int h) {
+ resize(w, h);
+ int N = w * h;
+
+ Color4uint8* dst = getCArray();
+ for (int i = 0; i < N; ++i) {
+ dst[i].r = dst[i].g = dst[i].b = Color1uint8(src[i]).value;
+ dst[i].a = 255;
+ }
+}
+
+
+void Image4uint8::copyArray(const Color4uint8* ptr, int w, int h) {
+ resize(w, h);
+ System::memcpy(getCArray(), ptr, w * h * 4);
+}
+
+
+void Image4uint8::copyArray(const Color4* src, int w, int h) {
+ resize(w, h);
+ int N = w * h;
+
+ Color4uint8* dst = getCArray();
+ for (int i = 0; i < N; ++i) {
+ dst[i] = Color4uint8(src[i]);
+ }
+}
+
+
+void Image4uint8::copyArray(const Color3uint8* ptr, int w, int h) {
+ resize(w, h);
+
+ GImage::RGBtoRGBA((const uint8*)ptr, (uint8*)getCArray(), w * h);
+}
+
+
+void Image4uint8::copyArray(const Color3* src, int w, int h) {
+ resize(w, h);
+ int N = w * h;
+
+ Color4uint8* dst = getCArray();
+ for (int i = 0; i < N; ++i) {
+ dst[i] = Color4uint8(Color4(src[i], 1.0f));
+ }
+}
+
+
+/** Saves in any of the formats supported by G3D::GImage. */
+void Image4uint8::save(const std::string& filename, GImage::Format fmt) {
+ GImage im(width(), height(), 4);
+ System::memcpy(im.byte(), getCArray(), width() * height() * 4);
+ im.save(filename, fmt);
+}
+
+
+ReferenceCountedPointer<class Image1uint8> Image4uint8::getChannel(int c) const {
+ debugAssert(c >= 0 && c <= 3);
+
+ Image1uint8Ref dst = Image1uint8::createEmpty(width(), height(), wrapMode());
+ const Color4uint8* srcArray = getCArray();
+ Color1uint8* dstArray = dst->getCArray();
+
+ const int N = width() * height();
+ for (int i = 0; i < N; ++i) {
+ dstArray[i] = Color1uint8(srcArray[i][c]);
+ }
+
+ return dst;
+}
+
+
+const ImageFormat* Image4uint8::format() const {
+ return ImageFormat::RGBA8();
+}
+
+} // G3D
diff --git a/externals/g3dlite/G3D.lib/source/ImageFormat.cpp b/externals/g3dlite/G3D.lib/source/ImageFormat.cpp
new file mode 100644
index 00000000000..3cbf6ad711e
--- /dev/null
+++ b/externals/g3dlite/G3D.lib/source/ImageFormat.cpp
@@ -0,0 +1,440 @@
+/**
+ @file ImageFormat.cpp
+
+ @maintainer Morgan McGuire, morgan@graphics3d.com
+
+ @created 2003-05-23
+ @edited 2006-01-11
+ */
+
+#include "../../GLG3D.lib/include/GLG3D/glheaders.h"
+#include "../../GLG3D.lib/include/GLG3D/glcalls.h"
+#include "G3D/ImageFormat.h"
+
+namespace G3D {
+
+ImageFormat::ImageFormat(
+ int _numComponents,
+ bool _compressed,
+ int _glFormat,
+ int _glBaseFormat,
+ int _luminanceBits,
+ int _alphaBits,
+ int _redBits,
+ int _greenBits,
+ int _blueBits,
+ int _depthBits,
+ int _stencilBits,
+ int _hardwareBitsPerTexel,
+ int _packedBitsPerTexel,
+ int glDataFormat,
+ bool _opaque,
+ bool _floatingPoint,
+ Code _code,
+ ColorSpace _colorSpace,
+ BayerPattern _bayerPattern) :
+
+ numComponents(_numComponents),
+ compressed(_compressed),
+ code(_code),
+ colorSpace(_colorSpace),
+ bayerPattern(_bayerPattern),
+ openGLFormat(_glFormat),
+ openGLBaseFormat(_glBaseFormat),
+ luminanceBits(_luminanceBits),
+ alphaBits(_alphaBits),
+ redBits(_redBits),
+ greenBits(_greenBits),
+ blueBits(_blueBits),
+ stencilBits(_stencilBits),
+ depthBits(_depthBits),
+ cpuBitsPerPixel(_packedBitsPerTexel),
+ packedBitsPerTexel(_packedBitsPerTexel),
+ openGLBitsPerPixel(_hardwareBitsPerTexel),
+ hardwareBitsPerTexel(_hardwareBitsPerTexel),
+ openGLDataFormat(glDataFormat),
+ opaque(_opaque),
+ floatingPoint(_floatingPoint) {
+
+ debugAssert(_packedBitsPerTexel <= _hardwareBitsPerTexel);
+}
+
+const ImageFormat* ImageFormat::depth(int depthBits) {
+
+ switch (depthBits) {
+ case 16:
+ return DEPTH16();
+
+ case 24:
+ return DEPTH24();
+
+ case 32:
+ return DEPTH32();
+
+ default:
+ debugAssertM(false, "Depth must be 16, 24, or 32.");
+ return DEPTH32();
+ }
+}
+
+
+const ImageFormat* ImageFormat::stencil(int bits) {
+ switch (bits) {
+ case 1:
+ return STENCIL1();
+
+ case 4:
+ return STENCIL4();
+
+ case 8:
+ return STENCIL8();
+
+ case 16:
+ return STENCIL16();
+
+ default:
+ debugAssertM(false, "Stencil must be 1, 4, 8 or 16.");
+ return STENCIL16();
+ }
+}
+
+
+std::string ImageFormat::name() const {
+
+ static const std::string nameArray[] =
+ {
+ "L8",
+ "L16",
+ "L16F",
+ "L32F",
+
+ "A8",
+ "A16",
+ "A16F",
+ "A32F",
+
+ "LA4",
+ "LA8",
+ "LA16",
+ "LA16F",
+ "LA32F",
+
+ "RGB5",
+ "RGB5A1",
+ "RGB8",
+ "RGB10",
+ "RGB10A2",
+ "RGB16",
+ "RGB16F",
+ "RGB32F",
+
+ "ARGB8",
+ "BGR8",
+
+ "RGBA8",
+ "RGBA16",
+ "RGBA16F",
+ "RGBA32F",
+
+ "BAYER_RGGB8",
+ "BAYER_GRBG8",
+ "BAYER_GBRG8",
+ "BAYER_BGGR8",
+ "BAYER_RGGB32F",
+ "BAYER_GRBG32F",
+ "BAYER_GBRG32F",
+ "BAYER_BGGR32F",
+
+ "HSV8",
+ "HSV32F",
+
+ "YUV420_PLANAR",
+ "YUV422",
+ "YUV444",
+
+ "RGB_DXT1",
+ "RGBA_DXT1",
+ "RGBA_DXT3",
+ "RGBA_DXT5",
+
+ "DEPTH16",
+ "DEPTH24",
+ "DEPTH32",
+ "DEPTH32F",
+
+ "STENCIL1",
+ "STENCIL4",
+ "STENCIL8",
+ "STENCIL16",
+
+ "DEPTH24_STENCIL8"
+ };
+
+ debugAssert(code < CODE_NUM);
+ return nameArray[code];
+}
+
+
+const ImageFormat* ImageFormat::fromCode(ImageFormat::Code code) {
+ switch (code) {
+ case ImageFormat::CODE_L8:
+ return ImageFormat::L8();
+ break;
+ case ImageFormat::CODE_L16:
+ return ImageFormat::L16();
+ break;
+ case ImageFormat::CODE_L16F:
+ return ImageFormat::L16F();
+ break;
+ case ImageFormat::CODE_L32F:
+ return ImageFormat::L32F();
+ break;
+
+ case ImageFormat::CODE_A8:
+ return ImageFormat::A8();
+ break;
+ case ImageFormat::CODE_A16:
+ return ImageFormat::A16();
+ break;
+ case ImageFormat::CODE_A16F:
+ return ImageFormat::A16F();
+ break;
+ case ImageFormat::CODE_A32F:
+ return ImageFormat::A32F();
+ break;
+
+ case ImageFormat::CODE_LA4:
+ return ImageFormat::LA4();
+ break;
+ case ImageFormat::CODE_LA8:
+ return ImageFormat::LA8();
+ break;
+ case ImageFormat::CODE_LA16:
+ return ImageFormat::LA16();
+ break;
+ case ImageFormat::CODE_LA16F:
+ return ImageFormat::LA16F();
+ break;
+ case ImageFormat::CODE_LA32F:
+ return ImageFormat::LA32F();
+ break;
+
+ case ImageFormat::CODE_RGB5:
+ return ImageFormat::RGB5();
+ break;
+ case ImageFormat::CODE_RGB5A1:
+ return ImageFormat::RGB5A1();
+ break;
+ case ImageFormat::CODE_RGB8:
+ return ImageFormat::RGB8();
+ break;
+ case ImageFormat::CODE_RGB10:
+ return ImageFormat::RGB10();
+ break;
+ case ImageFormat::CODE_RGB10A2:
+ return ImageFormat::RGB10A2();
+ break;
+ case ImageFormat::CODE_RGB16:
+ return ImageFormat::RGB16();
+ break;
+ case ImageFormat::CODE_RGB16F:
+ return ImageFormat::RGB16F();
+ break;
+ case ImageFormat::CODE_RGB32F:
+ return ImageFormat::RGB32F();
+ break;
+
+ case ImageFormat::CODE_ARGB8:
+ return NULL;
+
+ case ImageFormat::CODE_BGR8:
+ return NULL;
+
+ case ImageFormat::CODE_RGBA8:
+ return ImageFormat::RGBA8();
+ break;
+ case ImageFormat::CODE_RGBA16:
+ return ImageFormat::RGBA16();
+ break;
+ case ImageFormat::CODE_RGBA16F:
+ return ImageFormat::RGBA16F();
+ break;
+ case ImageFormat::CODE_RGBA32F:
+ return ImageFormat::RGBA32F();
+ break;
+
+ case ImageFormat::CODE_BAYER_RGGB8:
+ case ImageFormat::CODE_BAYER_GRBG8:
+ case ImageFormat::CODE_BAYER_GBRG8:
+ case ImageFormat::CODE_BAYER_BGGR8:
+ case ImageFormat::CODE_BAYER_RGGB32F:
+ case ImageFormat::CODE_BAYER_GRBG32F:
+ case ImageFormat::CODE_BAYER_GBRG32F:
+ case ImageFormat::CODE_BAYER_BGGR32F:
+
+ case ImageFormat::CODE_HSV8:
+ case ImageFormat::CODE_HSV32F:
+
+ case ImageFormat::CODE_RGB_DXT1:
+ return ImageFormat::RGB_DXT1();
+ break;
+ case ImageFormat::CODE_RGBA_DXT1:
+ return ImageFormat::RGBA_DXT1();
+ break;
+ case ImageFormat::CODE_RGBA_DXT3:
+ return ImageFormat::RGBA_DXT3();
+ break;
+ case ImageFormat::CODE_RGBA_DXT5:
+ return ImageFormat::RGBA_DXT5();
+ break;
+
+ case ImageFormat::CODE_DEPTH16:
+ return ImageFormat::DEPTH16();
+ break;
+ case ImageFormat::CODE_DEPTH24:
+ return ImageFormat::DEPTH24();
+ break;
+ case ImageFormat::CODE_DEPTH32:
+ return ImageFormat::DEPTH32();
+ break;
+ case ImageFormat::CODE_DEPTH32F:
+ return ImageFormat::DEPTH32F();
+ break;
+
+ case ImageFormat::CODE_STENCIL1:
+ return ImageFormat::STENCIL1();
+ break;
+ case ImageFormat::CODE_STENCIL4:
+ return ImageFormat::STENCIL4();
+ break;
+ case ImageFormat::CODE_STENCIL8:
+ return ImageFormat::STENCIL8();
+ break;
+ case ImageFormat::CODE_STENCIL16:
+ return ImageFormat::STENCIL16();
+ break;
+
+ case ImageFormat::CODE_DEPTH24_STENCIL8:
+ return ImageFormat::DEPTH24_STENCIL8();
+ break;
+
+ case ImageFormat::CODE_YUV420_PLANAR:
+ return ImageFormat::YUV420_PLANAR();
+ break;
+
+ case ImageFormat::CODE_YUV422:
+ return ImageFormat::YUV422();
+ break;
+
+ case ImageFormat::CODE_YUV444:
+ return ImageFormat::YUV444();
+ break;
+
+ default:
+ return NULL;
+ }
+}
+
+// Helper variables for defining texture formats
+
+// Is floating point format
+static const bool FLOAT_FORMAT = true;
+static const bool INT_FORMAT = false;
+
+// Is opaque format (no alpha)
+static const bool OPAQUE_FORMAT = true;
+static const bool CLEAR_FORMAT = false;
+
+// Is compressed format (not raw component data)
+static const bool COMP_FORMAT = true;
+static const bool UNCOMP_FORMAT = false;
+
+#define DEFINE_TEXTUREFORMAT_METHOD(name, cmpnts, cmprssd, glf, glbf, lb, rb, gb, bb, db, sb, hbpt, pbpt, gldf, opq, fp, code, cs, bp) \
+ const ImageFormat* ImageFormat::name() { \
+ static const ImageFormat format(cmpnts, cmprssd, glf, glbf, lb, rb, gb, bb, db, sb, hbpt, pbpt, gldf, opq, fp, code, cs, bp); \
+ return &format; }
+
+DEFINE_TEXTUREFORMAT_METHOD(L8, 1, UNCOMP_FORMAT, GL_LUMINANCE8, GL_LUMINANCE, 8, 0, 0, 0, 0, 0, 0, 8, 8, GL_UNSIGNED_BYTE, OPAQUE_FORMAT, INT_FORMAT, CODE_L8, COLOR_SPACE_NONE);
+
+DEFINE_TEXTUREFORMAT_METHOD(L16, 1, UNCOMP_FORMAT, GL_LUMINANCE16, GL_LUMINANCE, 16, 0, 0, 0, 0, 0, 0, 16, 16,GL_UNSIGNED_SHORT, OPAQUE_FORMAT, INT_FORMAT, CODE_L16, COLOR_SPACE_NONE);
+
+DEFINE_TEXTUREFORMAT_METHOD(L16F, 1, UNCOMP_FORMAT, GL_LUMINANCE16F_ARB,GL_LUMINANCE, 16, 0, 0, 0, 0, 0, 0, 16, 16, GL_FLOAT, OPAQUE_FORMAT, FLOAT_FORMAT, CODE_L16F, COLOR_SPACE_NONE);
+
+DEFINE_TEXTUREFORMAT_METHOD(L32F, 1, UNCOMP_FORMAT, GL_LUMINANCE32F_ARB,GL_LUMINANCE, 32, 0, 0, 0, 0, 0, 0, 32, 32, GL_FLOAT, OPAQUE_FORMAT, FLOAT_FORMAT, CODE_L32F, COLOR_SPACE_NONE);
+
+DEFINE_TEXTUREFORMAT_METHOD(A8, 1, UNCOMP_FORMAT, GL_ALPHA8, GL_ALPHA, 0, 8, 0, 0, 0, 0, 0, 8, 8, GL_UNSIGNED_BYTE, CLEAR_FORMAT, INT_FORMAT, CODE_A8, COLOR_SPACE_NONE);
+
+DEFINE_TEXTUREFORMAT_METHOD(A16, 1, UNCOMP_FORMAT, GL_ALPHA16, GL_ALPHA, 0, 16, 0, 0, 0, 0, 0, 16, 16, GL_UNSIGNED_SHORT, CLEAR_FORMAT, INT_FORMAT, CODE_A16, COLOR_SPACE_NONE);
+
+DEFINE_TEXTUREFORMAT_METHOD(A16F, 1, UNCOMP_FORMAT, GL_ALPHA16F_ARB, GL_ALPHA, 0, 16, 0, 0, 0, 0, 0, 16, 16, GL_FLOAT, CLEAR_FORMAT, FLOAT_FORMAT, CODE_A16F, COLOR_SPACE_NONE);
+
+DEFINE_TEXTUREFORMAT_METHOD(A32F, 1, UNCOMP_FORMAT, GL_ALPHA32F_ARB, GL_ALPHA, 0, 32, 0, 0, 0, 0, 0, 32, 32, GL_FLOAT, CLEAR_FORMAT, FLOAT_FORMAT, CODE_A32F, COLOR_SPACE_NONE);
+
+DEFINE_TEXTUREFORMAT_METHOD(LA4, 2, UNCOMP_FORMAT, GL_LUMINANCE4_ALPHA4, GL_LUMINANCE_ALPHA, 4, 4, 0, 0, 0, 0, 0, 8, 8, GL_UNSIGNED_BYTE, CLEAR_FORMAT, INT_FORMAT, CODE_LA4, COLOR_SPACE_NONE);
+
+DEFINE_TEXTUREFORMAT_METHOD(LA8, 2, UNCOMP_FORMAT, GL_LUMINANCE8_ALPHA8, GL_LUMINANCE_ALPHA, 8, 8, 0, 0, 0, 0, 0, 16, 16, GL_UNSIGNED_BYTE, CLEAR_FORMAT, INT_FORMAT, CODE_LA8, COLOR_SPACE_NONE);
+
+DEFINE_TEXTUREFORMAT_METHOD(LA16, 2, UNCOMP_FORMAT, GL_LUMINANCE16_ALPHA16, GL_LUMINANCE_ALPHA, 16, 16, 0, 0, 0, 0, 0, 16*2, 16*2, GL_UNSIGNED_SHORT, CLEAR_FORMAT, INT_FORMAT, CODE_LA16, COLOR_SPACE_NONE);
+
+DEFINE_TEXTUREFORMAT_METHOD(LA16F, 2, UNCOMP_FORMAT, GL_LUMINANCE_ALPHA16F_ARB, GL_LUMINANCE_ALPHA, 16, 16, 0, 0, 0, 0, 0, 16*2, 16*2, GL_FLOAT, CLEAR_FORMAT, FLOAT_FORMAT, ImageFormat::CODE_LA16F, ImageFormat::COLOR_SPACE_NONE);
+
+DEFINE_TEXTUREFORMAT_METHOD(LA32F, 2, UNCOMP_FORMAT, GL_LUMINANCE_ALPHA32F_ARB, GL_LUMINANCE_ALPHA, 32, 32, 0, 0, 0, 0, 0, 32*2, 32*2, GL_FLOAT, CLEAR_FORMAT, FLOAT_FORMAT, ImageFormat::CODE_LA32F, ImageFormat::COLOR_SPACE_NONE);
+
+DEFINE_TEXTUREFORMAT_METHOD(BGR8, 3, UNCOMP_FORMAT, GL_RGB8, GL_BGR, 0, 0, 8, 8, 8, 0, 0, 32, 24, GL_UNSIGNED_BYTE, OPAQUE_FORMAT, INT_FORMAT, ImageFormat::CODE_BGR8, ImageFormat::COLOR_SPACE_RGB);
+
+DEFINE_TEXTUREFORMAT_METHOD(RGB5, 3, UNCOMP_FORMAT, GL_RGB5, GL_RGBA, 0, 0, 5, 5, 5, 0, 0, 16, 16, GL_UNSIGNED_BYTE, OPAQUE_FORMAT, INT_FORMAT, ImageFormat::CODE_RGB5, ImageFormat::COLOR_SPACE_RGB);
+
+DEFINE_TEXTUREFORMAT_METHOD(RGB5A1, 4, UNCOMP_FORMAT, GL_RGB5_A1, GL_RGBA, 0, 1, 5, 5, 5, 0, 0, 16, 16, GL_UNSIGNED_BYTE, OPAQUE_FORMAT, INT_FORMAT, ImageFormat::CODE_RGB5A1, ImageFormat::COLOR_SPACE_RGB);
+
+DEFINE_TEXTUREFORMAT_METHOD(RGB8, 3, UNCOMP_FORMAT, GL_RGB8, GL_RGB, 0, 0, 8, 8, 8, 0, 0, 32, 24, GL_UNSIGNED_BYTE, OPAQUE_FORMAT, INT_FORMAT, ImageFormat::CODE_RGB8, ImageFormat::COLOR_SPACE_RGB);
+
+DEFINE_TEXTUREFORMAT_METHOD(RGB10, 3, UNCOMP_FORMAT, GL_RGB10, GL_RGB, 0, 0, 10, 10, 10, 0, 0, 32, 10*3, GL_UNSIGNED_SHORT, OPAQUE_FORMAT, INT_FORMAT, ImageFormat::CODE_RGB10, ImageFormat::COLOR_SPACE_RGB);
+
+DEFINE_TEXTUREFORMAT_METHOD(RGB10A2, 4, UNCOMP_FORMAT, GL_RGB10_A2, GL_RGBA, 0, 2, 10, 10, 10, 0, 0, 32, 32, GL_UNSIGNED_INT_10_10_10_2, OPAQUE_FORMAT, INT_FORMAT, ImageFormat::CODE_RGB10A2, ImageFormat::COLOR_SPACE_RGB);
+
+DEFINE_TEXTUREFORMAT_METHOD(RGB16, 3, UNCOMP_FORMAT, GL_RGB16, GL_RGB, 0, 0, 16, 16, 16, 0, 0, 16*3, 16*3, GL_UNSIGNED_SHORT, OPAQUE_FORMAT, INT_FORMAT, ImageFormat::CODE_RGB16, ImageFormat::COLOR_SPACE_RGB);
+
+DEFINE_TEXTUREFORMAT_METHOD(RGB16F, 3, UNCOMP_FORMAT, GL_RGB16F_ARB, GL_RGB, 0, 0, 16, 16, 16, 0, 0, 16*3, 16*3, GL_FLOAT, OPAQUE_FORMAT, FLOAT_FORMAT, ImageFormat::CODE_RGB16F, ImageFormat::COLOR_SPACE_RGB);
+
+DEFINE_TEXTUREFORMAT_METHOD(RGB32F, 3, UNCOMP_FORMAT, GL_RGB32F_ARB, GL_RGB, 0, 0, 32, 32, 32, 0, 0, 32*3, 32*3, GL_FLOAT, OPAQUE_FORMAT, FLOAT_FORMAT, ImageFormat::CODE_RGB32F, ImageFormat::COLOR_SPACE_RGB);
+
+DEFINE_TEXTUREFORMAT_METHOD(RGBA8, 4, UNCOMP_FORMAT, GL_RGBA8, GL_RGBA, 0, 8, 8, 8, 8, 0, 0, 32, 32, GL_UNSIGNED_BYTE, CLEAR_FORMAT, INT_FORMAT, ImageFormat::CODE_RGBA8, ImageFormat::COLOR_SPACE_RGB);
+
+DEFINE_TEXTUREFORMAT_METHOD(RGBA16, 4, UNCOMP_FORMAT, GL_RGBA16, GL_RGBA, 0, 16, 16, 16, 16, 0, 0, 16*4, 16*4, GL_UNSIGNED_SHORT, CLEAR_FORMAT, INT_FORMAT, ImageFormat::CODE_RGBA16, ImageFormat::COLOR_SPACE_RGB);
+
+DEFINE_TEXTUREFORMAT_METHOD(RGBA16F, 4, UNCOMP_FORMAT, GL_RGBA16F_ARB, GL_RGBA, 0, 16, 16, 16, 16, 0, 0, 16*4, 16*4, GL_FLOAT, CLEAR_FORMAT, FLOAT_FORMAT, ImageFormat::CODE_RGB16F, ImageFormat::COLOR_SPACE_RGB);
+
+DEFINE_TEXTUREFORMAT_METHOD(RGBA32F, 4, UNCOMP_FORMAT, GL_RGBA32F_ARB, GL_RGBA, 0, 32, 32, 32, 32, 0, 0, 32*4, 32*4, GL_FLOAT, CLEAR_FORMAT, FLOAT_FORMAT, ImageFormat::CODE_RGBA32F, ImageFormat::COLOR_SPACE_RGB);
+
+DEFINE_TEXTUREFORMAT_METHOD(RGB_DXT1, 3, COMP_FORMAT, GL_COMPRESSED_RGB_S3TC_DXT1_EXT, GL_RGB, 0, 0, 0, 0, 0, 0, 0, 64, 64, GL_UNSIGNED_BYTE, OPAQUE_FORMAT, INT_FORMAT, ImageFormat::CODE_RGB_DXT1, ImageFormat::COLOR_SPACE_RGB);
+
+DEFINE_TEXTUREFORMAT_METHOD(RGBA_DXT1, 4, COMP_FORMAT, GL_COMPRESSED_RGBA_S3TC_DXT1_EXT, GL_RGBA, 0, 0, 0, 0, 0, 0, 0, 64, 64, GL_UNSIGNED_BYTE, CLEAR_FORMAT, INT_FORMAT, ImageFormat::CODE_RGBA_DXT1, ImageFormat::COLOR_SPACE_RGB);
+
+DEFINE_TEXTUREFORMAT_METHOD(RGBA_DXT3, 4, COMP_FORMAT, GL_COMPRESSED_RGBA_S3TC_DXT3_EXT, GL_RGBA, 0, 0, 0, 0, 0, 0, 0, 128, 128, GL_UNSIGNED_BYTE, CLEAR_FORMAT, INT_FORMAT, ImageFormat::CODE_RGBA_DXT3, ImageFormat::COLOR_SPACE_RGB);
+
+DEFINE_TEXTUREFORMAT_METHOD(RGBA_DXT5, 4, COMP_FORMAT, GL_COMPRESSED_RGBA_S3TC_DXT5_EXT, GL_RGBA, 0, 0, 0, 0, 0, 0, 0, 128, 128, GL_UNSIGNED_BYTE, CLEAR_FORMAT, INT_FORMAT, ImageFormat::CODE_RGBA_DXT5, ImageFormat::COLOR_SPACE_RGB);
+
+DEFINE_TEXTUREFORMAT_METHOD(DEPTH16, 1, UNCOMP_FORMAT, GL_DEPTH_COMPONENT16_ARB, GL_DEPTH_COMPONENT, 0, 0, 0, 0, 0, 0, 16, 16, 16, GL_UNSIGNED_SHORT, CLEAR_FORMAT, INT_FORMAT, ImageFormat::CODE_DEPTH16, ImageFormat::COLOR_SPACE_NONE);
+
+DEFINE_TEXTUREFORMAT_METHOD(DEPTH24, 1, UNCOMP_FORMAT, GL_DEPTH_COMPONENT24_ARB, GL_DEPTH_COMPONENT, 0, 0, 0, 0, 0, 0, 24, 32, 24, GL_UNSIGNED_INT, CLEAR_FORMAT, INT_FORMAT, ImageFormat::CODE_DEPTH24, ImageFormat::COLOR_SPACE_NONE);
+
+DEFINE_TEXTUREFORMAT_METHOD(DEPTH32, 1, UNCOMP_FORMAT, GL_DEPTH_COMPONENT32_ARB, GL_DEPTH_COMPONENT, 0, 0, 0, 0, 0, 0, 32, 32, 32, GL_UNSIGNED_INT, CLEAR_FORMAT, INT_FORMAT, ImageFormat::CODE_DEPTH32, ImageFormat::COLOR_SPACE_NONE);
+
+DEFINE_TEXTUREFORMAT_METHOD(DEPTH32F, 1, UNCOMP_FORMAT, GL_DEPTH_COMPONENT32_ARB, GL_DEPTH_COMPONENT, 0, 0, 0, 0, 0, 0, 32, 32, 32, GL_FLOAT, CLEAR_FORMAT, FLOAT_FORMAT, ImageFormat::CODE_DEPTH32F, ImageFormat::COLOR_SPACE_NONE);
+
+// These formats are for use with Renderbuffers only!
+DEFINE_TEXTUREFORMAT_METHOD(STENCIL1, 1, UNCOMP_FORMAT, GL_STENCIL_INDEX1_EXT, GL_STENCIL_INDEX_EXT, 0, 0, 0, 0, 0, 0, 1, 1, 1, GL_UNSIGNED_BYTE, CLEAR_FORMAT, INT_FORMAT, ImageFormat::CODE_STENCIL1, ImageFormat::COLOR_SPACE_NONE);
+
+DEFINE_TEXTUREFORMAT_METHOD(STENCIL4, 1, UNCOMP_FORMAT, GL_STENCIL_INDEX4_EXT, GL_STENCIL_INDEX_EXT, 0, 0, 0, 0, 0, 0, 4, 4, 4, GL_UNSIGNED_BYTE, CLEAR_FORMAT, INT_FORMAT, ImageFormat::CODE_STENCIL4, ImageFormat::COLOR_SPACE_NONE);
+
+DEFINE_TEXTUREFORMAT_METHOD(STENCIL8, 1, UNCOMP_FORMAT, GL_STENCIL_INDEX8_EXT, GL_STENCIL_INDEX_EXT, 0, 0, 0, 0, 0, 0, 8, 8, 8, GL_UNSIGNED_BYTE, CLEAR_FORMAT, INT_FORMAT, ImageFormat::CODE_STENCIL8, ImageFormat::COLOR_SPACE_NONE);
+
+DEFINE_TEXTUREFORMAT_METHOD(STENCIL16, 1, UNCOMP_FORMAT, GL_STENCIL_INDEX16_EXT, GL_STENCIL_INDEX_EXT, 0, 0, 0, 0, 0, 0, 16, 16, 16, GL_UNSIGNED_SHORT, CLEAR_FORMAT, INT_FORMAT, ImageFormat::CODE_STENCIL16, ImageFormat::COLOR_SPACE_NONE);
+
+DEFINE_TEXTUREFORMAT_METHOD(DEPTH24_STENCIL8, 2, UNCOMP_FORMAT, GL_DEPTH24_STENCIL8_EXT, GL_DEPTH_STENCIL_EXT,0, 0, 0, 0, 0, 24, 8, 32, 32, GL_UNSIGNED_INT, CLEAR_FORMAT, INT_FORMAT, ImageFormat::CODE_DEPTH24_STENCIL8, ImageFormat::COLOR_SPACE_NONE);
+
+DEFINE_TEXTUREFORMAT_METHOD(YUV420_PLANAR, 3, UNCOMP_FORMAT, GL_NONE, GL_NONE, 0, 0, 0, 0, 0, 0, 0, 12, 12, GL_UNSIGNED_BYTE, OPAQUE_FORMAT, INT_FORMAT, ImageFormat::CODE_YUV420_PLANAR, ImageFormat::COLOR_SPACE_YUV);
+DEFINE_TEXTUREFORMAT_METHOD(YUV422, 3, UNCOMP_FORMAT, GL_NONE, GL_NONE, 0, 0, 0, 0, 0, 0, 0, 16, 16, GL_UNSIGNED_BYTE, OPAQUE_FORMAT, INT_FORMAT, ImageFormat::CODE_YUV422, ImageFormat::COLOR_SPACE_YUV);
+DEFINE_TEXTUREFORMAT_METHOD(YUV444, 3, UNCOMP_FORMAT, GL_NONE, GL_NONE, 0, 0, 0, 0, 0, 0, 0, 24, 24, GL_UNSIGNED_BYTE, OPAQUE_FORMAT, INT_FORMAT, ImageFormat::CODE_YUV444, ImageFormat::COLOR_SPACE_YUV);
+
+}
diff --git a/externals/g3dlite/G3D.lib/source/ImageFormat_convert.cpp b/externals/g3dlite/G3D.lib/source/ImageFormat_convert.cpp
new file mode 100644
index 00000000000..9cbc4edcb39
--- /dev/null
+++ b/externals/g3dlite/G3D.lib/source/ImageFormat_convert.cpp
@@ -0,0 +1,1305 @@
+#include "G3D/ImageFormat.h"
+#include "G3D/Color1uint8.h"
+#include "G3D/Color3uint8.h"
+#include "G3D/Color4uint8.h"
+#include "G3D/Color1.h"
+#include "G3D/Color3.h"
+#include "G3D/Color4.h"
+
+
+namespace G3D {
+
+// this is the signature for all conversion routines (same parameters as ImageFormat::convert)
+typedef void (*ConvertFunc)(const Array<const void*>& srcBytes, int srcWidth, int srcHeight, const ImageFormat* srcFormat, int srcRowPadBits, const Array<void*>& dstBytes, const ImageFormat* dstFormat, int dstRowPadBits, bool invertY, ImageFormat::BayerAlgorithm bayerAlg);
+
+// this defines the conversion routines for converting between compatible formats
+static const int NUM_CONVERT_IMAGE_FORMATS = 5;
+struct ConvertAttributes {
+ ConvertFunc m_converter;
+ ImageFormat::Code m_sourceFormats[NUM_CONVERT_IMAGE_FORMATS];
+ ImageFormat::Code m_destFormats[NUM_CONVERT_IMAGE_FORMATS];
+ bool m_handlesSourcePadding;
+ bool m_handlesDestPadding;
+ bool m_handleInvertY;
+};
+
+// forward declare the converters we can use them below
+#define DECLARE_CONVERT_FUNC(name) static void name(const Array<const void*>& srcBytes, int srcWidth, int srcHeight, const ImageFormat* srcFormat, int srcRowPadBits, const Array<void*>& dstBytes, const ImageFormat* dstFormat, int dstRowPadBits, bool invertY, ImageFormat::BayerAlgorithm bayerAlg);
+
+DECLARE_CONVERT_FUNC(l8_to_rgb8);
+DECLARE_CONVERT_FUNC(l32f_to_rgb8);
+DECLARE_CONVERT_FUNC(rgb8_to_rgba8);
+DECLARE_CONVERT_FUNC(rgb8_to_bgr8);
+DECLARE_CONVERT_FUNC(rgb8_to_rgba32f);
+DECLARE_CONVERT_FUNC(bgr8_to_rgb8);
+DECLARE_CONVERT_FUNC(bgr8_to_rgba8);
+DECLARE_CONVERT_FUNC(bgr8_to_rgba32f);
+DECLARE_CONVERT_FUNC(rgba8_to_rgb8);
+DECLARE_CONVERT_FUNC(rgba8_to_bgr8);
+DECLARE_CONVERT_FUNC(rgba8_to_rgba32f);
+DECLARE_CONVERT_FUNC(rgb32f_to_rgba32f);
+DECLARE_CONVERT_FUNC(rgba32f_to_rgb8);
+DECLARE_CONVERT_FUNC(rgba32f_to_rgba8);
+DECLARE_CONVERT_FUNC(rgba32f_to_bgr8);
+DECLARE_CONVERT_FUNC(rgba32f_to_rgb32f);
+DECLARE_CONVERT_FUNC(rgba32f_to_bayer_rggb8);
+DECLARE_CONVERT_FUNC(rgba32f_to_bayer_gbrg8);
+DECLARE_CONVERT_FUNC(rgba32f_to_bayer_grbg8);
+DECLARE_CONVERT_FUNC(rgba32f_to_bayer_bggr8);
+DECLARE_CONVERT_FUNC(bayer_rggb8_to_rgba32f);
+DECLARE_CONVERT_FUNC(bayer_gbrg8_to_rgba32f);
+DECLARE_CONVERT_FUNC(bayer_grbg8_to_rgba32f);
+DECLARE_CONVERT_FUNC(bayer_bggr8_to_rgba32f);
+DECLARE_CONVERT_FUNC(rgb8_to_yuv420p);
+DECLARE_CONVERT_FUNC(rgb8_to_yuv422);
+DECLARE_CONVERT_FUNC(rgb8_to_yuv444);
+DECLARE_CONVERT_FUNC(yuv420p_to_rgb8);
+DECLARE_CONVERT_FUNC(yuv422_to_rgb8);
+DECLARE_CONVERT_FUNC(yuv444_to_rgb8);
+
+// this is the list of mappings between formats and the routines to perform them
+static const ConvertAttributes sConvertMappings[] = {
+
+ // RGB -> RGB color space
+ // L8 ->
+ {l8_to_rgb8, {ImageFormat::CODE_L8, ImageFormat::CODE_NONE}, {ImageFormat::CODE_RGB8, ImageFormat::CODE_NONE}, false, false, true},
+
+ // L32F ->
+ {l32f_to_rgb8, {ImageFormat::CODE_L32F, ImageFormat::CODE_NONE}, {ImageFormat::CODE_RGB8, ImageFormat::CODE_NONE}, false, false, true},
+
+ // RGB8 ->
+ {rgb8_to_rgba8, {ImageFormat::CODE_RGB8, ImageFormat::CODE_NONE}, {ImageFormat::CODE_RGBA8, ImageFormat::CODE_NONE}, false, false, true},
+ {rgb8_to_bgr8, {ImageFormat::CODE_RGB8, ImageFormat::CODE_NONE}, {ImageFormat::CODE_BGR8, ImageFormat::CODE_NONE}, false, false, true},
+ {rgb8_to_rgba32f, {ImageFormat::CODE_RGB8, ImageFormat::CODE_NONE}, {ImageFormat::CODE_RGBA32F, ImageFormat::CODE_NONE}, true, false, true},
+
+ // BGR8 ->
+ {bgr8_to_rgb8, {ImageFormat::CODE_BGR8, ImageFormat::CODE_NONE}, {ImageFormat::CODE_RGB8, ImageFormat::CODE_NONE}, false, false, true},
+ {bgr8_to_rgba8, {ImageFormat::CODE_BGR8, ImageFormat::CODE_NONE}, {ImageFormat::CODE_RGBA8, ImageFormat::CODE_NONE}, false, false, true},
+ {bgr8_to_rgba32f, {ImageFormat::CODE_BGR8, ImageFormat::CODE_NONE}, {ImageFormat::CODE_RGBA32F, ImageFormat::CODE_NONE}, true, false, true},
+
+ // RGBA8 ->
+ {rgba8_to_rgb8, {ImageFormat::CODE_RGBA8, ImageFormat::CODE_NONE}, {ImageFormat::CODE_RGB8, ImageFormat::CODE_NONE}, false, false, true},
+ {rgba8_to_bgr8, {ImageFormat::CODE_RGBA8, ImageFormat::CODE_NONE}, {ImageFormat::CODE_BGR8, ImageFormat::CODE_NONE}, false, false, true},
+ {rgba8_to_rgba32f, {ImageFormat::CODE_RGBA8, ImageFormat::CODE_NONE}, {ImageFormat::CODE_RGBA32F, ImageFormat::CODE_NONE}, true, false, true},
+
+ // RGB32F ->
+ {rgb32f_to_rgba32f, {ImageFormat::CODE_RGB32F, ImageFormat::CODE_NONE}, {ImageFormat::CODE_RGBA32F, ImageFormat::CODE_NONE}, true, false, true},
+
+ // RGBA32F ->
+ {rgba32f_to_rgb8, {ImageFormat::CODE_RGBA32F, ImageFormat::CODE_NONE}, {ImageFormat::CODE_RGB8, ImageFormat::CODE_NONE}, false, true, true},
+ {rgba32f_to_rgba8, {ImageFormat::CODE_RGBA32F, ImageFormat::CODE_NONE}, {ImageFormat::CODE_RGBA8, ImageFormat::CODE_NONE}, false, true, true},
+ {rgba32f_to_bgr8, {ImageFormat::CODE_RGBA32F, ImageFormat::CODE_NONE}, {ImageFormat::CODE_BGR8, ImageFormat::CODE_NONE}, false, true, true},
+ {rgba32f_to_rgb32f, {ImageFormat::CODE_RGBA32F, ImageFormat::CODE_NONE}, {ImageFormat::CODE_RGB32F, ImageFormat::CODE_NONE}, false, true, true},
+
+ // RGB -> BAYER color space
+ {rgba32f_to_bayer_rggb8, {ImageFormat::CODE_RGBA32F, ImageFormat::CODE_NONE}, {ImageFormat::CODE_BAYER_RGGB8, ImageFormat::CODE_NONE}, false, true, true},
+ {rgba32f_to_bayer_gbrg8, {ImageFormat::CODE_RGBA32F, ImageFormat::CODE_NONE}, {ImageFormat::CODE_BAYER_GBRG8, ImageFormat::CODE_NONE}, false, true, true},
+ {rgba32f_to_bayer_grbg8, {ImageFormat::CODE_RGBA32F, ImageFormat::CODE_NONE}, {ImageFormat::CODE_BAYER_GRBG8, ImageFormat::CODE_NONE}, false, true, true},
+ {rgba32f_to_bayer_bggr8, {ImageFormat::CODE_RGBA32F, ImageFormat::CODE_NONE}, {ImageFormat::CODE_BAYER_BGGR8, ImageFormat::CODE_NONE}, false, true, true},
+
+ // BAYER -> RGB color space
+ {bayer_rggb8_to_rgba32f, {ImageFormat::CODE_BAYER_RGGB8, ImageFormat::CODE_NONE}, {ImageFormat::CODE_RGBA32F, ImageFormat::CODE_NONE}, false, false, true},
+ {bayer_gbrg8_to_rgba32f, {ImageFormat::CODE_BAYER_GBRG8, ImageFormat::CODE_NONE}, {ImageFormat::CODE_RGBA32F, ImageFormat::CODE_NONE}, false, false, true},
+ {bayer_grbg8_to_rgba32f, {ImageFormat::CODE_BAYER_GRBG8, ImageFormat::CODE_NONE}, {ImageFormat::CODE_RGBA32F, ImageFormat::CODE_NONE}, false, false, true},
+ {bayer_bggr8_to_rgba32f, {ImageFormat::CODE_BAYER_BGGR8, ImageFormat::CODE_NONE}, {ImageFormat::CODE_RGBA32F, ImageFormat::CODE_NONE}, false, false, true},
+
+ // RGB <-> YUV color space
+ {rgb8_to_yuv420p, {ImageFormat::CODE_RGB8, ImageFormat::CODE_NONE}, {ImageFormat::CODE_YUV420_PLANAR, ImageFormat::CODE_NONE}, false, false, false},
+ {rgb8_to_yuv422, {ImageFormat::CODE_RGB8, ImageFormat::CODE_NONE}, {ImageFormat::CODE_YUV422, ImageFormat::CODE_NONE}, false, false, false},
+ {rgb8_to_yuv444, {ImageFormat::CODE_RGB8, ImageFormat::CODE_NONE}, {ImageFormat::CODE_YUV444, ImageFormat::CODE_NONE}, false, false, false},
+ {yuv420p_to_rgb8, {ImageFormat::CODE_YUV420_PLANAR, ImageFormat::CODE_NONE}, {ImageFormat::CODE_RGB8, ImageFormat::CODE_NONE}, false, false, false},
+ {yuv422_to_rgb8, {ImageFormat::CODE_YUV422, ImageFormat::CODE_NONE}, {ImageFormat::CODE_RGB8, ImageFormat::CODE_NONE}, false, false, false},
+ {yuv444_to_rgb8, {ImageFormat::CODE_YUV444, ImageFormat::CODE_NONE}, {ImageFormat::CODE_RGB8, ImageFormat::CODE_NONE}, false, false, false},
+};
+
+static ConvertFunc findConverter(TextureFormat::Code sourceCode, TextureFormat::Code destCode, bool needsSourcePadding, bool needsDestPadding, bool needsInvertY) {
+ int numRoutines = sizeof(sConvertMappings) / sizeof(ConvertAttributes);
+ for (int routineIndex = 0; routineIndex < numRoutines; ++routineIndex) {
+ int sourceIndex = 0;
+ ConvertAttributes routine = sConvertMappings[routineIndex];
+
+ while (routine.m_sourceFormats[sourceIndex] != ImageFormat::CODE_NONE) {
+ // check for matching source
+ if (routine.m_sourceFormats[sourceIndex] == sourceCode) {
+ int destIndex = 0;
+
+ // now check for matching dest to see if the routine fits
+ while (routine.m_destFormats[destIndex] != ImageFormat::CODE_NONE) {
+
+ // check if dest format matches and padding + invert rules match
+ if ((routine.m_destFormats[destIndex] == destCode) &&
+ (!needsSourcePadding || (routine.m_handlesSourcePadding == needsSourcePadding)) &&
+ (!needsDestPadding || (routine.m_handlesDestPadding == needsDestPadding)) &&
+ (!needsInvertY || (routine.m_handleInvertY == needsInvertY))) {
+
+ // found compatible converter
+ return routine.m_converter;
+ }
+ ++destIndex;
+ }
+ }
+ ++sourceIndex;
+ }
+ }
+
+ return NULL;
+}
+
+bool conversionAvailable(const ImageFormat* srcFormat, int srcRowPadBits, const ImageFormat* dstFormat, int dstRowPadBits, bool invertY = false) {
+ bool conversionAvailable = false;
+
+ // check if a conversion is available
+ if ( (srcFormat->code == dstFormat->code) && (srcRowPadBits == dstRowPadBits) && !invertY) {
+ conversionAvailable = true;
+ } else {
+ ConvertFunc directConverter = findConverter(srcFormat->code, dstFormat->code, srcRowPadBits > 0, dstRowPadBits > 0, invertY);
+
+ conversionAvailable = (directConverter != NULL);
+ }
+
+ return conversionAvailable;
+}
+
+bool ImageFormat::convert(const Array<const void*>& srcBytes, int srcWidth, int srcHeight, const ImageFormat* srcFormat, int srcRowPadBits,
+ const Array<void*>& dstBytes, const ImageFormat* dstFormat, int dstRowPadBits,
+ bool invertY, BayerAlgorithm bayerAlg) {
+
+ bool conversionAvailable = false;
+
+ // Handle direct copy of image to same format
+ if ( (srcFormat->code == dstFormat->code) && (srcRowPadBits == dstRowPadBits) && !invertY) {
+
+ System::memcpy(dstBytes[0], srcBytes[0], iCeil(((srcWidth * srcFormat->cpuBitsPerPixel + srcRowPadBits) * srcHeight) / 8.0f));
+ conversionAvailable = true;
+ } else {
+ // if no direct conversion routine exists,
+ // then look for conversion to intermediate
+ // and then from intermediate to dest.
+ // intermediate format is RGBA32F
+ ConvertFunc directConverter = findConverter(srcFormat->code, dstFormat->code, srcRowPadBits > 0, dstRowPadBits > 0, invertY);
+
+ // if we have a direct converter, use it, otherwise find intermdiate path
+ if (directConverter) {
+ directConverter(srcBytes, srcWidth, srcHeight, srcFormat, srcRowPadBits, dstBytes, dstFormat, dstRowPadBits, invertY, bayerAlg);
+ conversionAvailable = true;
+ } else {
+ ConvertFunc toInterConverter = findConverter(srcFormat->code, ImageFormat::CODE_RGBA32F, srcRowPadBits > 0, false, false);;
+ ConvertFunc fromInterConverter = findConverter(ImageFormat::CODE_RGBA32F, dstFormat->code, false, dstRowPadBits > 0, invertY);;
+
+ if (toInterConverter && fromInterConverter) {
+ Array<void*> tmp;
+ tmp.append(System::malloc(srcWidth * srcHeight * ImageFormat::RGBA32F()->cpuBitsPerPixel * 8));
+
+ toInterConverter(srcBytes, srcWidth, srcHeight, srcFormat, srcRowPadBits, tmp, ImageFormat::RGBA32F(), 0, false, bayerAlg);
+ fromInterConverter(reinterpret_cast<Array<const void*>&>(tmp), srcWidth, srcHeight, ImageFormat::RGBA32F(), 0, dstBytes, dstFormat, dstRowPadBits, invertY, bayerAlg);
+
+ System::free(tmp[0]);
+
+ conversionAvailable = true;
+ }
+ }
+ }
+
+ return conversionAvailable;
+}
+
+
+// *******************
+// RGB -> RGB color space conversions
+// *******************
+
+// L8 ->
+static void l8_to_rgb8(const Array<const void*>& srcBytes, int srcWidth, int srcHeight, const ImageFormat* srcFormat, int srcRowPadBits, const Array<void*>& dstBytes, const ImageFormat* dstFormat, int dstRowPadBits, bool invertY, ImageFormat::BayerAlgorithm bayerAlg) {
+ uint8* dst = static_cast<uint8*>(dstBytes[0]);
+ const uint8* src = static_cast<const uint8*>(srcBytes[0]);
+ for (int y = 0; y < srcHeight; ++y) {
+ for (int x = 0; x < srcWidth; ++x) {
+ int i = (invertY) ? ((srcHeight-1-y) * srcWidth +x) : (y * srcWidth + x);
+ int i3 = i * 3;
+
+ dst[i3 + 0] = src[i];
+ dst[i3 + 1] = src[i];
+ dst[i3 + 2] = src[i];
+ }
+ }
+}
+
+// L32F ->
+static void l32f_to_rgb8(const Array<const void*>& srcBytes, int srcWidth, int srcHeight, const ImageFormat* srcFormat, int srcRowPadBits, const Array<void*>& dstBytes, const ImageFormat* dstFormat, int dstRowPadBits, bool invertY, ImageFormat::BayerAlgorithm bayerAlg) {
+ int srcIndex = 0;
+ int dstByteOffset = 0;
+ uint8* dst = static_cast<uint8*>(dstBytes[0]);
+ const float* src = static_cast<const float*>(srcBytes[0]);
+
+ for (int y = 0; y < srcHeight; ++y) {
+ if (invertY) {
+ srcIndex = srcWidth * (srcHeight - y - 1);
+ }
+
+ for (int x = 0; x < srcWidth; ++x, ++srcIndex, dstByteOffset += 3) {
+ Color3uint8& d = *reinterpret_cast<Color3uint8*>(dst + dstByteOffset);
+ float s = src[srcIndex];
+
+ uint8 c = iMin(255, iFloor(s * 256));
+ d = Color3uint8(c, c, c);
+ }
+ }
+}
+
+// RGB8 ->
+static void rgb8_to_rgba8(const Array<const void*>& srcBytes, int srcWidth, int srcHeight, const ImageFormat* srcFormat, int srcRowPadBits, const Array<void*>& dstBytes, const ImageFormat* dstFormat, int dstRowPadBits, bool invertY, ImageFormat::BayerAlgorithm bayerAlg) {
+ uint8* dst = static_cast<uint8*>(dstBytes[0]);
+ const uint8* src = static_cast<const uint8*>(srcBytes[0]);
+ for (int y = 0; y < srcHeight; ++y) {
+ for (int x = 0; x < srcWidth; ++x) {
+ int i = (invertY) ? ((srcHeight-1-y) * srcWidth +x) : (y * srcWidth + x);
+ int i3 = i * 3;
+ int i4 = i3 + i;
+
+ dst[i4 + 0] = src[i3 + 0];
+ dst[i4 + 1] = src[i3 + 1];
+ dst[i4 + 2] = src[i3 + 2];
+ dst[i4 + 3] = 255;
+ }
+ }
+}
+
+static void rgb8_to_bgr8(const Array<const void*>& srcBytes, int srcWidth, int srcHeight, const ImageFormat* srcFormat, int srcRowPadBits, const Array<void*>& dstBytes, const ImageFormat* dstFormat, int dstRowPadBits, bool invertY, ImageFormat::BayerAlgorithm bayerAlg) {
+ uint8* dst = static_cast<uint8*>(dstBytes[0]);
+ const uint8* src = static_cast<const uint8*>(srcBytes[0]);
+ for (int y = 0; y < srcHeight; ++y) {
+ for (int x = 0; x < srcWidth; ++x) {
+ int i = (invertY) ? ((srcHeight-1-y) * srcWidth +x) : (y * srcWidth + x);
+ int i3 = i * 3;
+ dst[i3 + 0] = src[i3 + 2];
+ dst[i3 + 1] = src[i3 + 1];
+ dst[i3 + 2] = src[i3 + 0];
+ }
+ }
+}
+
+static void rgb8_to_rgba32f(const Array<const void*>& srcBytes, int srcWidth, int srcHeight, const ImageFormat* srcFormat, int srcRowPadBits, const Array<void*>& dstBytes, const ImageFormat* dstFormat, int dstRowPadBits, bool invertY, ImageFormat::BayerAlgorithm bayerAlg) {
+ debugAssertM(srcRowPadBits % 8 == 0, "Source row padding must be a multiple of 8 bits for this format");
+
+ int dstIndex = 0;
+ int srcByteOffset = 0;
+ int srcRowPadBytes = srcRowPadBits / 8;
+ Color4* dst = static_cast<Color4*>(dstBytes[0]);
+ const uint8* src = static_cast<const uint8*>(srcBytes[0]);
+
+ for (int y = 0; y < srcHeight; ++y) {
+ if (invertY) {
+ dstIndex = srcWidth * (srcHeight - 1 - y);
+ }
+ for (int x = 0; x < srcWidth; ++x, ++dstIndex, srcByteOffset += 3) {
+ const Color3uint8& s = *reinterpret_cast<const Color3uint8*>(src + srcByteOffset);
+ dst[dstIndex] = Color4(Color3(s), 1.0f);
+ }
+ srcByteOffset += srcRowPadBytes;
+ }
+}
+
+// BGR8 ->
+static void bgr8_to_rgb8(const Array<const void*>& srcBytes, int srcWidth, int srcHeight, const ImageFormat* srcFormat, int srcRowPadBits, const Array<void*>& dstBytes, const ImageFormat* dstFormat, int dstRowPadBits, bool invertY, ImageFormat::BayerAlgorithm bayerAlg) {
+ uint8* dst = static_cast<uint8*>(dstBytes[0]);
+ const uint8* src = static_cast<const uint8*>(srcBytes[0]);
+ for (int y = 0; y < srcHeight; ++y) {
+ for (int x = 0; x < srcWidth; ++x) {
+ int i = (invertY) ? ((srcHeight-1-y) * srcWidth +x) : (y * srcWidth + x);
+ int i3 = i * 3;
+ dst[i3 + 0] = src[i3 + 2];
+ dst[i3 + 1] = src[i3 + 1];
+ dst[i3 + 2] = src[i3 + 0];
+ }
+ }
+}
+
+static void bgr8_to_rgba8(const Array<const void*>& srcBytes, int srcWidth, int srcHeight, const ImageFormat* srcFormat, int srcRowPadBits, const Array<void*>& dstBytes, const ImageFormat* dstFormat, int dstRowPadBits, bool invertY, ImageFormat::BayerAlgorithm bayerAlg) {
+ uint8* dst = static_cast<uint8*>(dstBytes[0]);
+ const uint8* src = static_cast<const uint8*>(srcBytes[0]);
+ for (int y = 0; y < srcHeight; ++y) {
+ for (int x = 0; x < srcWidth; ++x) {
+ int i = (invertY) ? ((srcHeight-1-y) * srcWidth +x) : (y * srcWidth + x);
+ int i3 = i * 3;
+ int i4 = i3 + i;
+
+ dst[i4 + 0] = src[i3 + 2];
+ dst[i4 + 1] = src[i3 + 1];
+ dst[i4 + 2] = src[i3 + 0];
+ dst[i4 + 3] = 255;
+ }
+ }
+}
+
+static void bgr8_to_rgba32f(const Array<const void*>& srcBytes, int srcWidth, int srcHeight, const ImageFormat* srcFormat, int srcRowPadBits, const Array<void*>& dstBytes, const ImageFormat* dstFormat, int dstRowPadBits, bool invertY, ImageFormat::BayerAlgorithm bayerAlg) {
+ debugAssertM(srcRowPadBits % 8 == 0, "Source row padding must be a multiple of 8 bits for this format");
+
+ int dstIndex = 0;
+ int srcByteOffset = 0;
+ int srcRowPadBytes = srcRowPadBits / 8;
+ Color4* dst = static_cast<Color4*>(dstBytes[0]);
+ const uint8* src = static_cast<const uint8*>(srcBytes[0]);
+
+ for (int y = 0; y < srcHeight; ++y) {
+ if (invertY) {
+ dstIndex = srcWidth * (srcHeight - 1 - y);
+ }
+
+ for (int x = 0; x < srcWidth; ++x, ++dstIndex, srcByteOffset += 3) {
+ const Color3uint8& s = *reinterpret_cast<const Color3uint8*>(src + srcByteOffset);
+ dst[dstIndex] = Color4(Color3(s).bgr(), 1.0f);
+ }
+ srcByteOffset += srcRowPadBytes;
+ }
+}
+
+// RGBA8 ->
+static void rgba8_to_rgb8(const Array<const void*>& srcBytes, int srcWidth, int srcHeight, const ImageFormat* srcFormat, int srcRowPadBits, const Array<void*>& dstBytes, const ImageFormat* dstFormat, int dstRowPadBits, bool invertY, ImageFormat::BayerAlgorithm bayerAlg) {
+ uint8* dst = static_cast<uint8*>(dstBytes[0]);
+ const uint8* src = static_cast<const uint8*>(srcBytes[0]);
+ for (int y = 0; y < srcHeight; ++y) {
+ for (int x = 0; x < srcWidth; ++x) {
+ int i = (invertY) ? ((srcHeight-1-y) * srcWidth +x) : (y * srcWidth + x);
+ int i3 = i * 3;
+ int i4 = i3 + i;
+
+ dst[i3 + 0] = src[i4 + 0];
+ dst[i3 + 1] = src[i4 + 1];
+ dst[i3 + 2] = src[i4 + 2];
+ }
+ }
+}
+
+static void rgba8_to_bgr8(const Array<const void*>& srcBytes, int srcWidth, int srcHeight, const ImageFormat* srcFormat, int srcRowPadBits, const Array<void*>& dstBytes, const ImageFormat* dstFormat, int dstRowPadBits, bool invertY, ImageFormat::BayerAlgorithm bayerAlg) {
+ uint8* dst = static_cast<uint8*>(dstBytes[0]);
+ const uint8* src = static_cast<const uint8*>(srcBytes[0]);
+ for (int y = 0; y < srcHeight; ++y) {
+ for (int x = 0; x < srcWidth; ++x) {
+ int i = (invertY) ? ((srcHeight-1-y) * srcWidth +x) : (y * srcWidth + x);
+ int i3 = i * 3;
+ int i4 = i3 + i;
+
+ dst[i3 + 0] = src[i4 + 2];
+ dst[i3 + 1] = src[i4 + 1];
+ dst[i3 + 2] = src[i4 + 0];
+ }
+ }
+}
+
+static void rgba8_to_rgba32f(const Array<const void*>& srcBytes, int srcWidth, int srcHeight, const ImageFormat* srcFormat, int srcRowPadBits, const Array<void*>& dstBytes, const ImageFormat* dstFormat, int dstRowPadBits, bool invertY, ImageFormat::BayerAlgorithm bayerAlg) {
+ debugAssertM(srcRowPadBits % 8 == 0, "Source row padding must be a multiple of 8 bits for this format");
+
+ int dstIndex = 0;
+ int srcByteOffset = 0;
+ int srcRowPadBytes = srcRowPadBits / 8;
+ Color4* dst = static_cast<Color4*>(dstBytes[0]);
+ const uint8* src = static_cast<const uint8*>(srcBytes[0]);
+
+ for (int y = 0; y < srcHeight; ++y) {
+ if (invertY) {
+ dstIndex = srcWidth * (srcHeight - 1 - y);
+ }
+
+ for (int x = 0; x < srcWidth; ++x, ++dstIndex, srcByteOffset += 4) {
+ const Color4uint8& s = *reinterpret_cast<const Color4uint8*>(src + srcByteOffset);
+ dst[dstIndex] = Color4(s);
+ }
+ srcByteOffset += srcRowPadBytes;
+ }
+}
+
+// RGB32F ->
+static void rgb32f_to_rgba32f(const Array<const void*>& srcBytes, int srcWidth, int srcHeight, const ImageFormat* srcFormat, int srcRowPadBits, const Array<void*>& dstBytes, const ImageFormat* dstFormat, int dstRowPadBits, bool invertY, ImageFormat::BayerAlgorithm bayerAlg) {
+ debugAssertM(srcRowPadBits % 8 == 0, "Source row padding must be a multiple of 8 bits for this format");
+
+ int dstIndex = 0;
+ int srcByteOffset = 0;
+ int srcRowPadBytes = srcRowPadBits / 8;
+ Color4* dst = static_cast<Color4*>(dstBytes[0]);
+ const uint8* src = static_cast<const uint8*>(srcBytes[0]);
+
+ for (int y = 0; y < srcHeight; ++y) {
+ if (invertY) {
+ dstIndex = srcWidth * (srcHeight - 1 - y);
+ }
+
+ for (int x = 0; x < srcWidth; ++x, ++dstIndex, srcByteOffset += 3 * sizeof(float)) {
+ const Color3& s = *reinterpret_cast<const Color3*>(src + srcByteOffset);
+ dst[dstIndex] = Color4(Color3(s), 1.0f);
+ }
+ srcByteOffset += srcRowPadBytes;
+ }
+}
+
+// RGBA32F ->
+static void rgba32f_to_rgb8(const Array<const void*>& srcBytes, int srcWidth, int srcHeight, const ImageFormat* srcFormat, int srcRowPadBits, const Array<void*>& dstBytes, const ImageFormat* dstFormat, int dstRowPadBits, bool invertY, ImageFormat::BayerAlgorithm bayerAlg) {
+ debugAssertM(dstRowPadBits % 8 == 0, "Destination row padding must be a multiple of 8 bits for this format");
+
+ int srcIndex = 0;
+ int dstByteOffset = 0;
+ int dstRowPadBytes = dstRowPadBits / 8;
+ uint8* dst = static_cast<uint8*>(dstBytes[0]);
+ const Color4* src = static_cast<const Color4*>(srcBytes[0]);
+
+ for (int y = 0; y < srcHeight; ++y) {
+ if (invertY) {
+ srcIndex = srcWidth * (srcHeight - y - 1);
+ }
+
+ for (int x = 0; x < srcWidth; ++x, ++srcIndex, dstByteOffset += 3) {
+ Color3uint8& d = *reinterpret_cast<Color3uint8*>(dst + dstByteOffset);
+ const Color4& s = src[srcIndex];
+
+ d = Color3uint8(s.rgb());
+ }
+ dstByteOffset += dstRowPadBytes;
+ }
+}
+
+static void rgba32f_to_rgba8(const Array<const void*>& srcBytes, int srcWidth, int srcHeight, const ImageFormat* srcFormat, int srcRowPadBits, const Array<void*>& dstBytes, const ImageFormat* dstFormat, int dstRowPadBits, bool invertY, ImageFormat::BayerAlgorithm bayerAlg) {
+ debugAssertM(dstRowPadBits % 8 == 0, "Destination row padding must be a multiple of 8 bits for this format");
+
+ int srcIndex = 0;
+ int dstByteOffset = 0;
+ int dstRowPadBytes = dstRowPadBits / 8;
+ uint8* dst = static_cast<uint8*>(dstBytes[0]);
+ const Color4* src = static_cast<const Color4*>(srcBytes[0]);
+
+ for (int y = 0; y < srcHeight; ++y) {
+ if (invertY) {
+ srcIndex = srcWidth * (srcHeight - 1 - y);
+ }
+ for (int x = 0; x < srcWidth; ++x, ++srcIndex, dstByteOffset += 4) {
+ Color4uint8& d = *reinterpret_cast<Color4uint8*>(dst + dstByteOffset);
+ const Color4& s = src[srcIndex];
+
+ d = Color4uint8(s);
+ }
+ dstByteOffset += dstRowPadBytes;
+ }
+}
+
+static void rgba32f_to_bgr8(const Array<const void*>& srcBytes, int srcWidth, int srcHeight, const ImageFormat* srcFormat, int srcRowPadBits, const Array<void*>& dstBytes, const ImageFormat* dstFormat, int dstRowPadBits, bool invertY, ImageFormat::BayerAlgorithm bayerAlg) {
+ debugAssertM(dstRowPadBits % 8 == 0, "Destination row padding must be a multiple of 8 bits for this format");
+
+ int srcIndex = 0;
+ int dstByteOffset = 0;
+ int dstRowPadBytes = dstRowPadBits / 8;
+ uint8* dst = static_cast<uint8*>(dstBytes[0]);
+ const Color4* src = static_cast<const Color4*>(srcBytes[0]);
+
+ for (int y = 0; y < srcHeight; ++y) {
+ if (invertY) {
+ srcIndex = srcWidth * (srcHeight - y - 1);
+ }
+
+ for (int x = 0; x < srcWidth; ++x, ++srcIndex, dstByteOffset += 3) {
+ Color3uint8& d = *reinterpret_cast<Color3uint8*>(dst + dstByteOffset);
+ const Color4& s = src[srcIndex];
+
+ d = Color3uint8(s.rgb()).bgr();
+ }
+ dstByteOffset += dstRowPadBytes;
+ }
+}
+
+static void rgba32f_to_rgb32f(const Array<const void*>& srcBytes, int srcWidth, int srcHeight, const ImageFormat* srcFormat, int srcRowPadBits, const Array<void*>& dstBytes, const ImageFormat* dstFormat, int dstRowPadBits, bool invertY, ImageFormat::BayerAlgorithm bayerAlg) {
+ debugAssertM(dstRowPadBits % 8 == 0, "Destination row padding must be a multiple of 8 bits for this format");
+
+ int srcIndex = 0;
+ int dstByteOffset = 0;
+ int dstRowPadBytes = dstRowPadBits / 8;
+ uint8* dst = static_cast<uint8*>(dstBytes[0]);
+ const Color4* src = static_cast<const Color4*>(srcBytes[0]);
+
+ for (int y = 0; y < srcHeight; ++y) {
+ if (invertY) {
+ srcIndex = srcWidth * (srcHeight - 1 - y);
+ }
+ for (int x = 0; x < srcWidth; ++x, ++srcIndex, dstByteOffset += 3 * sizeof(float)) {
+ Color3& d = *reinterpret_cast<Color3*>(dst + dstByteOffset);
+ const Color4& s = src[srcIndex];
+ d = Color3(s);
+ }
+ dstByteOffset += dstRowPadBytes;
+ }
+}
+
+// *******************
+// RGB <-> YUV color space conversions
+// *******************
+
+static uint32 blendPixels(uint32 pixel1, uint32 pixel2) {
+ static const uint32 rbMask = 0x00FF00FF;
+ static const uint32 agMask = 0xFF00FF00;
+
+ // Compute two color channels at a time. Use >> 1 for fast division by two
+ // Using alternating color channels prevents overflow
+ const uint32 rb = ((pixel1 & rbMask) + (pixel2 & rbMask)) >> 1;
+
+ // Shift first to avoid overflow in alpha channel
+ const uint32 ag = (((pixel1 & agMask) >> 1) + ((pixel2 & agMask) >> 1));
+
+ return ((rb & rbMask) | (ag & agMask));
+}
+
+#define PIXEL_RGB8_TO_YUV_Y(r, g, b) static_cast<uint8>(iClamp(((66 * r + 129 * g + 25 * b + 128) >> 8) + 16, 0, 255))
+#define PIXEL_RGB8_TO_YUV_U(r, g, b) static_cast<uint8>(iClamp(((-38 * r - 74 * g + 112 * b + 128) >> 8) + 128, 0, 255))
+#define PIXEL_RGB8_TO_YUV_V(r, g, b) static_cast<uint8>(iClamp(((112 * r - 94 * g - 18 * b + 128) >> 8) + 128, 0, 255))
+
+static void rgb8_to_yuv420p(const Array<const void*>& srcBytes, int srcWidth, int srcHeight, const ImageFormat* srcFormat, int srcRowPadBits, const Array<void*>& dstBytes, const ImageFormat* dstFormat, int dstRowPadBits, bool invertY, ImageFormat::BayerAlgorithm bayerAlg) {
+ debugAssertM(srcRowPadBits == 0, "Source row padding must be 0 for this format");
+ debugAssertM((srcWidth % 2 == 0) && (srcHeight % 2 == 0), "Source width and height must be a multiple of two");
+
+ const Color3uint8* src = static_cast<const Color3uint8*>(srcBytes[0]);
+
+ uint8* dstY = static_cast<uint8*>(dstBytes[0]);
+ uint8* dstU = static_cast<uint8*>(dstBytes[1]);
+ uint8* dstV = static_cast<uint8*>(dstBytes[2]);
+
+ for (int y = 0; y < srcHeight; y += 2) {
+ for (int x = 0; x < srcWidth; x += 2) {
+
+ // convert 4-pixel block at a time
+ int srcPixelOffset0 = y * srcWidth + x;
+ int srcPixelOffset1 = srcPixelOffset0 + 1;
+ int srcPixelOffset2 = srcPixelOffset0 + srcWidth;
+ int srcPixelOffset3 = srcPixelOffset2 + 1;
+
+ int yIndex = y * srcWidth + x;
+
+ dstY[yIndex] = PIXEL_RGB8_TO_YUV_Y(src[srcPixelOffset0].r, src[srcPixelOffset0].g, src[srcPixelOffset0].b);
+ dstY[yIndex + 1] = PIXEL_RGB8_TO_YUV_Y(src[srcPixelOffset1].r, src[srcPixelOffset1].g, src[srcPixelOffset1].b);
+
+ yIndex += srcWidth;
+ dstY[yIndex] = PIXEL_RGB8_TO_YUV_Y(src[srcPixelOffset2].r, src[srcPixelOffset2].g, src[srcPixelOffset2].b);
+ dstY[yIndex + 1] = PIXEL_RGB8_TO_YUV_Y(src[srcPixelOffset3].r, src[srcPixelOffset3].g, src[srcPixelOffset3].b);
+
+ uint32 blendedPixel = blendPixels(src[srcPixelOffset0].asUInt32(), src[srcPixelOffset2].asUInt32());
+ Color3uint8 uvSrcColor = Color3uint8::fromARGB(blendedPixel);
+
+ int uvIndex = y / 2 * srcWidth / 2 + x / 2;
+ dstU[uvIndex] = PIXEL_RGB8_TO_YUV_U(uvSrcColor.r, uvSrcColor.g, uvSrcColor.b);
+ dstV[uvIndex] = PIXEL_RGB8_TO_YUV_V(uvSrcColor.r, uvSrcColor.g, uvSrcColor.b);
+ }
+ }
+}
+
+static void rgb8_to_yuv422(const Array<const void*>& srcBytes, int srcWidth, int srcHeight, const ImageFormat* srcFormat, int srcRowPadBits, const Array<void*>& dstBytes, const ImageFormat* dstFormat, int dstRowPadBits, bool invertY, ImageFormat::BayerAlgorithm bayerAlg) {
+ debugAssertM(srcRowPadBits == 0, "Source row padding must be 0 for this format");
+ debugAssertM((srcWidth % 2 == 0), "Source width must be a multiple of two");
+
+ const Color3uint8* src = static_cast<const Color3uint8*>(srcBytes[0]);
+
+ uint8* dst = static_cast<uint8*>(dstBytes[0]);
+
+ for (int y = 0; y < srcHeight; ++y) {
+ for (int x = 0; x < srcWidth; x += 2) {
+
+ // convert 2-pixel horizontal block at a time
+ int srcIndex = y * srcWidth + x;
+ int dstIndex = srcIndex * 2;
+
+ uint32 blendedPixel = blendPixels(src[srcIndex].asUInt32(), src[srcIndex + 1].asUInt32());
+ Color3uint8 uvSrcColor = Color3uint8::fromARGB(blendedPixel);
+
+ dst[dstIndex] = PIXEL_RGB8_TO_YUV_Y(src[srcIndex].r, src[srcIndex].g, src[srcIndex].b);
+
+ dst[dstIndex + 1] = PIXEL_RGB8_TO_YUV_U(uvSrcColor.r, uvSrcColor.g, uvSrcColor.b);
+
+ dst[dstIndex + 2] = PIXEL_RGB8_TO_YUV_Y(src[srcIndex + 1].r, src[srcIndex + 1].g, src[srcIndex + 1].b);
+
+ dst[dstIndex + 3] = PIXEL_RGB8_TO_YUV_V(uvSrcColor.r, uvSrcColor.g, uvSrcColor.b);
+
+ }
+ }
+}
+
+static void rgb8_to_yuv444(const Array<const void*>& srcBytes, int srcWidth, int srcHeight, const ImageFormat* srcFormat, int srcRowPadBits, const Array<void*>& dstBytes, const ImageFormat* dstFormat, int dstRowPadBits, bool invertY, ImageFormat::BayerAlgorithm bayerAlg) {
+ debugAssertM(srcRowPadBits == 0, "Source row padding must be 0 for this format");
+
+ const Color3uint8* src = static_cast<const Color3uint8*>(srcBytes[0]);
+
+ Color3uint8* dst = static_cast<Color3uint8*>(dstBytes[0]);
+
+ for (int y = 0; y < srcHeight; ++y) {
+ for (int x = 0; x < srcWidth; ++x) {
+
+ // convert 1-pixels at a time
+ int index = y * srcWidth + x;
+ uint8 y = PIXEL_RGB8_TO_YUV_Y(src[index].r, src[index].g, src[index].b);
+ uint8 u = PIXEL_RGB8_TO_YUV_U(src[index].r, src[index].g, src[index].b);
+ uint8 v = PIXEL_RGB8_TO_YUV_V(src[index].r, src[index].g, src[index].b);
+
+ dst[index].r = y;
+ dst[index].g = u;
+ dst[index].b = v;
+ }
+ }
+}
+
+
+#define PIXEL_YUV_TO_RGB8_R(y, u, v) static_cast<uint8>(iClamp((298 * (y - 16) + 409 * (v - 128) + 128) >> 8, 0, 255))
+#define PIXEL_YUV_TO_RGB8_G(y, u, v) static_cast<uint8>(iClamp((298 * (y - 16) - 100 * (u - 128) - 208 * (v - 128) + 128) >> 8, 0, 255))
+#define PIXEL_YUV_TO_RGB8_B(y, u, v) static_cast<uint8>(iClamp((298 * (y - 16) + 516 * (u - 128) + 128) >> 8, 0, 255))
+
+static void yuv420p_to_rgb8(const Array<const void*>& srcBytes, int srcWidth, int srcHeight, const ImageFormat* srcFormat, int srcRowPadBits, const Array<void*>& dstBytes, const ImageFormat* dstFormat, int dstRowPadBits, bool invertY, ImageFormat::BayerAlgorithm bayerAlg) {
+ debugAssertM(srcRowPadBits == 0, "Source row padding must be 0 for this format");
+ debugAssertM((srcWidth % 2 == 0) && (srcHeight % 2 == 0), "Source width and height must be a multiple of two");
+
+ const uint8* srcY = static_cast<const uint8*>(srcBytes[0]);
+ const uint8* srcU = static_cast<const uint8*>(srcBytes[1]);
+ const uint8* srcV = static_cast<const uint8*>(srcBytes[2]);
+
+ Color3uint8* dst = static_cast<Color3uint8*>(dstBytes[0]);
+
+ for (int y = 0; y < srcHeight; ++y) {
+ for (int x = 0; x < srcWidth; x += 2) {
+
+ // convert to two rgb pixels in a row
+ Color3uint8* rgb = &dst[y * srcWidth + x];
+
+ int yOffset = y * srcWidth + x;
+ int uvOffset = y / 2 * srcWidth / 2 + x / 2;
+
+ rgb->r = PIXEL_YUV_TO_RGB8_R(srcY[yOffset], srcU[uvOffset], srcV[uvOffset]);
+ rgb->g = PIXEL_YUV_TO_RGB8_G(srcY[yOffset], srcU[uvOffset], srcV[uvOffset]);
+ rgb->b = PIXEL_YUV_TO_RGB8_B(srcY[yOffset], srcU[uvOffset], srcV[uvOffset]);
+
+ rgb += 1;
+ rgb->r = PIXEL_YUV_TO_RGB8_R(srcY[yOffset + 1], srcU[uvOffset], srcV[uvOffset]);
+ rgb->g = PIXEL_YUV_TO_RGB8_G(srcY[yOffset + 1], srcU[uvOffset], srcV[uvOffset]);
+ rgb->b = PIXEL_YUV_TO_RGB8_B(srcY[yOffset + 1], srcU[uvOffset], srcV[uvOffset]);
+ }
+ }
+}
+
+static void yuv422_to_rgb8(const Array<const void*>& srcBytes, int srcWidth, int srcHeight, const ImageFormat* srcFormat, int srcRowPadBits, const Array<void*>& dstBytes, const ImageFormat* dstFormat, int dstRowPadBits, bool invertY, ImageFormat::BayerAlgorithm bayerAlg) {
+ debugAssertM(srcRowPadBits == 0, "Source row padding must be 0 for this format");
+ debugAssertM((srcWidth % 2 == 0), "Source width must be a multiple of two");
+
+ const uint8* src = static_cast<const uint8*>(srcBytes[0]);
+
+ Color3uint8* dst = static_cast<Color3uint8*>(dstBytes[0]);
+
+ for (int y = 0; y < srcHeight; ++y) {
+ for (int x = 0; x < srcWidth; x += 2) {
+
+ // convert to two rgb pixels in a row
+ Color3uint8* rgb = &dst[y * srcWidth + x];
+
+ int srcIndex = (y * srcWidth + x) * 2;
+ uint8 y = src[srcIndex];
+ uint8 u = src[srcIndex + 1];
+ uint8 y2 = src[srcIndex + 2];
+ uint8 v = src[srcIndex + 3];
+
+ rgb->r = PIXEL_YUV_TO_RGB8_R(y, u, v);
+ rgb->g = PIXEL_YUV_TO_RGB8_G(y, u, v);
+ rgb->b = PIXEL_YUV_TO_RGB8_B(y, u, v);
+
+ rgb += 1;
+ rgb->r = PIXEL_YUV_TO_RGB8_R(y2, u, v);
+ rgb->g = PIXEL_YUV_TO_RGB8_G(y2, u, v);
+ rgb->b = PIXEL_YUV_TO_RGB8_B(y2, u, v);
+ }
+ }
+}
+
+static void yuv444_to_rgb8(const Array<const void*>& srcBytes, int srcWidth, int srcHeight, const ImageFormat* srcFormat, int srcRowPadBits, const Array<void*>& dstBytes, const ImageFormat* dstFormat, int dstRowPadBits, bool invertY, ImageFormat::BayerAlgorithm bayerAlg) {
+ debugAssertM(srcRowPadBits == 0, "Source row padding must be 0 for this format");
+
+ const Color3uint8* src = static_cast<const Color3uint8*>(srcBytes[0]);
+
+ Color3uint8* dst = static_cast<Color3uint8*>(dstBytes[0]);
+
+ for (int y = 0; y < srcHeight; ++y) {
+ for (int x = 0; x < srcWidth; ++x) {
+
+ // convert to one rgb pixels at a time
+ int index = y * srcWidth + x;
+ Color3uint8* rgb = &dst[index];
+
+ rgb->r = PIXEL_YUV_TO_RGB8_R(src[index].r, src[index].g, src[index].b);
+ rgb->g = PIXEL_YUV_TO_RGB8_G(src[index].r, src[index].g, src[index].b);
+ rgb->b = PIXEL_YUV_TO_RGB8_B(src[index].r, src[index].g, src[index].b);
+ }
+ }
+}
+
+////////////////////////////////////////////////////////////////////////////////////////////////
+//
+// Bayer conversions
+//
+
+// There are two kinds of rows (GR and BG).
+// In each row, there are two kinds of pixels (G/R, B/G).
+// We express the four kinds of INPUT pixels as:
+// GRG, GRG, BGB, BGG
+//
+// There are three kinds of OUTPUT pixels: R, G, B.
+// Thus there are nominally 12 different I/O combinations,
+// but several are impulses because needed output at that
+// location *is* the input (e.g., G_GRG and G_BGG).
+//
+// The following 5x5 row-major filters are named as output_input.
+
+// Green
+static const float G_GRR[5][5] =
+ {{ 0.0f, 0.0f, -1.0f, 0.0f, 0.0f},
+ { 0.0f, 0.0f, 2.0f, 0.0f, 0.0f},
+ { -1.0f, 2.0f, 4.0f, 2.0f, -1.0f},
+ { 0.0f, 0.0f, 2.0f, 0.0f, 0.0f},
+ { 0.0f, 0.0f, -1.0f, 0.0f, 0.0f}};
+
+static const float G_BGB[5][5] =
+ {{ 0.0f, 0.0f, -1.0f, 0.0f, 0.0f},
+ { 0.0f, 0.0f, 2.0f, 0.0f, 0.0f},
+ { -1.0f, 2.0f, 4.0f, 2.0f, -1.0f},
+ { 0.0f, 0.0f, 2.0f, 0.0f, 0.0f},
+ { 0.0f, 0.0f, -1.0f, 0.0f, 0.0f}};
+
+// Red
+//(the caption in the paper is wrong for this case:
+// "R row B column really means R row G column"
+static const float R_GRG[5][5] =
+ {{ 0.0f, 0.0f, 0.5f, 0.0f, 0.0f},
+ { 0.0f, -1.0f, 0.0f, -1.0f, 0.0f},
+ { -1.0f, 4.0f, 5.0f, 4.0f, -1.0f},
+ { 0.0f, -1.0f, 0.0f, -1.0f, 0.0f},
+ { 0.0f, 0.0f, 0.5f, 0.0f, 0.0f}};
+
+static const float R_BGG[5][5] =
+ {{ 0.0f, 0.0f, -1.0f, 0.0f, 0.0f},
+ { 0.0f, -1.0f, 4.0f, -1.0f, 0.0f},
+ { 0.5f, 0.0f, 5.0f, 0.0f, 0.5f},
+ { 0.0f, -1.0f, 4.0f, -1.0f, 0.0f},
+ { 0.0f, 0.0f, -1.0f, 0.0f, 0.0f}};
+
+static const float R_BGB[5][5] =
+ {{ 0.0f, 0.0f, -3.0f/2.0f, 0.0f, 0.0f},
+ { 0.0f, 2.0f, 0.0f, 2.0f, 0.0f},
+ {-3.0f/2.0f, 0.0f, 6.0f, 0.0f, -3.0f/2.0f},
+ { 0.0f, 2.0f, 0.0f, 2.0f, 0.0f},
+ { 0.0f, 0.0f, -3.0f/2.0f, 0.0f, 0.0f}};
+
+
+// Blue
+//(the caption in the paper is wrong for this case:
+// "B row R column really means B row G column")
+#define B_BGG R_GRG
+#define B_GRG R_BGG
+#define B_GRR R_BGB
+
+// =====================================================================
+// Helper methods
+// =====================================================================
+
+
+/** Applies a 5x5 filter to monochrome image I (wrapping at the boundaries) */
+static uint8 applyFilter(const uint8* I,
+ int x,
+ int y,
+ int w,
+ int h,
+ const float filter[5][5]) {
+
+ debugAssert(isEven(w));
+ debugAssert(isEven(h));
+
+ float sum = 0.0f;
+ float denom = 0.0f;
+
+ for (int dy = 0; dy < 5; ++dy) {
+ int offset = ((y + dy + h - 2) % h) * w;
+
+ for (int dx = 0; dx < 5; ++dx) {
+ float f = filter[dy][dx];
+ sum += f * I[((x + dx + w - 2) % w) + offset];
+ denom += f;
+ }
+ }
+
+ return (uint8)iClamp(iRound(sum / denom), 0, 255);
+}
+
+/** Helper method for Bayer grbg and bggr --> rgb8 */
+static void swapRedAndBlue(int N, Color3uint8* out) {
+ for (int i = N - 1; i >= 0; --i) {
+ uint8 tmp = out[i].r;
+ out[i].r = out[i].b;
+ out[i].b = tmp;
+ }
+}
+
+// RGB -> BAYER color space
+
+// =====================================================================
+// rgb8 --> bayer helpers
+// =====================================================================
+static void rgb8_to_bayer_rggb8(const int w, const int h,
+ const uint8* src, uint8* dst) {
+ Color3uint8* srcColor = (Color3uint8*)src;
+ Color1uint8* dstColor = (Color1uint8*)dst;
+
+ // Top row pixels
+ for (int y = 0; y < h - 1; y += 2) {
+ int offset = y * w;
+
+ // Top left pixels
+ for(int x = 0; x < w - 1; x += 2) {
+ dstColor[x + offset] = Color1(srcColor[x + offset].r);
+ }
+
+ // Top right pixels
+ for(int x = 1; x < w - 1; x += 2) {
+ dstColor[x + offset] = Color1(srcColor[x + offset].g);
+ }
+ }
+
+ // Bottom row pixels
+ for (int y = 1; y < h - 1; y += 2) {
+ int offset = y * w;
+
+ // Bottom left pixels
+ for (int x = 0; x < w - 1; x += 2) {
+ dstColor[x + offset] = Color1(srcColor[x + offset].g);
+ }
+
+ // Bottom right pixels
+ for (int x = 1; x < w - 1; x += 2) {
+ dstColor[x + offset] = Color1(srcColor[x + offset].b);
+ }
+ }
+}
+
+
+static void rgb8_to_bayer_grbg8(const int w, const int h,
+ const uint8* src, uint8* dst) {
+ Color3uint8* srcColor = (Color3uint8*)src;
+ Color1uint8* dstColor = (Color1uint8*)dst;
+
+ // Top row pixels
+ for (int y = 0; y < h - 1; y += 2) {
+ int offset = y * w;
+
+ // Top left pixels
+ for (int x = 0; x < w - 1; x += 2) {
+ dstColor[x + offset] = Color1(srcColor[x + offset].g);
+ }
+
+ // Top right pixels
+ for (int x = 1; x < w - 1; x += 2) {
+ dstColor[x + offset] = Color1(srcColor[x + offset].r);
+ }
+ }
+
+ // Bottom row pixels
+ for (int y = 1; y < h - 1; y += 2) {
+ int offset = y * w;
+
+ // Bottom left pixels
+ for (int x = 0; x < w - 1; x += 2) {
+ dstColor[x + offset] = Color1(srcColor[x + offset].b);
+ }
+
+ // Bottom right pixels
+ for (int x = 1; x < w - 1; x += 2) {
+ dstColor[x + offset] = Color1(srcColor[x + offset].g);
+ }
+ }
+}
+
+
+static void rgb8_to_bayer_bggr8(const int w, const int h,
+ const uint8* src, uint8* dst) {
+ Color3uint8* srcColor = (Color3uint8*)src;
+ Color1uint8* dstColor = (Color1uint8*)dst;
+
+ // Top row pixels
+ for (int y = 0; y < h - 1; y += 2) {
+ int offset = y * w;
+
+ // Top left pixels
+ for (int x = 0; x < w - 1; x += 2) {
+ dstColor[x + offset] = Color1(srcColor[x + offset].b);
+ }
+
+ // Top right pixels
+ for (int x = 1; x < w - 1; x += 2) {
+ dstColor[x + offset] = Color1(srcColor[x + offset].g);
+ }
+ }
+
+ // Bottom row pixels
+ for (int y = 1; y < h - 1; y += 2) {
+ int offset = y * w;
+
+ // Bottom left pixels
+ for(int x = 0; x < w - 1; x += 2) {
+ dstColor[x + offset] = Color1(srcColor[x + offset].g);
+ }
+
+ // Bottom right pixels
+ for(int x = 1; x < w - 1; x += 2) {
+ dstColor[x + offset] = Color1(srcColor[x + offset].r);
+ }
+ }
+}
+
+
+static void rgb8_to_bayer_gbrg8(const int w, const int h,
+ const uint8* src, uint8* dst) {
+ Color3uint8* srcColor = (Color3uint8*)src;
+ Color1uint8* dstColor = (Color1uint8*)dst;
+
+ // Top row pixels
+ for(int y = 0; y < h - 1; y += 2) {
+ int offset = y * w;
+
+ // Top left pixels
+ for(int x = 0; x < w - 1; x += 2) {
+ dstColor[x + offset] = Color1(srcColor[x + offset].g);
+ }
+
+ // Top right pixels
+ for(int x = 1; x < w - 1; x += 2) {
+ dstColor[x + offset] = Color1(srcColor[x + offset].b);
+ }
+ }
+
+ // Bottom row pixels
+ for(int y = 1; y < h - 1; y += 2) {
+ int offset = y * w;
+
+ // Bottom left pixels
+ for(int x = 0; x < w - 1; x += 2) {
+ dstColor[x + offset] = Color1(srcColor[x + offset].r);
+ }
+
+ // Bottom right pixels
+ for(int x = 1; x < w - 1; x += 2) {
+ dstColor[x + offset] = Color1(srcColor[x + offset].g);
+ }
+ }
+}
+
+// =====================================================================
+// rgba32f (-->rgb8) --> bayer converter implementations
+// =====================================================================
+static void rgba32f_to_bayer_rggb8(const Array<const void*>& srcBytes, int srcWidth, int srcHeight, const ImageFormat* srcFormat, int srcRowPadBits, const Array<void*>& dstBytes, const ImageFormat* dstFormat, int dstRowPadBits, bool invertY, ImageFormat::BayerAlgorithm bayerAlg) {
+ Array<void*> tmp;
+ tmp.append(System::malloc(srcWidth * srcHeight * sizeof(Color3uint8)));
+
+ rgba32f_to_rgb8(srcBytes, srcWidth, srcHeight, ImageFormat::RGBA32F(), 0, tmp, ImageFormat::RGB8(), 0, invertY, bayerAlg);
+ rgb8_to_bayer_rggb8(srcWidth, srcHeight, static_cast<uint8*>(tmp[0]), static_cast<uint8*>(dstBytes[0]));
+
+ System::free(tmp[0]);
+}
+
+static void rgba32f_to_bayer_gbrg8(const Array<const void*>& srcBytes, int srcWidth, int srcHeight, const ImageFormat* srcFormat, int srcRowPadBits, const Array<void*>& dstBytes, const ImageFormat* dstFormat, int dstRowPadBits, bool invertY, ImageFormat::BayerAlgorithm bayerAlg) {
+ Array<void*> tmp;
+ tmp.append(System::malloc(srcWidth * srcHeight * sizeof(Color3uint8)));
+
+ rgba32f_to_rgb8(srcBytes, srcWidth, srcHeight, ImageFormat::RGBA32F(), 0, tmp, ImageFormat::RGB8(), 0, invertY, bayerAlg);
+ rgb8_to_bayer_grbg8(srcWidth, srcHeight, static_cast<uint8*>(tmp[0]), static_cast<uint8*>(dstBytes[0]));
+
+ System::free(tmp[0]);
+}
+
+static void rgba32f_to_bayer_grbg8(const Array<const void*>& srcBytes, int srcWidth, int srcHeight, const ImageFormat* srcFormat, int srcRowPadBits, const Array<void*>& dstBytes, const ImageFormat* dstFormat, int dstRowPadBits, bool invertY, ImageFormat::BayerAlgorithm bayerAlg) {
+ Array<void*> tmp;
+ tmp.append(System::malloc(srcWidth * srcHeight * sizeof(Color3uint8)));
+
+ rgba32f_to_rgb8(srcBytes, srcWidth, srcHeight, ImageFormat::RGBA32F(), 0, tmp, ImageFormat::RGB8(), 0, invertY, bayerAlg);
+ rgb8_to_bayer_gbrg8(srcWidth, srcHeight, static_cast<uint8*>(tmp[0]), static_cast<uint8*>(dstBytes[0]));
+
+ System::free(tmp[0]);
+}
+
+static void rgba32f_to_bayer_bggr8(const Array<const void*>& srcBytes, int srcWidth, int srcHeight, const ImageFormat* srcFormat, int srcRowPadBits, const Array<void*>& dstBytes, const ImageFormat* dstFormat, int dstRowPadBits, bool invertY, ImageFormat::BayerAlgorithm bayerAlg) {
+ Array<void*> tmp;
+ tmp.append(System::malloc(srcWidth * srcHeight * sizeof(Color3uint8)));
+
+ rgba32f_to_rgb8(srcBytes, srcWidth, srcHeight, ImageFormat::RGBA32F(), 0, tmp, ImageFormat::RGB8(), 0, invertY, bayerAlg);
+ rgb8_to_bayer_bggr8(srcWidth, srcHeight, static_cast<uint8*>(tmp[0]), static_cast<uint8*>(dstBytes[0]));
+
+ System::free(tmp[0]);
+}
+
+// BAYER -> RGB color space
+
+// =====================================================================
+// bayer --> rgb8 helpers
+// =====================================================================
+static void bayer_rggb8_to_rgb8_mhc(int w, int h,
+ const uint8* in, uint8* _out) {
+ debugAssert(in != _out);
+
+ Color3uint8* out = (Color3uint8*)_out;
+
+ for (int y = 0; y < h; ++y) {
+
+ // Row beginning in the input array.
+ int offset = y * w;
+
+ // RG row
+ for (int x = 0; x < w; ++x, ++out) {
+ // R pixel
+ {
+ out->r = in[x + offset];
+ out->g = applyFilter(in, x, y, w, h, G_GRR);
+ out->b = applyFilter(in, x, y, w, h, B_GRR);
+ }
+ ++x; ++out;
+
+ // G pixel
+ {
+ out->r = applyFilter(in, x, y, w, h, R_GRG);
+ out->g = in[x + offset];
+ out->b = applyFilter(in, x, y, w, h, B_GRG);
+ }
+ }
+
+ ++y;
+ offset += w;
+
+ // GB row
+ for (int x = 0; x < w; ++x, ++out) {
+ // G pixel
+ {
+ out->r = applyFilter(in, x, y, w, h, R_BGG);
+ out->g = in[x + offset];
+ out->b = applyFilter(in, x, y, w, h, B_BGG);
+ }
+ ++x; ++out;
+
+ // B pixel
+ {
+ out->r = applyFilter(in, x, y, w, h, R_BGB);
+ out->g = applyFilter(in, x, y, w, h, G_BGB);
+ out->b = in[x + offset];
+ }
+ }
+ }
+}
+
+
+
+static void bayer_gbrg8_to_rgb8_mhc(int w, int h,
+ const uint8* in, uint8* _out) {
+
+ debugAssert(in != _out);
+
+ Color3uint8* out = (Color3uint8*)_out;
+
+ for (int y = 0; y < h; ++y) {
+
+ // Row beginning in the input array.
+ int offset = y * w;
+
+ // GB row
+ for (int x = 0; x < w; ++x, ++out) {
+ // G pixel
+ {
+ out->r = applyFilter(in, x, y, w, h, R_BGG);
+ out->g = in[x + offset];
+ out->b = applyFilter(in, x, y, w, h, B_BGG);
+ }
+ ++x; ++out;
+
+ // B pixel
+ {
+ out->r = applyFilter(in, x, y, w, h, R_BGB);
+ out->g = applyFilter(in, x, y, w, h, G_BGB);
+ out->b = in[x + offset];
+ }
+ }
+ }
+}
+
+
+static void bayer_grbg8_to_rgb8_mhc(int w, int h,
+ const uint8* in, uint8* _out) {
+ // Run the equivalent function for red
+ bayer_gbrg8_to_rgb8_mhc(w, h, in, _out);
+
+ // Now swap red and blue
+ swapRedAndBlue(w * h, (Color3uint8*)_out);
+}
+
+
+static void bayer_bggr8_to_rgb8_mhc(int w, int h,
+ const uint8* in, uint8* _out) {
+ // Run the equivalent function for red
+ bayer_rggb8_to_rgb8_mhc(w, h, in, _out);
+
+ // Now swap red and blue
+ swapRedAndBlue(w * h, (Color3uint8*)_out);
+}
+
+// =====================================================================
+// bayer (--> rgb8) --> rgba32f converter implementations
+// =====================================================================
+static void bayer_rggb8_to_rgba32f(const Array<const void*>& srcBytes, int srcWidth, int srcHeight, const ImageFormat* srcFormat, int srcRowPadBits, const Array<void*>& dstBytes, const ImageFormat* dstFormat, int dstRowPadBits, bool invertY, ImageFormat::BayerAlgorithm bayerAlg) {
+ Array<void*> tmp;
+ tmp.append(System::malloc(srcWidth * srcHeight * sizeof(Color3uint8)));
+
+ bayer_rggb8_to_rgb8_mhc(srcWidth, srcHeight, static_cast<const uint8*>(srcBytes[0]), static_cast<uint8*>(tmp[0]));
+ rgb8_to_rgba32f(reinterpret_cast<Array<const void*>&>(tmp), srcWidth, srcHeight, ImageFormat::RGB8(), 0, dstBytes, ImageFormat::RGBA32F(), 0, invertY, bayerAlg);
+
+ System::free(tmp[0]);
+}
+
+static void bayer_gbrg8_to_rgba32f(const Array<const void*>& srcBytes, int srcWidth, int srcHeight, const ImageFormat* srcFormat, int srcRowPadBits, const Array<void*>& dstBytes, const ImageFormat* dstFormat, int dstRowPadBits, bool invertY, ImageFormat::BayerAlgorithm bayerAlg) {
+ Array<void*> tmp;
+ tmp.append(System::malloc(srcWidth * srcHeight * sizeof(Color3uint8)));
+
+ bayer_grbg8_to_rgb8_mhc(srcWidth, srcHeight, static_cast<const uint8*>(srcBytes[0]), static_cast<uint8*>(tmp[0]));
+ rgb8_to_rgba32f(reinterpret_cast<Array<const void*>&>(tmp), srcWidth, srcHeight, ImageFormat::RGB8(), 0, dstBytes, ImageFormat::RGBA32F(), 0, invertY, bayerAlg);
+
+ System::free(tmp[0]);
+}
+
+static void bayer_grbg8_to_rgba32f(const Array<const void*>& srcBytes, int srcWidth, int srcHeight, const ImageFormat* srcFormat, int srcRowPadBits, const Array<void*>& dstBytes, const ImageFormat* dstFormat, int dstRowPadBits, bool invertY, ImageFormat::BayerAlgorithm bayerAlg) {
+ Array<void*> tmp;
+ tmp.append(System::malloc(srcWidth * srcHeight * sizeof(Color3uint8)));
+
+ bayer_gbrg8_to_rgb8_mhc(srcWidth, srcHeight, static_cast<const uint8*>(srcBytes[0]), static_cast<uint8*>(tmp[0]));
+ rgb8_to_rgba32f(reinterpret_cast<Array<const void*>&>(tmp), srcWidth, srcHeight, ImageFormat::RGB8(), 0, dstBytes, ImageFormat::RGBA32F(), 0, invertY, bayerAlg);
+
+ System::free(tmp[0]);
+}
+
+static void bayer_bggr8_to_rgba32f(const Array<const void*>& srcBytes, int srcWidth, int srcHeight, const ImageFormat* srcFormat, int srcRowPadBits, const Array<void*>& dstBytes, const ImageFormat* dstFormat, int dstRowPadBits, bool invertY, ImageFormat::BayerAlgorithm bayerAlg) {
+ Array<void*> tmp;
+ tmp.append(System::malloc(srcWidth * srcHeight * sizeof(Color3uint8)));
+
+ bayer_bggr8_to_rgb8_mhc(srcWidth, srcHeight, static_cast<const uint8*>(srcBytes[0]), static_cast<uint8*>(tmp[0]));
+ rgb8_to_rgba32f(reinterpret_cast<Array<const void*>&>(tmp), srcWidth, srcHeight, ImageFormat::RGB8(), 0, dstBytes, ImageFormat::RGBA32F(), 0, invertY, bayerAlg);
+
+ System::free(tmp[0]);
+}
+
+
+
+
+
+ // TODO: The following region is commented out because so far
+ // those conversions are not used anywhere else. Until it is
+ // decided that such conversions are not needed, this region
+ // remains commented out.
+
+
+// // =====================================================================
+// // bayer --> bgr8
+// // =====================================================================
+
+// static void bayer_rggb8_to_bgr8_mhc(int w, int h,
+// const uint8* in, uint8* _out) {
+// debugAssert(in != _out);
+
+// Color3uint8* out = (Color3uint8*)_out;
+
+// for (int y = 0; y < h; ++y) {
+
+// // Row beginning in the input array.
+// int offset = y * w;
+
+// // RG row
+// for (int x = 0; x < w; ++x, ++out) {
+// // R pixel
+// {
+// out->b = in[x + offset];
+// out->g = applyFilter(in, x, y, w, h, G_GRR);
+// out->r = applyFilter(in, x, y, w, h, B_GRR);
+// }
+// ++x; ++out;
+
+// // G pixel
+// {
+// out->b = applyFilter(in, x, y, w, h, R_GRG);
+// out->g = in[x + offset];
+// out->r = applyFilter(in, x, y, w, h, B_GRG);
+// }
+// }
+
+// ++y;
+// offset += w;
+
+// // GB row
+// for (int x = 0; x < w; ++x, ++out) {
+// // G pixel
+// {
+// out->b = applyFilter(in, x, y, w, h, R_BGG);
+// out->g = in[x + offset];
+// out->r = applyFilter(in, x, y, w, h, B_BGG);
+// }
+// ++x; ++out;
+
+// // B pixel
+// {
+// out->b = applyFilter(in, x, y, w, h, R_BGB);
+// out->g = applyFilter(in, x, y, w, h, G_BGB);
+// out->r = in[x + offset];
+// }
+// }
+// }
+// }
+
+
+// static void bayer_gbrg8_to_bgr8_mhc(int w, int h,
+// const uint8* in, uint8* _out) {
+
+// debugAssert(in != _out);
+
+// Color3uint8* out = (Color3uint8*)_out;
+
+// for (int y = 0; y < h; ++y) {
+
+// // Row beginning in the input array.
+// int offset = y * w;
+
+// // GB row
+// for (int x = 0; x < srcWidth; ++x, ++out) {
+// // G pixel
+// {
+// out->b = applyFilter(in, x, y, w, h, R_BGG);
+// out->g = in[x + offset];
+// out->r = applyFilter(in, x, y, w, h, B_BGG);
+// }
+// ++x; ++out;
+
+// // B pixel
+// {
+// out->b = applyFilter(in, x, y, w, h, R_BGB);
+// out->g = applyFilter(in, x, y, w, h, G_BGB);
+// out->r = in[x + offset];
+// }
+// }
+// }
+// }
+
+// static void bayer_grbg8_to_bgr8_mhc(int w, int h,
+// const uint8* in, uint8* _out) {
+// // Run the equivalent function for red
+// bayer_gbrg8_to_bgr8_mhc(w, h, in, _out);
+
+// // Now swap red and blue
+// swapRedAndBlue(srcWidth * h, (Color3uint8*)_out);
+// }
+
+// static void bayer_bggr8_to_bgr8_mhc(int w, int h,
+// const uint8* in, uint8* _out) {
+// // Run the equivalent function for red
+// bayer_rggb8_to_bgr8_mhc(w, h, in, _out);
+
+// // Now swap red and blue
+// swapRedAndBlue(srcWidth * h, (Color3uint8*)_out);
+// }
+
+
+
+///////////////////////////////////////////////////
+
+} // namespace G3D
diff --git a/externals/g3dlite/G3D.lib/source/Line.cpp b/externals/g3dlite/G3D.lib/source/Line.cpp
new file mode 100644
index 00000000000..195ae7197f2
--- /dev/null
+++ b/externals/g3dlite/G3D.lib/source/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/G3D.lib/source/LineSegment.cpp b/externals/g3dlite/G3D.lib/source/LineSegment.cpp
new file mode 100644
index 00000000000..44dfcb48bc1
--- /dev/null
+++ b/externals/g3dlite/G3D.lib/source/LineSegment.cpp
@@ -0,0 +1,236 @@
+/**
+ @file LineSegment.cpp
+
+ @maintainer Morgan McGuire, matrix@graphics3d.com
+
+ @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/G3D.lib/source/Log.cpp b/externals/g3dlite/G3D.lib/source/Log.cpp
new file mode 100644
index 00000000000..13cea7a31f0
--- /dev/null
+++ b/externals/g3dlite/G3D.lib/source/Log.cpp
@@ -0,0 +1,157 @@
+/**
+ @file Log.cpp
+
+ @maintainer Morgan McGuire, morgan@graphics3d.com
+ @created 2001-08-04
+ @edited 2005-07-01
+ */
+
+#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);
+}
+
+
+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");
+ }
+
+ // Turn off buffering.
+ setvbuf(logFile, NULL, _IONBF, 0);
+
+ 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, ...) {
+ printHeader();
+
+ 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);
+}
+
+
+void Log::print(const std::string& s) {
+ printHeader();
+ fprintf(logFile, "%s", s.c_str());
+}
+
+
+void Log::println(const std::string& s) {
+ printHeader();
+ fprintf(logFile, "%s\n", s.c_str());
+}
+
+
+void Log::printHeader() {
+ time_t t;
+ if (time(&t) != ((time_t)-1)) {
+ /*
+ char buf[32];
+ strftime(buf, 32, "[%H:%M:%S]", localtime(&t));
+
+ Removed because this doesn't work on SDL threads.
+
+ #ifdef _DEBUG
+ std::string bt = getBacktrace(15, 2, stripFromStackBottom);
+ fprintf(logFile, "\n %s %s\n\n", buf, bt.c_str());
+ #endif
+
+ fprintf(logFile, "\n %s \n", buf);
+ */
+
+ } else {
+ println("[Error getting time]");
+ }
+}
+
+}
diff --git a/externals/g3dlite/G3D.lib/source/Matrix.cpp b/externals/g3dlite/G3D.lib/source/Matrix.cpp
new file mode 100644
index 00000000000..0c6db214543
--- /dev/null
+++ b/externals/g3dlite/G3D.lib/source/Matrix.cpp
@@ -0,0 +1,1801 @@
+/**
+ @file Matrix.cpp
+ @author Morgan McGuire, matrix@graphics3d.com
+ */
+#include "G3D/Matrix.h"
+#include "G3D/TextOutput.h"
+
+static inline G3D::Matrix::T negate(G3D::Matrix::T x) {
+ return -x;
+}
+
+namespace G3D {
+
+int Matrix::debugNumCopyOps = 0;
+int Matrix::debugNumAllocOps = 0;
+
+void Matrix::serialize(TextOutput& t) const {
+ t.writeSymbol("%");
+ t.writeNumber(rows());
+ t.writeSymbol("x");
+ t.writeNumber(cols());
+ t.pushIndent();
+ t.writeNewline();
+
+ t.writeSymbol("[");
+ for (int r = 0; r < rows(); ++r) {
+ for (int c = 0; c < cols(); ++c) {
+ t.writeNumber(impl->get(r, c));
+ if (c < cols() - 1) {
+ t.writeSymbol(",");
+ } else {
+ if (r < rows() - 1) {
+ t.writeSymbol(";");
+ t.writeNewline();
+ }
+ }
+ }
+ }
+ t.writeSymbol("]");
+ t.popIndent();
+ t.writeNewline();
+}
+
+
+std::string Matrix::toString(const std::string& name) const {
+ std::string s;
+
+ if (name != "") {
+ s += format("%s = \n", name.c_str());
+ }
+
+ s += "[";
+ for (int r = 0; r < rows(); ++r) {
+ for (int c = 0; c < cols(); ++c) {
+ double v = impl->get(r, c);
+
+ if (::fabs(v) < 0.00001) {
+ // Don't print "negative zero"
+ s += format("% 10.04g", 0.0);
+ } else if (v == iRound(v)) {
+ // Print integers nicely
+ s += format("% 10.04g", v);
+ } else {
+ s += format("% 10.04f", v);
+ }
+
+ if (c < cols() - 1) {
+ s += ",";
+ } else if (r < rows() - 1) {
+ s += ";\n ";
+ } else {
+ s += "]\n";
+ }
+ }
+ }
+ return s;
+}
+
+
+#define INPLACE(OP)\
+ ImplRef A = impl;\
+\
+ if (! A.isLastReference()) {\
+ impl = new Impl(A->R, A->C);\
+ }\
+\
+ A->OP(B, *impl);
+
+Matrix& Matrix::operator*=(const T& B) {
+ INPLACE(mul)
+ return *this;
+}
+
+
+Matrix& Matrix::operator-=(const T& B) {
+ INPLACE(sub)
+ return *this;
+}
+
+
+Matrix& Matrix::operator+=(const T& B) {
+ INPLACE(add)
+ return *this;
+}
+
+
+Matrix& Matrix::operator/=(const T& B) {
+ INPLACE(div)
+ return *this;
+}
+
+
+Matrix& Matrix::operator*=(const Matrix& B) {
+ // We can't optimize this one
+ *this = *this * B;
+ return *this;
+}
+
+
+Matrix& Matrix::operator-=(const Matrix& _B) {
+ const Impl& B = *_B.impl;
+ INPLACE(sub)
+ return *this;
+}
+
+
+Matrix& Matrix::operator+=(const Matrix& _B) {
+ const Impl& B = *_B.impl;
+ INPLACE(add)
+ return *this;
+}
+
+
+void Matrix::arrayMulInPlace(const Matrix& _B) {
+ const Impl& B = *_B.impl;
+ INPLACE(arrayMul)
+}
+
+
+void Matrix::arrayDivInPlace(const Matrix& _B) {
+ const Impl& B = *_B.impl;
+ INPLACE(arrayDiv)
+}
+
+#undef INPLACE
+
+Matrix Matrix::fromDiagonal(const Matrix& d) {
+ debugAssert((d.rows() == 1) || (d.cols() == 1));
+
+ int n = d.numElements();
+ Matrix D = zero(n, n);
+ for (int i = 0; i < n; ++i) {
+ D.set(i, i, d.impl->data[i]);
+ }
+
+ return D;
+}
+
+void Matrix::set(int r, int c, T v) {
+ if (! impl.isLastReference()) {
+ // Copy the data before mutating; this object is shared
+ impl = new Impl(*impl);
+ }
+ impl->set(r, c, v);
+}
+
+
+void Matrix::setRow(int r, const Matrix& vec) {
+ debugAssertM(vec.cols() == cols(),
+ "A row must be set to a vector of the same size.");
+ debugAssertM(vec.rows() == 1,
+ "A row must be set to a row vector.");
+
+ debugAssert(r >= 0);
+ debugAssert(r < rows());
+
+ if (! impl.isLastReference()) {
+ // Copy the data before mutating; this object is shared
+ impl = new Impl(*impl);
+ }
+ impl->setRow(r, vec.impl->data);
+}
+
+
+void Matrix::setCol(int c, const Matrix& vec) {
+ debugAssertM(vec.rows() == rows(),
+ "A column must be set to a vector of the same size.");
+ debugAssertM(vec.cols() == 1,
+ "A column must be set to a column vector.");
+
+ debugAssert(c >= 0);
+
+ debugAssert(c < cols());
+
+ if (! impl.isLastReference()) {
+ // Copy the data before mutating; this object is shared
+ impl = new Impl(*impl);
+ }
+ impl->setCol(c, vec.impl->data);
+}
+
+
+Matrix::T Matrix::get(int r, int c) const {
+ return impl->get(r, c);
+}
+
+
+Matrix Matrix::row(int r) const {
+ debugAssert(r >= 0);
+ debugAssert(r < rows());
+ Matrix out(1, cols());
+ out.impl->setRow(1, impl->elt[r]);
+ return out;
+}
+
+
+Matrix Matrix::col(int c) const {
+ debugAssert(c >= 0);
+ debugAssert(c < cols());
+ Matrix out(rows(), 1);
+
+ T* outData = out.impl->data;
+ // Get a pointer to the first element in the column
+ const T* inElt = &(impl->elt[0][c]);
+ int R = rows();
+ int C = cols();
+ for (int r = 0; r < R; ++r) {
+ outData[r] = *inElt;
+ // Skip around to the next row
+ inElt += C;
+ }
+
+ return out;
+}
+
+
+Matrix Matrix::zero(int R, int C) {
+ Impl* A = new Impl(R, C);
+ A->setZero();
+ return Matrix(A);
+}
+
+
+Matrix Matrix::one(int R, int C) {
+ Impl* A = new Impl(R, C);
+ for (int i = R * C - 1; i >= 0; --i) {
+ A->data[i] = 1.0;
+ }
+ return Matrix(A);
+}
+
+
+Matrix Matrix::random(int R, int C) {
+ Impl* A = new Impl(R, C);
+ for (int i = R * C - 1; i >= 0; --i) {
+ A->data[i] = G3D::uniformRandom(0.0, 1.0);
+ }
+ return Matrix(A);
+}
+
+
+Matrix Matrix::identity(int N) {
+ Impl* m = new Impl(N, N);
+ m->setZero();
+ for (int i = 0; i < N; ++i) {
+ m->elt[i][i] = 1.0;
+ }
+ return Matrix(m);
+}
+
+
+// Implement an explicit-output unary method by trampolining to the impl
+#define TRAMPOLINE_EXPLICIT_1(method)\
+void Matrix::method(Matrix& out) const {\
+ if ((out.impl == impl) && impl.isLastReference()) {\
+ impl->method(*out.impl);\
+ } else {\
+ out = this->method();\
+ }\
+}
+
+TRAMPOLINE_EXPLICIT_1(abs)
+TRAMPOLINE_EXPLICIT_1(negate)
+TRAMPOLINE_EXPLICIT_1(arrayLog)
+TRAMPOLINE_EXPLICIT_1(arrayExp)
+TRAMPOLINE_EXPLICIT_1(arrayCos)
+TRAMPOLINE_EXPLICIT_1(arraySin)
+
+void Matrix::mulRow(int r, const T& v) {
+ debugAssert(r >= 0 && r < rows());
+
+ if (! impl.isLastReference()) {
+ impl = new Impl(*impl);
+ }
+
+ impl->mulRow(r, v);
+}
+
+
+void Matrix::transpose(Matrix& out) const {
+ if ((out.impl == impl) && impl.isLastReference() && (impl->R == impl->C)) {
+ // In place
+ impl->transpose(*out.impl);
+ } else {
+ out = this->transpose();
+ }
+}
+
+
+Matrix3 Matrix::toMatrix3() const {
+ debugAssert(impl->R == 3);
+ debugAssert(impl->C == 3);
+ return Matrix3(
+ impl->get(0,0), impl->get(0,1), impl->get(0,2),
+ impl->get(1,0), impl->get(1,1), impl->get(1,2),
+ impl->get(2,0), impl->get(2,1), impl->get(2,2));
+}
+
+
+Matrix4 Matrix::toMatrix4() const {
+ debugAssert(impl->R == 4);
+ debugAssert(impl->C == 4);
+ return Matrix4(
+ impl->get(0,0), impl->get(0,1), impl->get(0,2), impl->get(0,3),
+ impl->get(1,0), impl->get(1,1), impl->get(1,2), impl->get(1,3),
+ impl->get(2,0), impl->get(2,1), impl->get(2,2), impl->get(2,3),
+ impl->get(3,0), impl->get(3,1), impl->get(3,2), impl->get(3,3));
+}
+
+
+Vector2 Matrix::toVector2() const {
+ debugAssert(impl->R * impl->C == 2);
+ if (impl->R > impl->C) {
+ return Vector2(impl->get(0,0), impl->get(1,0));
+ } else {
+ return Vector2(impl->get(0,0), impl->get(0,1));
+ }
+}
+
+
+Vector3 Matrix::toVector3() const {
+ debugAssert(impl->R * impl->C == 3);
+ if (impl->R > impl->C) {
+ return Vector3(impl->get(0,0), impl->get(1,0), impl->get(2, 0));
+ } else {
+ return Vector3(impl->get(0,0), impl->get(0,1), impl->get(0, 2));
+ }
+}
+
+
+Vector4 Matrix::toVector4() const {
+ debugAssert(
+ ((impl->R == 4) && (impl->C == 1)) ||
+ ((impl->R == 1) && (impl->C == 4)));
+
+ if (impl->R > impl->C) {
+ return Vector4(impl->get(0,0), impl->get(1,0), impl->get(2, 0), impl->get(3,0));
+ } else {
+ return Vector4(impl->get(0,0), impl->get(0,1), impl->get(0, 2), impl->get(0,3));
+ }
+}
+
+
+void Matrix::swapRows(int r0, int r1) {
+ debugAssert(r0 >= 0 && r0 < rows());
+ debugAssert(r1 >= 0 && r1 < rows());
+
+ if (r0 == r1) {
+ return;
+ }
+
+ if (! impl.isLastReference()) {
+ impl = new Impl(*impl);
+ }
+
+ impl->swapRows(r0, r1);
+}
+
+
+void Matrix::swapAndNegateCols(int c0, int c1) {
+ debugAssert(c0 >= 0 && c0 < cols());
+ debugAssert(c1 >= 0 && c1 < cols());
+
+ if (c0 == c1) {
+ return;
+ }
+
+ if (! impl.isLastReference()) {
+ impl = new Impl(*impl);
+ }
+
+ impl->swapAndNegateCols(c0, c1);
+}
+
+Matrix Matrix::subMatrix(int r1, int r2, int c1, int c2) const {
+ debugAssert(r2>=r1);
+ debugAssert(c2>=c1);
+ debugAssert(c2<cols());
+ debugAssert(r2<rows());
+ debugAssert(r1>=0);
+ debugAssert(c1>=0);
+
+ Matrix X(r2 - r1 + 1, c2 - c1 + 1);
+
+ for (int r = 0; r < X.rows(); ++r) {
+ for (int c = 0; c < X.cols(); ++c) {
+ X.set(r, c, get(r + r1, c + c1));
+ }
+ }
+
+ return X;
+}
+
+
+bool Matrix::anyNonZero() const {
+ return impl->anyNonZero();
+}
+
+
+bool Matrix::allNonZero() const {
+ return impl->allNonZero();
+}
+
+
+void Matrix::svd(Matrix& U, Array<T>& d, Matrix& V, bool sort) const {
+ debugAssert(rows() >= cols());
+ debugAssertM(&U != &V, "Arguments to SVD must be different matrices");
+ debugAssertM(&U != this, "Arguments to SVD must be different matrices");
+ debugAssertM(&V != this, "Arguments to SVD must be different matrices");
+
+ int R = rows();
+ int C = cols();
+
+ // Make sure we don't overwrite a shared matrix
+ if (! V.impl.isLastReference()) {
+ V = Matrix::zero(C, C);
+ } else {
+ V.impl->setSize(C, C);
+ }
+
+ if (&U != this || ! impl.isLastReference()) {
+ // Make a copy of this for in-place SVD
+ U.impl = new Impl(*impl);
+ }
+
+ d.resize(C);
+ const char* ret = svdCore(U.impl->elt, R, C, d.getCArray(), V.impl->elt);
+
+ debugAssertM(ret == NULL, ret);
+ (void)ret;
+
+ if (sort) {
+ // Sort the singular values from greatest to least
+
+ Array<SortRank> rank(C);
+ for (int c = 0; c < C; ++c) {
+ rank[c].col = c;
+ rank[c].value = d[c];
+ }
+
+ rank.sort(SORT_INCREASING);
+
+ Matrix Uold = U;
+ Matrix Vold = V;
+
+ U = Matrix(U.rows(), U.cols());
+ V = Matrix(V.rows(), V.cols());
+
+ // Now permute U, d, and V appropriately
+ for (int c0 = 0; c0 < C; ++c0) {
+ const int c1 = rank[c0].col;
+
+ d[c0] = rank[c0].value;
+ U.setCol(c0, Uold.col(c1));
+ V.setCol(c0, Vold.col(c1));
+ }
+
+ }
+}
+
+
+#define COMPARE_SCALAR(OP)\
+Matrix Matrix::operator OP (const T& scalar) const {\
+ int R = rows();\
+ int C = cols();\
+ int N = R * C;\
+ Matrix out = Matrix::zero(R, C);\
+\
+ const T* raw = impl->data;\
+ T* outRaw = out.impl->data;\
+ for (int i = 0; i < N; ++i) {\
+ outRaw[i] = raw[i] OP scalar;\
+ }\
+\
+ return out;\
+}
+
+COMPARE_SCALAR(<)
+COMPARE_SCALAR(<=)
+COMPARE_SCALAR(>)
+COMPARE_SCALAR(>=)
+COMPARE_SCALAR(==)
+COMPARE_SCALAR(!=)
+
+#undef COMPARE_SCALAR
+
+double Matrix::normSquared() const {
+ int R = rows();
+ int C = cols();
+ int N = R * C;
+
+ double sum = 0.0;
+
+ const T* raw = impl->data;
+ for (int i = 0; i < N; ++i) {
+ sum += square(raw[i]);
+ }
+
+ return sum;
+}
+
+double Matrix::norm() const {
+ return sqrt(normSquared());
+}
+
+///////////////////////////////////////////////////////////
+
+Matrix::Impl::Impl(const Matrix3& M) : elt(NULL), data(NULL), R(0), C(0), dataSize(0){
+ setSize(3, 3);
+ for (int r = 0; r < 3; ++r) {
+ for (int c = 0; c < 3; ++c) {
+ set(r, c, M[r][c]);
+ }
+ }
+
+}
+
+
+Matrix::Impl::Impl(const Matrix4& M): elt(NULL), data(NULL), R(0), C(0), dataSize(0) {
+ setSize(4, 4);
+ for (int r = 0; r < 4; ++r) {
+ for (int c = 0; c < 4; ++c) {
+ set(r, c, M[r][c]);
+ }
+ }
+}
+
+
+void Matrix::Impl::setSize(int newRows, int newCols) {
+ if ((R == newRows) && (C == newCols)) {
+ // Nothing to do
+ return;
+ }
+
+ int newSize = newRows * newCols;
+
+ R = newRows; C = newCols;
+
+ // Only allocate if we need more space
+ // or the size difference is ridiculous
+ if ((newSize > dataSize) || (newSize < dataSize / 4)) {
+ System::alignedFree(data);
+ data = (float*)System::alignedMalloc(R * C * sizeof(T), 16);
+ ++Matrix::debugNumAllocOps;
+ dataSize = newSize;
+ }
+
+ // Construct the row pointers
+ //delete[] elt;
+ System::free(elt);
+ elt = (T**)System::malloc(R * sizeof(T*));// new T*[R];
+
+ for (int r = 0; r < R; ++ r) {
+ elt[r] = data + r * C;
+ }
+}
+
+
+Matrix::Impl::~Impl() {
+ //delete[] elt;
+ System::free(elt);
+ System::alignedFree(data);
+}
+
+
+Matrix::Impl& Matrix::Impl::operator=(const Impl& m) {
+ setSize(m.R, m.C);
+ System::memcpy(data, m.data, R * C * sizeof(T));
+ ++Matrix::debugNumCopyOps;
+ return *this;
+}
+
+
+void Matrix::Impl::setZero() {
+ System::memset(data, 0, R * C * sizeof(T));
+}
+
+
+void Matrix::Impl::swapRows(int r0, int r1) {
+ T* R0 = elt[r0];
+ T* R1 = elt[r1];
+
+ for (int c = 0; c < C; ++c) {
+ T temp = R0[c];
+ R0[c] = R1[c];
+ R1[c] = temp;
+ }
+}
+
+
+void Matrix::Impl::swapAndNegateCols(int c0, int c1) {
+ for (int r = 0; r < R; ++r) {
+ T* row = elt[r];
+
+ const T temp = -row[c0];
+ row[c0] = -row[c1];
+ row[c1] = temp;
+ }
+}
+
+
+void Matrix::Impl::mulRow(int r, const T& v) {
+ T* row = elt[r];
+
+ for (int c = 0; c < C; ++c) {
+ row[c] *= v;
+ }
+}
+
+
+void Matrix::Impl::mul(const Impl& B, Impl& out) const {
+ const Impl& A = *this;
+
+ debugAssertM(
+ (this != &out) && (&B != &out),
+ "Output argument to mul cannot be the same as an input argument.");
+
+ debugAssert(A.C == B.R);
+ debugAssert(A.R == out.R);
+ debugAssert(B.C == out.C);
+
+ for (int r = 0; r < out.R; ++r) {
+ for (int c = 0; c < out.C; ++c) {
+ T sum = 0.0;
+ for (int i = 0; i < A.C; ++i) {
+ sum += A.get(r, i) * B.get(i, c);
+ }
+ out.set(r, c, sum);
+ }
+ }
+}
+
+
+// We're about to define several similar methods,
+// so use a macro to share implementations. This
+// must be a macro because the difference between
+// the macros is the operation in the inner loop.
+#define IMPLEMENT_ARRAY_2(method, OP)\
+void Matrix::Impl::method(const Impl& B, Impl& out) const {\
+ const Impl& A = *this;\
+ \
+ debugAssert(A.C == B.C);\
+ debugAssert(A.R == B.R);\
+ debugAssert(A.C == out.C);\
+ debugAssert(A.R == out.R);\
+ \
+ for (int i = R * C - 1; i >= 0; --i) {\
+ out.data[i] = A.data[i] OP B.data[i];\
+ }\
+}
+
+
+#define IMPLEMENT_ARRAY_1(method, f)\
+void Matrix::Impl::method(Impl& out) const {\
+ const Impl& A = *this;\
+ \
+ debugAssert(A.C == out.C);\
+ debugAssert(A.R == out.R);\
+ \
+ for (int i = R * C - 1; i >= 0; --i) {\
+ out.data[i] = f(A.data[i]);\
+ }\
+}
+
+
+#define IMPLEMENT_ARRAY_SCALAR(method, OP)\
+void Matrix::Impl::method(Matrix::T B, Impl& out) const {\
+ const Impl& A = *this;\
+ \
+ debugAssert(A.C == out.C);\
+ debugAssert(A.R == out.R);\
+ \
+ for (int i = R * C - 1; i >= 0; --i) {\
+ out.data[i] = A.data[i] OP B;\
+ }\
+}
+
+IMPLEMENT_ARRAY_2(add, +)
+IMPLEMENT_ARRAY_2(sub, -)
+IMPLEMENT_ARRAY_2(arrayMul, *)
+IMPLEMENT_ARRAY_2(arrayDiv, /)
+
+IMPLEMENT_ARRAY_SCALAR(add, +)
+IMPLEMENT_ARRAY_SCALAR(sub, -)
+IMPLEMENT_ARRAY_SCALAR(mul, *)
+IMPLEMENT_ARRAY_SCALAR(div, /)
+
+IMPLEMENT_ARRAY_1(abs, ::fabs)
+IMPLEMENT_ARRAY_1(negate, ::negate)
+IMPLEMENT_ARRAY_1(arrayLog, ::log)
+IMPLEMENT_ARRAY_1(arraySqrt, ::sqrt)
+IMPLEMENT_ARRAY_1(arrayExp, ::exp)
+IMPLEMENT_ARRAY_1(arrayCos, ::cos)
+IMPLEMENT_ARRAY_1(arraySin, ::sin)
+
+#undef IMPLEMENT_ARRAY_SCALAR
+#undef IMPLEMENT_ARRAY_1
+#undef IMPLEMENT_ARRAY_2
+
+// lsub is special because the argument order is reversed
+void Matrix::Impl::lsub(Matrix::T B, Impl& out) const {
+ const Impl& A = *this;
+
+ debugAssert(A.C == out.C);
+ debugAssert(A.R == out.R);
+
+ for (int i = R * C - 1; i >= 0; --i) {
+ out.data[i] = B - A.data[i];
+ }
+}
+
+
+void Matrix::Impl::inverseViaAdjoint(Impl& out) const {
+ debugAssert(&out != this);
+
+ // Inverse = adjoint / determinant
+
+ adjoint(out);
+
+ // Don't call the determinant method when we already have an
+ // adjoint matrix; there's a faster way of computing it: the dot
+ // product of the first row and the adjoint's first col.
+ double det = 0.0;
+ for (int r = R - 1; r >= 0; --r) {
+ det += elt[0][r] * out.elt[r][0];
+ }
+
+ out.div(Matrix::T(det), out);
+}
+
+
+void Matrix::Impl::transpose(Impl& out) const {
+ debugAssert(out.R == C);
+ debugAssert(out.C == R);
+
+ if (&out == this) {
+ // Square matrix in place
+ for (int r = 0; r < R; ++r) {
+ for (int c = r + 1; c < C; ++c) {
+ T temp = get(r, c);
+ out.set(r, c, get(c, r));
+ out.set(c, r, temp);
+ }
+ }
+ } else {
+ for (int r = 0; r < R; ++r) {
+ for (int c = 0; c < C; ++c) {
+ out.set(c, r, get(r, c));
+ }
+ }
+ }
+}
+
+
+void Matrix::Impl::adjoint(Impl& out) const {
+ cofactor(out);
+ // Transpose is safe to perform in place
+ out.transpose(out);
+}
+
+
+void Matrix::Impl::cofactor(Impl& out) const {
+ debugAssert(&out != this);
+ for(int r = 0; r < R; ++r) {
+ for(int c = 0; c < C; ++c) {
+ out.set(r, c, cofactor(r, c));
+ }
+ }
+}
+
+
+Matrix::T Matrix::Impl::cofactor(int r, int c) const {
+ // Strang p. 217
+ float s = isEven(r + c) ? 1.0f : -1.0f;
+
+ return s * determinant(r, c);
+}
+
+
+Matrix::T Matrix::Impl::determinant(int nr, int nc) const {
+ debugAssert(R > 0);
+ debugAssert(C > 0);
+ Impl A(R - 1, C - 1);
+ withoutRowAndCol(nr, nc, A);
+ return A.determinant();
+}
+
+
+void Matrix::Impl::setRow(int r, const T* vals) {
+ debugAssert(r >= 0);
+ System::memcpy(elt[r], vals, sizeof(T) * C);
+}
+
+
+void Matrix::Impl::setCol(int c, const T* vals) {
+ for (int r = 0; r < R; ++r) {
+ elt[r][c] = vals[r];
+ }
+}
+
+
+Matrix::T Matrix::Impl::determinant() const {
+
+ debugAssert(R == C);
+
+ // Compute using cofactors
+ switch(R) {
+ case 0:
+ return 0;
+
+ case 1:
+ // Determinant of a 1x1 is the element
+ return elt[0][0];
+
+ case 2:
+ // Determinant of a 2x2 is ad-bc
+ return elt[0][0] * elt[1][1] - elt[0][1] * elt[1][0];
+
+ case 3:
+ {
+ // Determinant of an nxn matrix is the dot product of the first
+ // row with the first row of cofactors. The base cases of this
+ // method get called a lot, so we spell out the implementation
+ // for the 3x3 case.
+
+ double cofactor00 = elt[1][1] * elt[2][2] - elt[1][2] * elt[2][1];
+ double cofactor10 = elt[1][2] * elt[2][0] - elt[1][0] * elt[2][2];
+ double cofactor20 = elt[1][0] * elt[2][1] - elt[1][1] * elt[2][0];
+
+ return Matrix::T(
+ elt[0][0] * cofactor00 +
+ elt[0][1] * cofactor10 +
+ elt[0][2] * cofactor20);
+ }
+
+ default:
+ {
+ // Determinant of an n x n matrix is the dot product of the first
+ // row with the first row of cofactors
+ T det = 0.0;
+
+ for (int c = 0; c < C; ++c) {
+ det += elt[0][c] * cofactor(0, c);
+ }
+
+ return det;
+ }
+ }
+}
+
+
+void Matrix::Impl::withoutRowAndCol(int excludeRow, int excludeCol, Impl& out) const {
+ debugAssert(out.R == R - 1);
+ debugAssert(out.C == C - 1);
+
+ for (int r = 0; r < out.R; ++r) {
+ for (int c = 0; c < out.C; ++c) {
+ out.elt[r][c] = elt[r + ((r >= excludeRow) ? 1 : 0)][c + ((c >= excludeCol) ? 1 : 0)];
+ }
+ }
+}
+
+
+Matrix Matrix::pseudoInverse(float tolerance) const {
+ if ((cols() == 1) || (rows() == 1)) {
+ return vectorPseudoInverse();
+ } else if ((cols() <= 4) || (rows() <= 4)) {
+ return partitionPseudoInverse();
+ } else {
+ return svdPseudoInverse(tolerance);
+ }
+}
+
+/*
+ Public function for testing purposes only. Use pseudoInverse(), as it contains optimizations for
+ nonsingular matrices with at least one small (<5) dimension.
+*/
+Matrix Matrix::svdPseudoInverse(float tolerance) const {
+ if (cols() > rows()) {
+ return transpose().svdPseudoInverse(tolerance).transpose();
+ }
+
+ // Matrices from SVD
+ Matrix U, V;
+
+ // Diagonal elements
+ Array<T> d;
+
+ svd(U, d, V);
+
+ if (rows() == 1) {
+ d.resize(1, false);
+ }
+
+ if (tolerance < 0) {
+ // TODO: Should be eps(d[0]), which is the largest diagonal
+ tolerance = G3D::max(rows(), cols()) * 0.0001f;
+ }
+
+ Matrix X;
+
+ int r = 0;
+ for (int i = 0; i < d.size(); ++i) {
+ if (d[i] > tolerance) {
+ d[i] = Matrix::T(1) / d[i];
+ ++r;
+ }
+ }
+
+ if (r == 0) {
+ // There were no non-zero elements
+ X = zero(cols(), rows());
+ } else {
+ // Use the first r columns
+
+ // Test code (the rest is below)
+ /*
+ d.resize(r);
+ Matrix testU = U.subMatrix(0, U.rows() - 1, 0, r - 1);
+ Matrix testV = V.subMatrix(0, V.rows() - 1, 0, r - 1);
+ Matrix testX = testV * Matrix::fromDiagonal(d) * testU.transpose();
+ X = testX;
+ */
+
+
+ // We want to do this:
+ //
+ // d.resize(r);
+ // U = U.subMatrix(0, U.rows() - 1, 0, r - 1);
+ // X = V * Matrix::fromDiagonal(d) * U.transpose();
+ //
+ // but creating a large diagonal matrix and then
+ // multiplying by it is wasteful. So we instead
+ // explicitly perform A = (D * U')' = U * D, and
+ // then multiply X = V * A'.
+
+ Matrix A = Matrix(U.rows(), r);
+
+ const T* dPtr = d.getCArray();
+ for (int i = 0; i < A.rows(); ++i) {
+ const T* Urow = U.impl->elt[i];
+ T* Arow = A.impl->elt[i];
+ const int Acols = A.cols();
+ for (int j = 0; j < Acols; ++j) {
+ // A(i,j) = U(i,:) * D(:,j)
+ // This is non-zero only at j = i because D is diagonal
+ // A(i,j) = U(i,j) * D(j,j)
+ Arow[j] = Urow[j] * dPtr[j];
+ }
+ }
+
+ //
+ // Compute X = V.subMatrix(0, V.rows() - 1, 0, r - 1) * A.transpose()
+ //
+ // Avoid the explicit subMatrix call, and by storing A' instead of A, avoid
+ // both the transpose and the memory incoherence of striding across memory
+ // in big steps.
+
+ alwaysAssertM(A.cols() == r,
+ "Internal dimension mismatch during pseudoInverse()");
+ alwaysAssertM(V.cols() >= r,
+ "Internal dimension mismatch during pseudoInverse()");
+
+ X = Matrix(V.rows(), A.rows());
+ T** Xelt = X.impl->elt;
+ for (int i = 0; i < X.rows(); ++i) {
+ const T* Vrow = V.impl->elt[i];
+ for (int j = 0; j < X.cols(); ++j) {
+ const T* Arow = A.impl->elt[j];
+ T sum = 0;
+ for (int k = 0; k < r; ++k) {
+ sum += Vrow[k] * Arow[k];
+ }
+ Xelt[i][j] = sum;
+ }
+ }
+
+ /*
+ // Test that results are the same after optimizations:
+ Matrix diff = X - testX;
+ T n = diff.norm();
+ debugAssert(n < 0.0001);
+ */
+ }
+ return X;
+}
+
+// Computes pseudoinverse for a vector
+Matrix Matrix::vectorPseudoInverse() const {
+ // If vector A has nonzero elements: transpose A, then divide each elt. by the squared norm
+ // If A is zero vector: transpose A
+ double x = 0.0;
+
+ if (anyNonZero()) {
+ x = 1.0 / normSquared();
+ }
+
+ Matrix A(cols(), rows());
+ T** Aelt = A.impl->elt;
+ for (int r = 0; r < rows(); ++r) {
+ const T* MyRow = impl->elt[r];
+ for (int c = 0; c < cols(); ++c) {
+ Aelt[c][r] = T(MyRow[c] * x);
+ }
+ }
+ return Matrix(A);
+}
+
+
+Matrix Matrix::rowPartPseudoInverse() const{
+ int m = rows();
+ int n = cols();
+ alwaysAssertM((m<=n),"Row-partitioned block matrix pseudoinverse requires R<C");
+
+ // B = A * A'
+ Matrix A = *this;
+ Matrix B = Matrix(m,m);
+
+ T** Aelt = A.impl->elt;
+ T** Belt = B.impl->elt;
+ for (int i = 0; i < m; ++i) {
+ const T* Arow = Aelt[i];
+ for (int j = 0; j < m; ++j) {
+ const T* Brow = Aelt[j];
+ T sum = 0;
+ for (int k = 0; k < n; ++k) {
+ sum += Arow[k] * Brow[k];
+ }
+ Belt[i][j] = sum;
+ }
+ }
+
+ // B has size m x m
+ switch (m) {
+ case 2:
+ return row2PseudoInverse(B);
+
+ case 3:
+ return row3PseudoInverse(B);
+
+ case 4:
+ return row4PseudoInverse(B);
+
+ default:
+ alwaysAssertM(false, "G3D internal error: Should have used the vector or general case!");
+ return Matrix();
+ }
+}
+
+Matrix Matrix::colPartPseudoInverse() const{
+ int m = rows();
+ int n = cols();
+ alwaysAssertM((m>=n),"Column-partitioned block matrix pseudoinverse requires R>C");
+ // TODO: Put each of the individual cases in its own helper function
+ // TODO: Push the B computation down into the individual cases
+ // B = A' * A
+ Matrix A = *this;
+ Matrix B = Matrix(n, n);
+ T** Aelt = A.impl->elt;
+ T** Belt = B.impl->elt;
+ for (int i = 0; i < n; ++i) {
+ for (int j = 0; j < n; ++j) {
+ T sum = 0;
+ for (int k = 0; k < m; ++k) {
+ sum += Aelt[k][i] * Aelt[k][j];
+ }
+ Belt[i][j] = sum;
+ }
+ }
+
+ // B has size n x n
+ switch (n) {
+ case 2:
+ return col2PseudoInverse(B);
+
+ case 3:
+ return col3PseudoInverse(B);
+
+ case 4:
+ return col4PseudoInverse(B);
+
+ default:
+ alwaysAssertM(false, "G3D internal error: Should have used the vector or general case!");
+ return Matrix();
+ }
+}
+
+Matrix Matrix::col2PseudoInverse(const Matrix& B) const {
+
+ Matrix A = *this;
+ int m = rows();
+ int n = cols();
+ (void)n;
+
+ // Row-major 2x2 matrix
+ const float B2[2][2] =
+ {{B.get(0,0), B.get(0,1)},
+ {B.get(1,0), B.get(1,1)}};
+
+ float det = (B2[0][0]*B2[1][1]) - (B2[0][1]*B2[1][0]);
+
+ if (fuzzyEq(det, T(0))) {
+
+ // Matrix was singular; the block matrix pseudo-inverse can't
+ // handle that, so fall back to the old case
+ return svdPseudoInverse();
+
+ } else {
+ // invert using formula at http://www.netsoc.tcd.ie/~jgilbert/maths_site/applets/algebra/matrix_inversion.html
+
+ // Multiply by Binv * A'
+ Matrix X(cols(), rows());
+
+ T** Xelt = X.impl->elt;
+ T** Aelt = A.impl->elt;
+ float binv00 = B2[1][1]/det, binv01 = -B2[1][0]/det;
+ float binv10 = -B2[0][1]/det, binv11 = B2[0][0]/det;
+ for (int j = 0; j < m; ++j) {
+ const T* Arow = Aelt[j];
+ float a0 = Arow[0];
+ float a1 = Arow[1];
+ Xelt[0][j] = binv00 * a0 + binv01 * a1;
+ Xelt[1][j] = binv10 * a0 + binv11 * a1;
+ }
+ return X;
+ }
+}
+
+Matrix Matrix::col3PseudoInverse(const Matrix& B) const {
+ Matrix A = *this;
+ int m = rows();
+ int n = cols();
+
+ Matrix3 B3 = B.toMatrix3();
+ if (fuzzyEq(B3.determinant(), (T)0.0)) {
+
+ // Matrix was singular; the block matrix pseudo-inverse can't
+ // handle that, so fall back to the old case
+ return svdPseudoInverse();
+
+ } else {
+ Matrix3 B3inv = B3.inverse();
+
+ // Multiply by Binv * A'
+ Matrix X(cols(), rows());
+
+ T** Xelt = X.impl->elt;
+ T** Aelt = A.impl->elt;
+ for (int i = 0; i < n; ++i) {
+ T* Xrow = Xelt[i];
+ for (int j = 0; j < m; ++j) {
+ const T* Arow = Aelt[j];
+ T sum = 0;
+ const float* Binvrow = B3inv[i];
+ for (int k = 0; k < n; ++k) {
+ sum += Binvrow[k] * Arow[k];
+ }
+ Xrow[j] = sum;
+ }
+ }
+ return X;
+ }
+}
+
+Matrix Matrix::col4PseudoInverse(const Matrix& B) const {
+ Matrix A = *this;
+ int m = rows();
+ int n = cols();
+
+ Matrix4 B4 = B.toMatrix4();
+ if (fuzzyEq(B4.determinant(), (T)0.0)) {
+
+ // Matrix was singular; the block matrix pseudo-inverse can't
+ // handle that, so fall back to the old case
+ return svdPseudoInverse();
+
+ } else {
+ Matrix4 B4inv = B4.inverse();
+
+ // Multiply by Binv * A'
+ Matrix X(cols(), rows());
+
+ T** Xelt = X.impl->elt;
+ T** Aelt = A.impl->elt;
+ for (int i = 0; i < n; ++i) {
+ T* Xrow = Xelt[i];
+ for (int j = 0; j < m; ++j) {
+ const T* Arow = Aelt[j];
+ T sum = 0;
+ const float* Binvrow = B4inv[i];
+ for (int k = 0; k < n; ++k) {
+ sum += Binvrow[k] * Arow[k];
+ }
+ Xrow[j] = sum;
+ }
+ }
+ return X;
+ }
+}
+
+Matrix Matrix::row2PseudoInverse(const Matrix& B) const {
+
+ Matrix A = *this;
+ int m = rows();
+ int n = cols();
+ (void)m;
+
+ // Row-major 2x2 matrix
+ const float B2[2][2] =
+ {{B.get(0,0), B.get(0,1)},
+ {B.get(1,0), B.get(1,1)}};
+
+ float det = (B2[0][0]*B2[1][1]) - (B2[0][1]*B2[1][0]);
+
+ if (fuzzyEq(det, T(0))) {
+
+ // Matrix was singular; the block matrix pseudo-inverse can't
+ // handle that, so fall back to the old case
+ return svdPseudoInverse();
+
+ } else {
+ // invert using formula at http://www.netsoc.tcd.ie/~jgilbert/maths_site/applets/algebra/matrix_inversion.html
+
+ // Multiply by Binv * A'
+ Matrix X(cols(), rows());
+
+ T** Xelt = X.impl->elt;
+ T** Aelt = A.impl->elt;
+ float binv00 = B2[1][1]/det, binv01 = -B2[1][0]/det;
+ float binv10 = -B2[0][1]/det, binv11 = B2[0][0]/det;
+ for (int j = 0; j < n; ++j) {
+ Xelt[j][0] = Aelt[0][j] * binv00 + Aelt[1][j] * binv10;
+ Xelt[j][1] = Aelt[0][j] * binv01 + Aelt[1][j] * binv11;
+ }
+ return X;
+ }
+}
+
+Matrix Matrix::row3PseudoInverse(const Matrix& B) const {
+
+ Matrix A = *this;
+ int m = rows();
+ int n = cols();
+
+ Matrix3 B3 = B.toMatrix3();
+ if (fuzzyEq(B3.determinant(), (T)0.0)) {
+
+ // Matrix was singular; the block matrix pseudo-inverse can't
+ // handle that, so fall back to the old case
+ return svdPseudoInverse();
+
+ } else {
+ Matrix3 B3inv = B3.inverse();
+
+ // Multiply by Binv * A'
+ Matrix X(cols(), rows());
+
+ T** Xelt = X.impl->elt;
+ T** Aelt = A.impl->elt;
+ for (int i = 0; i < n; ++i) {
+ T* Xrow = Xelt[i];
+ for (int j = 0; j < m; ++j) {
+ T sum = 0;
+ for (int k = 0; k < m; ++k) {
+ sum += Aelt[k][i] * B3inv[j][k];
+ }
+ Xrow[j] = sum;
+ }
+ }
+ return X;
+ }
+}
+
+Matrix Matrix::row4PseudoInverse(const Matrix& B) const {
+
+ Matrix A = *this;
+ int m = rows();
+ int n = cols();
+
+ Matrix4 B4 = B.toMatrix4();
+ if (fuzzyEq(B4.determinant(), (T)0.0)) {
+
+ // Matrix was singular; the block matrix pseudo-inverse can't
+ // handle that, so fall back to the old case
+ return svdPseudoInverse();
+
+ } else {
+ Matrix4 B4inv = B4.inverse();
+
+ // Multiply by Binv * A'
+ Matrix X(cols(), rows());
+
+ T** Xelt = X.impl->elt;
+ T** Aelt = A.impl->elt;
+ for (int i = 0; i < n; ++i) {
+ T* Xrow = Xelt[i];
+ for (int j = 0; j < m; ++j) {
+ T sum = 0;
+ for (int k = 0; k < m; ++k) {
+ sum += Aelt[k][i] * B4inv[j][k];
+ }
+ Xrow[j] = sum;
+ }
+ }
+ return X;
+ }
+}
+
+// Uses the block matrix pseudoinverse to compute the pseudoinverse of a full-rank mxn matrix with m >= n
+// http://en.wikipedia.org/wiki/Block_matrix_pseudoinverse
+Matrix Matrix::partitionPseudoInverse() const {
+
+ // Logic:
+ // A^-1 = (A'A)^-1 A'
+ // A has few (n) columns, so A'A is small (n x n) and fast to invert
+
+ int m = rows();
+ int n = cols();
+
+ if (m < n) {
+ // TODO: optimize by pushing through the transpose
+ //return transpose().partitionPseudoInverse().transpose();
+ return rowPartPseudoInverse();
+
+ } else {
+ return colPartPseudoInverse();
+ }
+}
+
+void Matrix::Impl::inverseInPlaceGaussJordan() {
+ debugAssertM(R == C,
+ format(
+ "Cannot perform Gauss-Jordan inverse on a non-square matrix."
+ " (Argument was %dx%d)",
+ R, C));
+
+ // Exchange to float elements
+# define SWAP(x, y) {float temp = x; x = y; y = temp;}
+
+ // The integer arrays pivot, rowIndex, and colIndex are
+ // used for bookkeeping on the pivoting
+ static Array<int> colIndex, rowIndex, pivot;
+
+ int col = 0, row = 0;
+
+ colIndex.resize(R);
+ rowIndex.resize(R);
+ pivot.resize(R);
+
+ static const int NO_PIVOT = -1;
+
+ // Initialize the pivot array to default values.
+ for (int i = 0; i < R; ++i) {
+ pivot[i] = NO_PIVOT;
+ }
+
+ // This is the main loop over the columns to be reduced
+ // Loop over the columns.
+ for (int c = 0; c < R; ++c) {
+
+ // Find the largest element and use that as a pivot
+ float largestMagnitude = 0.0;
+
+ // This is the outer loop of the search for a pivot element
+ for (int r = 0; r < R; ++r) {
+
+ // Unless we've already found the pivot, keep going
+ if (pivot[r] != 0) {
+
+ // Find the largest pivot
+ for (int k = 0; k < R; ++k) {
+ if (pivot[k] == NO_PIVOT) {
+ const float mag = fabs(elt[r][k]);
+
+ if (mag >= largestMagnitude) {
+ largestMagnitude = mag;
+ row = r; col = k;
+ }
+ }
+ }
+ }
+ }
+
+ pivot[col] += 1;
+
+ // Interchange columns so that the pivot element is on the diagonal (we'll have to undo this
+ // at the end)
+ if (row != col) {
+ for (int k = 0; k < R; ++k) {
+ SWAP(elt[row][k], elt[col][k])
+ }
+ }
+
+ // The pivot is now at [row, col]
+ rowIndex[c] = row;
+ colIndex[c] = col;
+
+ double piv = elt[col][col];
+
+ debugAssertM(piv != 0.0, "Matrix is singular");
+
+ // Divide everything by the pivot (avoid computing the division
+ // multiple times).
+ const double pivotInverse = 1.0 / piv;
+ elt[col][col] = 1.0;
+
+ for (int k = 0; k < R; ++k) {
+ elt[col][k] *= Matrix::T(pivotInverse);
+ }
+
+ // Reduce all rows
+ for (int r = 0; r < R; ++r) {
+ // Skip over the pivot row
+ if (r != col) {
+
+ double oldValue = elt[r][col];
+ elt[r][col] = 0.0;
+
+ for (int k = 0; k < R; ++k) {
+ elt[r][k] -= Matrix::T(elt[col][k] * oldValue);
+ }
+ }
+ }
+ }
+
+
+ // Put the columns back in the correct locations
+ for (int i = R - 1; i >= 0; --i) {
+ if (rowIndex[i] != colIndex[i]) {
+ for (int k = 0; k < R; ++k) {
+ SWAP(elt[k][rowIndex[i]], elt[k][colIndex[i]]);
+ }
+ }
+ }
+
+# undef SWAP
+}
+
+
+bool Matrix::Impl::anyNonZero() const {
+ int N = R * C;
+ for (int i = 0; i < N; ++i) {
+ if (data[i] != 0.0) {
+ return true;
+ }
+ }
+ return false;
+}
+
+
+bool Matrix::Impl::allNonZero() const {
+ int N = R * C;
+ for (int i = 0; i < N; ++i) {
+ if (data[i] == 0.0) {
+ return false;
+ }
+ }
+ return true;
+}
+
+
+/** Helper for svdCore */
+static double pythag(double a, double b) {
+
+ double at = fabs(a), bt = fabs(b), ct, result;
+
+ if (at > bt) {
+ ct = bt / at;
+ result = at * sqrt(1.0 + square(ct));
+ } else if (bt > 0.0) {
+ ct = at / bt;
+ result = bt * sqrt(1.0 + square(ct));
+ } else {
+ result = 0.0;
+ }
+
+ return result;
+}
+
+#define SIGN(a, b) ((b) >= 0.0 ? fabs(a) : -fabs(a))
+
+const char* Matrix::svdCore(float** U, int rows, int cols, float* D, float** V) {
+ const int MAX_ITERATIONS = 30;
+
+ int flag, i, its, j, jj, k, l = 0, nm = 0;
+ double c, f, h, s, x, y, z;
+ double anorm = 0.0, g = 0.0, scale = 0.0;
+
+ // Temp row vector
+ double* rv1;
+
+ debugAssertM(rows >= cols, "Must have more rows than columns");
+
+ rv1 = (double*)System::alignedMalloc(cols * sizeof(double), 16);
+ debugAssert(rv1);
+
+ // Householder reduction to bidiagonal form
+ for (i = 0; i < cols; ++i) {
+
+ // Left-hand reduction
+ l = i + 1;
+ rv1[i] = scale * g;
+ g = s = scale = 0.0;
+
+ if (i < rows) {
+
+ for (k = i; k < rows; ++k) {
+ scale += fabs((double)U[k][i]);
+ }
+
+ if (scale) {
+ for (k = i; k < rows; ++k) {
+ U[k][i] = (float)((double)U[k][i]/scale);
+ s += ((double)U[k][i] * (double)U[k][i]);
+ }
+
+ f = (double)U[i][i];
+
+ // TODO: what is this 2-arg sign function?
+ g = -SIGN(sqrt(s), f);
+ h = f * g - s;
+ U[i][i] = (float)(f - g);
+
+ if (i != cols - 1) {
+ for (j = l; j < cols; j++) {
+
+ for (s = 0.0, k = i; k < rows; ++k) {
+ s += ((double)U[k][i] * (double)U[k][j]);
+ }
+
+ f = s / h;
+ for (k = i; k < rows; ++k) {
+ U[k][j] += (float)(f * (double)U[k][i]);
+ }
+ }
+ }
+ for (k = i; k < rows; ++k) {
+ U[k][i] = (float)((double)U[k][i]*scale);
+ }
+ }
+ }
+ D[i] = (float)(scale * g);
+
+ // right-hand reduction
+ g = s = scale = 0.0;
+ if (i < rows && i != cols - 1) {
+ for (k = l; k < cols; ++k) {
+ scale += fabs((double)U[i][k]);
+ }
+
+ if (scale) {
+ for (k = l; k < cols; ++k) {
+ U[i][k] = (float)((double)U[i][k]/scale);
+ s += ((double)U[i][k] * (double)U[i][k]);
+ }
+
+ f = (double)U[i][l];
+ g = -SIGN(sqrt(s), f);
+ h = f * g - s;
+ U[i][l] = (float)(f - g);
+
+ for (k = l; k < cols; ++k) {
+ rv1[k] = (double)U[i][k] / h;
+ }
+
+ if (i != rows - 1) {
+
+ for (j = l; j < rows; ++j) {
+ for (s = 0.0, k = l; k < cols; ++k) {
+ s += ((double)U[j][k] * (double)U[i][k]);
+ }
+
+ for (k = l; k < cols; ++k) {
+ U[j][k] += (float)(s * rv1[k]);
+ }
+ }
+ }
+
+ for (k = l; k < cols; ++k) {
+ U[i][k] = (float)((double)U[i][k]*scale);
+ }
+ }
+ }
+
+ anorm = max(anorm, fabs((double)D[i]) + fabs(rv1[i]));
+ }
+
+ // accumulate the right-hand transformation
+ for (i = cols - 1; i >= 0; --i) {
+ if (i < cols - 1) {
+ if (g) {
+ for (j = l; j < cols; j++) {
+ V[j][i] = (float)(((double)U[i][j] / (double)U[i][l]) / g);
+ }
+
+ // double division to avoid underflow
+ for (j = l; j < cols; ++j) {
+ for (s = 0.0, k = l; k < cols; k++) {
+ s += ((double)U[i][k] * (double)V[k][j]);
+ }
+
+ for (k = l; k < cols; ++k) {
+ V[k][j] += (float)(s * (double)V[k][i]);
+ }
+ }
+ }
+
+ for (j = l; j < cols; ++j) {
+ V[i][j] = V[j][i] = 0.0;
+ }
+ }
+
+ V[i][i] = 1.0;
+ g = rv1[i];
+ l = i;
+ }
+
+ // accumulate the left-hand transformation
+ for (i = cols - 1; i >= 0; --i) {
+ l = i + 1;
+ g = (double)D[i];
+ if (i < cols - 1) {
+ for (j = l; j < cols; ++j) {
+ U[i][j] = 0.0;
+ }
+ }
+
+ if (g) {
+ g = 1.0 / g;
+ if (i != cols - 1) {
+ for (j = l; j < cols; ++j) {
+ for (s = 0.0, k = l; k < rows; ++k) {
+ s += ((double)U[k][i] * (double)U[k][j]);
+ }
+
+ f = (s / (double)U[i][i]) * g;
+
+ for (k = i; k < rows; ++k) {
+ U[k][j] += (float)(f * (double)U[k][i]);
+ }
+ }
+ }
+
+ for (j = i; j < rows; ++j) {
+ U[j][i] = (float)((double)U[j][i]*g);
+ }
+
+ } else {
+ for (j = i; j < rows; ++j) {
+ U[j][i] = 0.0;
+ }
+ }
+ ++U[i][i];
+ }
+
+ // diagonalize the bidiagonal form
+ for (k = cols - 1; k >= 0; --k) {
+ // loop over singular values
+ for (its = 0; its < MAX_ITERATIONS; ++its) {
+ // loop over allowed iterations
+ flag = 1;
+
+ for (l = k; l >= 0; --l) {
+ // test for splitting
+ nm = l - 1;
+ if (fabs(rv1[l]) + anorm == anorm) {
+ flag = 0;
+ break;
+ }
+
+ if (fabs((double)D[nm]) + anorm == anorm) {
+ break;
+ }
+ }
+
+ if (flag) {
+ c = 0.0;
+ s = 1.0;
+ for (i = l; i <= k; ++i) {
+ f = s * rv1[i];
+ if (fabs(f) + anorm != anorm) {
+ g = (double)D[i];
+ h = pythag(f, g);
+ D[i] = (float)h;
+ h = 1.0 / h;
+ c = g * h;
+ s = (- f * h);
+ for (j = 0; j < rows; ++j) {
+ y = (double)U[j][nm];
+ z = (double)U[j][i];
+ U[j][nm] = (float)(y * c + z * s);
+ U[j][i] = (float)(z * c - y * s);
+ }
+ }
+ }
+ }
+
+ z = (double)D[k];
+ if (l == k) {
+ // convergence
+ if (z < 0.0) {
+ // make singular value nonnegative
+ D[k] = (float)(-z);
+
+ for (j = 0; j < cols; ++j) {
+ V[j][k] = (-V[j][k]);
+ }
+ }
+ break;
+ }
+
+ if (its >= MAX_ITERATIONS) {
+ free(rv1);
+ rv1 = NULL;
+ return "Failed to converge.";
+ }
+
+ // shift from bottom 2 x 2 minor
+ x = (double)D[l];
+ nm = k - 1;
+ y = (double)D[nm];
+ g = rv1[nm];
+ h = rv1[k];
+ f = ((y - z) * (y + z) + (g - h) * (g + h)) / (2.0 * h * y);
+ g = pythag(f, 1.0);
+ f = ((x - z) * (x + z) + h * ((y / (f + SIGN(g, f))) - h)) / x;
+
+ // next QR transformation
+ c = s = 1.0;
+ for (j = l; j <= nm; ++j) {
+ i = j + 1;
+ g = rv1[i];
+ y = (double)D[i];
+ h = s * g;
+ g = c * g;
+ z = pythag(f, h);
+ rv1[j] = z;
+ c = f / z;
+ s = h / z;
+ f = x * c + g * s;
+ g = g * c - x * s;
+ h = y * s;
+ y = y * c;
+
+ for (jj = 0; jj < cols; ++jj) {
+ x = (double)V[jj][j];
+ z = (double)V[jj][i];
+ V[jj][j] = (float)(x * c + z * s);
+ V[jj][i] = (float)(z * c - x * s);
+ }
+ z = pythag(f, h);
+ D[j] = (float)z;
+ if (z) {
+ z = 1.0 / z;
+ c = f * z;
+ s = h * z;
+ }
+ f = (c * g) + (s * y);
+ x = (c * y) - (s * g);
+ for (jj = 0; jj < rows; jj++) {
+ y = (double)U[jj][j];
+ z = (double)U[jj][i];
+ U[jj][j] = (float)(y * c + z * s);
+ U[jj][i] = (float)(z * c - y * s);
+ }
+ }
+ rv1[l] = 0.0;
+ rv1[k] = f;
+ D[k] = (float)x;
+ }
+ }
+
+ System::alignedFree(rv1);
+ rv1 = NULL;
+
+ return NULL;
+}
+
+#undef SIGN
+
+}
diff --git a/externals/g3dlite/G3D.lib/source/Matrix3.cpp b/externals/g3dlite/G3D.lib/source/Matrix3.cpp
new file mode 100644
index 00000000000..7fb6648d3f7
--- /dev/null
+++ b/externals/g3dlite/G3D.lib/source/Matrix3.cpp
@@ -0,0 +1,1725 @@
+/**
+ @file Matrix3.cpp
+
+ 3x3 matrix class
+
+ @author Morgan McGuire, graphics3d.com
+
+ @created 2001-06-02
+ @edited 2006-04-06
+*/
+
+#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"
+
+namespace G3D {
+
+const float Matrix3::EPSILON = 1e-06f;
+
+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::isOrthonormal() const {
+ Vector3 X = column(0);
+ Vector3 Y = column(1);
+ 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]);
+}
+
+
+Vector3 Matrix3::getColumn (int iCol) const {
+ assert((0 <= iCol) && (iCol < 3));
+ return Vector3(elt[0][iCol], elt[1][iCol],
+ elt[2][iCol]);
+}
+
+Vector3 Matrix3::getRow (int iRow) const {
+ return Vector3(elt[iRow][0], elt[iRow][1], elt[iRow][2]);
+}
+
+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 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];
+}
+
+//----------------------------------------------------------------------------
+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;
+}
+
+//----------------------------------------------------------------------------
+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/G3D.lib/source/Matrix4.cpp b/externals/g3dlite/G3D.lib/source/Matrix4.cpp
new file mode 100644
index 00000000000..091af4a9bd5
--- /dev/null
+++ b/externals/g3dlite/G3D.lib/source/Matrix4.cpp
@@ -0,0 +1,433 @@
+/**
+ @file Matrix4.cpp
+
+
+ @maintainer Morgan McGuire, matrix@graphics3d.com
+
+ @created 2003-10-02
+ @edited 2008-07-17
+ */
+
+#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"
+
+namespace G3D {
+
+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) {
+ return Matrix4::orthogonalProjection(rect.x0(), rect.x1(), rect.y1(), rect.y0(), nearval, farval);
+}
+
+
+Matrix4 Matrix4::orthogonalProjection(
+ float left,
+ float right,
+ float bottom,
+ float top,
+ float nearval,
+ float farval) {
+
+ // 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);
+
+ 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 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 ((float)farval >= (float)inf()) {
+ // Infinite view frustum
+ c = -1.0f;
+ d = -2.0f * nearval;
+ } else {
+ c = -(farval+nearval) / (farval-nearval);
+ d = -(2.0f*farval*nearval) / (farval-nearval);
+ }
+
+ return Matrix4(
+ x, 0, a, 0,
+ 0, y, b, 0,
+ 0, 0, c, d,
+ 0, 0, -1, 0);
+}
+
+
+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];
+ }
+}
+
+
+Vector4 Matrix4::getRow(int r) const {
+ return row(r);
+}
+
+
+const Vector4& Matrix4::row(int r) const {
+ return reinterpret_cast<const Vector4*>(elt[r])[0];
+}
+
+
+Vector4 Matrix4::getColumn(int c) const {
+ return column(c);
+}
+
+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().getRow(0).dot(getRow(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.getColumn(0).dot(getRow(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/G3D.lib/source/MeshAlg.cpp b/externals/g3dlite/G3D.lib/source/MeshAlg.cpp
new file mode 100644
index 00000000000..24b90ab5a92
--- /dev/null
+++ b/externals/g3dlite/G3D.lib/source/MeshAlg.cpp
@@ -0,0 +1,733 @@
+/**
+ @file MeshAlg.cpp
+
+ @maintainer Morgan McGuire, matrix@graphics3d.com
+ @created 2003-09-14
+ @edited 2008-09-03
+
+ Copyright 2000-2008, Morgan McGuire.
+ All rights reserved.
+
+ */
+
+#include "G3D/MeshAlg.h"
+#include "G3D/Table.h"
+#include "G3D/Set.h"
+#include "G3D/Box.h"
+#include "G3D/Sphere.h"
+#include "G3D/vectorMath.h"
+#include "G3D/AABox.h"
+
+#include <climits>
+
+namespace G3D {
+
+const int MeshAlg::Face::NONE = INT_MIN;
+
+void MeshAlg::generateGrid(
+ Array<Vector3>& vertex,
+ Array<Vector2>& texCoord,
+ Array<int>& index,
+ int wCells,
+ int hCells,
+ const Vector2& textureScale,
+ bool spaceCentered,
+ bool twoSided,
+ const CoordinateFrame& xform) {
+
+ vertex.fastClear();
+ texCoord.fastClear();
+ index.fastClear();
+
+ // Generate vertices
+ for (int z = 0; z <= hCells; ++z) {
+ for (int x = 0; x <= wCells; ++x) {
+ Vector3 v(x / (float)wCells, 0, z / (float)hCells);
+
+ Vector2 t = v.xz() * textureScale;
+
+ texCoord.append(t);
+
+ if (spaceCentered) {
+ v -= Vector3(0.5f, 0, 0.5f);
+ }
+ v = xform.pointToWorldSpace(v);
+ vertex.append(v);
+ }
+ }
+
+ // Generate indices
+ for (int z = 0; z < hCells; ++z) {
+ for (int x = 0; x < wCells; ++x) {
+ int A = x + z * (wCells + 1);
+ int B = A + 1;
+ int C = A + (wCells + 1);
+ int D = C + 1;
+
+ // A B
+ // *-----*
+ // | \ |
+ // | \ |
+ // *-----*
+ // C D
+
+ index.append(A, D, B);
+ index.append(A, C, D);
+ }
+ }
+
+ if (twoSided) {
+ // The index array needs to have reversed winding for the bottom
+ // and offset by the original number of vertices
+ Array<int> ti = index;
+ ti.reverse();
+ for (int i = 0; i < ti.size(); ++i) {
+ ti[i] += vertex.size();
+ }
+ index.append(ti);
+
+ // Duplicate the arrays
+ vertex.append(Array<Vector3>(vertex));
+ texCoord.append(Array<Vector2>(texCoord));
+ }
+}
+
+MeshAlg::Face::Face() {
+ for (int i = 0; i < 3; ++i) {
+ edgeIndex[i] = 0;
+ vertexIndex[i] = 0;
+ }
+}
+
+
+MeshAlg::Edge::Edge() {
+ for (int i = 0; i < 2; ++i) {
+ vertexIndex[i] = 0;
+ // Negative face indices are faces that don't exist
+ faceIndex[i] = -1;
+ }
+}
+
+
+MeshAlg::Geometry& MeshAlg::Geometry::operator=(const MeshAlg::Geometry& src) {
+ vertexArray.resize(src.vertexArray.size());
+ normalArray.resize(src.vertexArray.size());
+
+ System::memcpy(vertexArray.getCArray(), src.vertexArray.getCArray(), sizeof(Vector3)*vertexArray.size());
+ System::memcpy(normalArray.getCArray(), src.normalArray.getCArray(), sizeof(Vector3)*normalArray.size());
+
+ return *this;
+}
+
+
+void MeshAlg::computeNormals(
+ Geometry& geometry,
+ const Array<int>& indexArray) {
+
+ Array<Face> faceArray;
+ Array<Vertex> vertexArray;
+ Array<Edge> edgeArray;
+ Array<Vector3> faceNormalArray;
+
+ computeAdjacency(geometry.vertexArray, indexArray, faceArray, edgeArray, vertexArray);
+
+ computeNormals(geometry.vertexArray, faceArray, vertexArray,
+ geometry.normalArray, faceNormalArray);
+}
+
+
+void MeshAlg::computeNormals(
+ const Array<Vector3>& vertexGeometry,
+ const Array<Face>& faceArray,
+ const Array< Array<int> >& adjacentFaceArray,
+ Array<Vector3>& vertexNormalArray,
+ Array<Vector3>& faceNormalArray) {
+
+ // Construct a fake vertex array for backwards compatibility
+ Array<Vertex> fakeVertexArray(adjacentFaceArray.size());
+
+ for (int v = 0; v < adjacentFaceArray.size(); ++v) {
+ fakeVertexArray[v].faceIndex = adjacentFaceArray[v];
+ // We leave out the edges because they aren't used to compute normals
+ }
+
+ computeNormals(vertexGeometry, faceArray, fakeVertexArray,
+ vertexNormalArray, faceNormalArray);
+}
+
+
+void MeshAlg::computeNormals(
+ const Array<Vector3>& vertexGeometry,
+ const Array<Face>& faceArray,
+ const Array<Vertex>& vertexArray,
+ Array<Vector3>& vertexNormalArray,
+ Array<Vector3>& faceNormalArray) {
+
+ // Face normals (not unit length)
+ faceNormalArray.resize(faceArray.size());
+ for (int f = 0; f < faceArray.size(); ++f) {
+ const Face& face = faceArray[f];
+
+ Vector3 vertex[3];
+ for (int j = 0; j < 3; ++j) {
+ vertex[j] = vertexGeometry[face.vertexIndex[j]];
+ debugAssert(vertex[j].isFinite());
+ }
+
+ faceNormalArray[f] = (vertex[1] - vertex[0]).cross(vertex[2] - vertex[0]);
+# ifdef G3D_DEBUG
+ const Vector3& N = faceNormalArray[f];
+ debugAssert(N.isFinite());
+# endif
+ }
+
+ // Per-vertex normals, computed by averaging
+ vertexNormalArray.resize(vertexGeometry.size());
+ for (int v = 0; v < vertexNormalArray.size(); ++v) {
+ Vector3 sum = Vector3::zero();
+ for (int k = 0; k < vertexArray[v].faceIndex.size(); ++k) {
+ const int f = vertexArray[v].faceIndex[k];
+ sum += faceNormalArray[f];
+ }
+ vertexNormalArray[v] = sum.directionOrZero();
+# ifdef G3D_DEBUG
+ const Vector3& N = vertexNormalArray[v];
+ debugAssert(N.isUnit() || N.isZero());
+# endif
+ }
+
+
+ for (int f = 0; f < faceArray.size(); ++f) {
+ faceNormalArray[f] = faceNormalArray[f].directionOrZero();
+# ifdef G3D_DEBUG
+ const Vector3& N = faceNormalArray[f];
+ debugAssert(N.isUnit() || N.isZero());
+# endif
+ }
+
+}
+
+
+void MeshAlg::computeFaceNormals(
+ const Array<Vector3>& vertexArray,
+ const Array<MeshAlg::Face>& faceArray,
+ Array<Vector3>& faceNormals,
+ bool normalize) {
+
+ faceNormals.resize(faceArray.size());
+
+ for (int f = 0; f < faceArray.size(); ++f) {
+ const MeshAlg::Face& face = faceArray[f];
+
+ const Vector3& v0 = vertexArray[face.vertexIndex[0]];
+ const Vector3& v1 = vertexArray[face.vertexIndex[1]];
+ const Vector3& v2 = vertexArray[face.vertexIndex[2]];
+
+ faceNormals[f] = (v1 - v0).cross(v2 - v0);
+ }
+
+ if (normalize) {
+ for (int f = 0; f < faceArray.size(); ++f) {
+ faceNormals[f] = faceNormals[f].direction();
+ }
+ }
+}
+
+
+void MeshAlg::identifyBackfaces(
+ const Array<Vector3>& vertexArray,
+ const Array<MeshAlg::Face>& faceArray,
+ const Vector4& HP,
+ Array<bool>& backface) {
+
+ Vector3 P = HP.xyz();
+
+ backface.resize(faceArray.size());
+
+ if (fuzzyEq(HP.w, 0.0)) {
+ // Infinite case
+ for (int f = faceArray.size() - 1; f >= 0; --f) {
+ const MeshAlg::Face& face = faceArray[f];
+
+ const Vector3& v0 = vertexArray[face.vertexIndex[0]];
+ const Vector3& v1 = vertexArray[face.vertexIndex[1]];
+ const Vector3& v2 = vertexArray[face.vertexIndex[2]];
+
+ const Vector3 N = (v1 - v0).cross(v2 - v0);
+
+ backface[f] = N.dot(P) < 0;
+ }
+ } else {
+ // Finite case
+ for (int f = faceArray.size() - 1; f >= 0; --f) {
+ const MeshAlg::Face& face = faceArray[f];
+
+ const Vector3& v0 = vertexArray[face.vertexIndex[0]];
+ const Vector3& v1 = vertexArray[face.vertexIndex[1]];
+ const Vector3& v2 = vertexArray[face.vertexIndex[2]];
+
+ const Vector3 N = (v1 - v0).cross(v2 - v0);
+
+ backface[f] = N.dot(P - v0) < 0;
+ }
+ }
+}
+
+
+void MeshAlg::identifyBackfaces(
+ const Array<Vector3>& vertexArray,
+ const Array<MeshAlg::Face>& faceArray,
+ const Vector4& HP,
+ Array<bool>& backface,
+ const Array<Vector3>& faceNormals) {
+
+ Vector3 P = HP.xyz();
+
+ backface.resize(faceArray.size());
+
+ if (fuzzyEq(HP.w, 0.0)) {
+ // Infinite case
+ for (int f = faceArray.size() - 1; f >= 0; --f) {
+ const Vector3& N = faceNormals[f];
+ backface[f] = N.dot(P) < 0;
+ }
+ } else {
+ // Finite case
+ for (int f = faceArray.size() - 1; f >= 0; --f) {
+ const MeshAlg::Face& face = faceArray[f];
+ const Vector3& v0 = vertexArray[face.vertexIndex[0]];
+ const Vector3& N = faceNormals[f];
+
+ backface[f] = N.dot(P - v0) < 0;
+ }
+ }
+}
+
+
+void MeshAlg::createIndexArray(int n, Array<int>& array, int start, int run, int skip) {
+ debugAssert(skip >= 0);
+ debugAssert(run >= 0);
+
+ array.resize(n);
+ if (skip == 0) {
+ for (int i = 0; i < n; ++i) {
+ array[i] = start + i;
+ }
+ } else {
+ int rcount = 0;
+ int j = start;
+ for (int i = 0; i < n; ++i) {
+ array[i] = j;
+
+ ++j;
+ ++rcount;
+
+ if (rcount == run) {
+ rcount = 0;
+ j += skip;
+ }
+ }
+ }
+}
+
+
+void MeshAlg::computeAreaStatistics(
+ const Array<Vector3>& vertexArray,
+ const Array<int>& indexArray,
+ double& minEdgeLength,
+ double& meanEdgeLength,
+ double& medianEdgeLength,
+ double& maxEdgeLength,
+ double& minFaceArea,
+ double& meanFaceArea,
+ double& medianFaceArea,
+ double& maxFaceArea) {
+
+ debugAssert(indexArray.size() % 3 == 0);
+
+ Array<double> area(indexArray.size() / 3);
+ Array<double> magnitude(indexArray.size());
+
+ for (int i = 0; i < indexArray.size(); i += 3) {
+ const Vector3& v0 = vertexArray[indexArray[i]];
+ const Vector3& v1 = vertexArray[indexArray[i + 1]];
+ const Vector3& v2 = vertexArray[indexArray[i + 2]];
+
+ area[i / 3] = (v1 - v0).cross(v2 - v0).magnitude() / 2.0;
+ magnitude[i] = (v1 - v0).magnitude();
+ magnitude[i + 1] = (v2 - v1).magnitude();
+ magnitude[i + 2] = (v0 - v2).magnitude();
+ }
+
+ area.sort();
+ magnitude.sort();
+
+ minEdgeLength = max(0.0, magnitude[0]);
+ maxEdgeLength = max(0.0, magnitude.last());
+ medianEdgeLength = max(0.0, magnitude[magnitude.size() / 2]);
+ meanEdgeLength = 0;
+ for (int i = 0; i < magnitude.size(); ++i) {
+ meanEdgeLength += magnitude[i];
+ }
+ meanEdgeLength /= magnitude.size();
+
+ minFaceArea = max(0.0, area[0]);
+ maxFaceArea = max(0.0, area.last());
+ medianFaceArea = max(0.0, area[area.size() / 2]);
+ meanFaceArea = 0;
+ for (int i = 0; i < area.size(); ++i) {
+ meanFaceArea += area[i];
+ }
+ meanFaceArea /= area.size();
+
+
+ // Make sure round-off hasn't pushed values less than zero
+ meanFaceArea = max(0.0, meanFaceArea);
+ meanEdgeLength = max(0.0, meanEdgeLength);
+}
+
+
+int MeshAlg::countBoundaryEdges(const Array<MeshAlg::Edge>& edgeArray) {
+ int b = 0;
+
+ for (int i = 0; i < edgeArray.size(); ++i) {
+ if ((edgeArray[i].faceIndex[0] == MeshAlg::Face::NONE) !=
+ (edgeArray[i].faceIndex[1] == MeshAlg::Face::NONE)) {
+ ++b;
+ }
+ }
+
+ return b;
+}
+
+void MeshAlg::computeBounds(
+ const Array<Vector3>& vertexArray,
+ const Array<int>& indexArray,
+ Box& box,
+ Sphere& sphere) {
+
+ Array<Vector3> newArray(indexArray.size());
+ for (int i = 0; i < indexArray.size(); ++i) {
+ newArray[i] = vertexArray[indexArray[i]];
+ }
+ computeBounds(newArray, box, sphere);
+}
+
+
+void MeshAlg::computeBounds(
+ const Array<Vector3>& vertexArray,
+ Box& box,
+ Sphere& sphere) {
+
+ Vector3 xmin, xmax, ymin, ymax, zmin, zmax;
+
+ // FIRST PASS: find 6 minima/maxima points
+ xmin.x = ymin.y = zmin.z = inf();
+ xmax.x = ymax.y = zmax.z = -inf();
+
+ for (int v = 0; v < vertexArray.size(); ++v) {
+ const Vector3& vertex = vertexArray[v];
+
+ if (vertex.x < xmin.x) {
+ xmin = vertex;
+ }
+
+ if (vertex.x > xmax.x) {
+ xmax = vertex;
+ }
+
+ if (vertex.y < ymin.y) {
+ ymin = vertex;
+ }
+
+ if (vertex.y > ymax.y) {
+ ymax = vertex;
+ }
+
+ if (vertex.z < zmin.z) {
+ zmin = vertex;
+ }
+
+ if (vertex.z > zmax.z) {
+ zmax = vertex;
+ }
+ }
+
+ // Set points dia1 & dia2 to the maximally separated pair
+ Vector3 dia1 = xmin;
+ Vector3 dia2 = xmax;
+ {
+ // Set xspan = distance between the 2 points xmin & xmax (squared)
+ double xspan = (xmax - xmin).squaredMagnitude();
+
+ // Same for y & z spans
+ double yspan = (ymax - ymin).squaredMagnitude();
+ double zspan = (zmax - zmin).squaredMagnitude();
+
+ double maxspan = xspan;
+
+ if (yspan > maxspan) {
+ maxspan = yspan;
+ dia1 = ymin;
+ dia2 = ymax;
+ }
+
+ if (zspan > maxspan) {
+ maxspan = zspan;
+ dia1 = zmin;
+ dia2 = zmax;
+ }
+ }
+
+
+ // dia1, dia2 is a diameter of initial sphere
+
+ // calc initial center
+ Vector3 center = (dia1 + dia2) / 2.0;
+
+ // calculate initial radius^2 and radius
+ Vector3 d = dia2 - sphere.center;
+
+ double radSq = d.squaredMagnitude();
+ double rad = sqrt(radSq);
+
+ // SECOND PASS: increment current sphere
+ double old_to_p, old_to_new;
+
+ for (int v = 0; v < vertexArray.size(); ++v) {
+ const Vector3& vertex = vertexArray[v];
+
+ d = vertex - center;
+
+ double old_to_p_sq = d.squaredMagnitude();
+
+ // do r^2 test first
+ if (old_to_p_sq > radSq) {
+ // this point is outside of current sphere
+ old_to_p = sqrt(old_to_p_sq);
+
+ // calc radius of new sphere
+ rad = (rad + old_to_p) / 2.0;
+
+ // for next r^2 compare
+ radSq = rad * rad;
+ old_to_new = old_to_p - rad;
+
+ // calc center of new sphere
+ center = (rad * center + old_to_new * vertex) / old_to_p;
+ }
+ }
+
+ const Vector3 min(xmin.x, ymin.y, zmin.z);
+ const Vector3 max(xmax.x, ymax.y, zmax.z);
+
+ box = Box(min, max);
+
+ const double boxRadSq = (max - min).squaredMagnitude() * 0.25;
+
+ if (boxRadSq >= radSq){
+ if (isNaN(center.x) || ! isFinite(rad)) {
+ sphere = Sphere(Vector3::zero(), inf());
+ } else {
+ sphere = Sphere(center, rad);
+ }
+ }else{
+ sphere = Sphere((max + min) * 0.5, sqrt(boxRadSq));
+ }
+}
+
+
+void MeshAlg::computeTangentVectors(
+ const Vector3& normal,
+ const Vector3 position[3],
+ const Vector2 texCoord[3],
+ Vector3& tangent,
+ Vector3& binormal) {
+
+ Vector3 v[3];
+ Vector2 t[3];
+
+ // TODO: don't need the copy
+ // Make a copy so that we can sort
+ for (int i = 0; i < 3; ++i) {
+ v[i] = position[i];
+ t[i] = texCoord[i];
+ }
+
+ /////////////////////////////////////////////////
+ // Begin by computing the tangent
+
+ // Bubble sort the vertices by decreasing texture coordinate y.
+ if (t[0].y < t[1].y) {
+ std::swap(v[0], v[1]);
+ std::swap(t[0], t[1]);
+ }
+
+ // t0 >= t1
+
+ if (t[0].y < t[2].y) {
+ std::swap(v[0], v[2]);
+ std::swap(t[0], t[2]);
+ }
+
+ // t0 >= t2, t0 >= t1
+
+ if (t[1].y < t[2].y) {
+ std::swap(v[1], v[2]);
+ std::swap(t[1], t[2]);
+ }
+
+ // t0 >= t1 >= t2
+
+ float amount;
+
+ // Compute the direction of constant y.
+ if (fuzzyEq(t[2].y, t[0].y)) {
+ // Degenerate case-- the texture coordinates do not vary across this
+ // triangle.
+ amount = 1.0;
+ } else {
+ // Solve lerp(t[0].y, t[2].y, amount) = t[1].y for amount:
+ //
+ // t0 + (t2 - t0) * a = t1
+ // a = (t1 - t0) / (t2 - t0)
+
+ amount = (t[1].y - t[0].y) / (t[2].y - t[0].y);
+ }
+
+ tangent = lerp(v[0], v[2], amount) - v[1];
+
+ // Make sure the tangent points in the right direction and is
+ // perpendicular to the normal.
+ if (lerp(t[0].x, t[2].x, amount) < t[1].x) {
+ tangent = -tangent;
+ }
+
+ // TODO: do we need this? We take this component off
+ // at the end anyway
+ tangent -= tangent.dot(normal) * normal;
+
+ // Normalize the tangent so it contributes
+ // equally at the vertex (TODO: do we need this?)
+ if (fuzzyEq(tangent.magnitude(), 0.0)) {
+ tangent = Vector3::unitX();
+ } else {
+ tangent = tangent.direction();
+ }
+
+ //////////////////////////////////////////////////
+ // Now compute the binormal (same code, but in x)
+
+ // Sort the vertices by texture coordinate x.
+ if (t[0].x < t[1].x) {
+ std::swap(v[0], v[1]);
+ std::swap(t[0], t[1]);
+ }
+
+ if (t[0].x < t[2].x) {
+ std::swap(v[0], v[2]);
+ std::swap(t[0], t[2]);
+ }
+
+ if (t[1].x < t[2].x) {
+ std::swap(v[1], v[2]);
+ std::swap(t[1], t[2]);
+ }
+
+ // Compute the direction of constant x.
+ if (fuzzyEq(t[2].x, t[0].x)) {
+ amount = 1.0;
+ } else {
+ amount = (t[1].x - t[0].x) / (t[2].x - t[0].x);
+ }
+
+ binormal = lerp(v[0], v[2], amount) - v[1];
+
+ // Make sure the binormal points in the right direction and is
+ // perpendicular to the normal.
+ if (lerp(t[0].y, t[2].y, amount) < t[1].y) {
+ binormal = -binormal;
+ }
+
+ binormal -= binormal.dot(normal) * normal;
+
+ // Normalize the binormal so that it contributes
+ // an equal amount to the per-vertex value (TODO: do we need this?
+ // Nelson Max showed that we don't for computing per-vertex normals)
+ if (fuzzyEq(binormal.magnitude(), 0.0)) {
+ binormal = Vector3::unitZ();
+ } else {
+ binormal = binormal.direction();
+ }
+
+ // This computation gives the opposite convention of what we want.
+ binormal = -binormal;
+
+}
+
+
+void MeshAlg::computeTangentSpaceBasis(
+ const Array<Vector3>& vertexArray,
+ const Array<Vector2>& texCoordArray,
+ const Array<Vector3>& vertexNormalArray,
+ const Array<Face>& faceArray,
+ Array<Vector3>& tangent,
+ Array<Vector3>& binormal) {
+
+ debugAssertM(faceArray.size() != 0, "Unable to calculate valid tangent space without faces.");
+
+ // The three vertices and texCoords of each face
+ Vector3 position[3];
+ Vector2 texCoord[3];
+ Vector3 t, b;
+
+ tangent.resize(vertexArray.size());
+ binormal.resize(vertexArray.size());
+
+ // Zero the output arrays.
+ System::memset(tangent.getCArray(), 0, sizeof(Vector3) * tangent.size());
+ System::memset(binormal.getCArray(), 0, sizeof(Vector3) * binormal.size());
+
+ // Iterate over faces, computing the tangent vectors for each
+ // vertex. Accumulate those into the tangent and binormal arrays
+ // and then orthonormalize at the end.
+
+ for (int f = 0; f < faceArray.size(); ++f) {
+ const Face& face = faceArray[f];
+
+ for (int v = 0; v < 3; ++v) {
+ int i = face.vertexIndex[v];
+ position[v] = vertexArray[i];
+ texCoord[v] = texCoordArray[i];
+ }
+
+ const Vector3 faceNormal((position[1] - position[0]).cross(position[2] - position[0]).direction());
+ computeTangentVectors(faceNormal, position, texCoord, t, b);
+
+ for (int v = 0; v < 3; ++v) {
+ int i = face.vertexIndex[v];
+ tangent[i] += t;
+ binormal[i] += b;
+ }
+ }
+
+ // Normalize the basis vectors
+ for (int v = 0; v < vertexArray.size(); ++v) {
+ // Remove the component parallel to the normal
+ const Vector3& N = vertexNormalArray[v];
+ debugAssertM(N.isUnit() || N.isZero(), "Input normals must have unit length");
+
+ tangent[v] -= tangent[v].dot(N) * N;
+ binormal[v] -= binormal[v].dot(N) * N;
+
+ // Normalize
+ tangent[v] = tangent[v].directionOrZero();
+ binormal[v] = binormal[v].directionOrZero();
+
+ // Note that the tangent and binormal might not be perpendicular anymore
+ }
+}
+
+
+
+} // G3D namespace
diff --git a/externals/g3dlite/G3D.lib/source/MeshAlgAdjacency.cpp b/externals/g3dlite/G3D.lib/source/MeshAlgAdjacency.cpp
new file mode 100644
index 00000000000..a8b35f32c86
--- /dev/null
+++ b/externals/g3dlite/G3D.lib/source/MeshAlgAdjacency.cpp
@@ -0,0 +1,729 @@
+/**
+ @file MeshAlgAdjacency.cpp
+
+ @maintainer Morgan McGuire, matrix@graphics3d.com
+ @created 2003-09-14
+ @edited 2005-02-24
+
+ Copyright 2000-2003, Morgan McGuire.
+ All rights reserved.
+
+ */
+
+#include "G3D/Table.h"
+#include "G3D/MeshAlg.h"
+#include "G3D/Set.h"
+
+
+namespace G3D {
+
+/**
+ A half [i.e. directed] edge.
+ */
+class MeshDirectedEdgeKey {
+public:
+
+ /** vertexIndex[0] <= vertexIndex[1] */
+ int vertexIndex[2];
+
+ MeshDirectedEdgeKey() {}
+
+ MeshDirectedEdgeKey(
+ const int i0,
+ const int i1) {
+
+ if (i0 <= i1) {
+ vertexIndex[0] = i0;
+ vertexIndex[1] = i1;
+ } else {
+ vertexIndex[0] = i1;
+ vertexIndex[1] = i0;
+ }
+ }
+
+
+ bool operator==(const MeshDirectedEdgeKey& e2) const {
+ for (int i = 0; i < 2; ++i) {
+ if (vertexIndex[i] != e2.vertexIndex[i]) {
+ return false;
+ }
+ }
+ return true;
+ }
+};
+
+}
+
+template<> struct HashTrait<G3D::MeshDirectedEdgeKey> {
+ static size_t hashCode(const G3D::MeshDirectedEdgeKey& key) {
+ return key.vertexIndex[0] + (key.vertexIndex[1] << 16);
+ }
+};
+
+namespace G3D {
+
+/**
+ A hashtable mapping edges to lists of indices for
+ faces. This is used instead of Table because of the
+ special logic in insert.
+
+ Used only for MeshAlg::computeAdjacency.
+
+ In the face lists, index <I>f</I> >= 0 indicates that
+ <I>f</I> contains the edge as a forward edge. Index <I>f</I> < 0
+ indicates that ~<I>f</I> contains the edge as a backward edge.
+ */
+class MeshEdgeTable {
+public:
+ typedef Table<MeshDirectedEdgeKey, Array<int> > ET;
+
+private:
+
+ ET table;
+
+public:
+
+ /**
+ Clears the table.
+ */
+ void clear() {
+ table.clear();
+ }
+
+ /**
+ Inserts the faceIndex into the edge's face list.
+ The index may be a negative number indicating a backface.
+ */
+ void insert(const MeshDirectedEdgeKey& edge, int faceIndex) {
+
+ // debugAssertM((table.size() > 20) && (table.debugGetLoad() < 0.5 || table.debugGetNumBuckets() < 20),
+ // "MeshEdgeTable is using a poor hash function.");
+
+ if (! table.containsKey(edge)) {
+ // First time
+ Array<int> x(1);
+ x[0] = faceIndex;
+ table.set(edge, x);
+ } else {
+ table[edge].append(faceIndex);
+ }
+ }
+
+ /**
+ Returns the face list for a given edge
+ */
+ const Array<int>& get(const MeshDirectedEdgeKey& edge) {
+ return table[edge];
+ }
+
+ ET::Iterator begin() {
+ return table.begin();
+ }
+
+ const ET::Iterator end() const {
+ return table.end();
+ }
+
+};
+
+
+/**
+ edgeTable[edgeKey] is a list of faces containing
+
+ Used and cleared by MeshModel::computeAdjacency()
+ */
+static MeshEdgeTable edgeTable;
+
+/**
+ Assigns the edge index into the next unassigned edge
+ index. The edge index may be negative, indicating
+ a reverse edge.
+ */
+static void assignEdgeIndex(MeshAlg::Face& face, int e) {
+ for (int i = 0; i < 3; ++i) {
+ if (face.edgeIndex[i] == MeshAlg::Face::NONE) {
+ face.edgeIndex[i] = e;
+ return;
+ }
+ }
+
+ debugAssertM(false, "Face has already been assigned 3 edges");
+}
+
+
+void MeshAlg::computeAdjacency(
+ const Array<Vector3>& vertexGeometry,
+ const Array<int>& indexArray,
+ Array<Face>& faceArray,
+ Array<Edge>& edgeArray,
+ Array< Array<int> >& adjacentFaceArray) {
+
+ Array<Vertex> vertexArray;
+
+ computeAdjacency(vertexGeometry, indexArray, faceArray, edgeArray, vertexArray);
+
+ // Convert the vertexArray into adjacentFaceArray
+ adjacentFaceArray.clear();
+ adjacentFaceArray.resize(vertexArray.size());
+ for (int v = 0; v < adjacentFaceArray.size(); ++v) {
+ adjacentFaceArray[v] = vertexArray[v].faceIndex;
+ }
+}
+
+
+void MeshAlg::computeAdjacency(
+ const Array<Vector3>& vertexGeometry,
+ const Array<int>& indexArray,
+ Array<Face>& faceArray,
+ Array<Edge>& edgeArray,
+ Array<Vertex>& vertexArray) {
+
+ edgeArray.clear();
+ vertexArray.clear();
+ faceArray.clear();
+ edgeTable.clear();
+
+ // Face normals
+ Array<Vector3> faceNormal;
+
+ // This array has the same size as the vertex array
+ vertexArray.resize(vertexGeometry.size());
+
+ // Iterate through the triangle list
+ for (int q = 0; q < indexArray.size(); q += 3) {
+
+ Vector3 vertex[3];
+ int f = faceArray.size();
+ MeshAlg::Face& face = faceArray.next();
+
+ // Construct the face
+ for (int j = 0; j < 3; ++j) {
+ int v = indexArray[q + j];
+ face.vertexIndex[j] = v;
+ face.edgeIndex[j] = Face::NONE;
+
+ // Store back pointers in the vertices
+ vertexArray[v].faceIndex.append(f);
+
+ // We'll need these vertices to find the face normal
+ vertex[j] = vertexGeometry[v];
+ }
+
+ // Compute the face normal
+ Vector3 N = (vertex[1] - vertex[0]).cross(vertex[2] - vertex[0]);
+ faceNormal.append(N.directionOrZero());
+
+ static const int nextIndex[] = {1, 2, 0};
+
+ // Add each edge to the edge table.
+ for (int j = 0; j < 3; ++j) {
+ const int i0 = indexArray[q + j];
+ const int i1 = indexArray[q + nextIndex[j]];
+
+ const MeshDirectedEdgeKey edge(i0, i1);
+
+ if (i0 == edge.vertexIndex[0]) {
+ // The edge was directed in the same manner as in the face
+ edgeTable.insert(edge, f);
+ } else {
+ // The edge was directed in the opposite manner as in the face
+ edgeTable.insert(edge, ~f);
+ }
+ }
+ }
+
+ // For each edge in the edge table, create an edge in the edge array.
+ // Collapse every 2 edges from adjacent faces.
+
+ MeshEdgeTable::ET::Iterator cur = edgeTable.begin();
+ MeshEdgeTable::ET::Iterator end = edgeTable.end();
+
+ Array<Edge> tempEdgeArray;
+ while (cur != end) {
+ MeshDirectedEdgeKey& edgeKey = cur->key;
+ Array<int>& faceIndexArray = cur->value;
+
+ // Process this edge
+ while (faceIndexArray.size() > 0) {
+
+ // Remove the last index
+ int f0 = faceIndexArray.pop();
+
+ // Find the normal to that face
+ const Vector3& n0 = faceNormal[(f0 >= 0) ? f0 : ~f0];
+
+ bool found = false;
+
+ // We try to find the matching face with the closest
+ // normal. This ensures that we don't introduce a lot
+ // of artificial ridges into flat parts of a mesh.
+ double ndotn = -2;
+ int f1 = -1, i1 = -1;
+
+ // Try to Find the face with the matching edge
+ for (int i = faceIndexArray.size() - 1; i >= 0; --i) {
+ int f = faceIndexArray[i];
+
+ if ((f >= 0) != (f0 >= 0)) {
+ // This face contains the oppositely oriented edge
+ // and has not been assigned too many edges
+
+ const Vector3& n1 = faceNormal[(f >= 0) ? f : ~f];
+ double d = n1.dot(n0);
+
+ if (found) {
+ // We previously found a good face; see if this
+ // one is better.
+ if (d > ndotn) {
+ // This face is better.
+ ndotn = d;
+ f1 = f;
+ i1 = i;
+ }
+ } else {
+ // This is the first face we've found
+ found = true;
+ ndotn = d;
+ f1 = f;
+ i1 = i;
+ }
+ }
+ }
+
+ // Create the new edge
+ int e = tempEdgeArray.size();
+ Edge& edge = tempEdgeArray.next();
+
+ edge.vertexIndex[0] = edgeKey.vertexIndex[0];
+ edge.vertexIndex[1] = edgeKey.vertexIndex[1];
+
+ if (f0 >= 0) {
+ edge.faceIndex[0] = f0;
+ edge.faceIndex[1] = Face::NONE;
+ assignEdgeIndex(faceArray[f0], e);
+ } else {
+ // The face indices above are two's complemented.
+ // this code restores them to regular indices.
+ debugAssert((~f0) >= 0);
+ edge.faceIndex[1] = ~f0;
+ edge.faceIndex[0] = Face::NONE;
+
+ // The edge index *does* need to be inverted, however.
+ assignEdgeIndex(faceArray[~f0], ~e);
+ }
+
+ if (found) {
+ // We found a matching face; remove both
+ // faces from the active list.
+ faceIndexArray.fastRemove(i1);
+
+ if (f1 >= 0) {
+ edge.faceIndex[0] = f1;
+ assignEdgeIndex(faceArray[f1], e);
+ } else {
+ edge.faceIndex[1] = ~f1;
+ assignEdgeIndex(faceArray[~f1], ~e);
+ }
+ }
+ }
+
+ ++cur;
+ }
+
+ edgeTable.clear();
+
+ // Move boundary edges to the end of the list and then
+ // clean up the face references into them
+ {
+ // Map old edge indices to new edge indices
+ Array<int> newIndex(tempEdgeArray.size());
+
+
+ // Index of the start and end of the edge array
+ int i = 0;
+ int j = tempEdgeArray.size() - 1;
+
+ edgeArray.resize(tempEdgeArray.size());
+ for (int e = 0; e < tempEdgeArray.size(); ++e) {
+ if (tempEdgeArray[e].boundary()) {
+ newIndex[e] = j;
+ --j;
+ } else {
+ newIndex[e] = i;
+ ++i;
+ }
+ edgeArray[newIndex[e]] = tempEdgeArray[e];
+ }
+
+ debugAssertM(i == j + 1, "Counting from front and back of array did not match");
+
+ // Fix the faces
+ for (int f = 0; f < faceArray.size(); ++f) {
+ Face& face = faceArray[f];
+ for (int q = 0; q < 3; ++q) {
+ int e = face.edgeIndex[q];
+ if (e < 0) {
+ // Backwards edge; twiddle before and after conversion
+ face.edgeIndex[q] = ~newIndex[~e];
+ } else {
+ // Regular edge; remap the index
+ face.edgeIndex[q] = newIndex[e];
+ }
+ }
+ }
+ }
+
+ // Now order the edge indices inside the faces correctly.
+ for (int f = 0; f < faceArray.size(); ++f) {
+ Face& face = faceArray[f];
+ int e0 = face.edgeIndex[0];
+ int e1 = face.edgeIndex[1];
+ int e2 = face.edgeIndex[2];
+
+ // e0 will always remain first. The only
+ // question is whether e1 and e2 should be swapped.
+
+ // See if e1 begins at the vertex where e1 ends.
+ const int e0End = (e0 < 0) ?
+ edgeArray[~e0].vertexIndex[0] :
+ edgeArray[e0].vertexIndex[1];
+
+ const int e1Begin = (e1 < 0) ?
+ edgeArray[~e1].vertexIndex[1] :
+ edgeArray[e1].vertexIndex[0];
+
+ if (e0End != e1Begin) {
+ // We must swap e1 and e2
+ face.edgeIndex[1] = e2;
+ face.edgeIndex[2] = e1;
+ }
+ }
+
+ // Fill out the edge adjacency information in the vertex array
+ for (int e = 0; e < edgeArray.size(); ++e) {
+ const Edge& edge = edgeArray[e];
+ vertexArray[edge.vertexIndex[0]].edgeIndex.append(e);
+ vertexArray[edge.vertexIndex[1]].edgeIndex.append(~e);
+ }
+}
+
+
+void MeshAlg::weldBoundaryEdges(
+ Array<Face>& faceArray,
+ Array<Edge>& edgeArray,
+ Array<Vertex>& vertexArray) {
+
+ // Copy over the original edge array
+ Array<Edge> oldEdgeArray = edgeArray;
+
+ // newEdgeIndex[e] is the new index of the old edge with index e
+ // Note that newEdgeIndex[e] might be negative, indicating that
+ // the edge switched direction between the arrays.
+ Array<int> newEdgeIndex(edgeArray.size());
+ edgeArray.resize(0);
+
+ // boundaryEdgeIndices[v_low] is an array of the indices of
+ // all boundary edges whose lower vertex is v_low.
+ Table<int, Array<int> > boundaryEdgeIndices;
+
+ // Copy over non-boundary edges to the new array
+ for (int e = 0; e < oldEdgeArray.size(); ++e) {
+ if (oldEdgeArray[e].boundary()) {
+
+ // Add to the boundary table
+ const int v_low = iMin(oldEdgeArray[e].vertexIndex[0], oldEdgeArray[e].vertexIndex[1]);
+ if (! boundaryEdgeIndices.containsKey(v_low)) {
+ boundaryEdgeIndices.set(v_low, Array<int>());
+ }
+ boundaryEdgeIndices[v_low].append(e);
+
+ // We'll fill out newEdgeIndex[e] later, when we find pairs
+
+ } else {
+
+ // Copy the edge to the new array
+ newEdgeIndex[e] = edgeArray.size();
+ edgeArray.append(oldEdgeArray[e]);
+
+ }
+ }
+
+
+ // Remove all edges from the table that have pairs.
+ Table<int, Array<int> >::Iterator cur = boundaryEdgeIndices.begin();
+ Table<int, Array<int> >::Iterator end = boundaryEdgeIndices.end();
+ while (cur != end) {
+ Array<int>& boundaryEdge = cur->value;
+
+ for (int i = 0; i < boundaryEdge.size(); ++i) {
+ int ei = boundaryEdge[i];
+ const Edge& edgei = oldEdgeArray[ei];
+
+ for (int j = i + 1; j < boundaryEdge.size(); ++j) {
+ int ej = boundaryEdge[j];
+ const Edge& edgej = oldEdgeArray[ej];
+
+ // See if edge ei is the reverse (match) of edge ej.
+
+ // True if the edges match
+ bool match = false;
+
+ // True if edgej's vertex indices are reversed from
+ // edgei's (usually true).
+ bool reversej = false;
+
+ int u = edgei.vertexIndex[0];
+ int v = edgei.vertexIndex[1];
+
+ if (edgei.faceIndex[0] != Face::NONE) {
+ // verts|faces
+ // edgei = [u v A /]
+
+ if (edgej.faceIndex[0] != Face::NONE) {
+ if ((edgej.vertexIndex[0] == v) && (edgej.vertexIndex[1] == u)) {
+ // This is the most common of the four cases
+
+ // edgej = [v u B /]
+ match = true;
+ reversej = true;
+ }
+ } else {
+ if ((edgej.vertexIndex[0] == u) && (edgej.vertexIndex[1] == v)) {
+ // edgej = [u v / B]
+ match = true;
+ }
+ }
+ } else {
+ // edgei = [u v / A]
+ if (edgej.faceIndex[0] != Face::NONE) {
+ if ((edgej.vertexIndex[0] == u) && (edgej.vertexIndex[1] == v)) {
+ // edgej = [u v B /]
+ match = true;
+ }
+ } else {
+ if ((edgej.vertexIndex[0] == v) && (edgej.vertexIndex[1] == u)) {
+ // edgej = [v u / B]
+ match = true;
+ reversej = true;
+ }
+ }
+ }
+
+ if (match) {
+ // ei and ej can be paired as a single edge
+ int e = edgeArray.size();
+ Edge& edge = edgeArray.next();
+
+ // Follow the direction of edgei.
+ edge = edgei;
+ newEdgeIndex[ei] = e;
+
+ // Insert the face index for edgej.
+ int fj = edgej.faceIndex[0];
+ if (fj == Face::NONE) {
+ fj = edgej.faceIndex[1];
+ }
+
+ if (edge.faceIndex[0] == Face::NONE) {
+ edge.faceIndex[0] = fj;
+ } else {
+ edge.faceIndex[1] = fj;
+ }
+
+ if (reversej) {
+ // The new edge is backwards of the old edge for ej
+ newEdgeIndex[ej] = ~e;
+ } else {
+ newEdgeIndex[ej] = e;
+ }
+
+ // Remove both ei and ej from being candidates for future pairing.
+ // Remove ej first since it comes later in the list (removing
+ // ei would decrease the index of ej to j - 1).
+ boundaryEdge.fastRemove(j);
+ boundaryEdge.fastRemove(i);
+
+ // Re-process element i, which is now a new edge index
+ --i;
+
+ // Jump out of the j for-loop
+ break;
+ }
+ }
+ }
+ ++cur;
+ }
+
+ // Anything remaining in the table is a real boundary edge; just copy it to
+ // the end of the array.
+ cur = boundaryEdgeIndices.begin();
+ end = boundaryEdgeIndices.end();
+ while (cur != end) {
+ Array<int>& boundaryEdge = cur->value;
+
+ for (int b = 0; b < boundaryEdge.size(); ++b) {
+ const int e = boundaryEdge[b];
+
+ newEdgeIndex[e] = edgeArray.size();
+ edgeArray.append(oldEdgeArray[e]);
+ }
+
+ ++cur;
+ }
+
+ // Finally, fix up edge indices in the face and vertex arrays
+ for (int f = 0; f < faceArray.size(); ++f) {
+ Face& face = faceArray[f];
+ for (int i = 0; i < 3; ++i) {
+ int e = face.edgeIndex[i];
+
+ if (e < 0) {
+ face.edgeIndex[i] = ~newEdgeIndex[~e];
+ } else {
+ face.edgeIndex[i] = newEdgeIndex[e];
+ }
+ }
+ }
+
+ for (int v = 0; v < vertexArray.size(); ++v) {
+ Vertex& vertex = vertexArray[v];
+ for (int i = 0; i < vertex.edgeIndex.size(); ++i) {
+ int e = vertex.edgeIndex[i];
+
+ if (e < 0) {
+ vertex.edgeIndex[i] = ~newEdgeIndex[~e];
+ } else {
+ vertex.edgeIndex[i] = newEdgeIndex[e];
+ }
+ }
+ }
+}
+
+
+void MeshAlg::weldAdjacency(
+ const Array<Vector3>& originalGeometry,
+ Array<Face>& faceArray,
+ Array<Edge>& edgeArray,
+ Array<Vertex>& vertexArray,
+ double radius) {
+
+ // Num vertices
+ const int n = originalGeometry.size();
+
+ // canonical[v] = first occurance of any vertex near oldVertexArray[v]
+ Array<int> canonical(n);
+
+ Array<int> toNew, toOld;
+ // Throw away the new vertex array
+ Array<Vector3> dummy;
+ computeWeld(originalGeometry, dummy, toNew, toOld, radius);
+
+ for (int v = 0; v < canonical.size(); ++v) {
+ // Round-trip through the toNew/toOld process. This will give
+ // us the original vertex.
+ canonical[v] = toOld[toNew[v]];
+ }
+
+ // Destroy vertexArray (we reconstruct it below)
+ vertexArray.clear();
+ vertexArray.resize(n);
+
+ bool hasBoundaryEdges = false;
+
+ // Fix edge vertex indices
+ for (int e = 0; e < edgeArray.size(); ++e) {
+ Edge& edge = edgeArray[e];
+
+ const int v0 = canonical[edge.vertexIndex[0]];
+ const int v1 = canonical[edge.vertexIndex[1]];
+
+ edge.vertexIndex[0] = v0;
+ edge.vertexIndex[1] = v1;
+
+ vertexArray[v0].edgeIndex.append(e);
+ vertexArray[v1].edgeIndex.append(~e);
+
+ hasBoundaryEdges = hasBoundaryEdges || edge.boundary();
+ }
+
+ // Fix face vertex indices
+ for (int f = 0; f < faceArray.size(); ++f) {
+ Face& face = faceArray[f];
+ for (int i = 0; i < 3; ++i) {
+ const int v = canonical[face.vertexIndex[i]];
+
+ face.vertexIndex[i] = v;
+
+ // Add the back pointer
+ vertexArray[v].faceIndex.append(f);
+ }
+ }
+
+ if (hasBoundaryEdges) {
+ // As a result of the welding, some of the boundary edges at
+ // the end of the array may now have mates and no longer be
+ // boundaries. Try to pair these up.
+
+ weldBoundaryEdges(faceArray, edgeArray, vertexArray);
+ }
+}
+
+
+void MeshAlg::debugCheckConsistency(
+ const Array<Face>& faceArray,
+ const Array<Edge>& edgeArray,
+ const Array<Vertex>& vertexArray) {
+
+#ifdef _DEBUG
+ for (int v = 0; v < vertexArray.size(); ++v) {
+ const MeshAlg::Vertex& vertex = vertexArray[v];
+
+ for (int i = 0; i < vertex.edgeIndex.size(); ++i) {
+ const int e = vertex.edgeIndex[i];
+ debugAssert(edgeArray[(e >= 0) ? e : ~e].containsVertex(v));
+ }
+
+ for (int i = 0; i < vertex.faceIndex.size(); ++i) {
+ const int f = vertex.faceIndex[i];
+ debugAssert(faceArray[f].containsVertex(v));
+ }
+
+ }
+
+ for (int e = 0; e < edgeArray.size(); ++e) {
+ const MeshAlg::Edge& edge = edgeArray[e];
+
+ for (int i = 0; i < 2; ++i) {
+ debugAssert((edge.faceIndex[i] == MeshAlg::Face::NONE) ||
+ faceArray[edge.faceIndex[i]].containsEdge(e));
+
+ debugAssert(vertexArray[edge.vertexIndex[i]].inEdge(e));
+ }
+ }
+
+ // Every face's edge must be on that face
+ for (int f = 0; f < faceArray.size(); ++f) {
+ const MeshAlg::Face& face = faceArray[f];
+ for (int i = 0; i < 3; ++i) {
+ int e = face.edgeIndex[i];
+ int ei = (e >= 0) ? e : ~e;
+ debugAssert(edgeArray[ei].inFace(f));
+
+ // Make sure the edge is oriented appropriately
+ if (e >= 0) {
+ debugAssert(edgeArray[ei].faceIndex[0] == (int)f);
+ } else {
+ debugAssert(edgeArray[ei].faceIndex[1] == (int)f);
+ }
+
+ debugAssert(vertexArray[face.vertexIndex[i]].inFace(f));
+ }
+ }
+#else
+ (void)faceArray;
+ (void)edgeArray;
+ (void)vertexArray;
+#endif // _DEBUG
+}
+
+} // G3D namespace
diff --git a/externals/g3dlite/G3D.lib/source/MeshAlgWeld.cpp b/externals/g3dlite/G3D.lib/source/MeshAlgWeld.cpp
new file mode 100644
index 00000000000..cd4d1f9c7d5
--- /dev/null
+++ b/externals/g3dlite/G3D.lib/source/MeshAlgWeld.cpp
@@ -0,0 +1,213 @@
+/**
+ @file MeshAlgWeld.cpp
+
+ The MeshAlg::computeWeld method.
+
+ @maintainer Morgan McGuire, matrix@graphics3d.com
+ @created 2003-10-22
+ @edited 2005-02-24
+
+ Copyright 2000-2003, Morgan McGuire.
+ All rights reserved.
+
+ */
+
+#include "G3D/MeshAlg.h"
+#include "G3D/Table.h"
+#include "G3D/Set.h"
+
+namespace G3D {
+
+namespace _internal {
+
+class Welder {
+private:
+
+ // Intentionally illegal
+ Welder& operator=(const Welder& w);
+
+public:
+ /** Indices of newVertexArray elements in <B>or near</B> a grid cell. */
+ typedef Array<int> List;
+
+ enum {GRID_RES = 32};
+
+ List grid[GRID_RES][GRID_RES][GRID_RES];
+
+ const Array<Vector3>& oldVertexArray;
+ Array<Vector3>& newVertexArray;
+ Array<int>& toNew;
+ Array<int>& toOld;
+
+ /** Must be less than one grid cell, not checked */
+ const double radius;
+
+ /** (oldVertexArray[i] - offset) * scale is on the range [0, 1] */
+ Vector3 offset;
+ Vector3 scale;
+
+ Welder(
+ const Array<Vector3>& _oldVertexArray,
+ Array<Vector3>& _newVertexArray,
+ Array<int>& _toNew,
+ Array<int>& _toOld,
+ double _radius);
+
+ /**
+ Computes the grid index from an ordinate.
+ */
+ void toGridCoords(Vector3 v, int& x, int& y, int& z) const;
+
+ /** Gets the index of a vertex, adding it to
+ newVertexArray if necessary. */
+ int getIndex(const Vector3& vertex);
+
+ void weld();
+};
+
+} // namespace _internal
+
+} // namespace G3D
+
+template<> struct HashTrait<G3D::_internal::Welder::List*> {
+ static size_t hashCode(const G3D::_internal::Welder::List* key) { return reinterpret_cast<size_t>(key); }
+};
+
+namespace G3D {
+namespace _internal {
+
+Welder::Welder(
+ const Array<Vector3>& _oldVertexArray,
+ Array<Vector3>& _newVertexArray,
+ Array<int>& _toNew,
+ Array<int>& _toOld,
+ double _radius) :
+ oldVertexArray(_oldVertexArray),
+ newVertexArray(_newVertexArray),
+ toNew(_toNew),
+ toOld(_toOld),
+ radius(_radius) {
+
+ // Compute a scale factor that moves the range
+ // of all ordinates to [0, 1]
+ Vector3 minBound = Vector3::inf();
+ Vector3 maxBound = -minBound;
+
+ for (int i = 0; i < oldVertexArray.size(); ++i) {
+ minBound = minBound.min(oldVertexArray[i]);
+ maxBound = maxBound.max(oldVertexArray[i]);
+ }
+
+ offset = minBound;
+ scale = maxBound - minBound;
+ for (int i = 0; i < 3; ++i) {
+ // The model might have zero extent along some axis
+ if (fuzzyEq(scale[i], 0.0)) {
+ scale[i] = 1.0;
+ } else {
+ scale[i] = 1.0 / scale[i];
+ }
+ }
+}
+
+
+void Welder::toGridCoords(Vector3 v, int& x, int& y, int& z) const {
+ v = (v - offset) * scale;
+ x = iClamp(iFloor(v.x * GRID_RES), 0, GRID_RES - 1);
+ y = iClamp(iFloor(v.y * GRID_RES), 0, GRID_RES - 1);
+ z = iClamp(iFloor(v.z * GRID_RES), 0, GRID_RES - 1);
+}
+
+
+int Welder::getIndex(const Vector3& vertex) {
+
+ int closestIndex = -1;
+ double distanceSquared = inf();
+
+ int ix, iy, iz;
+ toGridCoords(vertex, ix, iy, iz);
+
+ // Check against all vertices within radius of this grid cube
+ const List& list = grid[ix][iy][iz];
+
+ for (int i = 0; i < list.size(); ++i) {
+ double d = (newVertexArray[list[i]] - vertex).squaredMagnitude();
+
+ if (d < distanceSquared) {
+ distanceSquared = d;
+ closestIndex = list[i];
+ }
+ }
+
+ if (distanceSquared <= radius * radius) {
+
+ return closestIndex;
+
+ } else {
+
+ // This is a new vertex
+ int newIndex = newVertexArray.size();
+ newVertexArray.append(vertex);
+
+ // Create a new vertex and store its index in the
+ // neighboring grid cells (usually, only 1 neighbor)
+
+ Set<List*> neighbors;
+
+ for (float dx = -1; dx <= +1; ++dx) {
+ for (float dy = -1; dy <= +1; ++dy) {
+ for (float dz = -1; dz <= +1; ++dz) {
+ int ix, iy, iz;
+ toGridCoords(vertex + Vector3(dx, dy, dz) * radius, ix, iy, iz);
+ neighbors.insert(&(grid[ix][iy][iz]));
+ }
+ }
+ }
+
+ Set<List*>::Iterator neighbor(neighbors.begin());
+ Set<List*>::Iterator none(neighbors.end());
+
+ while (neighbor != none) {
+ (*neighbor)->append(newIndex);
+ ++neighbor;
+ }
+
+ return newIndex;
+ }
+}
+
+
+void Welder::weld() {
+ newVertexArray.resize(0);
+
+ // Prime the vertex positions
+ for (int i = 0; i < oldVertexArray.size(); ++i) {
+ getIndex(oldVertexArray[i]);
+ }
+
+ // Now create the official remapping by snapping to
+ // nearby vertices.
+ toNew.resize(oldVertexArray.size());
+ toOld.resize(newVertexArray.size());
+
+ for (int oi = 0; oi < oldVertexArray.size(); ++oi) {
+ toNew[oi] = getIndex(oldVertexArray[oi]);
+ toOld[toNew[oi]] = oi;
+ }
+}
+
+} // internal namespace
+
+
+void MeshAlg::computeWeld(
+ const Array<Vector3>& oldVertexArray,
+ Array<Vector3>& newVertexArray,
+ Array<int>& toNew,
+ Array<int>& toOld,
+ double radius) {
+
+ _internal::Welder welder(oldVertexArray, newVertexArray, toNew, toOld, radius);
+ welder.weld();
+}
+
+} // G3D namespace
diff --git a/externals/g3dlite/G3D.lib/source/MeshAlgWeld2.cpp b/externals/g3dlite/G3D.lib/source/MeshAlgWeld2.cpp
new file mode 100644
index 00000000000..13f731353a6
--- /dev/null
+++ b/externals/g3dlite/G3D.lib/source/MeshAlgWeld2.cpp
@@ -0,0 +1,377 @@
+/**
+ @file MeshAlgWeld2.cpp
+
+ @author Morgan McGuire, Kyle Whitson, Corey Taylor
+
+ @created 2008-07-30
+ @edited 2008-11-10
+ */
+
+#include "G3D/platform.h"
+#include "G3D/Vector2.h"
+#include "G3D/Vector3.h"
+#include "G3D/Sphere.h"
+#include "G3D/PointHashGrid.h"
+#include "G3D/MeshAlg.h"
+
+namespace G3D { namespace _internal{
+
+/** Used by WeldHelper2::smoothNormals. */
+class VN {
+public:
+ Vector3 vertex;
+ Vector3 normal;
+
+ VN() {}
+ VN(const Vector3& v, const Vector3& n) : vertex(v), normal(n) {}
+};
+
+/** Used by WeldHelper::getIndex to maintain a list of vertices by location. */
+class VNTi {
+public:
+ Vector3 vertex;
+ Vector3 normal;
+ Vector2 texCoord;
+ int index;
+
+ VNTi() : index(0) {}
+
+ VNTi(const Vector3& v, const Vector3& n, const Vector2& t, int i) :
+ vertex(v), normal(n), texCoord(t), index(i) {}
+};
+
+
+}} // G3D
+
+template <> struct HashTrait <G3D::_internal::VN> {
+ static size_t hashCode(const G3D::_internal::VN& k) { return static_cast<size_t>(k.vertex.hashCode()); }
+};
+template <> struct HashTrait <G3D::_internal::VNTi> {
+ static size_t hashCode(const G3D::_internal::VNTi& k) { return static_cast<size_t>(k.vertex.hashCode()); }
+};
+
+
+template<> struct EqualsTrait <G3D::_internal::VN> {
+ static bool equals(const G3D::_internal::VN& a, const G3D::_internal::VN& b) { return a.vertex == b.vertex; }
+};
+template<> struct EqualsTrait <G3D::_internal::VNTi> {
+ static bool equals(const G3D::_internal::VNTi& a, const G3D::_internal::VNTi& b) { return a.vertex == b.vertex; }
+};
+
+template<> struct PositionTrait<G3D::_internal::VN> {
+ static void getPosition(const G3D::_internal::VN& v, G3D::Vector3& p) { p = v.vertex; }
+};
+template<> struct PositionTrait<G3D::_internal::VNTi> {
+ static void getPosition(const G3D::_internal::VNTi& v, G3D::Vector3& p) { p = v.vertex; }
+};
+
+namespace G3D { namespace _internal {
+
+class WeldHelper {
+private:
+ /** Used by getIndex and updateTriLists */
+ PointHashGrid<VNTi> weldGrid;
+
+ Array<Vector3>* outputVertexArray;
+ Array<Vector3>* outputNormalArray;
+ Array<Vector2>* outputTexCoordArray;
+
+ float vertexWeldRadius;
+ /** Squared radius allowed for welding similar normals. */
+ float normalWeldRadius2;
+ float texCoordWeldRadius2;
+
+ float normalSmoothingAngle;
+
+ /**
+ Returns the index of the vertex in
+ outputVertexArray/outputNormalArray/outputTexCoordArray
+ that is within the global tolerances of v,n,t. If there
+ is no such vertex, adds it to the arrays and returns that index.
+
+ Called from updateTriLists().
+ */
+ int getIndex(const Vector3& v, const Vector3& n, const Vector2& t) {
+ PointHashGrid<VNTi>::SphereIterator it =
+ weldGrid.beginSphereIntersection(Sphere(v, vertexWeldRadius));
+
+ if (n.isZero()) {
+ // Don't bother trying to match the surface normal, since this vertex has no surface normal.
+ while (it.hasMore()) {
+ if ((t - it->texCoord).squaredLength() <= texCoordWeldRadius2) {
+ // This is the vertex
+ return it->index;
+ }
+ ++it;
+ }
+ } else {
+ while (it.hasMore()) {
+ if (((n - it->normal).squaredLength() <= normalWeldRadius2) &&
+ ((t - it->texCoord).squaredLength() <= texCoordWeldRadius2)) {
+ // This is the vertex
+ return it->index;
+ }
+ ++it;
+ }
+ }
+
+ // Note that a sliver triangle processed before its neighbors may reach here
+ // with a zero length normal.
+
+ // The vertex does not exist. Create it.
+ const int i = outputVertexArray->size();
+ outputVertexArray->append(v);
+ outputNormalArray->append(n);
+ outputTexCoordArray->append(t);
+
+ // Store in the grid so that it will be remembered.
+ weldGrid.insert(VNTi(v, n, t, i));
+
+ return i;
+ }
+
+
+ /**
+ Updates each indexArray to refer to vertices in the
+ outputVertexArray.
+
+ Called from process()
+ */
+ void updateTriLists(
+ Array<Array<int>*>& indexArrayArray,
+ const Array<Vector3>& vertexArray,
+ const Array<Vector3>& normalArray,
+ const Array<Vector2>& texCoordArray) {
+
+ // Compute a hash grid so that we can find neighbors quickly.
+ // It begins empty and is extended as the tri lists are iterated
+ // through.
+ weldGrid.clear();
+
+ // Process all triLists
+ int numTriLists = indexArrayArray.size();
+ int u = 0;
+ for (int t = 0; t < numTriLists; ++t) {
+ Array<int>& triList = *(indexArrayArray[t]);
+
+ // For all vertices in this list
+ for (int v = 0; v < triList.size(); ++v) {
+ // This vertex mapped to u in the flatVertexArray
+ triList[v] = getIndex(vertexArray[u], normalArray[u], texCoordArray[u]);
+
+ /*
+# ifdef G3D_DEBUG
+ {
+ int i = triList[v];
+ Vector3 N = normalArray[i];
+ debugAssertM(N.length() > 0.9f, "Produced non-unit normal");
+ }
+# endif
+ */
+ ++u;
+ }
+ }
+ }
+
+ /** Expands the indexed triangle lists into a triangle list.
+
+ Called from process() */
+ void unroll(
+ const Array<Array<int>*>& indexArrayArray,
+ const Array<Vector3>& vertexArray,
+ const Array<Vector2>& texCoordArray,
+ Array<Vector3>& unrolledVertexArray,
+ Array<Vector2>& unrolledTexCoordArray) {
+
+ int numTriLists = indexArrayArray.size();
+ for (int t = 0; t < numTriLists; ++t) {
+ const Array<int>& triList = *(indexArrayArray[t]);
+ for (int v = 0; v < triList.size(); ++v) {
+ int i = triList[v];
+ unrolledVertexArray.append(vertexArray[i]);
+ unrolledTexCoordArray.append(texCoordArray[i]);
+ }
+ }
+ }
+
+ /** For every three vertices, compute the face normal and store it three times.
+ Sliver triangles have a zero surface normal, which we will later take to
+ match *any* surface normal. */
+ void computeFaceNormals(
+ const Array<Vector3>& vertexArray,
+ Array<Vector3>& faceNormalArray) {
+
+ debugAssertM(vertexArray.size() % 3 == 0, "Input is not a triangle soup");
+ debugAssertM(faceNormalArray.size() == 0, "Output must start empty.");
+
+ for (int v = 0; v < vertexArray.size(); v += 3) {
+ const Vector3& e0 = vertexArray[v + 1] - vertexArray[v];
+ const Vector3& e1 = vertexArray[v + 2] - vertexArray[v];
+
+ // Note that the length may be zero in the case of sliver polygons, e.g.,
+ // those correcting a T-junction.
+ const Vector3& n = e0.cross(e1).directionOrZero();
+
+ // Append the normal once per vertex.
+ faceNormalArray.append(n, n, n);
+ }
+ }
+
+
+ /**
+ Computes @a smoothNormalArray, whose elements are those of normalArray averaged
+ with neighbors within the angular cutoff.
+ */
+ void smoothNormals(
+ const Array<Vector3>& vertexArray,
+ const Array<Vector3>& normalArray,
+ Array<Vector3>& smoothNormalArray) {
+
+ const float cosThresholdAngle = (float)cos(normalSmoothingAngle);
+
+ debugAssert(vertexArray.size() == normalArray.size());
+ smoothNormalArray.resize(normalArray.size());
+
+ // Compute a hash grid so that we can find neighbors quickly.
+ PointHashGrid<VN> grid(vertexWeldRadius);
+ for (int v = 0; v < normalArray.size(); ++v) {
+ grid.insert(VN(vertexArray[v], normalArray[v]));
+ }
+
+ for (int v = 0; v < normalArray.size(); ++v) {
+ // Compute the sum of all nearby normals within the cutoff angle.
+ // Search within the vertexWeldRadius, since those are the vertices
+ // that will collapse to the same point.
+ PointHashGrid<VN>::SphereIterator it =
+ grid.beginSphereIntersection(Sphere(vertexArray[v], vertexWeldRadius));
+
+ Vector3 sum;
+
+ const Vector3& original = normalArray[v];
+ while (it.hasMore()) {
+ const Vector3& N = it->normal;
+ const float cosAngle = N.dot(original);
+
+ if (cosAngle > cosThresholdAngle) {
+ // This normal is close enough to consider
+ sum += N;
+ }
+ ++it;
+ }
+
+ const Vector3& average = sum.directionOrZero();
+
+ const bool indeterminate = average.isZero();
+ // Never "smooth" a normal so far that it points backwards
+ const bool backFacing = original.dot(average) < 0;
+
+ if (indeterminate || backFacing) {
+ // Revert to the face normal
+ smoothNormalArray[v] = original;
+ } else {
+ // Average available normals
+ smoothNormalArray[v] = average;
+ }
+ }
+ }
+
+public:
+
+
+ /**
+ Algorithm:
+
+ 1. Unroll the indexed triangle list into a triangle list, where
+ there are duplicated vertices.
+
+ 2. Compute face normals for all triangles, and expand those into
+ the triangle vertices.
+
+ 3. At each vertex, average all normals that are within normalSmoothingAngle.
+
+ 4. Generate output indexArrayArray. While doing so, merge all vertices where
+ the distance between position, texCoord, and normal is within the thresholds.
+ */
+ void process(
+ Array<Vector3>& vertexArray,
+ Array<Vector2>& texCoordArray,
+ Array<Vector3>& normalArray,
+ Array<Array<int>*>& indexArrayArray,
+ float normAngle,
+ float texRadius,
+ float normRadius) {
+
+ normalSmoothingAngle = normAngle;
+ normalWeldRadius2 = square(normRadius);
+ texCoordWeldRadius2 = square(texRadius);
+
+ const bool hasTexCoords = (texCoordArray.size() > 0);
+
+ if (hasTexCoords) {
+ debugAssertM(vertexArray.size() == texCoordArray.size(),
+ "Input arrays are not parallel.");
+ }
+
+ Array<Vector3> unrolledVertexArray;
+ Array<Vector3> unrolledFaceNormalArray;
+ Array<Vector3> unrolledSmoothNormalArray;
+ Array<Vector2> unrolledTexCoordArray;
+
+ if (! hasTexCoords) {
+ // Generate all zero texture coordinates
+ texCoordArray.resize(vertexArray.size());
+ }
+
+ // Generate a flat (unrolled) triangle list with texture coordinates.
+ unroll(indexArrayArray, vertexArray, texCoordArray,
+ unrolledVertexArray, unrolledTexCoordArray);
+
+ // Put the output back into the input slots. Clear immediately to reduce peak
+ // memory.
+ outputVertexArray = &vertexArray;
+ outputNormalArray = &normalArray;
+ outputTexCoordArray = &texCoordArray;
+ outputVertexArray->fastClear();
+ outputNormalArray->fastClear();
+ outputTexCoordArray->fastClear();
+
+ // For every three vertices, generate their face normal and store it at
+ // each vertex. The output array has the same length as the input.
+ computeFaceNormals(unrolledVertexArray, unrolledFaceNormalArray);
+
+ // Compute smooth normals at vertices.
+ smoothNormals(unrolledVertexArray, unrolledFaceNormalArray, unrolledSmoothNormalArray);
+ unrolledFaceNormalArray.clear();
+
+ // Regenerate the triangle lists
+ updateTriLists(indexArrayArray, unrolledVertexArray, unrolledSmoothNormalArray, unrolledTexCoordArray);
+
+ if (! hasTexCoords) {
+ // Throw away the generated texCoords
+ texCoordArray.resize(0);
+ }
+ }
+
+ WeldHelper(float vertRadius) :
+ weldGrid(vertRadius),
+ vertexWeldRadius(vertRadius) {}
+
+};
+} // Internal
+
+void MeshAlg::weld(
+ Array<Vector3>& vertexArray,
+ Array<Vector2>& texCoordArray,
+ Array<Vector3>& normalArray,
+ Array<Array<int>*>& indexArrayArray,
+ float normalSmoothingAngle,
+ float vertexWeldRadius,
+ float textureWeldRadius,
+ float normalWeldRadius) {
+
+ _internal::WeldHelper(vertexWeldRadius).process(
+ vertexArray, texCoordArray, normalArray, indexArrayArray,
+ normalSmoothingAngle, textureWeldRadius, normalWeldRadius);
+}
+
+} // G3D
diff --git a/externals/g3dlite/G3D.lib/source/MeshBuilder.cpp b/externals/g3dlite/G3D.lib/source/MeshBuilder.cpp
new file mode 100644
index 00000000000..43ee6e50ac8
--- /dev/null
+++ b/externals/g3dlite/G3D.lib/source/MeshBuilder.cpp
@@ -0,0 +1,113 @@
+/**
+ @file MeshBuilder.cpp
+
+ @maintainer Morgan McGuire, matrix@graphics3d.com
+
+ @created 2002-02-27
+ @edited 2005-02-24
+ */
+
+#include "G3D/MeshBuilder.h"
+#include "G3D/MeshAlg.h"
+
+namespace G3D {
+
+void MeshBuilder::setName(const std::string& n) {
+ name = n;
+}
+
+
+void MeshBuilder::commit(std::string& n, Array<int>& indexArray, Array<Vector3>& outvertexArray) {
+ n = name;
+
+ // Make the data fit in a unit cube
+ centerTriList();
+
+ Array<int> toNew, toOld;
+
+ if (close == MeshBuilder::AUTO_WELD) {
+ Array<int> index;
+ MeshAlg::createIndexArray(triList.size(), index);
+ double minEdgeLen, maxEdgeLen, meanEdgeLen, medianEdgeLen;
+ double minFaceArea, maxFaceArea, meanFaceArea, medianFaceArea;
+ MeshAlg::computeAreaStatistics(triList, index,
+ minEdgeLen, meanEdgeLen, medianEdgeLen, maxEdgeLen,
+ minFaceArea, meanFaceArea, medianFaceArea, maxFaceArea);
+ close = minEdgeLen * 0.1;
+ }
+
+ MeshAlg::computeWeld(triList, outvertexArray, toNew, toOld, close);
+
+ // Construct triangles
+ for (int t = 0; t < triList.size(); t += 3) {
+ int index[3];
+
+ for (int i = 0; i < 3; ++i) {
+ index[i] = toNew[t + i];
+ }
+
+ // Throw out zero size triangles
+ if ((index[0] != index[1]) &&
+ (index[1] != index[2]) &&
+ (index[2] != index[0])) {
+ indexArray.append(index[0], index[1], index[2]);
+ }
+ }
+}
+
+
+void MeshBuilder::centerTriList() {
+ // Compute the range of the vertices
+ Vector3 vmin, vmax;
+
+ computeBounds(vmin, vmax);
+
+ Vector3 diagonal = vmax - vmin;
+ double scale = max(max(diagonal.x, diagonal.y), diagonal.z) / 2;
+ debugAssert(scale > 0);
+
+ Vector3 translation = vmin + diagonal / 2;
+
+ // Center and scale all vertices in the input list
+ int v;
+
+ //Matrix3 rot90 = Matrix3::fromAxisAngle(Vector3::UNIT_Y, toRadians(180)) * Matrix3::fromAxisAngle(Vector3::UNIT_X, toRadians(90));
+ for (v = 0; v < triList.size(); ++v) {
+ triList[v] = (triList[v] - translation) / scale;
+ //triList[v] = rot90 * triList[v];
+ }
+}
+
+
+void MeshBuilder::computeBounds(Vector3& min, Vector3& max) {
+ min = Vector3::inf();
+ max = -min;
+
+ int v;
+ for (v = 0; v < triList.size(); ++v) {
+ min = min.min(triList[v]);
+ max = max.max(triList[v]);
+ }
+}
+
+
+void MeshBuilder::addTriangle(const Vector3& a, const Vector3& b, const Vector3& c) {
+ triList.append(a, b, c);
+
+ if (_twoSided) {
+ triList.append(c, b, a);
+ }
+}
+
+
+void MeshBuilder::addQuad(const Vector3& a, const Vector3& b, const Vector3& c, const Vector3& d) {
+ addTriangle(a, b, c);
+ addTriangle(a, c, d);
+}
+
+
+void MeshBuilder::addTriangle(const Triangle& t) {
+ addTriangle(t.vertex(0), t.vertex(1), t.vertex(2));
+}
+
+} // namespace
diff --git a/externals/g3dlite/G3D.lib/source/NetAddress.cpp b/externals/g3dlite/G3D.lib/source/NetAddress.cpp
new file mode 100644
index 00000000000..64d692d4763
--- /dev/null
+++ b/externals/g3dlite/G3D.lib/source/NetAddress.cpp
@@ -0,0 +1,164 @@
+/**
+ @file NetMessage.cpp
+
+ @maintainer Morgan McGuire, morgan@cs.brown.edu
+ @created 2005-02-06
+ @edited 2005-02-06
+ */
+#include "G3D/platform.h"
+#include "G3D/NetAddress.h"
+#include "G3D/BinaryInput.h"
+#include "G3D/BinaryOutput.h"
+#include "G3D/Array.h"
+#include "G3D/stringutils.h"
+#include "G3D/System.h"
+#include "G3D/NetworkDevice.h"
+
+#if defined(G3D_LINUX) || defined(G3D_OSX)
+ #include <unistd.h>
+ #include <errno.h>
+ #include <sys/socket.h>
+ #include <netinet/in.h>
+ #include <arpa/inet.h>
+ #include <netdb.h>
+ #include <netinet/tcp.h>
+ #define _alloca alloca
+
+# ifndef SOCKADDR_IN
+# define SOCKADDR_IN struct sockaddr_in
+# endif
+# ifndef SOCKET
+# define SOCKET int
+# endif
+
+// SOCKADDR_IN is supposed to be defined in NetAddress.h
+#ifndef SOCKADDR_IN
+# error Network headers included in wrong order
+#endif
+#endif
+
+
+namespace G3D {
+
+NetAddress::NetAddress() {
+ System::memset(&addr, 0, sizeof(addr));
+}
+
+void NetAddress::init(uint32 host, uint16 port) {
+ if ((host != 0) || (port != 0)) {
+ addr.sin_family = AF_INET;
+ addr.sin_port = htons(port);
+ if (host == 0) {
+ host = INADDR_ANY;
+ }
+ addr.sin_addr.s_addr = htonl(host);
+ } else {
+ System::memset(&addr, 0, sizeof(addr));
+ }
+}
+
+
+NetAddress::NetAddress(
+ const std::string& hostname,
+ uint16 port) {
+ init(hostname, port);
+}
+
+
+void NetAddress::init(
+ const std::string& hostname,
+ uint16 port) {
+
+ uint32 addr;
+
+ if (hostname == "") {
+ addr = INADDR_NONE;
+ } else {
+ addr = inet_addr(hostname.c_str());
+ }
+
+ // The address wasn't in numeric form, resolve it
+ if (addr == INADDR_NONE) {
+ // Get the IP address of the server and store it in host
+ struct hostent* host = gethostbyname(hostname.c_str());
+
+ if (host == NULL) {
+ init(0, 0);
+ return;
+ }
+
+ System::memcpy(&addr, host->h_addr_list[0], host->h_length);
+ }
+
+ if (addr != INADDR_NONE) {
+ addr = ntohl(addr);
+ }
+ init(addr, port);
+}
+
+
+NetAddress::NetAddress(uint32 hostip, uint16 port) {
+ init(hostip, port);
+}
+
+
+NetAddress NetAddress::broadcastAddress(uint16 port) {
+ return NetAddress(NetworkDevice::instance()->broadcastAddressArray()[0], port);
+}
+
+
+NetAddress::NetAddress(const std::string& hostnameAndPort) {
+
+ Array<std::string> part = stringSplit(hostnameAndPort, ':');
+
+ debugAssert(part.length() == 2);
+ init(part[0], atoi(part[1].c_str()));
+}
+
+
+NetAddress::NetAddress(const SOCKADDR_IN& a) {
+ addr = a;
+}
+
+
+NetAddress::NetAddress(const struct in_addr& addr, uint16 port) {
+ #ifdef G3D_WIN32
+ init(ntohl(addr.S_un.S_addr), port);
+ #else
+ init(htonl(addr.s_addr), port);
+ #endif
+}
+
+
+void NetAddress::serialize(class BinaryOutput& b) const {
+ b.writeUInt32(ip());
+ b.writeUInt16(port());
+}
+
+
+void NetAddress::deserialize(class BinaryInput& b) {
+ uint32 i;
+ uint16 p;
+
+ i = b.readUInt32();
+ p = b.readUInt16();
+
+ init(i, p);
+}
+
+
+bool NetAddress::ok() const {
+ return addr.sin_family != 0;
+}
+
+
+std::string NetAddress::ipString() const {
+ return format("%s", inet_ntoa(*(in_addr*)&(addr.sin_addr)));
+}
+
+
+std::string NetAddress::toString() const {
+ return ipString() + format(":%d", ntohs(addr.sin_port));
+}
+
+}
diff --git a/externals/g3dlite/G3D.lib/source/NetworkDevice.cpp b/externals/g3dlite/G3D.lib/source/NetworkDevice.cpp
new file mode 100644
index 00000000000..246c97d4dbf
--- /dev/null
+++ b/externals/g3dlite/G3D.lib/source/NetworkDevice.cpp
@@ -0,0 +1,1362 @@
+/**
+ @file NetworkDevice.cpp
+
+ @maintainer Morgan McGuire, morgan@cs.brown.edu
+ @created 2002-11-22
+ @edited 2006-02-24
+ */
+
+#include <stdlib.h>
+#include <time.h>
+#include "G3D/platform.h"
+#include "G3D/TextOutput.h"
+#include "G3D/NetworkDevice.h"
+#include "G3D/NetAddress.h"
+#include "G3D/BinaryInput.h"
+#include "G3D/BinaryOutput.h"
+#include "G3D/Log.h"
+#include "G3D/G3DGameUnits.h"
+#include "G3D/stringutils.h"
+#include "G3D/debug.h"
+
+#include <cstring>
+
+#if defined(G3D_LINUX) || defined(G3D_OSX) || defined(G3D_FREEBSD)
+# include <sys/types.h>
+# include <sys/socket.h>
+# include <ifaddrs.h>
+# include <netinet/in.h>
+# include <net/if.h>
+# ifdef __linux__
+# include <sys/ioctl.h>
+# include <netinet/in.h>
+# include <unistd.h>
+# include <string.h>
+// Match Linux to FreeBSD
+# define AF_LINK AF_PACKET
+# else
+# include <net/if_dl.h>
+# include <sys/sockio.h>
+# endif
+
+ #include <unistd.h>
+ #include <errno.h>
+ #include <sys/socket.h>
+ #include <netinet/in.h>
+ #include <arpa/inet.h>
+ #include <netdb.h>
+ #include <netinet/tcp.h>
+ #include <sys/ioctl.h>
+ #include <netinet/if_ether.h>
+ #include <net/ethernet.h>
+ #include <net/if.h>
+
+ #include <sys/types.h>
+
+ #define _alloca alloca
+
+ /** Define an error code for non-windows platforms. */
+ int WSAGetLastError() {
+ return -1;
+ }
+
+ #define SOCKET_ERROR -1
+
+ static std::string socketErrorCode(int code) {
+ return G3D::format("CODE %d: %s\n", code, strerror(code));
+ }
+
+ static std::string socketErrorCode() {
+ return socketErrorCode(errno);
+ }
+
+ static const int WSAEWOULDBLOCK = -100;
+
+ typedef int SOCKET;
+ typedef struct sockaddr_in SOCKADDR_IN;
+
+#else
+
+ // Windows
+ static std::string socketErrorCode(int code) {
+ LPTSTR formatMsg = NULL;
+
+ FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER |
+ FORMAT_MESSAGE_IGNORE_INSERTS |
+ FORMAT_MESSAGE_FROM_SYSTEM,
+ NULL,
+ code,
+ 0,
+ (LPTSTR)&formatMsg,
+ 0,
+ NULL);
+
+ return G3D::format("CODE %d: %s\n", code, formatMsg);
+ }
+
+ static std::string socketErrorCode() {
+ return socketErrorCode(GetLastError());
+ }
+
+#endif
+
+
+#ifndef _SOCKLEN_T
+# if defined(G3D_WIN32) || defined(G3D_OSX)
+ typedef int socklen_t;
+# endif
+#endif
+
+namespace G3D {
+
+NetworkDevice* NetworkDevice::s_instance = NULL;
+
+std::ostream& operator<<(std::ostream& os, const NetAddress& a) {
+ return os << a.toString();
+}
+
+
+static void logSocketInfo(const SOCKET& sock) {
+ uint32 val;
+ socklen_t sz = 4;
+ int ret;
+
+ ret = getsockopt(sock, SOL_SOCKET, SO_RCVBUF, (char*)&val, (socklen_t*)&sz);
+ logPrintf("SOL_SOCKET/SO_RCVBUF = %d\n", val);
+
+ ret = getsockopt(sock, SOL_SOCKET, SO_SNDBUF, (char*)&val, (socklen_t*)&sz);
+ logPrintf("SOL_SOCKET/SO_SNDBUF = %d\n", val);
+
+ // Note: timeout = 0 means no timeout
+ ret = getsockopt(sock, SOL_SOCKET, SO_RCVTIMEO, (char*)&val, (socklen_t*)&sz);
+ logPrintf("SOL_SOCKET/SO_RCVTIMEO = %d\n", val);
+
+ ret = getsockopt(sock, SOL_SOCKET, SO_SNDTIMEO, (char*)&val, (socklen_t*)&sz);
+ logPrintf("SOL_SOCKET/SO_SNDTIMEO = %d\n", val);
+}
+
+
+/////////////////////////////////////////////////////////////////////////////
+
+/** Invokes select on one socket. Returns SOCKET_ERROR on error, 0 if
+ there is no read pending, sock if there a read pending. */
+static int selectOneReadSocket(const SOCKET& sock) {
+ // 0 time timeout is specified to poll and return immediately
+ struct timeval timeout;
+ timeout.tv_sec = 0;
+ timeout.tv_usec = 0;
+
+ // Create a set that contains just this one socket
+ fd_set socketSet;
+ FD_ZERO(&socketSet);
+ FD_SET(sock, &socketSet);
+
+ int ret = select(sock + 1, &socketSet, NULL, NULL, &timeout);
+
+ return ret;
+}
+
+
+/** Returns true if the socket has a read pending */
+static bool readWaiting(const SOCKET& sock) {
+ int ret = selectOneReadSocket(sock);
+
+ switch (ret) {
+ case SOCKET_ERROR:
+ logPrintf("ERROR: selectOneReadSocket returned "
+ "SOCKET_ERROR in readWaiting(). %s", socketErrorCode().c_str());
+ // Return true so that we'll force an error on read and close
+ // the socket.
+ return true;
+
+ case 0:
+ return false;
+
+ default:
+ return true;
+ }
+}
+
+
+/** Invokes select on one socket. */
+static int selectOneWriteSocket(const SOCKET& sock) {
+ // 0 time timeout is specified to poll and return immediately
+ struct timeval timeout;
+ timeout.tv_sec = 0;
+ timeout.tv_usec = 0;
+
+ // Create a set that contains just this one socket
+ fd_set socketSet;
+ FD_ZERO(&socketSet);
+ FD_SET(sock, &socketSet);
+
+ return select(sock + 1, NULL, &socketSet, NULL, &timeout);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+NetworkDevice* NetworkDevice::instance() {
+ if (s_instance == NULL) {
+ s_instance = new NetworkDevice();
+ if (! s_instance->init()) {
+ delete s_instance;
+ s_instance = NULL;
+ }
+ }
+ return s_instance;
+}
+
+
+void NetworkDevice::cleanup() {
+ if (s_instance) {
+ s_instance->_cleanup();
+ delete s_instance;
+ s_instance = NULL;
+ }
+}
+
+
+NetworkDevice::NetworkDevice() {
+ initialized = false;
+}
+
+
+NetworkDevice::~NetworkDevice() {
+}
+
+
+std::string NetworkDevice::localHostName() const {
+ char ac[128];
+ if (gethostname(ac, sizeof(ac)) == -1) {
+ Log::common()->printf("Error while getting local host name\n");
+ return "localhost";
+ }
+ return gethostbyname(ac)->h_name;
+}
+
+#ifndef G3D_WIN32
+const char* errnoToString() {
+ switch (errno) {
+ case EBADF:
+ return "file descriptor is invalid.";
+
+ case EINVAL:
+ return "Request or argp is not valid.";
+
+ case ENOTTY:
+ return
+ "file descriptor is not associated with a character special device OR "
+ "The specified request does not apply to the "
+ "kind of object that the descriptor fildes references.";
+
+ case EADDRNOTAVAIL:
+ return "Address not available.";
+
+ default:
+ {
+ static char buffer[20];
+ sprintf(buffer, "Error %d", errno);
+ return buffer;
+ }
+ }
+}
+#endif
+
+
+NetworkDevice::EthernetAdapter::EthernetAdapter() {
+ name = "";
+ ip = 0;
+ hostname = "";
+ subnet = 0;
+ broadcast = 0;
+ for (int i = 0; i < 6; ++i) {
+ mac[i] = 0;
+ }
+}
+
+void NetworkDevice::EthernetAdapter::describe(TextOutput& t) const {
+ t.writeSymbol("{");
+ t.pushIndent();
+ t.writeNewline();
+
+ t.writeSymbols("hostname", "=");
+ t.writeString(hostname);
+ t.writeNewline();
+
+ t.writeSymbols("name", "=");
+ t.writeString(name);
+ t.writeNewline();
+
+ t.writeSymbols("ip", "=");
+ t.writeSymbol(formatIP(ip));
+ t.writeNewline();
+
+ t.writeSymbols("subnet", "=");
+ t.writeSymbol(formatIP(subnet));
+ t.writeNewline();
+
+ t.writeSymbols("broadcast", "=");
+ t.writeSymbol(formatIP(broadcast));
+ t.writeNewline();
+
+ t.writeSymbols("mac", "=");
+ t.writeSymbol(formatMAC(mac));
+ t.writeNewline();
+
+ t.popIndent();
+ t.writeSymbol("}");
+ t.writeNewline();
+}
+
+
+void NetworkDevice::addAdapter(const EthernetAdapter& a) {
+ m_adapterArray.append(a);
+ if (a.broadcast != 0) {
+ int i = m_broadcastAddresses.findIndex(a.broadcast);
+ if (i == -1) {
+ m_broadcastAddresses.append(a.broadcast);
+ }
+ }
+}
+
+
+std::string NetworkDevice::formatIP(uint32 addr) {
+ return format("%3d.%3d.%3d.%3d", (addr >> 24) & 0xFF, (addr >> 16) & 0xFF,
+ (addr >> 8) & 0xFF, addr & 0xFF);
+}
+
+
+std::string NetworkDevice::formatMAC(const uint8 MAC[6]) {
+ return format("%02x:%02x:%02x:%02x:%02x:%02x", MAC[0], MAC[1], MAC[2], MAC[3], MAC[4], MAC[5]);
+}
+
+
+#ifdef G3D_WIN32
+
+bool NetworkDevice::init() {
+ debugAssert(! initialized);
+
+ logPrintf("Network Startup");
+ logPrintf("Starting WinSock networking.\n");
+ WSADATA wsda;
+ WSAStartup(MAKEWORD(G3D_WINSOCK_MAJOR_VERSION, G3D_WINSOCK_MINOR_VERSION), &wsda);
+
+ std::string hostname = "localhost";
+ {
+ char ac[128];
+ if (gethostname(ac, sizeof(ac)) == -1) {
+ logPrintf("Warning: Error while getting local host name\n");
+ } else {
+ hostname = gethostbyname(ac)->h_name;
+ }
+ }
+
+ EthernetAdapter a;
+ a.hostname = hostname;
+ a.name = "";
+ a.ip = NetAddress(hostname, 0).ip();
+
+ // TODO: Find subnet on Win32
+ a.subnet = 0x0000FFFF;
+
+ // TODO: Find broadcast on Win32
+ a.broadcast = 0xFFFFFFFF;
+
+ // TODO: find MAC on Win32
+
+ addAdapter(a);
+
+ std::string machine = localHostName();
+ std::string addr = NetAddress(machine, 0).ipString();
+ logPrintf(
+ "Network:\n"
+ " Status: %s\n"
+ " Loaded winsock specification version %d (%d is "
+ "the highest available)\n"
+ " %d sockets available\n"
+ " Largest UDP datagram packet size is %d bytes\n\n",
+ wsda.szDescription,
+ wsda.szSystemStatus,
+ wsda.wVersion,
+ wsda.wHighVersion,
+ wsda.iMaxSockets,
+ wsda.iMaxUdpDg);
+
+ // TODO: WSAIoctl for subnet and broadcast addresses
+ // http://msdn.microsoft.com/en-us/library/ms741621(VS.85).aspx
+ //
+ // TODO: SIO_GET_INTERFACE_LIST
+
+ initialized = true;
+
+ return true;
+}
+#endif
+
+
+#if defined(G3D_LINUX) || defined(G3D_OSX) || defined(G3D_FREEBSD)
+
+const sockaddr_in* castToIP4(const sockaddr* addr) {
+ if (addr == NULL) {
+ return NULL;
+ } else if (addr->sa_family == AF_INET) {
+ // An IPv4 address
+ return reinterpret_cast<const sockaddr_in*>(addr);
+ } else {
+ // Not an IPv4 address
+ return NULL;
+ }
+}
+
+uint32 getIP(const sockaddr_in* addr) {
+ if (addr != NULL) {
+ return ntohl(addr->sin_addr.s_addr);
+ } else {
+ return 0;
+ }
+}
+
+
+bool NetworkDevice::init() {
+ debugAssert(! initialized);
+
+ // Used for combining the MAC and ip information
+ typedef Table<std::string, EthernetAdapter> AdapterTable;
+
+ AdapterTable table;
+
+ // Head of a linked list of network interfaces on this machine
+ ifaddrs* ifap = NULL;
+
+ int r = getifaddrs(&ifap);
+
+ if (r != 0) {
+ logPrintf("ERROR: getifaddrs returned %d\n", r);
+ return false;
+ }
+
+ ifaddrs* current = ifap;
+
+ if (current == NULL) {
+ logPrintf("WARNING: No network interfaces found\n");
+ EthernetAdapter a;
+ a.name = "fallback";
+ a.hostname = "localhost";
+ a.ip = (127 << 24) | 1;
+ a.broadcast = 0xFFFFFFFF;
+ a.subnet = 0x000000FF;
+ addAdapter(a);
+
+ } else {
+
+ while (current != NULL) {
+
+ bool up = (current->ifa_flags & IFF_UP);
+ bool loopback = (current->ifa_flags & IFF_LOOPBACK);
+
+ if (! up || loopback) {
+ // Skip this adapter; it is offline or is a loopback
+ current = current->ifa_next;
+ continue;
+ }
+
+ if (! table.containsKey(current->ifa_name)) {
+ EthernetAdapter a;
+ a.name = current->ifa_name;
+ table.set(a.name, a);
+ }
+
+ // This adapter must exist because it was created above
+ EthernetAdapter& adapter = table[current->ifa_name];
+
+ const sockaddr_in* interfaceAddress = castToIP4(current->ifa_addr);
+ const sockaddr_in* broadcastAddress = castToIP4(current->ifa_dstaddr);
+ const sockaddr_in* subnetMask = castToIP4(current->ifa_netmask);
+
+ uint32 ip = getIP(interfaceAddress);
+ uint32 ba = getIP(broadcastAddress);
+ uint32 sn = getIP(subnetMask);
+
+ if (ip != 0) {
+ adapter.ip = ip;
+ }
+
+ if (ba != 0) {
+ adapter.broadcast = ba;
+ }
+
+ if (sn != 0) {
+ adapter.subnet = sn;
+ }
+
+ uint8_t* MAC = NULL;
+ // Extract MAC address
+ if ((current->ifa_addr != NULL) && (current->ifa_addr->sa_family == AF_LINK)) {
+# ifdef __linux__
+ {
+ // Linux
+ struct ifreq ifr;
+
+ int fd = socket(AF_INET, SOCK_DGRAM, 0);
+
+ ifr.ifr_addr.sa_family = AF_INET;
+ strcpy(ifr.ifr_name, current->ifa_name);
+ ioctl(fd, SIOCGIFHWADDR, &ifr);
+ close(fd);
+
+ MAC = reinterpret_cast<uint8_t*>(ifr.ifr_hwaddr.sa_data);
+ }
+# else
+ {
+ // The MAC address and the interfaceAddress come in as
+ // different interfaces with the same name.
+
+ // Posix/FreeBSD/Mac OS
+ sockaddr_dl* sdl = (struct sockaddr_dl *)current->ifa_addr;
+ MAC = reinterpret_cast<uint8_t*>(LLADDR(sdl));
+ }
+# endif
+
+ // See if there was a MAC address
+ if (MAC != NULL) {
+ bool anyNonZero = false;
+ for (int i = 0; i < 6; ++i) {
+ anyNonZero = anyNonZero || (MAC[i] != 0);
+ }
+ if (anyNonZero) {
+ System::memcpy(adapter.mac, MAC, 6);
+ }
+ }
+ }
+
+ current = current->ifa_next;
+ }
+
+ freeifaddrs(ifap);
+ ifap = NULL;
+ }
+
+ // Extract all interesting adapters from the table
+ for (AdapterTable::Iterator it = table.begin(); it.hasMore(); ++it) {
+ const EthernetAdapter& adapter = it->value;
+
+ // Only add adapters that have IP addresses
+ if (adapter.ip != 0) {
+ addAdapter(adapter);
+ } else {
+ logPrintf("NetworkDevice: Ignored adapter %s because ip = 0\n", adapter.name.c_str());
+ }
+ }
+
+ initialized = true;
+
+ return true;
+}
+
+#endif
+
+
+void NetworkDevice::_cleanup() {
+ debugAssert(initialized);
+
+ logPrintf("Network Cleanup");
+# ifdef G3D_WIN32
+ WSACleanup();
+# endif
+ logPrintf("Network cleaned up.");
+}
+
+bool NetworkDevice::bind(SOCKET sock, const NetAddress& addr) const {
+ Log::common()->printf("Binding socket %d on port %d ",
+ sock, htons(addr.addr.sin_port));
+ if (::bind(sock, (struct sockaddr*)&(addr.addr), sizeof(addr.addr)) ==
+ SOCKET_ERROR) {
+
+ Log::common()->println("FAIL");
+ Log::common()->println(socketErrorCode());
+ closesocket(sock);
+ return false;
+ }
+
+ Log::common()->println("Ok");
+ return true;
+}
+
+
+void NetworkDevice::closesocket(SOCKET& sock) const {
+ if (sock != 0) {
+ #ifdef G3D_WIN32
+ ::closesocket(sock);
+ #else
+ close(sock);
+ #endif
+
+ Log::common()->printf("Closed socket %d\n", sock);
+ sock = 0;
+ }
+}
+
+
+void NetworkDevice::localHostAddresses(Array<NetAddress>& array) const {
+ array.resize(0);
+
+ char ac[128];
+
+ if (gethostname(ac, sizeof(ac)) == SOCKET_ERROR) {
+ Log::common()->printf("Error while getting local host name\n");
+ return;
+ }
+
+ struct hostent* phe = gethostbyname(ac);
+ if (phe == 0) {
+ Log::common()->printf("Error while getting local host address\n");
+ return;
+ }
+
+ for (int i = 0; (phe->h_addr_list[i] != 0); ++i) {
+ struct in_addr addr;
+ memcpy(&addr, phe->h_addr_list[i], sizeof(struct in_addr));
+ array.append(NetAddress(addr));
+ }
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+Conduit::Conduit() : binaryOutput("<memory>", G3D_LITTLE_ENDIAN) {
+ sock = 0;
+ mSent = 0;
+ mReceived = 0;
+ bSent = 0;
+ bReceived = 0;
+}
+
+
+Conduit::~Conduit() {
+ NetworkDevice::instance()->closesocket(sock);
+}
+
+
+uint64 Conduit::bytesSent() const {
+ return bSent;
+}
+
+
+uint64 Conduit::bytesReceived() const {
+ return bReceived;
+}
+
+
+uint64 Conduit::messagesSent() const {
+ return mSent;
+}
+
+
+uint64 Conduit::messagesReceived() const {
+ return mReceived;
+}
+
+
+bool Conduit::ok() const {
+ return (sock != 0) && (sock != SOCKET_ERROR);
+}
+
+
+bool Conduit::messageWaiting() {
+ return readWaiting(sock);
+}
+
+
+/**
+ Increases the send and receive sizes of a socket to 2 MB from 8k
+ */
+static void increaseBufferSize(SOCKET sock) {
+
+ // Increase the buffer size; the default (8192) is too easy to
+ // overflow when the network latency is high.
+ {
+ uint32 val = 1024 * 1024 * 2;
+ if (setsockopt(sock, SOL_SOCKET, SO_RCVBUF,
+ (char*)&val, sizeof(val)) == SOCKET_ERROR) {
+ Log::common()->printf("WARNING: Increasing socket "
+ "receive buffer to %d failed.\n", val);
+ Log::common()->println(socketErrorCode());
+ }
+
+ if (setsockopt(sock, SOL_SOCKET, SO_SNDBUF,
+ (char*)&val, sizeof(val)) == SOCKET_ERROR) {
+ Log::common()->printf("WARNING: Increasing socket "
+ "send buffer to %d failed.\n", val);
+ Log::common()->println(socketErrorCode());
+ }
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////////
+
+ReliableConduitRef ReliableConduit::create(const NetAddress& address) {
+ return new ReliableConduit(address);
+}
+
+
+ReliableConduit::ReliableConduit(
+ const NetAddress& _addr) : state(NO_MESSAGE), receiveBuffer(NULL),
+ receiveBufferTotalSize(0), receiveBufferUsedSize(0) {
+
+ NetworkDevice* nd = NetworkDevice::instance();
+
+ messageType = 0;
+
+ addr = _addr;
+ Log::common()->print("Creating a TCP socket ");
+ sock = socket(AF_INET, SOCK_STREAM, IPPROTO_IP);
+
+ if (sock == SOCKET_ERROR) {
+ Log::common()->println("FAIL");
+ Log::common()->println(socketErrorCode());
+ nd->closesocket(sock);
+ return;
+ }
+
+ Log::common()->println("Ok");
+
+ // Setup socket options (both constructors should set the same options)
+
+ // Disable Nagle's algorithm (we send lots of small packets)
+ const int T = true;
+ if (setsockopt(sock, IPPROTO_TCP, TCP_NODELAY,
+ (const char*)&T, sizeof(T)) == SOCKET_ERROR) {
+
+ Log::common()->println("WARNING: Disabling Nagel's "
+ "algorithm failed.");
+ Log::common()->println(socketErrorCode());
+ } else {
+ Log::common()->println("Disabled Nagel's algorithm.");
+ }
+
+ // Set the NO LINGER option so the socket doesn't hang around if
+ // there is unsent data in the queue when it closes.
+ struct linger ling;
+ ling.l_onoff = 0;
+ ling.l_linger = 0;
+ if (setsockopt(sock, SOL_SOCKET, SO_LINGER,
+ (const char*)&ling, sizeof(ling)) == SOCKET_ERROR) {
+
+ Log::common()->println("WARNING: Setting socket no linger failed.");
+ Log::common()->println(socketErrorCode());
+ } else {
+ Log::common()->println("Set socket option no_linger.");
+ }
+
+ // Set reuse address so that a new server can start up soon after
+ // an old one has closed.
+ if (setsockopt(sock, SOL_SOCKET, SO_REUSEADDR,
+ (const char*)&T, sizeof(T)) == SOCKET_ERROR) {
+
+ Log::common()->println("WARNING: Setting socket reuseaddr failed.");
+ Log::common()->println(socketErrorCode());
+ } else {
+ Log::common()->println("Set socket option reuseaddr.");
+ }
+
+ // Ideally, we'd like to specify IPTOS_LOWDELAY as well.
+
+ logSocketInfo(sock);
+
+ increaseBufferSize(sock);
+
+ Log::common()->printf("Created TCP socket %d\n", sock);
+
+ std::string x = addr.toString();
+ Log::common()->printf("Connecting to %s on TCP socket %d ", x.c_str(), sock);
+
+ int ret = connect(sock, (struct sockaddr *) &(addr.addr), sizeof(addr.addr));
+
+ if (ret == WSAEWOULDBLOCK) {
+ RealTime t = System::time() + 5.0;
+ // Non-blocking; we must wait until select returns non-zero
+ while ((selectOneWriteSocket(sock) == 0) && (System::time() < t)) {
+ System::sleep(0.02);
+ }
+
+ // TODO: check for failure on the select call
+
+ } else if (ret != 0) {
+ sock = (SOCKET)SOCKET_ERROR;
+ Log::common()->println("FAIL");
+ Log::common()->println(socketErrorCode());
+ return;
+ }
+
+ Log::common()->println("Ok");
+}
+
+
+ReliableConduit::ReliableConduit(
+ const SOCKET& _sock,
+ const NetAddress& _addr) :
+ state(NO_MESSAGE),
+ receiveBuffer(NULL),
+ receiveBufferTotalSize(0),
+ receiveBufferUsedSize(0) {
+ sock = _sock;
+ addr = _addr;
+
+ messageType = 0;
+
+ // Setup socket options (both constructors should set the same options)
+
+ // Disable Nagle's algorithm (we send lots of small packets)
+ const int T = true;
+ if (setsockopt(sock, IPPROTO_TCP, TCP_NODELAY,
+ (const char*)&T, sizeof(T)) == SOCKET_ERROR) {
+
+ Log::common()->println("WARNING: Disabling Nagel's algorithm failed.");
+ Log::common()->println(socketErrorCode());
+ } else {
+ Log::common()->println("Disabled Nagel's algorithm.");
+ }
+
+ // Set the NO LINGER option so the socket doesn't hang around if
+ // there is unsent data in the queue when it closes.
+ struct linger ling;
+ ling.l_onoff = 0;
+ ling.l_linger = 0;
+ if (setsockopt(sock, SOL_SOCKET, SO_LINGER,
+ (const char*)&ling, sizeof(ling)) == SOCKET_ERROR) {
+
+ Log::common()->println("WARNING: Setting socket no linger failed.");
+ Log::common()->println(socketErrorCode());
+ } else {
+ Log::common()->println("Set socket option no_linger.");
+ }
+
+ // Set reuse address so that a new server can start up soon after
+ // an old one has closed.
+ if (setsockopt(sock, SOL_SOCKET, SO_REUSEADDR,
+ (const char*)&T, sizeof(T)) == SOCKET_ERROR) {
+
+ Log::common()->println("WARNING: Setting socket reuseaddr failed.");
+ Log::common()->println(socketErrorCode());
+ } else {
+ Log::common()->println("Set socket option reuseaddr.");
+ }
+
+ // Ideally, we'd like to specify IPTOS_LOWDELAY as well.
+
+ logSocketInfo(sock);
+}
+
+
+ReliableConduit::~ReliableConduit() {
+ free(receiveBuffer);
+ receiveBuffer = NULL;
+ receiveBufferTotalSize = 0;
+ receiveBufferUsedSize = 0;
+}
+
+
+bool ReliableConduit::messageWaiting() {
+ switch (state) {
+ case HOLDING:
+ // We've already read the message and are waiting
+ // for a receive call.
+ return true;
+
+ case RECEIVING:
+
+ if (! ok()) {
+ return false;
+ }
+ // We're currently receiving the message. Read a little more.
+ receiveIntoBuffer();
+
+ if (messageSize == receiveBufferUsedSize) {
+ // We've read the whole mesage. Switch to holding state
+ // and return true.
+ state = HOLDING;
+ return true;
+ } else {
+ // There are more bytes left to read. We'll read them on
+ // the next call. Because the *entire* message is not ready,
+ // return false.
+ return false;
+ }
+ break;
+
+ case NO_MESSAGE:
+ if (Conduit::messageWaiting()) {
+ // Message incoming. Read the header.
+
+ state = RECEIVING;
+ receiveHeader();
+
+ // Loop back around now that we're in the receive state; we
+ // may be able to read the whole message before returning
+ // to the caller.
+ return messageWaiting();
+ } else {
+ // No message incoming.
+ return false;
+ }
+ }
+
+ debugAssertM(false, "Should not reach this point");
+ return false;
+}
+
+
+uint32 ReliableConduit::waitingMessageType() {
+ // The messageWaiting call is what actually receives the message.
+ if (messageWaiting()) {
+ return messageType;
+ } else {
+ return 0;
+ }
+}
+
+
+void ReliableConduit::sendBuffer(const BinaryOutput& b) {
+ NetworkDevice* nd = NetworkDevice::instance();
+ int ret = ::send(sock, (const char*)b.getCArray(), b.size(), 0);
+
+ if (ret == SOCKET_ERROR) {
+ Log::common()->println("Error occured while sending message.");
+ Log::common()->println(socketErrorCode());
+ nd->closesocket(sock);
+ return;
+ }
+
+ ++mSent;
+ bSent += b.size();
+
+ // Verify the packet was actually sent
+ // Conversion to unsigned is safe because -1 is caught earlier
+ debugAssert(ret == b.size());
+}
+
+
+/** Null serializer. Used by reliable conduit::send(type) */
+class Dummy {
+public:
+ void serialize(BinaryOutput& b) const { (void)b; }
+};
+
+
+void ReliableConduit::send(uint32 type) {
+ static Dummy dummy;
+ send(type, dummy);
+}
+
+
+
+NetAddress ReliableConduit::address() const {
+ return addr;
+}
+
+
+void ReliableConduit::receiveHeader() {
+ NetworkDevice* nd = NetworkDevice::instance();
+ debugAssert(state == RECEIVING);
+
+ // Read the type
+ uint32 tmp;
+ int ret = recv(sock, (char*)&tmp, sizeof(tmp), 0);
+
+ // The type is the first four bytes. It is little endian.
+ if (System::machineEndian() == G3D_LITTLE_ENDIAN) {
+ messageType = tmp;
+ } else {
+ // Swap the byte order
+ for (int i = 0; i < 4; ++i) {
+ ((char*)&messageType)[i] = ((char*)&tmp)[3 - i];
+ }
+ }
+
+ if ((ret == SOCKET_ERROR) || (ret != sizeof(messageType))) {
+ Log::common()->printf("Call to recv failed. ret = %d,"
+ " sizeof(messageType) = %d\n",
+ (int)ret, (int)sizeof(messageType));
+ Log::common()->println(socketErrorCode());
+ nd->closesocket(sock);
+ messageType = 0;
+ return;
+ }
+
+ // Read the size
+ ret = recv(sock, (char*)&messageSize, sizeof(messageSize), 0);
+
+ if ((ret == SOCKET_ERROR) || (ret != sizeof(messageSize))) {
+ Log::common()->printf("Call to recv failed. ret = %d,"
+ " sizeof(len) = %d\n", (int)ret,
+ (int)sizeof(messageSize));
+ Log::common()->println(socketErrorCode());
+ nd->closesocket(sock);
+ messageType = 0;
+ return;
+ }
+
+ messageSize = ntohl(messageSize);
+ debugAssert(messageSize < 6e7);
+
+ debugAssert(receiveBufferUsedSize == 0);
+
+ // Extend the size of the buffer.
+ if (messageSize > receiveBufferTotalSize) {
+ receiveBuffer = realloc(receiveBuffer, messageSize);
+ receiveBufferTotalSize = messageSize;
+ }
+
+ if (receiveBuffer == NULL) {
+ Log::common()->println("Could not allocate a memory buffer "
+ "during receivePacket.");
+ nd->closesocket(sock);
+ }
+
+ bReceived += 4;
+}
+
+
+void ReliableConduit::receiveIntoBuffer() {
+ NetworkDevice* nd = NetworkDevice::instance();
+
+ debugAssert(state == RECEIVING);
+ debugAssert(messageType != 0);
+ debugAssertM(receiveBufferUsedSize < messageSize, "Message already received.");
+ debugAssertM(messageSize >= receiveBufferUsedSize, "Message size overflow.");
+
+ // Read the data itself
+ int ret = 0;
+ uint32 left = messageSize - receiveBufferUsedSize;
+ int count = 0;
+ while ((ret != SOCKET_ERROR) && (left > 0) && (count < 100)) {
+
+ ret = recv(sock, ((char*)receiveBuffer) + receiveBufferUsedSize, left, 0);
+
+ if (ret > 0) {
+ left -= ret;
+ receiveBufferUsedSize += ret;
+ bReceived += ret;
+
+ if (left > 0) {
+ // There's still more. Give the machine a chance to read
+ // more data, but don't wait forever.
+
+ ++count;
+ System::sleep(0.001);
+ }
+ } else {
+ // Something went wrong; our blocking read returned nothing.
+ break;
+ }
+ }
+
+ if ((ret == 0) || (ret == SOCKET_ERROR)) {
+
+ if (ret == SOCKET_ERROR) {
+ Log::common()->printf("Call to recv failed. ret = %d,"
+ " sizeof(messageSize) = %d\n", ret, messageSize);
+ Log::common()->println(socketErrorCode());
+ } else {
+ Log::common()->printf("recv returned 0\n");
+ }
+ nd->closesocket(sock);
+ return;
+ }
+
+ ++mReceived;
+}
+
+
+///////////////////////////////////////////////////////////////////////////////
+LightweightConduitRef LightweightConduit::create(
+ uint16 receivePort,
+ bool enableReceive,
+ bool enableBroadcast) {
+
+ return new LightweightConduit(receivePort, enableReceive, enableBroadcast);
+}
+
+LightweightConduit::LightweightConduit(
+ uint16 port,
+ bool enableReceive,
+ bool enableBroadcast) {
+ NetworkDevice* nd = NetworkDevice::instance();
+
+ Log::common()->print("Creating a UDP socket ");
+ sock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
+
+ if (sock == SOCKET_ERROR) {
+ sock = 0;
+ Log::common()->println("FAIL");
+ Log::common()->println(socketErrorCode());
+ return;
+ }
+ Log::common()->println("Ok");
+
+ if (enableReceive) {
+ debugAssert(port != 0);
+ if (! nd->bind(sock, NetAddress(0, port))) {
+ nd->closesocket(sock);
+ sock = (SOCKET)SOCKET_ERROR;
+ }
+ }
+
+ // Figuring out the MTU seems very complicated, so we just set it to 1000,
+ // which is likely to be safe. See IP_MTU for more information.
+ MTU = 1000;
+
+ increaseBufferSize(sock);
+
+ if (enableBroadcast) {
+ int TR = true;
+ if (setsockopt(sock, SOL_SOCKET, SO_BROADCAST,
+ (const char*)&TR, sizeof(TR)) != 0) {
+ Log::common()->println("Call to setsockopt failed");
+ Log::common()->println(socketErrorCode());
+ nd->closesocket(sock);
+ sock = 0;
+ return;
+ }
+ }
+
+ Log::common()->printf("Done creating UDP socket %d\n", sock);
+
+ alreadyReadMessage = false;
+}
+
+
+LightweightConduit::~LightweightConduit() {
+}
+
+
+bool LightweightConduit::receive(NetAddress& sender) {
+ // This both checks to ensure that a message was waiting and
+ // actively consumes the message from the network stream if
+ // it has not been read yet.
+ uint32 t = waitingMessageType();
+ if (t == 0) {
+ return false;
+ }
+
+ sender = messageSender;
+ alreadyReadMessage = false;
+
+ if (messageBuffer.size() < 4) {
+ // Something went wrong
+ return false;
+ }
+
+ return true;
+}
+
+
+void LightweightConduit::sendBuffer(const NetAddress& a, BinaryOutput& b) {
+ NetworkDevice* nd = NetworkDevice::instance();
+ if (sendto(sock, (const char*)b.getCArray(), b.size(), 0,
+ (struct sockaddr *) &(a.addr), sizeof(a.addr)) == SOCKET_ERROR) {
+ Log::common()->printf("Error occured while sending packet "
+ "to %s\n", inet_ntoa(a.addr.sin_addr));
+ Log::common()->println(socketErrorCode());
+ nd->closesocket(sock);
+ } else {
+ ++mSent;
+ bSent += b.size();
+ }
+}
+
+
+bool LightweightConduit::messageWaiting() {
+ // We may have already pulled the message off the network stream
+ return alreadyReadMessage || Conduit::messageWaiting();
+}
+
+
+uint32 LightweightConduit::waitingMessageType() {
+ NetworkDevice* nd = NetworkDevice::instance();
+ if (! messageWaiting()) {
+ return 0;
+ }
+
+ if (! alreadyReadMessage) {
+ messageBuffer.resize(8192);
+
+ SOCKADDR_IN remote_addr;
+ int iRemoteAddrLen = sizeof(sockaddr);
+
+ int ret = recvfrom(sock, (char*)messageBuffer.getCArray(),
+ messageBuffer.size(), 0, (struct sockaddr *) &remote_addr,
+ (socklen_t*)&iRemoteAddrLen);
+
+ if (ret == SOCKET_ERROR) {
+ Log::common()->println("Error: recvfrom failed in "
+ "LightweightConduit::waitingMessageType().");
+ Log::common()->println(socketErrorCode());
+ nd->closesocket(sock);
+ messageBuffer.resize(0);
+ messageSender = NetAddress();
+ messageType = 0;
+ return 0;
+ }
+
+ messageSender = NetAddress(remote_addr);
+
+ ++mReceived;
+ bReceived += ret;
+
+ messageBuffer.resize(ret, DONT_SHRINK_UNDERLYING_ARRAY);
+
+ // The type is the first four bytes. It is little endian.
+ if (System::machineEndian() == G3D_LITTLE_ENDIAN) {
+ messageType = *((uint32*)messageBuffer.getCArray());
+ } else {
+ // Swap the byte order
+ for (int i = 0; i < 4; ++i) {
+ ((char*)&messageType)[i] = messageBuffer[3 - i];
+ }
+ }
+
+ alreadyReadMessage = true;
+ }
+
+ return messageType;
+}
+
+
+///////////////////////////////////////////////////////////////////////////////
+
+NetListenerRef NetListener::create(const uint16 port) {
+ return new NetListener(port);
+}
+
+
+NetListener::NetListener(uint16 port) {
+ NetworkDevice* nd = NetworkDevice::instance();
+
+ // Start the listener socket
+ Log::common()->print("Creating a listener ");
+ sock = socket(AF_INET, SOCK_STREAM, IPPROTO_IP);
+
+ if (sock == SOCKET_ERROR) {
+ Log::common()->printf("FAIL");
+ Log::common()->println(socketErrorCode());
+ return;
+ }
+ Log::common()->println("Ok");
+
+ const int T = true;
+
+ // Set reuse address so that a new server can start up soon after
+ // an old one has closed.
+ if (setsockopt(sock, SOL_SOCKET, SO_REUSEADDR,
+ (const char*)&T, sizeof(T)) == SOCKET_ERROR) {
+
+ Log::common()->println("WARNING: Setting socket reuseaddr failed.");
+ Log::common()->println(socketErrorCode());
+ } else {
+ Log::common()->println("Set socket option reuseaddr.");
+ }
+
+
+ if (! nd->bind(sock, NetAddress(0, port))) {
+ Log::common()->printf("Unable to bind!\n");
+ nd->closesocket(sock);
+ sock = (SOCKET)SOCKET_ERROR;
+ return;
+ }
+
+ Log::common()->printf("Listening on port %5d ", port);
+
+ // listen is supposed to return 0 when there is no error.
+ // The 2nd argument is the number of connections to allow pending
+ // at any time.
+ int L = listen(sock, 100);
+ if (L == SOCKET_ERROR) {
+ Log::common()->println("FAIL");
+ Log::common()->println(socketErrorCode());
+ nd->closesocket(sock);
+ sock = (SOCKET)SOCKET_ERROR;
+ return;
+ }
+ Log::common()->println("Ok");
+ Log::common()->printf("Now listening on socket %d.\n\n", sock);
+}
+
+
+NetListener::~NetListener() {
+ NetworkDevice* nd = NetworkDevice::instance();
+ nd->closesocket(sock);
+}
+
+
+ReliableConduitRef NetListener::waitForConnection() {
+ NetworkDevice* nd = NetworkDevice::instance();
+ // The address of the connecting host
+ SOCKADDR_IN remote_addr;
+ int iAddrLen = sizeof(remote_addr);
+
+ Log::common()->println("Blocking in NetListener::waitForConnection().");
+
+ SOCKET sClient = accept(sock, (struct sockaddr*) &remote_addr,
+ (socklen_t*)&iAddrLen);
+
+ if (sClient == SOCKET_ERROR) {
+ Log::common()->println("Error in NetListener::acceptConnection.");
+ Log::common()->println(socketErrorCode());
+ nd->closesocket(sock);
+ return NULL;
+ }
+
+ Log::common()->printf("%s connected, transferred to socket %d.\n",
+ inet_ntoa(remote_addr.sin_addr), sClient);
+
+ #ifndef G3D_WIN32
+ return new ReliableConduit(sClient,
+ NetAddress(htonl(remote_addr.sin_addr.s_addr),
+ ntohs(remote_addr.sin_port)));
+ #else
+ return new ReliableConduit(sClient,
+ NetAddress(ntohl(remote_addr.sin_addr.S_un.S_addr),
+ ntohs(remote_addr.sin_port)));
+ #endif
+}
+
+
+bool NetListener::ok() const {
+ return (sock != 0) && (sock != SOCKET_ERROR);
+}
+
+
+bool NetListener::clientWaiting() const {
+ return readWaiting(sock);
+}
+
+////////////////////////////////////////////////////////////////////////////////////////////////
+
+void NetworkDevice::describeSystem(
+ TextOutput& t) {
+
+ t.writeSymbols("Network", "{");
+ t.writeNewline();
+ t.pushIndent();
+
+ for (int i = 0; i < m_adapterArray.size(); ++i) {
+ m_adapterArray[i].describe(t);
+ }
+
+
+ t.popIndent();
+ t.writeSymbols("}");
+ t.writeNewline();
+ t.writeNewline();
+}
+
+
+void NetworkDevice::describeSystem(
+ std::string& s) {
+
+ TextOutput t;
+ describeSystem(t);
+ t.commitString(s);
+}
+
+} // namespace
diff --git a/externals/g3dlite/G3D.lib/source/PhysicsFrame.cpp b/externals/g3dlite/G3D.lib/source/PhysicsFrame.cpp
new file mode 100644
index 00000000000..034e585d01f
--- /dev/null
+++ b/externals/g3dlite/G3D.lib/source/PhysicsFrame.cpp
@@ -0,0 +1,77 @@
+/**
+ @file PhysicsFrame.cpp
+
+ @maintainer Morgan McGuire, matrix@graphics3d.com
+
+ @created 2002-07-09
+ @edited 2006-01-25
+*/
+
+#include "G3D/platform.h"
+#include "G3D/PhysicsFrame.h"
+#include "G3D/BinaryInput.h"
+#include "G3D/BinaryOutput.h"
+
+namespace G3D {
+
+PhysicsFrame::PhysicsFrame() {
+ translation = Vector3::zero();
+ rotation = Quat();
+}
+
+
+PhysicsFrame::PhysicsFrame(
+ const CoordinateFrame& coordinateFrame) {
+
+ translation = coordinateFrame.translation;
+ rotation = Quat(coordinateFrame.rotation);
+}
+
+
+PhysicsFrame PhysicsFrame::operator*(const PhysicsFrame& other) const {
+ PhysicsFrame result;
+
+ result.rotation = rotation * other.rotation;
+ result.translation = translation + rotation.toRotationMatrix() * other.translation;
+
+ return result;
+}
+
+
+CoordinateFrame PhysicsFrame::toCoordinateFrame() const {
+ CoordinateFrame f;
+
+ f.translation = translation;
+ f.rotation = rotation.toRotationMatrix();
+
+ return f;
+}
+
+
+PhysicsFrame PhysicsFrame::lerp(
+ const PhysicsFrame& other,
+ float alpha) const {
+
+ PhysicsFrame result;
+
+ result.translation = translation.lerp(other.translation, alpha);
+ result.rotation = rotation.slerp(other.rotation, alpha);
+
+ return result;
+}
+
+
+void PhysicsFrame::deserialize(class BinaryInput& b) {
+ translation.deserialize(b);
+ rotation.deserialize(b);
+}
+
+
+void PhysicsFrame::serialize(class BinaryOutput& b) const {
+ translation.serialize(b);
+ rotation.serialize(b);
+}
+
+
+}; // namespace
+
diff --git a/externals/g3dlite/G3D.lib/source/Plane.cpp b/externals/g3dlite/G3D.lib/source/Plane.cpp
new file mode 100644
index 00000000000..bb8ed5c6033
--- /dev/null
+++ b/externals/g3dlite/G3D.lib/source/Plane.cpp
@@ -0,0 +1,149 @@
+/**
+ @file Plane.cpp
+
+ @maintainer Morgan McGuire, matrix@graphics3d.com
+
+ @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/G3D.lib/source/Quat.cpp b/externals/g3dlite/G3D.lib/source/Quat.cpp
new file mode 100644
index 00000000000..225c5b51acc
--- /dev/null
+++ b/externals/g3dlite/G3D.lib/source/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/G3D.lib/source/Ray.cpp b/externals/g3dlite/G3D.lib/source/Ray.cpp
new file mode 100644
index 00000000000..bf6aadc08d1
--- /dev/null
+++ b/externals/g3dlite/G3D.lib/source/Ray.cpp
@@ -0,0 +1,112 @@
+/**
+ @file Ray.cpp
+
+ @maintainer Morgan McGuire, matrix@graphics3d.com
+
+ @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 {
+
+Ray::Ray(class BinaryInput& b) {
+ deserialize(b);
+}
+
+
+void Ray::serialize(class BinaryOutput& b) const {
+ origin.serialize(b);
+ direction.serialize(b);
+}
+
+
+void Ray::deserialize(class BinaryInput& b) {
+ origin.deserialize(b);
+ direction.deserialize(b);
+}
+
+
+Ray Ray::refract(
+ const Vector3& newOrigin,
+ const Vector3& normal,
+ float iInside,
+ float iOutside) const {
+
+ Vector3 D = direction.refractionDirection(normal, iInside, iOutside);
+ return Ray::fromOriginAndDirection(
+ newOrigin + (direction + normal * (float)sign(direction.dot(normal))) * 0.001f, D);
+}
+
+
+Ray Ray::reflect(
+ const Vector3& newOrigin,
+ const Vector3& normal) const {
+
+ Vector3 D = direction.reflectionDirection(normal);
+ return Ray::fromOriginAndDirection(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 = direction.dot(normal);
+
+ if (rate >= 0.0f) {
+ return Vector3::inf();
+ } else {
+ float t = -(d + origin.dot(normal)) / rate;
+
+ return origin + direction * t;
+ }
+}
+
+
+float Ray::intersectionTime(const class Sphere& sphere) const {
+ Vector3 dummy;
+ return CollisionDetection::collisionTimeForMovingPointFixedSphere(
+ origin, direction, sphere, dummy);
+}
+
+
+float Ray::intersectionTime(const class Plane& plane) const {
+ Vector3 dummy;
+ return CollisionDetection::collisionTimeForMovingPointFixedPlane(
+ origin, direction, plane, dummy);
+}
+
+
+float Ray::intersectionTime(const class Box& box) const {
+ Vector3 dummy;
+ float time = CollisionDetection::collisionTimeForMovingPointFixedBox(
+ origin, direction, box, dummy);
+
+ if ((time == inf()) && (box.contains(origin))) {
+ return 0.0f;
+ } else {
+ return time;
+ }
+}
+
+
+float Ray::intersectionTime(const class AABox& box) const {
+ Vector3 dummy;
+ bool inside;
+ float time = CollisionDetection::collisionTimeForMovingPointFixedAABox(
+ origin, direction, box, dummy, inside);
+
+ if ((time == inf()) && inside) {
+ return 0.0f;
+ } else {
+ return time;
+ }
+}
+
+}
diff --git a/externals/g3dlite/G3D.lib/source/RegistryUtil.cpp b/externals/g3dlite/G3D.lib/source/RegistryUtil.cpp
new file mode 100644
index 00000000000..28ff6955d9b
--- /dev/null
+++ b/externals/g3dlite/G3D.lib/source/RegistryUtil.cpp
@@ -0,0 +1,290 @@
+/**
+ @file RegistryUtil.cpp
+
+ @created 2006-04-06
+ @edited 2006-04-24
+
+ Copyright 2000-2006, Morgan McGuire.
+ All rights reserved.
+*/
+
+#include "G3D/platform.h"
+
+// This file is only used on Windows
+#ifdef G3D_WIN32
+
+#include "G3D/RegistryUtil.h"
+#include "G3D/System.h"
+
+namespace G3D {
+
+// static helpers
+static HKEY getRootKeyFromString(const char* str, size_t length);
+
+
+bool RegistryUtil::keyExists(const std::string& key) {
+ size_t pos = key.find('\\', 0);
+ if (pos == std::string::npos) {
+ return false;
+ }
+
+ HKEY hkey = getRootKeyFromString(key.c_str(), pos);
+
+ if (hkey == NULL) {
+ return false;
+ }
+
+ HKEY openKey;
+ int32 result = RegOpenKeyExA(hkey, (key.c_str() + pos + 1), 0, KEY_READ, &openKey);
+
+ debugAssert(result == ERROR_SUCCESS || result == ERROR_FILE_NOT_FOUND);
+
+ if (result == ERROR_SUCCESS) {
+ RegCloseKey(openKey);
+ return true;
+ } else {
+ return false;
+ }
+}
+
+bool RegistryUtil::valueExists(const std::string& key, const std::string& value) {
+ size_t pos = key.find('\\', 0);
+ if (pos == std::string::npos) {
+ return false;
+ }
+
+ HKEY hkey = getRootKeyFromString(key.c_str(), pos);
+
+ if ( hkey == NULL ) {
+ return false;
+ }
+
+ HKEY openKey;
+ int32 result = RegOpenKeyExA(hkey, (key.c_str() + pos + 1), 0, KEY_READ, &openKey);
+ debugAssert(result == ERROR_SUCCESS || result == ERROR_FILE_NOT_FOUND);
+
+ if (result == ERROR_SUCCESS) {
+ uint32 dataSize = 0;
+ result = RegQueryValueExA(openKey, value.c_str(), NULL, NULL, NULL, reinterpret_cast<LPDWORD>(&dataSize));
+
+ debugAssert(result == ERROR_SUCCESS || result == ERROR_FILE_NOT_FOUND);
+ RegCloseKey(openKey);
+ }
+ return (result == ERROR_SUCCESS);
+}
+
+
+bool RegistryUtil::readInt32(const std::string& key, const std::string& value, int32& data) {
+ size_t pos = key.find('\\', 0);
+ if (pos == std::string::npos) {
+ return false;
+ }
+
+ HKEY hkey = getRootKeyFromString(key.c_str(), pos);
+
+ if ( hkey == NULL ) {
+ return false;
+ }
+
+ HKEY openKey;
+ int32 result = RegOpenKeyExA(hkey, (key.c_str() + pos + 1), 0, KEY_READ, &openKey);
+ debugAssert(result == ERROR_SUCCESS || result == ERROR_FILE_NOT_FOUND);
+
+ if (result == ERROR_SUCCESS) {
+ uint32 dataSize = sizeof(int32);
+ result = RegQueryValueExA(openKey, value.c_str(), NULL, NULL, reinterpret_cast<LPBYTE>(&data), reinterpret_cast<LPDWORD>(&dataSize));
+
+ debugAssertM(result == ERROR_SUCCESS, "Could not read registry key value.");
+
+ RegCloseKey(openKey);
+ }
+ return (result == ERROR_SUCCESS);
+}
+
+bool RegistryUtil::readBytes(const std::string& key, const std::string& value, uint8* data, uint32& dataSize) {
+ size_t pos = key.find('\\', 0);
+ if (pos == std::string::npos) {
+ return false;
+ }
+
+ HKEY hkey = getRootKeyFromString(key.c_str(), pos);
+
+ if (hkey == NULL) {
+ return false;
+ }
+
+ HKEY openKey;
+ int32 result = RegOpenKeyExA(hkey, (key.c_str() + pos + 1), 0, KEY_READ, &openKey);
+ debugAssert(result == ERROR_SUCCESS || result == ERROR_FILE_NOT_FOUND);
+
+ if (result == ERROR_SUCCESS) {
+ if (data == NULL) {
+ result = RegQueryValueExA(openKey, value.c_str(), NULL, NULL, NULL, reinterpret_cast<LPDWORD>(&dataSize));
+ } else {
+ result = RegQueryValueExA(openKey, value.c_str(), NULL, NULL, reinterpret_cast<LPBYTE>(&data), reinterpret_cast<LPDWORD>(&dataSize));
+ }
+
+ debugAssertM(result == ERROR_SUCCESS, "Could not read registry key value.");
+
+ RegCloseKey(openKey);
+ }
+ return (result == ERROR_SUCCESS);
+}
+
+bool RegistryUtil::readString(const std::string& key, const std::string& value, std::string& data) {
+ size_t pos = key.find('\\', 0);
+ if (pos == std::string::npos) {
+ return false;
+ }
+
+ HKEY hkey = getRootKeyFromString(key.c_str(), pos);
+
+ if (hkey == NULL) {
+ return false;
+ }
+
+ HKEY openKey;
+ int32 result = RegOpenKeyExA(hkey, (key.c_str() + pos + 1), 0, KEY_READ, &openKey);
+ debugAssert(result == ERROR_SUCCESS || result == ERROR_FILE_NOT_FOUND);
+
+ if (result == ERROR_SUCCESS) {
+ uint32 dataSize = 0;
+
+ result = RegQueryValueExA(openKey, value.c_str(), NULL, NULL, NULL, reinterpret_cast<LPDWORD>(&dataSize));
+ debugAssertM(result == ERROR_SUCCESS, "Could not read registry key value.");
+
+ // increment datasize to allow for non null-terminated strings in registry
+ dataSize += 1;
+
+ if (result == ERROR_SUCCESS) {
+ char* tmpStr = static_cast<char*>(System::malloc(dataSize));
+ System::memset(tmpStr, 0, dataSize);
+
+ result = RegQueryValueExA(openKey, value.c_str(), NULL, NULL, reinterpret_cast<LPBYTE>(tmpStr), reinterpret_cast<LPDWORD>(&dataSize));
+ debugAssertM(result == ERROR_SUCCESS, "Could not read registry key value.");
+
+ if (result == ERROR_SUCCESS) {
+ data = tmpStr;
+ }
+
+ RegCloseKey(openKey);
+ System::free(tmpStr);
+ }
+ }
+ return (result == ERROR_SUCCESS);
+}
+
+bool RegistryUtil::writeInt32(const std::string& key, const std::string& value, int32 data) {
+ size_t pos = key.find('\\', 0);
+ if (pos == std::string::npos) {
+ return false;
+ }
+
+ HKEY hkey = getRootKeyFromString(key.c_str(), pos);
+
+ if (hkey == NULL) {
+ return false;
+ }
+
+ HKEY openKey;
+ int32 result = RegOpenKeyExA(hkey, (key.c_str() + pos + 1), 0, KEY_WRITE, &openKey);
+ debugAssert(result == ERROR_SUCCESS || result == ERROR_FILE_NOT_FOUND);
+
+ if (result == ERROR_SUCCESS) {
+ result = RegSetValueExA(openKey, value.c_str(), 0, REG_DWORD, reinterpret_cast<const BYTE*>(&data), sizeof(int32));
+
+ debugAssertM(result == ERROR_SUCCESS, "Could not write registry key value.");
+
+ RegCloseKey(openKey);
+ }
+ return (result == ERROR_SUCCESS);
+}
+
+bool RegistryUtil::writeBytes(const std::string& key, const std::string& value, const uint8* data, uint32 dataSize) {
+ debugAssert(data);
+
+ size_t pos = key.find('\\', 0);
+ if (pos == std::string::npos) {
+ return false;
+ }
+
+ HKEY hkey = getRootKeyFromString(key.c_str(), pos);
+
+ if (hkey == NULL) {
+ return false;
+ }
+
+ HKEY openKey;
+ int32 result = RegOpenKeyExA(hkey, (key.c_str() + pos + 1), 0, KEY_WRITE, &openKey);
+ debugAssert(result == ERROR_SUCCESS || result == ERROR_FILE_NOT_FOUND);
+
+ if (result == ERROR_SUCCESS) {
+ if (data) {
+ result = RegSetValueExA(openKey, value.c_str(), 0, REG_BINARY, reinterpret_cast<const BYTE*>(data), dataSize);
+ }
+
+ debugAssertM(result == ERROR_SUCCESS, "Could not write registry key value.");
+
+ RegCloseKey(openKey);
+ }
+ return (result == ERROR_SUCCESS);
+}
+
+bool RegistryUtil::writeString(const std::string& key, const std::string& value, const std::string& data) {
+ size_t pos = key.find('\\', 0);
+ if (pos == std::string::npos) {
+ return false;
+ }
+
+ HKEY hkey = getRootKeyFromString(key.c_str(), pos);
+
+ if (hkey == NULL) {
+ return false;
+ }
+
+ HKEY openKey;
+ int32 result = RegOpenKeyExA(hkey, (key.c_str() + pos + 1), 0, KEY_WRITE, &openKey);
+ debugAssert(result == ERROR_SUCCESS || result == ERROR_FILE_NOT_FOUND);
+
+ if (result == ERROR_SUCCESS) {
+ result = RegSetValueExA(openKey, value.c_str(), 0, REG_SZ, reinterpret_cast<const BYTE*>(data.c_str()), (data.size() + 1));
+ debugAssertM(result == ERROR_SUCCESS, "Could not write registry key value.");
+
+ RegCloseKey(openKey);
+ }
+ return (result == ERROR_SUCCESS);
+}
+
+
+// static helpers
+static HKEY getRootKeyFromString(const char* str, uint32 length) {
+ debugAssert(str);
+
+ if (str) {
+ if ( strncmp(str, "HKEY_CLASSES_ROOT", length) == 0 ) {
+ return HKEY_CLASSES_ROOT;
+ } else if ( strncmp(str, "HKEY_CURRENT_CONFIG", length) == 0 ) {
+ return HKEY_CURRENT_CONFIG;
+ } else if ( strncmp(str, "HKEY_CURRENT_USER", length) == 0 ) {
+ return HKEY_CURRENT_USER;
+ } else if ( strncmp(str, "HKEY_LOCAL_MACHINE", length) == 0 ) {
+ return HKEY_LOCAL_MACHINE;
+ } else if ( strncmp(str, "HKEY_PERFORMANCE_DATA", length) == 0 ) {
+ return HKEY_PERFORMANCE_DATA;
+ } else if ( strncmp(str, "HKEY_PERFORMANCE_NLSTEXT", length) == 0 ) {
+ return HKEY_PERFORMANCE_NLSTEXT;
+ } else if ( strncmp(str, "HKEY_PERFORMANCE_TEXT", length) == 0 ) {
+ return HKEY_PERFORMANCE_TEXT;
+ } else if ( strncmp(str, "HKEY_CLASSES_ROOT", length) == 0 ) {
+ return HKEY_CLASSES_ROOT;
+ } else {
+ return NULL;
+ }
+ } else {
+ return NULL;
+ }
+}
+
+} // namespace G3D
+
+#endif // G3D_WIN32
diff --git a/externals/g3dlite/G3D.lib/source/Sphere.cpp b/externals/g3dlite/G3D.lib/source/Sphere.cpp
new file mode 100644
index 00000000000..935598d52dd
--- /dev/null
+++ b/externals/g3dlite/G3D.lib/source/Sphere.cpp
@@ -0,0 +1,196 @@
+/**
+ @file Sphere.cpp
+
+ Sphere class
+
+ @maintainer Morgan McGuire, matrix@graphics3d.com
+
+ @created 2001-04-17
+ @edited 2006-02-05
+ */
+
+#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 {
+ double distance = (center - point).squaredMagnitude();
+ return distance <= (radius * radius);
+}
+
+
+bool Sphere::intersects(const Sphere& other) const {
+ return (other.center - center).length() <= (radius + other.radius);
+}
+
+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 == inf()) {
+ // 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/G3D.lib/source/SplineBase.cpp b/externals/g3dlite/G3D.lib/source/SplineBase.cpp
new file mode 100644
index 00000000000..41221624b06
--- /dev/null
+++ b/externals/g3dlite/G3D.lib/source/SplineBase.cpp
@@ -0,0 +1,162 @@
+#include "G3D/platform.h"
+#include "G3D/Spline.h"
+
+namespace G3D {
+
+float SplineBase::getFinalInterval() const {
+ if (! cyclic) {
+ return 0;
+ } else if (finalInterval <= 0) {
+ int N = time.size();
+ if (N >= 2) {
+ return (time[1] - time[0] + time[N - 1] - time[N - 2]) * 0.5f;
+ } else {
+ return 1.0f;
+ }
+ } else {
+ return finalInterval;
+ }
+}
+
+
+Matrix4 SplineBase::computeBasis() {
+ // The standard Catmull-Rom spline basis (e.g., Watt & Watt p108)
+ // is for [u^3 u^2 u^1 u^0] * B * [p[0] p[1] p[2] p[3]]^T.
+ // We need a basis formed for:
+ //
+ // U * C * [2*p'[1] p[1] p[2] 2*p'[2]]^T
+ //
+ // U * C * [p2-p0 p1 p2 p3-p1]^T
+ //
+ // To make this transformation, compute the differences of columns in C:
+ // For [p0 p1 p2 p3]
+ Matrix4 basis =
+ Matrix4( -1, 3, -3, 1,
+ 2, -5, 4, -1,
+ -1, 0, 1, 0,
+ 0, 2, 0, 0) * 0.5f;
+
+ // For [-p0 p1 p2 p3]^T
+ basis.setColumn(0, -basis.column(0));
+
+ // For [-p0 p1 p2 p3-p1]^T
+ basis.setColumn(1, basis.column(1) + basis.column(3));
+
+ // For [p2-p0 p1 p2 p3-p1]^T
+ basis.setColumn(2, basis.column(2) - basis.column(0));
+
+ return basis;
+}
+
+
+float SplineBase::duration() const {
+ if (time.size() == 0) {
+ return 0;
+ } else {
+ return time.last() - time[0] + getFinalInterval();
+ }
+}
+
+
+void SplineBase::computeIndexInBounds(float s, int& i, float& u) const {
+ int N = time.size();
+ float t0 = time[0];
+ float tn = time[N - 1];
+
+ i = iFloor((N - 1) * (s - t0) / (tn - t0));
+
+ // Inclusive bounds for binary search
+ int hi = N - 1;
+ int lo = 0;
+
+ while ((time[i] > s) || (time[i + 1] <= s)) {
+
+ if (time[i] > s) {
+ // too big
+ hi = i - 1;
+ } else if (time[i + 1] <= s) {
+ // too small
+ lo = i + 1;
+ }
+
+ i = (hi + lo) / 2;
+ }
+
+ // Having exited the above loop, i must be correct, so compute u.
+ u = (s - time[i]) / (time[i + 1] - time[i]);
+}
+
+
+void SplineBase::computeIndex(float s, int& i, float& u) const {
+ int N = time.size();
+ debugAssertM(N > 0, "No control points");
+ float t0 = time[0];
+ float tn = time[N - 1];
+
+ if (N < 2) {
+ // No control points to work with
+ i = 0;
+ u = 0.0;
+ } else if (cyclic) {
+ float fi = getFinalInterval();
+
+ // Cyclic spline
+ if ((s < t0) || (s >= tn + fi)) {
+ // Cyclic, off the bottom or top
+
+ // Compute offset and reduce to the in-bounds case
+
+ float d = duration();
+ // Number of times we wrapped around the cyclic array
+ int wraps = iFloor((s - t0) / d);
+
+ debugAssert(s - d * wraps >= t0);
+ debugAssert(s - d * wraps < tn + getFinalInterval());
+
+ computeIndex(s - d * wraps, i, u);
+ i += wraps * N;
+
+ } else if (s >= tn) {
+ debugAssert(s < tn + fi);
+ // Cyclic, off the top but before the end of the last interval
+ i = N - 1;
+ u = (s - tn) / fi;
+
+ } else {
+ // Cyclic, in bounds
+ computeIndexInBounds(s, i, u);
+ }
+
+ } else {
+ // Non-cyclic
+
+ if (s < t0) {
+ // Non-cyclic, off the bottom. Assume points are spaced
+ // following the first time interval.
+
+ float dt = time[1] - t0;
+ float x = (s - t0) / dt;
+ i = iFloor(x);
+ u = x - i;
+
+ } else if (s >= tn) {
+ // Non-cyclic, off the top. Assume points are spaced following
+ // the last time interval.
+
+ float dt = tn - time[N - 2];
+ float x = N - 1 + (s - tn) / dt;
+ i = iFloor(x);
+ u = x - i;
+
+ } else {
+ // In bounds, non-cyclic. Assume a regular
+ // distribution (which gives O(1) for uniform spacing)
+ // and then binary search to handle the general case
+ // efficiently.
+ computeIndexInBounds(s, i, u);
+
+ } // if in bounds
+ } // if cyclic
+}
+
+}
diff --git a/externals/g3dlite/G3D.lib/source/Stopwatch.cpp b/externals/g3dlite/G3D.lib/source/Stopwatch.cpp
new file mode 100644
index 00000000000..e55f689a9e2
--- /dev/null
+++ b/externals/g3dlite/G3D.lib/source/Stopwatch.cpp
@@ -0,0 +1,96 @@
+/**
+ @file Stopwatch.cpp
+
+ @maintainer Morgan McGuire, matrix@graphics3d.com
+
+ @created 2005-10-05
+ @edited 2005-10-05
+
+ Copyright 2000-2003, Morgan McGuire.
+ All rights reserved.
+ */
+
+#include "G3D/Stopwatch.h"
+#include "G3D/System.h"
+
+namespace G3D {
+
+Stopwatch::Stopwatch() : inBetween(false), lastTockTime(-1),
+ lastDuration(0), lastCycleCount(0), m_fps(0), emwaFPS(0),
+ m_smoothFPS(0), emwaDuration(0) {
+ computeOverhead();
+}
+
+
+void Stopwatch::computeOverhead() {
+ cycleOverhead = 0;
+ tick();
+ tock();
+ cycleOverhead = elapsedCycles();
+}
+
+
+void Stopwatch::tick() {
+ // This is 'alwaysAssert' instead of 'debugAssert'
+ // since people rarely profile in debug mode.
+ alwaysAssertM(! inBetween, "Stopwatch::tick() called twice in a row.");
+ inBetween = true;
+
+ // We read RDTSC twice here, but it is more abstract to implement this
+ // way and at least we're reading the cycle count last.
+ timeStart = System::time();
+ System::beginCycleCount(cycleStart);
+}
+
+
+void Stopwatch::tock() {
+ System::endCycleCount(cycleStart);
+ RealTime now = System::time();
+ lastDuration = now - timeStart;
+ if (abs(emwaDuration - lastDuration) > max(emwaDuration, lastDuration) * 0.50) {
+ // Off by more than 50%
+ emwaDuration = lastDuration;
+ } else {
+ emwaDuration = lastDuration * 0.05 + emwaDuration * 0.95;
+ }
+
+ lastCycleCount = cycleStart - cycleOverhead;
+ if (lastCycleCount < 0) {
+ lastCycleCount = 0;
+ }
+
+ if (lastTockTime != -1.0) {
+ m_fps = 1.0 / (now - lastTockTime);
+
+ // Time smooth the average
+ emwaFPS = m_fps * 0.01 + emwaFPS * 0.99;
+
+ if (abs(emwaFPS - m_fps) > max(emwaFPS, m_fps) * 0.08) {
+ // The difference between emwa and m_fps is way off
+ // update emwa directly.
+ emwaFPS = m_fps;
+ }
+
+ // Update m_smoothFPS only when the value varies significantly
+ // We round so as to not mislead the user as to the accuracy of
+ // the number.
+ if (m_smoothFPS == 0) {
+ m_smoothFPS = m_fps;
+ } else if (emwaFPS <= 10) {
+ if (::fabs(m_smoothFPS - emwaFPS) > .75) {
+ m_smoothFPS = floor(emwaFPS * 10.0 + 0.5) / 10.0;
+ }
+ } else {
+ if (::fabs(m_smoothFPS - emwaFPS) > 1.25) {
+ m_smoothFPS = floor(emwaFPS + 0.5);
+ }
+ }
+ }
+ lastTockTime = now;
+
+ alwaysAssertM(inBetween, "Stopwatch::tock() called without matching tick.");
+ inBetween = false;
+}
+
+}
+
diff --git a/externals/g3dlite/G3D.lib/source/System.cpp b/externals/g3dlite/G3D.lib/source/System.cpp
new file mode 100644
index 00000000000..748e13257f1
--- /dev/null
+++ b/externals/g3dlite/G3D.lib/source/System.cpp
@@ -0,0 +1,1864 @@
+/**
+ @file System.cpp
+
+ @maintainer Morgan McGuire, matrix@graphics3d.com
+
+ 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.
+
+ @cite Rob Wyatt http://www.gamasutra.com/features/wyatts_world/19990709/processor_detection_01.htm
+ @cite Benjamin Jurke http://www.flipcode.com/cgi-bin/msg.cgi?showThread=COTD-ProcessorDetectionClass&forum=cotd&id=-1
+ @cite Michael Herf http://www.stereopsis.com/memcpy.html
+
+ @created 2003-01-25
+ @edited 2008-09-02
+ */
+
+#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/Log.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
+
+#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
+
+#if defined(SSE)
+ #include <xmmintrin.h>
+#endif
+
+namespace G3D {
+
+struct CpuInfo
+{
+public:
+ int m_cpuSpeed;
+ bool m_hasCPUID;
+ bool m_hasRDTSC;
+ bool m_hasMMX;
+ bool m_hasSSE;
+ bool m_hasSSE2;
+ bool m_hasSSE3;
+ bool m_has3DNOW;
+ char m_cpuVendorStr[1024];
+};
+
+// helper macro to call cpuid functions and return all values
+#ifdef _MSC_VER
+
+ // VC on Intel
+# define CALL_CPUID(func, areg, breg, creg, dreg) \
+ __asm mov eax, func \
+ __asm cpuid \
+ __asm mov areg, eax \
+ __asm mov breg, ebx \
+ __asm mov creg, ecx \
+ __asm mov dreg, edx
+
+#elif defined(__GNUC__) && defined(G3D_OSX_INTEL)
+ // GCC on OS X intel
+# define CALL_CPUID(func, areg, breg, creg, dreg) \
+ areg = 0; \
+ breg = 0; \
+ creg = 0; \
+ dreg = 0;
+#else
+ // Any other compiler/platform, likely GCC
+# define CALL_CPUID(func, areg, breg, creg, dreg) \
+ __asm__ ( \
+ "cpuid \n": \
+ "=a" (areg), \
+ "=b" (breg), \
+ "=c" (creg), \
+ "=d" (dreg): \
+ "a" (func) \
+ );
+#endif
+
+// this holds the data directory set by the application (currently GApp) for use by findDataFile
+static char g_appDataDir[FILENAME_MAX] = "";
+
+static CpuInfo g_cpuInfo = {
+ 0, false, false, false, false, false, false, false, {'U', 'n', 'k', 'n', 'o', 'w', 'n', '\0'}};
+
+static G3DEndian _machineEndian = G3D_LITTLE_ENDIAN;
+static char _cpuArchCstr[1024];
+static char _operatingSystemCstr[1024];
+
+#ifdef G3D_WIN32
+/** Used by getTick() for timing */
+static LARGE_INTEGER _start;
+static LARGE_INTEGER _counterFrequency;
+#else
+static struct timeval _start;
+#endif
+
+static char versionCstr[1024];
+System::OutOfMemoryCallback System::outOfMemoryCallback = NULL;
+
+#ifdef G3D_OSX
+long System::m_OSXCPUSpeed;
+double System::m_secondsPerNS;
+#endif
+
+/** The Real-World time of System::getTick() time 0. Set by initTime */
+static RealTime realWorldGetTickTime0;
+
+
+static unsigned int maxSupportedCPUIDLevel = 0;
+static unsigned int maxSupportedExtendedLevel = 0;
+
+/** Checks if the CPUID command is available on the processor (called from init) */
+static void checkForCPUID();
+
+/** ReadRead the standard processor extensions. Called from init(). */
+static void getStandardProcessorExtensions();
+
+/** Called from init */
+static void initTime();
+
+
+std::string System::findDataFile
+(const std::string& full,
+ bool errorIfNotFound) {
+
+ if (fileExists(full)) {
+ return full;
+ }
+
+ std::string initialAppDataDir(g_appDataDir);
+
+ std::string name = filenameBaseExt(full);
+ std::string originalPath = filenamePath(full);
+
+ // Search several paths
+ Array<std::string> pathBase;
+
+ int backlen = 4;
+
+ // add what should be the current working directory
+ pathBase.append("");
+
+ // add application specified data directory to be searched first
+ pathBase.append(initialAppDataDir);
+
+ // try walking back along the directory tree
+ std::string prev = "";
+ for (int i = 0; i < backlen; ++i) {
+ pathBase.append(originalPath + prev);
+ prev = prev + "../";
+ }
+
+ prev = "../";
+ for (int i = 0; i < backlen; ++i) {
+ pathBase.append(prev);
+ prev = prev + "../";
+ }
+
+ // Hard-code in likely install directories
+ int ver = G3D_VER;
+ std::string lname = format("G3D-%d.%02d", ver / 10000, (ver / 100) % 100);
+
+ if (G3D_VER % 100 != 0) {
+ lname = lname + format("-b%02d/", ver % 100);
+ } else {
+ lname = lname + "/";
+ }
+
+ // Look in some other likely places
+# ifdef G3D_WIN32
+ std::string lpath = "libraries/G3D/";
+ pathBase.append(std::string("c:/") + lpath);
+ pathBase.append(std::string("d:/") + lpath);
+ pathBase.append(std::string("e:/") + lpath);
+ pathBase.append(std::string("f:/") + lpath);
+ pathBase.append(std::string("g:/") + lpath);
+ pathBase.append(std::string("x:/") + lpath);
+# endif
+# if defined(G3D_LINUX)
+ pathBase.append("/usr/local/");
+ pathBase.append("/course/cs224/");
+ pathBase.append("/map/gfx0/common/games/");
+# endif
+# if defined(G3D_FREEBSD)
+ pathBase.append("/usr/local/");
+ pathBase.append("/usr/local/371/");
+ pathBase.append("/usr/cs-local/371/");
+# endif
+# if defined(G3D_OSX)
+ pathBase.append("/usr/local/" + lname);
+ pathBase.append("/Volumes/McGuire/Projects/");
+# endif
+
+ // Add the library name to all variations
+ int N = pathBase.size();
+ for (int i = 0; i < N; ++i) {
+ pathBase.append(pathBase[i] + lname);
+ pathBase.append(pathBase[i] + "G3D/");
+ }
+
+ Array<std::string> subDir;
+ subDir.append("", "font/", "sky/", "gui/");
+ subDir.append("image/", "quake2/", "quake2/players/");
+ subDir.append("quake3/", "SuperShader/", "ifs/", "3ds/");
+ subDir.append("quake2/speedway/");
+
+ Array<std::string> path;
+ for (int p = 0; p < pathBase.size(); ++p) {
+ for (int s = 0; s < subDir.size(); ++s) {
+ path.append(pathBase[p] + subDir[s]);
+ path.append(pathBase[p] + "data/" + subDir[s]);
+ path.append(pathBase[p] + "data-files/" + subDir[s]);
+ }
+ }
+
+ for (int i = 0; i < path.length(); ++i) {
+ std::string filename = path[i] + name;
+ if (fileExists(filename)) {
+ logPrintf("\nWARNING: Could not find '%s' so '%s' "
+ "was substituted.\n", full.c_str(),
+ filename.c_str());
+ return filename;
+ }
+ }
+
+ if (errorIfNotFound) {
+ // Generate an error message
+ std::string locations;
+ for (int i = 0; i < path.size(); ++i) {
+ locations += path[i] + name + "\n";
+ }
+ alwaysAssertM(false, "Could not find '" + full + "' in:\n" + locations);
+ }
+ // Not found
+ return "";
+}
+
+
+void System::setAppDataDir(const std::string& path) {
+ // just copy the path, it needs to be valid
+ strncpy(g_appDataDir, path.c_str(), sizeof(g_appDataDir));
+}
+
+
+std::string demoFindData(bool errorIfNotFound) {
+ // Directories that might contain the data
+ Array<std::string> potential;
+
+ // Look back up the directory tree
+ std::string x = "../";
+ std::string f = "";
+ for (int i = 0; i < 6; ++i) {
+ potential.append(f);
+ f = f + x;
+ }
+
+ // Hard-code in likely install directories
+ int ver = G3D_VER;
+ std::string lname = format("G3D-%d.%02d", ver / 10000, (ver / 100) % 100);
+
+ if (G3D_VER % 100 != 0) {
+ lname = lname + format("-b%02d/", ver % 100);
+ } else {
+ lname = lname + "/";
+ }
+
+ std::string lpath = "libraries/" + lname;
+ #ifdef G3D_WIN32
+ potential.append(std::string("c:/") + lpath);
+ potential.append(std::string("d:/") + lpath);
+ potential.append(std::string("e:/") + lpath);
+ potential.append(std::string("f:/") + lpath);
+ potential.append(std::string("g:/") + lpath);
+ potential.append(std::string("x:/") + lpath);
+ #elif defined(G3D_LINUX)
+ potential.append("/usr/local/" + lname);
+ potential.append("/course/cs224/");
+ potential.append("/map/gfx0/common/games/");
+ #elif defined(G3D_FREEBSD)
+ potential.append("/usr/local/" + lname);
+ potential.append("/usr/local/371/")
+ potential.append("/usr/cs-local/371/")
+ #elif defined(G3D_OSX)
+ potential.append("/usr/local/" + lname);
+ potential.append("/Volumes/McGuire/Projects/");
+ potential.append("/Volumes/McGuire/Projects/G3D/");
+ #endif
+
+ // Scan all potentials for the font directory
+ for (int p = 0; p < potential.size(); ++p) {
+ std::string path = potential[p];
+ //debugPrintf("Looking at: %sdata\n", path.c_str());
+ if (fileExists(path + "data") && fileExists(path + "data/font")) {
+ return path + "data/";
+ }
+ if (fileExists(path + "data-files") && fileExists(path + "data-files/font")) {
+ return path + "data-files/";
+ }
+ }
+
+ if (errorIfNotFound) {
+ const char* choice[] = {"Exit"};
+
+ prompt("Demo Error", "The demo could not locate the data directory. "
+ "The data is required to run this demo. If you have not downloaded "
+ "the data zipfile, get it from http://g3d-cpp.sf.net. If you have "
+ "downloaded it, it needs to be no more than 4 directories above the "
+ "demo directory.", choice, 1, true);
+ }
+
+ return "";
+}
+
+bool System::hasCPUID() {
+ init();
+ return g_cpuInfo.m_hasCPUID;
+}
+
+bool System::hasRDTSC() {
+ init();
+ return g_cpuInfo.m_hasRDTSC;
+}
+
+
+bool System::hasSSE() {
+ init();
+ return g_cpuInfo.m_hasSSE;
+}
+
+
+bool System::hasSSE2() {
+ init();
+ return g_cpuInfo.m_hasSSE2;
+}
+
+bool System::hasSSE3() {
+ init();
+ return g_cpuInfo.m_hasSSE3;
+}
+
+bool System::hasMMX() {
+ init();
+ return g_cpuInfo.m_hasMMX;
+}
+
+
+bool System::has3DNow() {
+ init();
+ return g_cpuInfo.m_has3DNOW;
+}
+
+
+const std::string& System::cpuVendor() {
+ init();
+ static const std::string _cpuVendor = g_cpuInfo.m_cpuVendorStr;
+ return _cpuVendor;
+}
+
+
+G3DEndian System::machineEndian() {
+ init();
+ return _machineEndian;
+}
+
+const std::string& System::operatingSystem() {
+ init();
+ static const std::string _operatingSystem =_operatingSystemCstr;
+ return _operatingSystem;
+}
+
+
+const std::string& System::cpuArchitecture() {
+ init();
+ static const std::string _cpuArch = _cpuArchCstr;
+ return _cpuArch;
+}
+
+const std::string& System::build() {
+ const static std::string b =
+# ifdef _DEBUG
+ "Debug";
+# else
+ "Release";
+# endif
+
+ return b;
+}
+
+const std::string& System::version() {
+ init();
+
+ static const std::string _version = versionCstr;
+ return _version;
+}
+
+
+void System::init() {
+ // Cannot use most G3D data structures or utility functions in here because
+ // they are not initialized.
+
+ static bool initialized = false;
+
+ if (initialized) {
+ return;
+ }
+
+ initialized = true;
+
+ if ((G3D_VER % 100) != 0) {
+ sprintf(versionCstr, "G3D %d.%02d beta %d",
+ G3D_VER / 10000,
+ (G3D_VER / 100) % 100,
+ G3D_VER % 100);
+ } else {
+ sprintf(versionCstr, "G3D %d.%02d",
+ G3D_VER / 10000,
+ (G3D_VER / 100) % 100);
+ }
+
+ // First of all we check if the CPUID command is available
+ checkForCPUID();
+
+ // Figure out if this machine is little or big endian.
+ {
+ int32 a = 1;
+ if (*(uint8*)&a == 1) {
+ _machineEndian = G3D_LITTLE_ENDIAN;
+ } else {
+ _machineEndian = G3D_BIG_ENDIAN;
+ }
+ }
+
+# ifdef G3D_NOT_OSX_PPC
+ // Process the CPUID information
+ if (g_cpuInfo.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;
+
+ CALL_CPUID(0x00, eaxreg, ebxreg, ecxreg, edxreg);
+
+ // Then we connect the single register values to the vendor string
+ *((unsigned int*) g_cpuInfo.m_cpuVendorStr) = ebxreg;
+ *((unsigned int*) (g_cpuInfo.m_cpuVendorStr + 4)) = edxreg;
+ *((unsigned int*) (g_cpuInfo.m_cpuVendorStr + 8)) = ecxreg;
+ g_cpuInfo.m_cpuVendorStr[12] = '\0';
+
+ // We can also read the max. supported standard CPUID level
+ maxSupportedCPUIDLevel = eaxreg & 0xFFFF;
+
+ // Then we read the ext. CPUID level 0x80000000
+ CALL_CPUID(0x80000000, eaxreg, ebxreg, ecxreg, edxreg);
+
+ // ...to check the max. supported extended CPUID level
+ maxSupportedExtendedLevel = eaxreg;
+
+ // Then we switch to the specific processor vendors.
+ // Fill out _cpuArch based on this information. It will
+ // be overwritten by the next block of code on Windows,
+ // but on Linux will stand.
+ switch (ebxreg) {
+ case 0x756E6547: // GenuineIntel
+ strcpy(_cpuArchCstr, "Intel Processor");
+ break;
+
+ case 0x68747541: // AuthenticAMD
+ strcpy(_cpuArchCstr, "AMD Processor");
+ break;
+
+ case 0x69727943: // CyrixInstead
+ strcpy(_cpuArchCstr, "Cyrix Processor");
+ break;
+
+ default:
+ strcpy(_cpuArchCstr, "Unknown Processor Vendor");
+ break;
+ }
+ }
+ #endif // G3D_NOT_OSX_PPC
+
+ #ifdef G3D_WIN32
+ bool success = RegistryUtil::readInt32
+ ("HKEY_LOCAL_MACHINE\\HARDWARE\\DESCRIPTION\\System\\CentralProcessor\\0", "~MHz", g_cpuInfo.m_cpuSpeed);
+
+ SYSTEM_INFO systemInfo;
+ GetSystemInfo(&systemInfo);
+ char* arch;
+ 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";
+ }
+
+ uint32 maxAddr = (uint32)systemInfo.lpMaximumApplicationAddress;
+ sprintf(_cpuArchCstr, "%d x %d-bit %s processor",
+ systemInfo.dwNumberOfProcessors,
+ (int)(::log((double)maxAddr) / ::log(2.0) + 2.0),
+ arch);
+ // _CPUSpeed / (1024.0 * 1024));
+
+ OSVERSIONINFO osVersionInfo;
+ osVersionInfo.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
+ success = GetVersionEx(&osVersionInfo) != 0;
+
+ if (success) {
+ sprintf(_operatingSystemCstr, "Windows %d.%d build %d Platform %d %s",
+ osVersionInfo.dwMajorVersion,
+ osVersionInfo.dwMinorVersion,
+ osVersionInfo.dwBuildNumber,
+ osVersionInfo.dwPlatformId,
+ osVersionInfo.szCSDVersion);
+ } else {
+ strcpy(_operatingSystemCstr, "Windows");
+ }
+
+ #elif defined(G3D_LINUX)
+
+ {
+ // Shell out to 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);
+
+ strcpy(_operatingSystemCstr, 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;
+
+ sprintf(_operatingSystemCstr, "OS X %x.%x.%x", major, minor, revision);
+
+ // Clock Cycle Timing Information:
+ Gestalt('pclk', &System::m_OSXCPUSpeed);
+ g_cpuInfo.m_cpuSpeed = iRound((double)m_OSXCPUSpeed / (1024 * 1024));
+ m_secondsPerNS = 1.0 / 1.0e9;
+
+ // System Architecture:
+ const NXArchInfo* pInfo = NXGetLocalArchInfo();
+
+ if (pInfo) {
+ strcpy(_cpuArchCstr, 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:
+ strcpy(g_cpuInfo.m_cpuVendorStr, "Motorola");
+ break;
+ case CPU_SUBTYPE_POWERPC_970:
+ strcpy(g_cpuInfo.m_cpuVendorStr, "IBM");
+ break;
+ }
+ break;
+
+ case CPU_TYPE_I386:
+ strcpy(g_cpuInfo.m_cpuVendorStr, "Intel");
+ break;
+ }
+ }
+ #endif
+
+ initTime();
+
+ getStandardProcessorExtensions();
+}
+
+static void checkForCPUID() {
+ unsigned int bitChanged = 0;
+
+ // We've to check if we can toggle the flag register bit 21.
+ // If we can't the processor does not support the CPUID command.
+
+#if defined(_MSC_VER)
+ __asm {
+ push eax
+ push ebx
+ pushfd
+ pushfd
+ pop eax
+ mov ebx, eax
+ xor eax, 0x00200000
+ push eax
+ popfd
+ pushfd
+ pop eax
+ popfd
+ xor eax, ebx
+ mov bitChanged, eax
+ pop ebx
+ pop eax
+ }
+#elif defined(__GNUC__) && defined(i386) && !defined(G3D_OSX_INTEL)
+ // 32-bit g++
+ __asm__ (
+ "pushfl # Get original EFLAGS \n"
+ "pushfl \n"
+ "popl %%eax \n"
+ "movl %%eax, %%ecx \n"
+ "xorl $0x200000, %%eax # Flip ID bit in EFLAGS \n"
+ "pushl %%eax # Save new EFLAGS value on stack \n"
+ "popfl # Replace current EFLAGS value \n"
+ "pushfl # Get new EFLAGS \n"
+ "popl %%eax # Store new EFLAGS in EAX \n"
+ "popfl \n"
+ "xorl %%ecx, %%eax # Can not toggle ID bit, \n"
+ "movl %%eax, %0 # We have CPUID support \n"
+ : "=m" (bitChanged)
+ : // No inputs
+ : "%eax", "%ecx"
+ );
+#elif defined(__GNUC__) && defined(__x86_64__) && !defined(G3D_OSX_INTEL)
+ // x86_64 has SSE and CPUID
+
+ bitChanged = 1;
+#else
+ // Unknown architecture
+ bitChanged = 0;
+#endif
+
+ g_cpuInfo.m_hasCPUID = ((bitChanged == 0) ? false : true);
+}
+
+void getStandardProcessorExtensions() {
+#if !defined(G3D_OSX) || defined(G3D_OSX_INTEL)
+ if (! g_cpuInfo.m_hasCPUID) {
+ return;
+ }
+
+ unsigned int eaxreg = 0, ebxreg = 0, ecxreg = 0;
+ unsigned int features = 0;
+
+ // http://www.amd.com/us-en/assets/content_type/white_papers_and_tech_docs/25481.pdf
+ // call cpuid with function 0x01 in EAX
+
+ // Invoking CPUID with '1' in EAX fills out edx with a bit string.
+ // The bits of this value indicate the presence or absence of
+ // useful processor features.
+ CALL_CPUID(0x01, eaxreg, ebxreg, ecxreg, features);
+
+ #define checkBit(var, bit) ((var & (1 << bit)) ? true : false)
+
+ g_cpuInfo.m_hasRDTSC = checkBit(features, 16);
+ g_cpuInfo.m_hasMMX = checkBit(features, 23);
+ g_cpuInfo.m_hasSSE = checkBit(features, 25);
+ g_cpuInfo.m_hasSSE2 = checkBit(features, 26);
+ g_cpuInfo.m_hasSSE3 = checkBit(ecxreg, 0);
+
+ if (maxSupportedExtendedLevel >= 0x80000001) {
+ // function 0x80000001 changes bit 31 of edx to 3dnow support flag
+ CALL_CPUID(0x80000001, eaxreg, ebxreg, ecxreg, features);
+ g_cpuInfo.m_has3DNOW = checkBit(features, 31);
+ } else {
+ g_cpuInfo.m_has3DNOW = false;
+ }
+
+ #undef checkBit
+#endif
+}
+
+#if defined(SSE)
+
+// Copy in 128 bytes chunks, where each chunk contains 8*float32x4 = 8 * 4 * 4 bytes = 128 bytes
+//
+//
+void memcpySSE2(void* dst, const void* src, int nbytes) {
+ int remainingBytes = nbytes;
+
+ if (nbytes > 128) {
+
+ // Number of chunks
+ int N = nbytes / 128;
+
+ float* restrict d = (float*)dst;
+ const float* restrict s = (const float*)src;
+
+ // Finish when the destination pointer has moved 8N elements
+ float* stop = d + (N * 8 * 4);
+
+ while (d < stop) {
+ // Inner loop unrolled 8 times
+ const __m128 r0 = _mm_loadu_ps(s);
+ const __m128 r1 = _mm_loadu_ps(s + 4);
+ const __m128 r2 = _mm_loadu_ps(s + 8);
+ const __m128 r3 = _mm_loadu_ps(s + 12);
+ const __m128 r4 = _mm_loadu_ps(s + 16);
+ const __m128 r5 = _mm_loadu_ps(s + 20);
+ const __m128 r6 = _mm_loadu_ps(s + 24);
+ const __m128 r7 = _mm_loadu_ps(s + 28);
+
+ _mm_storeu_ps(d, r0);
+ _mm_storeu_ps(d + 4, r1);
+ _mm_storeu_ps(d + 8, r2);
+ _mm_storeu_ps(d + 12, r3);
+ _mm_storeu_ps(d + 16, r4);
+ _mm_storeu_ps(d + 20, r5);
+ _mm_storeu_ps(d + 24, r6);
+ _mm_storeu_ps(d + 28, r7);
+
+ s += 32;
+ d += 32;
+ }
+
+ remainingBytes -= N * 8 * 4 * 4;
+ }
+
+ if (remainingBytes > 0) {
+ // Memcpy the rest
+ memcpy((uint8*)dst + (nbytes - remainingBytes), (const uint8*)src + (nbytes - remainingBytes), remainingBytes);
+ }
+}
+#else
+
+ // Fall back to memcpy
+ void memcpySSE2(void *dst, const void *src, int nbytes) {
+ memcpy(dst, src, nbytes);
+ }
+
+#endif
+
+#if defined(G3D_WIN32) && defined(SSE)
+/** 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);
+ }
+}
+
+#else
+ // Fall back to memcpy
+ void memcpyMMX(void *dst, const void *src, int nbytes) {
+ memcpy(dst, src, nbytes);
+ }
+
+#endif
+
+
+void System::memcpy(void* dst, const void* src, size_t numBytes) {
+ if (System::hasSSE2() && System::hasMMX()) {
+ G3D::memcpyMMX(dst, src, numBytes);
+ } else if (System::hasSSE() && System::hasMMX()) {
+ G3D::memcpyMMX(dst, src, numBytes);
+ } else {
+ ::memcpy(dst, src, numBytes);
+ }
+}
+
+
+/** Michael Herf's fastest memset. n32 must be filled with the same
+ character repeated. */
+#if defined(G3D_WIN32) && defined(SSE)
+
+// 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);
+ }
+}
+
+#else
+
+// For non x86 processors, we fall back to the standard memset
+void memfill(void *dst, int n32, unsigned long i) {
+ ::memset(dst, n32, i);
+}
+
+#endif
+
+
+void System::memset(void* dst, uint8 value, size_t numBytes) {
+ if (System::hasSSE() && System::hasMMX()) {
+ uint32 v = value;
+ v = v + (v << 8) + (v << 16) + (v << 24);
+ G3D::memfill(dst, v, numBytes);
+ } else {
+ ::memset(dst, value, numBytes);
+ }
+}
+
+
+std::string& System::appName() {
+ static std::string n = filenameBase(currentProgramFilename());
+ return n;
+}
+
+
+std::string System::currentProgramFilename() {
+ char filename[2048];
+
+ #ifdef G3D_WIN32
+ {
+ GetModuleFileNameA(NULL, filename, sizeof(filename));
+ }
+ #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.
+ static const RealTime OVERHEAD = .000006;
+
+ RealTime now = time();
+ RealTime wakeupTime = now + t - OVERHEAD;
+
+ RealTime remainingTime = wakeupTime - now;
+ RealTime sleepTime = 0;
+
+ while (remainingTime > 0) {
+
+
+ if (remainingTime > 0.001) {
+ // Safe to use Sleep with a time... sleep for half the remaining time
+ sleepTime = max(remainingTime * .5, 0.0005);
+ } else if (remainingTime > 0.0001) {
+ // 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 initTime() {
+ #ifdef G3D_WIN32
+ if (QueryPerformanceFrequency(&_counterFrequency)) {
+ QueryPerformanceCounter(&_start);
+ }
+
+ struct _timeb t;
+ _ftime(&t);
+
+ realWorldGetTickTime0 = (RealTime)t.time - t.timezone * G3D::MINUTE + (t.dstflag ? G3D::HOUR : 0);
+
+ #else
+ gettimeofday(&_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;
+ }
+
+ realWorldGetTickTime0 = local;
+ #endif
+}
+
+
+RealTime System::time() {
+ init();
+ #ifdef G3D_WIN32
+ LARGE_INTEGER now;
+ QueryPerformanceCounter(&now);
+
+ return ((RealTime)(now.QuadPart - _start.QuadPart) /
+ _counterFrequency.QuadPart) + 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 - _start.tv_sec) +
+ (now.tv_usec - _start.tv_usec) / 1e6
+ + 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.
+ 128000 * 128 = 16 MB (preallocated)
+ 2048 * 1024 = 2 MB (allocated on demand)
+ 1024 * 4096 = 4 MB (allocated on demand)
+ */
+ enum {maxTinyBuffers = 128000, maxSmallBuffers = 2048, 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;
+
+# 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
+ }
+
+ /**
+ 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;
+
+# ifdef G3D_WIN32
+ InitializeCriticalSection(&mutex);
+# else
+ pthread_mutex_init(&mutex, NULL);
+# endif
+ }
+
+
+ ~BufferPool() {
+ ::free(tinyHeap);
+# ifdef G3D_WIN32
+ DeleteCriticalSection(&mutex);
+# else
+ // No destruction on pthreads
+# endif
+ }
+
+
+ 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", 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());
+ 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();
+}
+
+
+int System::cpuSpeedMHz() {
+ return g_cpuInfo.m_cpuSpeed;
+}
+
+
+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);
+}
+
+
+} // namespace
diff --git a/externals/g3dlite/G3D.lib/source/TextInput.cpp b/externals/g3dlite/G3D.lib/source/TextInput.cpp
new file mode 100644
index 00000000000..d5dc14fb6a0
--- /dev/null
+++ b/externals/g3dlite/G3D.lib/source/TextInput.cpp
@@ -0,0 +1,988 @@
+/**
+ @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 {
+
+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 ()
+ : cComments(true), cppComments(true), escapeSequencesInStrings(true),
+ otherCommentCharacter('\0'), otherCommentCharacter2('\0'),
+ signedNumbers(true), singleQuotedStrings(true), 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 >= (unsigned int)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.
+ //
+ // We update even for CR because the user is allowed to do arbitrarily
+ // stupid things, like put a bunch of literal CRs inside a quoted string.
+ //
+ // We eat all whitespace between tokens, so they should never see a
+ // lineNumber that points to a CR. However, if they have some kind of
+ // syntax error in a token that appears *after* a quoted string
+ // containing CRs, they should get a correct character number.
+
+ // TODO: http://sourceforge.net/tracker/index.php?func=detail&aid=1341266&group_id=76879&atid=548565
+
+ if (c == '\n') {
+ ++lineNumber;
+ charNumber = 1;
+ } else {
+ ++charNumber;
+ }
+
+ return c;
+}
+
+int TextInput::peekInputChar(unsigned int distance) {
+ // Don't go off the end
+ if ((currentCharOffset + distance) >= (unsigned int)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;
+ }
+
+ bool whitespaceDone = false;
+ while (! whitespaceDone) {
+ whitespaceDone = true;
+
+ // Consume whitespace
+ while (isWhiteSpace(c)) {
+ c = eatAndPeekInputChar();
+ }
+
+ int c2 = peekInputChar(1);
+ if ((options.cppComments && c == '/' && c2 == '/')
+ || (options.otherCommentCharacter != '\0'
+ && c == options.otherCommentCharacter)
+ || (options.otherCommentCharacter2 != '\0'
+ && c == options.otherCommentCharacter2)) {
+
+ // Single line comment, consume to newline or EOF.
+
+ do {
+ c = eatAndPeekInputChar();
+ } while (! isNewline(c) && c != EOF);
+
+ // 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.cComments && (c == '/') && (c2 == '*')) {
+
+ // consume both start-comment chars, can't let the trailing one
+ // help close the comment.
+ eatInputChar();
+ eatInputChar();
+
+ // Multi-line comment, consume to end-marker or EOF.
+ c = peekInputChar();
+ c2 = peekInputChar(1);
+ while (! ((c == '*') && (c2 == '/')) && (c != EOF)) {
+ eatInputChar();
+ c = c2;
+ c2 = peekInputChar(1);
+ }
+ eatInputChar(); // eat closing '*'
+ eatInputChar(); // eat closing '/'
+
+ c = peekInputChar();
+
+ // May be whitespace after comment.
+ 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]) 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();
+ }
+ }
+ }
+ 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 == '\'') {
+
+ // Discard the single-quote.
+ eatInputChar();
+
+ if (options.singleQuotedStrings) {
+ // Single quoted string
+ parseQuotedString('\'', 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 == '\'') {
+ 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 '\"':
+ case '\'':
+ t._string += (char)c;
+ break;
+
+ default:
+ 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::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) : sourceFile(src), line(ln), character(ch) {
+
+ message = format("%s(%d) : ", sourceFile.c_str(), line);
+}
+
+///////////////////////////////////////////////////////////////////////////////////
+
+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/G3D.lib/source/TextOutput.cpp b/externals/g3dlite/G3D.lib/source/TextOutput.cpp
new file mode 100644
index 00000000000..f7a40d9798b
--- /dev/null
+++ b/externals/g3dlite/G3D.lib/source/TextOutput.cpp
@@ -0,0 +1,452 @@
+/**
+ @file TextOutput.cpp
+
+ @maintainer Morgan McGuire, morgan@graphics3d.com
+ @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");
+ debugAssert(f);
+ 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/G3D.lib/source/ThreadSet.cpp b/externals/g3dlite/G3D.lib/source/ThreadSet.cpp
new file mode 100644
index 00000000000..59060892247
--- /dev/null
+++ b/externals/g3dlite/G3D.lib/source/ThreadSet.cpp
@@ -0,0 +1,147 @@
+#include "G3D/ThreadSet.h"
+
+namespace G3D {
+
+int ThreadSet::size() const {
+ ThreadSet* me = const_cast<ThreadSet*>(this);
+ me->m_lock.lock();
+ int s = m_thread.size();
+ me->m_lock.unlock();
+ return s;
+}
+
+
+int ThreadSet::numStarted() const {
+ ThreadSet* me = const_cast<ThreadSet*>(this);
+ me->m_lock.lock();
+ int count = 0;
+ for (int i = 0; i < m_thread.size(); ++i) {
+ if (m_thread[i]->started()) {
+ ++count;
+ }
+ }
+ me->m_lock.unlock();
+ return count;
+}
+
+
+void ThreadSet::start() const {
+ ThreadSet* me = const_cast<ThreadSet*>(this);
+ me->m_lock.lock();
+ for (int i = 0; i < m_thread.size(); ++i) {
+ if (! m_thread[i]->started()) {
+ m_thread[i]->start();
+ }
+ }
+ me->m_lock.unlock();
+}
+
+
+void ThreadSet::terminate() const {
+ ThreadSet* me = const_cast<ThreadSet*>(this);
+ me->m_lock.lock();
+ for (int i = 0; i < m_thread.size(); ++i) {
+ if (m_thread[i]->started()) {
+ m_thread[i]->terminate();
+ }
+ }
+ me->m_lock.unlock();
+}
+
+
+void ThreadSet::waitForCompletion() const {
+ ThreadSet* me = const_cast<ThreadSet*>(this);
+ me->m_lock.lock();
+ for (int i = 0; i < m_thread.size(); ++i) {
+ if (m_thread[i]->started()) {
+ m_thread[i]->waitForCompletion();
+ }
+ }
+ me->m_lock.unlock();
+}
+
+
+int ThreadSet::removeCompleted() {
+ m_lock.lock();
+ for (int i = 0; i < m_thread.size(); ++i) {
+ if (m_thread[i]->completed()) {
+ m_thread.fastRemove(i);
+ --i;
+ }
+ }
+
+ int s = m_thread.size();
+ m_lock.unlock();
+ return s;
+}
+
+
+void ThreadSet::clear() {
+ m_lock.lock();
+ m_thread.clear();
+ m_lock.unlock();
+}
+
+
+int ThreadSet::insert(const ThreadRef& t) {
+ m_lock.lock();
+ bool found = false;
+ for (int i = 0; i < m_thread.size() && ! found; ++i) {
+ found = (m_thread[i] == t);
+ }
+ if (! found) {
+ m_thread.append(t);
+ }
+ int s = m_thread.size();
+ m_lock.unlock();
+ return s;
+}
+
+
+bool ThreadSet::remove(const ThreadRef& t) {
+ m_lock.lock();
+ bool found = false;
+ for (int i = 0; i < m_thread.size() && ! found; ++i) {
+ found = (m_thread[i] == t);
+ if (found) {
+ m_thread.fastRemove(i);
+ }
+ }
+ m_lock.unlock();
+ return found;
+}
+
+
+bool ThreadSet::contains(const ThreadRef& t) const {
+ ThreadSet* me = const_cast<ThreadSet*>(this);
+ me->m_lock.lock();
+ bool found = false;
+ for (int i = 0; i < m_thread.size() && ! found; ++i) {
+ found = (m_thread[i] == t);
+ }
+ me->m_lock.unlock();
+ return found;
+}
+
+
+ThreadSet::Iterator ThreadSet::begin() {
+ return m_thread.begin();
+}
+
+
+ThreadSet::Iterator ThreadSet::end() {
+ return m_thread.end();
+}
+
+
+ThreadSet::ConstIterator ThreadSet::begin() const {
+ return m_thread.begin();
+}
+
+
+ThreadSet::ConstIterator ThreadSet::end() const {
+ return m_thread.end();
+}
+
+
+} // namespace G3D
diff --git a/externals/g3dlite/G3D.lib/source/Triangle.cpp b/externals/g3dlite/G3D.lib/source/Triangle.cpp
new file mode 100644
index 00000000000..ad264b1f72a
--- /dev/null
+++ b/externals/g3dlite/G3D.lib/source/Triangle.cpp
@@ -0,0 +1,135 @@
+/**
+ @file Triangle.cpp
+
+ @maintainer Morgan McGuire, graphics3d.com
+
+ @created 2001-04-06
+ @edited 2006-01-20
+
+ Copyright 2000-2006, 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"
+
+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);
+}
+
+} // G3D
diff --git a/externals/g3dlite/G3D.lib/source/UprightFrame.cpp b/externals/g3dlite/G3D.lib/source/UprightFrame.cpp
new file mode 100644
index 00000000000..78b2c0bb0bb
--- /dev/null
+++ b/externals/g3dlite/G3D.lib/source/UprightFrame.cpp
@@ -0,0 +1,132 @@
+/**
+ @file UprightFrame.cpp
+ Box class
+
+ @maintainer Morgan McGuire, matrix@graphics3d.com
+
+ @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/G3D.lib/source/Vector2.cpp b/externals/g3dlite/G3D.lib/source/Vector2.cpp
new file mode 100644
index 00000000000..6b7f96a764e
--- /dev/null
+++ b/externals/g3dlite/G3D.lib/source/Vector2.cpp
@@ -0,0 +1,211 @@
+/**
+ @file Vector2.cpp
+
+ 2D vector class, used for texture coordinates primarily.
+
+ @maintainer Morgan McGuire, matrix@graphics3d.com
+
+ @cite Portions based on Dave Eberly'x Magic Software Library
+ at http://www.magic-software.com
+
+ @created 2001-06-02
+ @edited 2006-01-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"
+
+namespace G3D {
+
+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::inf(), (float)G3D::inf());
+ return v;
+}
+
+
+const Vector2& Vector2::nan() {
+ static Vector2 v((float)G3D::nan(), (float)G3D::nan());
+ 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() {
+ Vector2 result;
+
+ do {
+ result = Vector2(uniformRandom(-1, 1), uniformRandom(-1, 1));
+
+ } while (result.squaredLength() >= 1.0f);
+
+ result.unitize();
+
+ return result;
+}
+
+//----------------------------------------------------------------------------
+Vector2 Vector2::operator/ (float fScalar) const {
+ Vector2 kQuot;
+
+ if ( fScalar != 0.0f ) {
+ float fInvScalar = 1.0f / fScalar;
+ kQuot.x = fInvScalar * x;
+ kQuot.y = fInvScalar * y;
+ return kQuot;
+ } else {
+ return Vector2::inf();
+ }
+}
+
+//----------------------------------------------------------------------------
+Vector2& Vector2::operator/= (float fScalar) {
+ if (fScalar != 0.0f) {
+ float fInvScalar = 1.0f / fScalar;
+ x *= fInvScalar;
+ y *= fInvScalar;
+ } else {
+ x = (float)G3D::inf();
+ y = (float)G3D::inf();
+ }
+
+ 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/G3D.lib/source/Vector2int16.cpp b/externals/g3dlite/G3D.lib/source/Vector2int16.cpp
new file mode 100644
index 00000000000..04efee2b4b7
--- /dev/null
+++ b/externals/g3dlite/G3D.lib/source/Vector2int16.cpp
@@ -0,0 +1,47 @@
+/**
+ @file Vector2int16.cpp
+
+ @author Morgan McGuire, matrix@graphics3d.com
+
+ @created 2003-08-09
+ @edited 2006-01-29
+ */
+
+#include "G3D/platform.h"
+#include "G3D/g3dmath.h"
+#include "G3D/Vector2int16.h"
+#include "G3D/Vector2.h"
+#include "G3D/BinaryInput.h"
+#include "G3D/BinaryOutput.h"
+
+namespace G3D {
+
+Vector2int16::Vector2int16(const class Vector2& v) {
+ x = (int16)iFloor(v.x + 0.5);
+ y = (int16)iFloor(v.y + 0.5);
+}
+
+
+Vector2int16::Vector2int16(class BinaryInput& bi) {
+ deserialize(bi);
+}
+
+
+void Vector2int16::serialize(class BinaryOutput& bo) const {
+ bo.writeInt16(x);
+ bo.writeInt16(y);
+}
+
+
+void Vector2int16::deserialize(class BinaryInput& bi) {
+ x = bi.readInt16();
+ y = bi.readInt16();
+}
+
+
+Vector2int16 Vector2int16::clamp(const Vector2int16& lo, const Vector2int16& hi) {
+ return Vector2int16(iClamp(x, lo.x, hi.x), iClamp(y, lo.y, hi.y));
+}
+
+
+}
diff --git a/externals/g3dlite/G3D.lib/source/Vector3.cpp b/externals/g3dlite/G3D.lib/source/Vector3.cpp
new file mode 100644
index 00000000000..bc52640d297
--- /dev/null
+++ b/externals/g3dlite/G3D.lib/source/Vector3.cpp
@@ -0,0 +1,493 @@
+/**
+ @file Vector3.cpp
+
+ 3D vector class
+
+ @maintainer Morgan McGuire, matrix@graphics3d.com
+
+ @cite Portions based on Dave Eberly's Magic Software Library at http://www.magic-software.com
+
+ @created 2001-06-02
+ @edited 2006-01-30
+ */
+
+#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/Vector4int8.h"
+
+namespace G3D {
+
+Vector3 Vector3::dummy;
+
+
+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::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() {
+ Vector3 result;
+
+ do {
+ result = Vector3(uniformRandom(-1.0, 1.0),
+ uniformRandom(-1.0, 1.0),
+ uniformRandom(-1.0, 1.0));
+ } while (result.squaredMagnitude() >= 1.0f);
+
+ result.unitize();
+
+ return result;
+}
+
+//----------------------------------------------------------------------------
+Vector3 Vector3::operator/ (float fScalar) const {
+ Vector3 kQuot;
+
+ if ( fScalar != 0.0 ) {
+ float fInvScalar = 1.0f / fScalar;
+ kQuot.x = fInvScalar * x;
+ kQuot.y = fInvScalar * y;
+ kQuot.z = fInvScalar * z;
+ return kQuot;
+ } else {
+ return Vector3::inf();
+ }
+}
+
+//----------------------------------------------------------------------------
+Vector3& Vector3::operator/= (float fScalar) {
+ if (fScalar != 0.0) {
+ float fInvScalar = 1.0f / fScalar;
+ x *= fInvScalar;
+ y *= fInvScalar;
+ z *= fInvScalar;
+ } else {
+ x = (float)G3D::inf();
+ y = (float)G3D::inf();
+ z = (float)G3D::inf();
+ }
+
+ return *this;
+}
+
+//----------------------------------------------------------------------------
+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::cosRandom(const Vector3& normal) {
+ double e1 = G3D::uniformRandom(0, 1);
+ double e2 = G3D::uniformRandom(0, 1);
+
+ // Angle from normal
+ double theta = acos(sqrt(e1));
+
+ // Angle about normal
+ double phi = 2 * pi() * e2;
+
+ // Make a coordinate system
+ Vector3 U = normal.direction();
+ Vector3 V = Vector3::unitX();
+
+ if (abs(U.dot(V)) > .9) {
+ V = Vector3::unitY();
+ }
+
+ Vector3 W = U.cross(V).direction();
+ V = W.cross(U);
+
+ // Convert to rectangular form
+ return cos(theta) * U + sin(theta) * (cos(phi) * V + sin(phi) * W);
+}
+//----------------------------------------------------------------------------
+
+Vector3 Vector3::hemiRandom(const Vector3& normal) {
+ Vector3 V = Vector3::random();
+
+ 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/G3D.lib/source/Vector3int16.cpp b/externals/g3dlite/G3D.lib/source/Vector3int16.cpp
new file mode 100644
index 00000000000..8d8bb00bbef
--- /dev/null
+++ b/externals/g3dlite/G3D.lib/source/Vector3int16.cpp
@@ -0,0 +1,49 @@
+/**
+ @file Vector3int16.cpp
+
+ @author Morgan McGuire, matrix@graphics3d.com
+
+ @created 2003-04-07
+ @edited 2006-01-17
+ */
+
+#include "G3D/platform.h"
+#include "G3D/g3dmath.h"
+#include "G3D/Vector3int16.h"
+#include "G3D/Vector3.h"
+#include "G3D/BinaryInput.h"
+#include "G3D/BinaryOutput.h"
+#include "G3D/format.h"
+
+namespace G3D {
+
+Vector3int16::Vector3int16(const class Vector3& v) {
+ x = (int16)iFloor(v.x + 0.5);
+ y = (int16)iFloor(v.y + 0.5);
+ z = (int16)iFloor(v.z + 0.5);
+}
+
+
+Vector3int16::Vector3int16(class BinaryInput& bi) {
+ deserialize(bi);
+}
+
+
+void Vector3int16::serialize(class BinaryOutput& bo) const {
+ bo.writeInt16(x);
+ bo.writeInt16(y);
+ bo.writeInt16(z);
+}
+
+
+void Vector3int16::deserialize(class BinaryInput& bi) {
+ x = bi.readInt16();
+ y = bi.readInt16();
+ z = bi.readInt16();
+}
+
+std::string Vector3int16::toString() const {
+ return G3D::format("(%d, %d, %d)", x, y, z);
+}
+
+}
diff --git a/externals/g3dlite/G3D.lib/source/Vector3int32.cpp b/externals/g3dlite/G3D.lib/source/Vector3int32.cpp
new file mode 100644
index 00000000000..1328ba1aba5
--- /dev/null
+++ b/externals/g3dlite/G3D.lib/source/Vector3int32.cpp
@@ -0,0 +1,57 @@
+/**
+ @file Vector3int32.cpp
+
+ @author Morgan McGuire, matrix@graphics3d.com
+
+ @created 2008-07-01
+ @edited 2008-07-01
+ */
+
+#include "G3D/platform.h"
+#include "G3D/g3dmath.h"
+#include "G3D/Vector3int32.h"
+#include "G3D/Vector3int16.h"
+#include "G3D/Vector3.h"
+#include "G3D/BinaryInput.h"
+#include "G3D/BinaryOutput.h"
+#include "G3D/format.h"
+
+namespace G3D {
+
+Vector3int32::Vector3int32(const class Vector3& v) {
+ x = (int32)iFloor(v.x + 0.5);
+ y = (int32)iFloor(v.y + 0.5);
+ z = (int32)iFloor(v.z + 0.5);
+}
+
+
+Vector3int32::Vector3int32(const class Vector3int16& v) {
+ x = v.x;
+ y = v.y;
+ z = v.z;
+}
+
+
+Vector3int32::Vector3int32(class BinaryInput& bi) {
+ deserialize(bi);
+}
+
+
+void Vector3int32::serialize(class BinaryOutput& bo) const {
+ bo.writeInt32(x);
+ bo.writeInt32(y);
+ bo.writeInt32(z);
+}
+
+
+void Vector3int32::deserialize(class BinaryInput& bi) {
+ x = bi.readInt32();
+ y = bi.readInt32();
+ z = bi.readInt32();
+}
+
+std::string Vector3int32::toString() const {
+ return G3D::format("(%d, %d, %d)", x, y, z);
+}
+
+}
diff --git a/externals/g3dlite/G3D.lib/source/Vector4.cpp b/externals/g3dlite/G3D.lib/source/Vector4.cpp
new file mode 100644
index 00000000000..f562f2d6877
--- /dev/null
+++ b/externals/g3dlite/G3D.lib/source/Vector4.cpp
@@ -0,0 +1,475 @@
+/**
+ @file Vector4.cpp
+
+ @maintainer Morgan McGuire, matrix@graphics3d.com
+
+ @created 2001-07-09
+ @edited 2007-02-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"
+
+namespace G3D {
+
+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) {
+}
+
+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/G3D.lib/source/Vector4int8.cpp b/externals/g3dlite/G3D.lib/source/Vector4int8.cpp
new file mode 100644
index 00000000000..29ca0d678fc
--- /dev/null
+++ b/externals/g3dlite/G3D.lib/source/Vector4int8.cpp
@@ -0,0 +1,58 @@
+/**
+ @file Vector4int8.cpp
+
+ Homogeneous vector class.
+
+ @maintainer Morgan McGuire, matrix@graphics3d.com
+
+ @created 2007-02-09
+ @edited 2007-02-09
+
+ Copyright 2000-2007, Morgan McGuire.
+ All rights reserved.
+ */
+
+#include "G3D/platform.h"
+#include "G3D/Vector4int8.h"
+#include "G3D/Vector3.h"
+#include "G3D/Vector4.h"
+#include "G3D/BinaryInput.h"
+#include "G3D/BinaryOutput.h"
+#include <string>
+
+namespace G3D {
+
+Vector4int8::Vector4int8(const Vector4& source) {
+ x = iClamp(iRound(source.x), -128, 127);
+ y = iClamp(iRound(source.y), -128, 127);
+ z = iClamp(iRound(source.z), -128, 127);
+ w = iClamp(iRound(source.w), -128, 127);
+}
+
+Vector4int8::Vector4int8(const Vector3& source, int8 w) : w(w) {
+ x = iClamp(iRound(source.x), -128, 127);
+ y = iClamp(iRound(source.y), -128, 127);
+ z = iClamp(iRound(source.z), -128, 127);
+}
+
+Vector4int8::Vector4int8(class BinaryInput& b) {
+ deserialize(b);
+}
+
+void Vector4int8::serialize(class BinaryOutput& b) const {
+ // Intentionally write individual bytes to avoid endian issues
+ b.writeInt8(x);
+ b.writeInt8(y);
+ b.writeInt8(z);
+ b.writeInt8(w);
+}
+
+void Vector4int8::deserialize(class BinaryInput& b) {
+ x = b.readInt8();
+ y = b.readInt8();
+ z = b.readInt8();
+ w = b.readInt8();
+}
+
+} // namespace G3D
+
diff --git a/externals/g3dlite/G3D.lib/source/WinMain.cpp b/externals/g3dlite/G3D.lib/source/WinMain.cpp
new file mode 100644
index 00000000000..94653de4d43
--- /dev/null
+++ b/externals/g3dlite/G3D.lib/source/WinMain.cpp
@@ -0,0 +1,155 @@
+/*
+ Dervied from SDL_main.c, which was placed in the public domain by Sam Lantinga 4/13/98
+
+ The WinMain function -- calls your program's main() function
+*/
+
+#include "G3D/platform.h"
+
+#ifdef G3D_WIN32
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <cctype>
+
+#ifdef main
+# ifndef _WIN32_WCE_EMULATION
+# undef main
+# endif /* _WIN32_WCE_EMULATION */
+#endif /* main */
+
+#if defined(_WIN32_WCE) && _WIN32_WCE < 300
+/* seems to be undefined in Win CE although in online help */
+#define isspace(a) (((CHAR)a == ' ') || ((CHAR)a == '\t'))
+#endif /* _WIN32_WCE < 300 */
+
+// Turn off the G3D for loop scoping for C++
+#ifdef for
+# undef for
+#endif
+
+extern int main(int argc, const char** argv);
+
+/* Parse a command line buffer into arguments */
+static int ParseCommandLine(char *cmdline, char **argv) {
+ char *bufp;
+ int argc;
+
+ argc = 0;
+ for (bufp = cmdline; *bufp;) {
+ /* Skip leading whitespace */
+ while (isspace(*bufp)) {
+ ++bufp;
+ }
+ /* Skip over argument */
+ if (*bufp == '"') {
+ ++bufp;
+ if (*bufp) {
+ if (argv) {
+ argv[argc] = bufp;
+ }
+ ++argc;
+ }
+ /* Skip over word */
+ while (*bufp && (*bufp != '"')) {
+ ++bufp;
+ }
+ } else {
+ if (*bufp) {
+ if (argv) {
+ argv[argc] = bufp;
+ }
+ ++argc;
+ }
+ /* Skip over word */
+ while (*bufp && !isspace(*bufp)) {
+ ++bufp;
+ }
+ }
+ if (*bufp) {
+ if (argv) {
+ *bufp = '\0';
+ }
+ ++bufp;
+ }
+ }
+ if (argv) {
+ argv[argc] = NULL;
+ }
+ return (argc);
+}
+
+/* Show an error message */
+static void ShowError(const char *title, const char *message) {
+/* If USE_MESSAGEBOX is defined, you need to link with user32.lib */
+#ifdef USE_MESSAGEBOX
+ MessageBox(NULL, message, title, MB_ICONEXCLAMATION | MB_OK);
+#else
+ fprintf(stderr, "%s: %s\n", title, message);
+#endif
+}
+
+/* Pop up an out of memory message, returns to Windows */
+static BOOL OutOfMemory(void) {
+ ShowError("Fatal Error", "Out of memory - aborting");
+ return FALSE;
+}
+
+
+int WINAPI G3D_WinMain(HINSTANCE hInst, HINSTANCE hPrev, LPSTR szCmdLine, int sw) {
+ char **argv;
+ int argc;
+ int status;
+ char *cmdline;
+# ifdef _WIN32_WCE
+ wchar_t *bufp;
+ int nLen;
+# else
+ char *bufp;
+ size_t nLen;
+# endif
+
+#ifdef _WIN32_WCE
+#error WinCE not supported
+ /*
+ nLen = wcslen(szCmdLine) + 128 + 1;
+ bufp = SDL_stack_alloc(wchar_t, nLen * 2);
+ wcscpy(bufp, TEXT("\""));
+ GetModuleFileName(NULL, bufp + 1, 128 - 3);
+ wcscpy(bufp + wcslen(bufp), TEXT("\" "));
+ wcsncpy(bufp + wcslen(bufp), szCmdLine, nLen - wcslen(bufp));
+ nLen = wcslen(bufp) + 1;
+ cmdline = SDL_stack_alloc(char, nLen);
+ if (cmdline == NULL) {
+ return OutOfMemory();
+ }
+ WideCharToMultiByte(CP_ACP, 0, bufp, -1, cmdline, nLen, NULL, NULL);
+ */
+#else
+ /* Grab the command line */
+ bufp = GetCommandLineA();
+ nLen = strlen(bufp) + 1;
+ cmdline = (char*)malloc(sizeof(char) * nLen);
+ if (cmdline == NULL) {
+ return OutOfMemory();
+ }
+ strncpy(cmdline, bufp, nLen);
+#endif
+
+ /* Parse it into argv and argc */
+ argc = ParseCommandLine(cmdline, NULL);
+ argv = (char**)malloc(sizeof(char*) * (argc + 1));
+ if (argv == NULL) {
+ return OutOfMemory();
+ }
+ ParseCommandLine(cmdline, argv);
+
+ /* Run the main program */
+ status = main(argc, (const char**)argv);
+ free(argv);
+ free(cmdline);
+
+ return status;
+}
+
+#endif // if Win32
diff --git a/externals/g3dlite/G3D.lib/source/debugAssert.cpp b/externals/g3dlite/G3D.lib/source/debugAssert.cpp
new file mode 100644
index 00000000000..4b24546a661
--- /dev/null
+++ b/externals/g3dlite/G3D.lib/source/debugAssert.cpp
@@ -0,0 +1,392 @@
+/**
+ @file debugAssert.cpp
+
+ Windows implementation of assertion routines.
+
+ @maintainer Morgan McGuire, graphics3d.com
+
+ @created 2001-08-26
+ @edited 2006-02-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
+ Display* x11Display = NULL;
+ Window x11Window = 0;
+#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& ignoreAlways,
+ 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 cIgnoreAlways = 2;
+ const int cAbort = 3;
+
+ static const char* choices[] = {"Debug", "Ignore", "Ignore Always", "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, 4, 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 cIgnoreAlways:
+ ignoreAlways = true;
+ 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& ignoreAlways,
+ bool useGuiPrompt) {
+
+ (void)ignoreAlways;
+
+ 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();
+ postToClipboard(dialogText.c_str());
+ debugPrintf("\n%s\n", dialogText.c_str());
+ #endif
+
+ static const char* choices[] = {"Ok"};
+
+ std::string m =
+ std::string("An internal error has occured in your program and it will now close. Details about the error have been reported in \"") +
+ Log::getCommonLogFilename() + "\".";
+
+ 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 (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);
+ }
+ #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(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/G3D.lib/source/fileutils.cpp b/externals/g3dlite/G3D.lib/source/fileutils.cpp
new file mode 100644
index 00000000000..3a5e8c715c8
--- /dev/null
+++ b/externals/g3dlite/G3D.lib/source/fileutils.cpp
@@ -0,0 +1,1092 @@
+/**
+ @file fileutils.cpp
+
+ @author Morgan McGuire, graphics3d.com
+
+ @author 2002-06-06
+ @edited 2008-01-20
+ */
+
+#include "G3D/platform.h"
+#include "G3D/fileutils.h"
+#include "G3D/BinaryInput.h"
+#include "G3D/g3dmath.h"
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <zip/zip.h>
+#include <zip/unzip.h>
+#include "G3D/stringutils.h"
+#include "G3D/Set.h"
+
+#include <cstring>
+
+#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
+#endif
+#include <stdio.h>
+#include "G3D/BinaryOutput.h"
+
+#ifdef G3D_WIN32
+ //for _mkdir and _stat
+# include <direct.h>
+#else
+# 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 (zipfileExists(file, zip, desiredFile)) {
+ unzFile f = unzOpen(zip.c_str());
+ {
+ unzLocateFile(f, desiredFile.c_str(), 2);
+ unz_file_info info;
+ unzGetCurrentFileInfo(f, &info, NULL, 0, NULL, 0, NULL, 0);
+ length = info.uncompressed_size;
+ // sets machines up to use MMX, if they want
+ data = System::alignedMalloc(length, 16);
+ unzOpenCurrentFile(f);
+ {
+ int test = unzReadCurrentFile(f, data, length);
+ debugAssertM((size_t)test == length,
+ desiredFile + " was corrupt because it unzipped to the wrong size.");
+ (void)test;
+ }
+ unzCloseCurrentFile(f);
+ }
+ unzClose(f);
+ } else {
+ data = NULL;
+ }
+}
+
+
+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) {
+ std::string zip, contents;
+ if(zipfileExists(filename, zip, contents)){
+ int64 requiredMem;
+
+ unzFile f = unzOpen(zip.c_str());
+ {
+ unzLocateFile(f, contents.c_str(), 2);
+ unz_file_info info;
+ unzGetCurrentFileInfo(f, &info, NULL, 0, NULL, 0, NULL, 0);
+ requiredMem = info.uncompressed_size;
+ }
+ unzClose(f);
+ return requiredMem;
+ } else {
+ return -1;
+ }
+ }
+
+ return st.st_size;
+}
+
+/** Used by robustTmpfile. Returns nonzero if fread, fwrite, and fseek all
+succeed on the file.
+ @author Morgan McGuire, morgan@graphics3d.com */
+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
+ }
+ }
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+bool fileExists(
+ const std::string& filename,
+ const bool lookInZipfiles) {
+
+ if (filename == "") {
+ return true;
+ }
+
+
+ // 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) {
+ // Exists
+ return true;
+ } else if (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 {
+ // Does not exist
+ return false;
+ }
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+/* 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){
+ unzFile f = unzOpen(zipDir.c_str());
+ //the last parameter, an int, determines case sensitivity:
+ //1 is sensitive, 2 is not, 0 is default
+ int test = unzLocateFile(f, desiredFile.c_str(), 2);
+ unzClose(f);
+ if(test == UNZ_END_OF_LIST_OF_FILE){
+ return false;
+ }
+ return true;
+}
+
+
+// If no zipfile exists, outZipfile and outInternalFile are unchanged
+bool zipfileExists(const std::string& filename, std::string& outZipfile,
+ std::string& outInternalFile){
+ if (fileExists(filename, false)) {
+ // Not inside a zipfile if the file itself exists
+ return false;
+ } else {
+ 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 (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;
+ }
+ }
+
+ }
+ }
+
+ // not a valid directory structure ever,
+ // obviously no .zip was found within the path
+ return false;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+std::string generateFilenameBase(const std::string& prefix) {
+ 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";
+ 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
+}
+
+/**
+
+ c:/temp/foo.zip/plainsky\\*
+" path "
+ "prefix "
+
+ @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);
+ }
+ }
+ }
+}
+
+
+static void getFileOrDirListZip(const std::string& path,
+ const std::string& prefix,
+ Array<std::string>& files,
+ bool wantFiles,
+ bool includePath){
+ unzFile f = unzOpen(path.c_str());
+
+ enum {MAX_STRING_LENGTH=1024};
+ char filename[MAX_STRING_LENGTH];
+ Set<std::string> fileSet;
+
+ do {
+
+ // prefix is valid, either "" or a subfolder
+ unzGetCurrentFileInfo(f, NULL, filename, MAX_STRING_LENGTH, NULL, 0, NULL, 0);
+ _zip_addEntry(path, prefix, filename, fileSet, wantFiles, includePath);
+
+ } while (unzGoToNextFile(f) != UNZ_END_OF_LIST_OF_FILE);
+
+ unzClose(f);
+
+ fileSet.getMembers(files);
+}
+
+
+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 (fileExists(path, false)) {
+ if (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/G3D.lib/source/filter.cpp b/externals/g3dlite/G3D.lib/source/filter.cpp
new file mode 100644
index 00000000000..f6c0467820f
--- /dev/null
+++ b/externals/g3dlite/G3D.lib/source/filter.cpp
@@ -0,0 +1,32 @@
+/**
+ @file filter.cpp
+
+ @author Morgan McGuire, matrix@graphics3d.com
+ @created 2007-03-01
+ @edited 2007-03-01
+
+ Copyright 2000-2007, Morgan McGuire.
+ All rights reserved.
+ */
+#include "G3D/filter.h"
+
+namespace G3D {
+
+void gaussian1D(Array<float>& coeff, int N, float std) {
+ coeff.resize(N);
+ float sum = 0.0f;
+ for (int i = 0; i < N; ++i) {
+ float x = i - (N - 1) / 2.0f;
+ float p = -square(x / std) / 2.0f;
+ float y = exp(p);
+ coeff[i] = y;
+ sum += y;
+ }
+
+ for (int i = 0; i < N; ++i) {
+ coeff[i] /= sum;
+ }
+}
+
+
+} // namespace
diff --git a/externals/g3dlite/G3D.lib/source/format.cpp b/externals/g3dlite/G3D.lib/source/format.cpp
new file mode 100644
index 00000000000..d9d1b516393
--- /dev/null
+++ b/externals/g3dlite/G3D.lib/source/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/G3D.lib/source/g3dmath.cpp b/externals/g3dlite/G3D.lib/source/g3dmath.cpp
new file mode 100644
index 00000000000..95c9e16cc24
--- /dev/null
+++ b/externals/g3dlite/G3D.lib/source/g3dmath.cpp
@@ -0,0 +1,70 @@
+/**
+ @file g3dmath.cpp
+
+ @author Morgan McGuire, graphics3d.com
+
+ @created 2001-06-02
+ @edited 2004-02-24
+ */
+
+#include "G3D/g3dmath.h"
+#include <stdlib.h>
+
+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;
+}
+
+
+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/G3D.lib/source/license.cpp b/externals/g3dlite/G3D.lib/source/license.cpp
new file mode 100644
index 00000000000..b362ad3f45f
--- /dev/null
+++ b/externals/g3dlite/G3D.lib/source/license.cpp
@@ -0,0 +1,70 @@
+/**
+ @file license.cpp
+
+ @author Morgan McGuire, graphics3d.com
+
+ @created 2004-04-15
+ @edited 2004-04-15
+*/
+
+#include "G3D/format.h"
+#include <string>
+
+namespace G3D {
+
+std::string license() {
+ return format(
+
+"This software is based in part on the PNG Reference Library which is\n"
+"Copyright (c) 2004 Glenn Randers-Pehrson\n\n"
+"This software is based in part on the work of the Independent JPEG Group.\n\n"
+"This software is based on part on the FFmpeg libavformat and libavcodec libraries\n"
+"(\"FFmpeg\", http://ffmpeg.mplayerhq.hu), which are included under the terms of the\n"
+"GNU Lesser General Public License (LGPL), (http://www.gnu.org/copyleft/lesser.html).\n\n"
+"%s"
+"This program uses the G3D Library (http://g3d-cpp.sf.net), which\n"
+"is licensed under the \"BSD\" Open Source license. The Graphics3D library\n"
+"source code is Copyright © 2000-2008, Morgan McGuire, All rights reserved.\n"
+"The BSD license requires the following statement regarding G3D:\n"
+"\n"
+"Redistribution and use in source and binary forms, with or without\n"
+"modification, are permitted provided that the following conditions\n"
+"are met:\n"
+"\n"
+"Redistributions of source code must retain the above copyright\n"
+"notice, this list of conditions and the following disclaimer.\n"
+"\n"
+"Redistributions in binary form must reproduce the above copyright\n"
+"notice, this list of conditions and the following disclaimer in the\n"
+"documentation and/or other materials provided with the distribution.\n"
+"\n"
+"Neither the name of Morgan McGuire, Brown University, Williams College, nor the names\n"
+"of the G3D contributors may be used to endorse or promote products derived\n"
+"from this software without specific prior written permission.\n"
+"\n"
+"THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS\n"
+"\"AS IS\" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT\n"
+"LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR\n"
+"A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT\n"
+"OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,\n"
+"SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT\n"
+"LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,\n"
+"DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY\n"
+"THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n"
+"(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n"
+"OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n"
+"\n\n"
+"G3D VERSION %d\n",
+
+#ifdef G3D_WIN32
+ "" // Win32 doesn't use SDL
+#else
+ "This software uses the Simple DirectMedia Layer library (\"SDL\",\n"
+ "http://www.libsdl.org), which is included under the terms of the\n"
+ "GNU Lesser General Public License, (http://www.gnu.org/copyleft/lesser.html).\n\n"
+#endif
+,
+G3D_VER);
+}
+
+}
diff --git a/externals/g3dlite/G3D.lib/source/prompt.cpp b/externals/g3dlite/G3D.lib/source/prompt.cpp
new file mode 100644
index 00000000000..1fb3bd4cfee
--- /dev/null
+++ b/externals/g3dlite/G3D.lib/source/prompt.cpp
@@ -0,0 +1,716 @@
+/**
+ @file prompt.cpp
+
+ @author Morgan McGuire, morgan@graphics3d.com
+ @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
+# include <Carbon/Carbon.h>
+#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)&params);
+
+ 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/G3D.lib/source/stringutils.cpp b/externals/g3dlite/G3D.lib/source/stringutils.cpp
new file mode 100644
index 00000000000..a21fc1f377c
--- /dev/null
+++ b/externals/g3dlite/G3D.lib/source/stringutils.cpp
@@ -0,0 +1,231 @@
+/**
+ @file stringutils.cpp
+
+ @maintainer Morgan McGuire, matrix@graphics3d.com
+
+ @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'); }
+#endif
+
+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/g3dlite/G3D.lib/source/uint128.cpp b/externals/g3dlite/G3D.lib/source/uint128.cpp
new file mode 100644
index 00000000000..450009a5cff
--- /dev/null
+++ b/externals/g3dlite/G3D.lib/source/uint128.cpp
@@ -0,0 +1,155 @@
+/**
+ @file uint128.cpp
+
+ @maintainer Morgan McGuire, matrix@graphics3d.com
+ @author Kyle Whitson
+
+ @created 2008-07-17
+ @edited 2008-07-17
+ */
+
+#include "G3D/uint128.h"
+
+namespace G3D {
+
+/** Adds two 64-bit integers, placing the result and the overflow into 64-bit integers.*/
+static void addAndCarry(const uint64& _a, const uint64& _b, uint64& carry, uint64& result) {
+
+ // Break each number into 4 32-bit chunks. Since we are using uints, right-shifting will fill with zeros.
+ // This eliminates the need to and with 0xFFFFFFFF.
+ uint32 a [2] = {_a & 0xFFFFFFFF, _a >> 32};
+ uint32 b [2] = {_b & 0xFFFFFFFF, _b >> 32};
+
+ uint64 tmp = uint64(a[0]) + b[0];
+
+ result = tmp & 0xFFFFFFFF;
+ uint32 c = tmp >> 32;
+
+ tmp = uint64(c) + a[1] + b[1];
+ result += tmp << 32;
+ carry = (tmp >> 32);
+}
+
+/** Multiplies two unsigned 64-bit integers, placing the result into one 64-bit int and the overflow into another.*/
+void multiplyAndCarry(const uint64& _a, const uint64& _b, uint64& carry, uint64& result) {
+
+ // Break each number into 4 32-bit chunks. Since we are using uints, right-shifting will fill with zeros.
+ // This eliminates the need to and with 0xFFFFFFFF.
+ uint32 a [2] = {_a & 0xFFFFFFFF, _a >> 32};
+ uint32 b [2] = {_b & 0xFFFFFFFF, _b >> 32};
+
+ uint64 prod [2][2];
+ for(int i = 0; i < 2; ++i) {
+ for(int j = 0; j < 2; ++j) {
+ prod[i][j] = uint64(a[i]) * b[j];
+ }
+ }
+
+ // The product of the low bits of a and b will always fit into the result
+ result = prod[0][0];
+
+ // The product of the high bits of a and b will never fit into the result
+ carry = prod[1][1];
+
+ // The high 32 bits of prod[0][1] and prod[1][0] will never fit into the result
+ carry += prod[0][1] >> 32;
+ carry += prod[1][0] >> 32;
+
+ uint64 tmp;
+ addAndCarry(result, (prod[0][1] << 32), tmp, result);
+ carry += tmp;
+ addAndCarry(result, (prod[1][0] << 32), tmp, result);
+ carry += tmp;
+}
+
+
+uint128::uint128(const uint64& hi, const uint64& lo) : hi(hi), lo(lo) {
+}
+
+uint128::uint128(const uint64& lo) : hi(0), lo(lo) {
+}
+
+uint128& uint128::operator+=(const uint128& x) {
+
+ G3D::uint64 carry;
+ addAndCarry(lo, x.lo, carry, lo);
+
+ // Adding the carry will change hi. Save the old hi bits in case this == x.
+ const uint64 xHi = x.hi;
+ hi += carry;
+ hi += xHi;
+ return *this;
+}
+
+uint128& uint128::operator*=(const uint128& x) {
+
+ // The low bits will get overwritten when doing the multiply, so back up both (in case &x == this)
+ const uint64 oldLo = lo;
+ const uint64 oldXLo = x.lo;
+
+ G3D::uint64 carry;
+ multiplyAndCarry(oldLo, oldXLo, carry, lo);
+
+ // Overflow doesn't matter here because the result is going into hi - any overflow will exceed the capacity of a 128-bit number
+ // Note: hi * x.hi will always overflow, since (x * 2^64) * (y * 2^64) = x*y*(2^128). The largest number expressable in 128 bits is
+ // 2^128 - 1.
+ hi = carry + (oldLo * x.hi) + (hi * oldXLo);
+
+ return *this;
+}
+
+uint128& uint128::operator^=(const uint128& x) {
+ hi ^= x.hi;
+ lo ^= x.lo;
+ return *this;
+}
+
+uint128& uint128::operator&=(const uint128& x) {
+ hi &= x.hi;
+ lo &= x.lo;
+ return *this;
+}
+
+uint128& uint128::operator|=(const uint128& x) {
+ hi |= x.hi;
+ lo |= x.lo;
+ return *this;
+}
+
+bool uint128::operator==(const uint128& x) {
+ return (hi == x.hi) && (lo == x.lo);
+}
+
+uint128& uint128::operator>>=(const int x) {
+
+ //Before shifting, mask out the bits that will be shifted out of hi.
+ //Put a 1 in the first bit that will not be lost in the shift, then subtract 1 to get the mask.
+ uint64 mask = ((uint64)1L << x) - 1;
+ uint64 tmp = hi & mask;
+ hi >>= x;
+
+ //Shift lo and add the bits shifted down from hi
+ lo = (lo >> x) + (tmp << (64 - x));
+
+ return *this;
+}
+
+uint128& uint128::operator<<=(const int x) {
+
+ //Before shifting, mask out the bits that will be shifted out of lo.
+ //Put a 1 in the last bit that will be lost in the shift, then subtract 1 to get the logical inverse of the mask.
+ //A bitwise NOT will then produce the correct mask.
+ uint64 mask = ~((((uint64)1L) << (64 - x)) - 1);
+ uint64 tmp = lo & mask;
+ lo <<= x;
+
+ //Shift hi and add the bits shifted up from lo
+ hi = (hi << x) + (tmp >> (64 - x));
+
+ return *this;
+}
+
+uint128 uint128::operator&(const uint128& x) {
+ return uint128(hi & x.hi, lo & x.lo);
+}
+}
diff --git a/externals/g3dlite/delme b/externals/g3dlite/delme
deleted file mode 100644
index e69de29bb2d..00000000000
--- a/externals/g3dlite/delme
+++ /dev/null
diff --git a/externals/g3dlite/doc-files/changelog.dox b/externals/g3dlite/doc-files/changelog.dox
new file mode 100644
index 00000000000..fd52b99f271
--- /dev/null
+++ b/externals/g3dlite/doc-files/changelog.dox
@@ -0,0 +1,1872 @@
+/**
+ @page changelog Change Log
+
+<P>
+
+ Major version numbers introduce API changes that are not backwards
+ compatible. Minor versions are backwards compatible to the
+ previous major release, except for critical bug fixes. Deprecated functionality
+ will be supported until (at least) the next major release.
+
+
+<hr>
+
+ <P>
+ Changes in 7.01:
+ <ul>
+ <li> Video file reading and writing via FFmpeg added
+ <li> Added computeBounds method to ArticulatedModel::Part that calls computeBounds on each TriList. Changed updateAll to automatically call computeBounds [Kyle]
+ <li> Added constructor to Matrix4 to construct a matrix from an upper-left 3x3 submatrix and an upper-right 3x1 submatrix [Kyle]
+ <li> <b>Incompatible change</b>: RegistryUtil functions now require an explicit value parameter instead of extracting the value from the key string.
+ <li> <b>Incompatible change</b>:GApp now calls the onLogic handler before the simulation handlers but after the user input and network handlers
+ <li> <b>Incompatible change</b>: Changed GHashCode and other functors to traits. See \link guidenewuser \endlink. Added typedefs and adapters to make this mostly backwards compatible.
+ <li> Added parallax occlusion mapping to G3D::SuperShader (specify Material::parallaxSteps > 1)
+ <li> Added normal mapping to G3D::SuperShader (specify Material::parallaxSteps == 0)
+ <li> G3D::Texture resizes textures that exceed the device maximum size
+ <li> G3D::Array now allows control over MIN_ELEMENTS and MIN_BYTES using template parameters
+ <li> Clarified G3D::Any file format in documentation
+ <li> G3D::Texture::PreProcess::gammaAdjust
+ <li> G3D::ShadowMap::lightProjection(), G3D::ShadowMap::lightFrame()
+ <li> Added Barycentric coordinates to CollisionDetection::isPointInTriangle
+ <li> G3D::RenderDevice::beginOpenGL, G3D::RenderDevice::endOpenGL
+ <li> PointAABSPTree::clearData
+ <li> AABSPTree -> KDTree
+ <li> GApp now allows the MidgetManager to process events in onEvent before the GApp::onEvent executes
+ <li> Added spotlight support to SuperShader
+ <li> Switched SuperShader to use ph
+ <li> Adjusted standard deviation used in G3D::GaussianBlur to provide smoother filtering
+ <li> Put HashTrait and EqualsTrait in their own headers separate from Table.h
+ <li> ArticulatedModel::facet
+ <li> Renamed GWindow to G3D::OSWindow
+ <li> ReferenceCountedPointer now asserts that the pointer is not NULL on method invocation
+ <li> G3D::ShadowMap now computes appropriate matrices for spot lights
+ <li> Added ImageFormat::convert [Danny and Kyle]
+ <li> 3- and 4- argument min and max
+ <li> G3D::GaussianBlur now correctly sets the output viewport
+ <li> G3D::Framebuffer::clear
+ <li> IFSModel and ArticulatedModel now load Princeton Shape Benchmark OFF files.
+ <li> G3D::Any CoordinateFrame now serialized using angles
+ <li> CameraControlWindow now prints angles in degrees
+ <li> ImageXXX classes now have a format() method
+ <li> OSWindow::create
+ <li> MeshAlg::toIndexedTriList now supports TRIANGLE_FAN input.
+ <li> Tuned Table and hash functions for performance [Kyle & Morgan]
+ <li> GEvent::toString
+ <li> G3D::TextInput now treats characters with ASCII code greater than 127 as symbols
+ <li> G3D::ThreadSet
+ <li> G3D::Texture::white
+ <li> G3D::Matrix4::upper3x3
+ <li> G3D::Matrix4::homoMul
+ <li> ArticulateModel::fromFile now takes a Matrix4 instead of a CoordinateFrame to allow arbitrary linear transformations.
+ <li> ArticulatedModel::createCornellBox
+ <li> Material::createDiffuse
+ <li> ImageFormat::convert
+ <li> G3D::filenameBase
+ <li> Removed SDL_SysWMEvent, which was never supported by GEvent anyway
+ <li> Removed TextureFormat::SAME_AS_SCREEN to break dependence on OpenGL
+ <li> TextureFormat has been renamed to G3D::ImageFormat and moved into G3D.lib
+ <li> Added variable time control points to G3D::Spline
+ <li> Gui controls now have configureable GuiControl::setCaptionSize
+ <li> Gui controls now default to no indent if the caption is "" (use " " for indent with no caption)
+ <li> G3D::GuiContainer
+ <li> G3D::GThread::started
+ <li> buildg3d installation arguments changed--see
+ <li> G3D::Vector3int32
+ <li> GuiButton now accepts an optional callback function/method
+ <li> FileDialog now accepts an extra "note" argument
+ <li> FileDialog::getFilename non-static to support subclassing
+ <li> System::currentDateString
+ <li> Expanded G3D::ArticulatedModel documentation
+ <li> Build system now executes on multiple processors (about 1.8x speedup for dual-core)
+ <li> Build system now caches dependencies (about 5x speedup for small incremental builds)
+ <li> Patched LOAD_EXTENSION to work around gcc pointer-to-function cast issues
+ <li> Tool buttons added to a G3D::GuiPane automatically align to the previous one.
+ <li> Added invisible GuiPane style
+ <li> G3D::uint128 [Kyle]
+ <li> Increased BSPMap rendering by 10% by reducing state changes
+ <li> Added prompt argument to FileDialog::getFilename
+ <li> G3D::PosedModel::getBoxBounds on an array
+ <li> G3D::PosedModel::getSphereBounds on an array
+ <li> Changed RenderDevice::screenshot to save .png instead of .jpg files
+ <li> G3D::SuperShader now supports a customMap and customConstant for experimenting with shaders.
+ <li> G3D::SuperShader now does not ever light the "back" of a bump-mapped poly, even if the bumps should create a light-facing surface
+ <li> G3D::Material promoted to its own class (was G3D::SuperShader::Material)
+ <li> G3D::Matrix2
+ <li> G3D::VertexAndPixelShader::ArgList::size
+ <li> G3D::pathConcat
+ <li> G3D::WidgetManager::moveWidgetToBack
+ <li> SuperShader / NonShadowed.pix now uses arrays of lights instead of separate variables
+ <li> Reduced cost of release-mode Shader argument validation
+ <li> G3D::PosedModel::sortAndRender now performs view-frustum culling of objects
+ <li> G3D::Draw::lighting for visualizing light sources
+ <li> G3D::SuperShader::Pass::purgeCache
+ <li> G3D::GuiSlider::setRange
+ <li> G3D::GuiPane::addPane no longer takes a
+ <li> G3D::VertexAndPixelShader::ArgList::remove
+ <li> Optimized G3D::Matrix::pseudoInverse; now about 2x faster
+ <li> G3D::GLight::effectSphere
+ <li> G3D::GuiWindow::moveTo
+ <li> G3D::GuiWindow::setEnabled, enabled
+ <li> G3D::GuiButton now sizes to its caption
+ <li> G3D::GuiSlider now fires events on change and drag
+ <li> G3D::Shader arguments (in G3D::VertexAndPixelShader::ArgList) may now be "optional"
+ <li> G3D::GLight::point now has quadratic attenuation by default.
+ <li> G3D::ImageFormat::name
+ <li> g3dmath.h now includes inttypes.h on gcc and simulates it on visual studio
+ <li> G3D::RenderDevice::cullFace
+ <li> G3D::LineSegment2D::intersection
+ <li> G3D::BinaryInput::setEndian
+ <li> G3D::GEvent::MOUSE_BUTTON_CLICK
+ <li> Generalized ShadowMap to work with spotlights as well as directional lights
+ <li> G3D::GLCaps::supportsTexture, G3D::GLCaps::supportsRenderBuffer
+ <li> Opaque G3D::ArticulatedModels now support more than 2 non-shadow casting light sources
+ <li> Added proof symbol parsing to TextInput [Corey Taylor]
+ <li> Added G3D::AABox::corner() to match G3D::Box::corner() [Corey Taylor]
+ <li> OS X: G3D::CarbonWindow [Casey]
+ <li> OS X: iCompile now generates OS X application bundles and dmg files
+ <li> OS X build no longer depends on X11
+ <li> G3D::FileDialog
+ <li> G3D::Table now allows overriding the default equality operator for keys
+ <li> <b>Incompatible change</b>: GApp::onBeforeSimulation now allows mutation of the timesteps
+ <li> <b>Incompatible change</b>: Merged GApp::simTime and idealSimTime (the sim time is now idealized)
+ <li> cmake now generates project files for Xcode, MinGW, and all Visual Studio versions [Corey Taylor]
+ <li> OS X: icompile and buildg3d now generate universal binaries on Intel machines
+ <li> G3D::PosedModel::objectSpaceTangents
+ <li> G3D::IFSModel::fromData
+ <li> G3D::MeshAlg::generateGrid
+ <li> G3D::BinaryOutput::ok()
+ <li> G3D::generateFilenameBase
+ <li> G3D::IFSModel::fromFile now defaults to not welding for improved performance
+ <li> G3D::IFSModel members are now protected to allow subclassing
+ <li> Removed G3D::uint in favor of G3D::uint32 [Corey Taylor]
+ <li> Added G3D::GMaterial(TextureRef) constructor
+ <li> Made G3D::GMaterial fields floats
+ <li> G3D::GuiControl::setCaption, G3D::GuiWindow::setCaption
+ <li> G3D::GuiControl can now be subclassed for custom user-defined controls
+ <li> G3D::GuiTheme::renderCanvas
+ <li> G3D::GuiTheme::pauseRendering, G3D::GuiTheme::resumeRendering
+ <li> G3D::PosedModel::sortAndRender
+ <li> G3D::Framebuffer can now attach cube map faces
+ <li> System::describeSystem now prints current working directory and application name
+ <li> Added /usr/local/-G3D dir- to system data file path [Kai Schroeder]
+ <li> Various patches for detecting new CPUs in System.cpp [Corey Taylor]
+ <li> g3d_Index macro now available in G3D::Shader GLSL code
+ <li> G3D::BackgroundWidget
+ <li> G3D::TriangleShape
+ <li> Fix: <B>incompatible change</b> OSWindow::Settings::asynchronous is now spelled correclty, with two "n"s
+ <li> Fix: Fixes for point-in-triangle and moving-sphere-fixed-tri; previous code projected onto the wrong axes, so barycentric coords were wrong for nearly vertical triangles.
+ <li> Fix: Changed some doubles to floats in G3D::Triangle
+ <li> Fix: Changed all of the isXXX(char) methods to take unsigned char arguments so that they can parse extended symbols
+ <li> Fix: AABSPTree::deserializeStructure was missing a return statement [Derex]
+ <li> Fix: Draw::plane was drawing the plane reflected through the origin [Macklin Chaffee]
+ <li> Fix: Added template parameters to friends in AABSPTree and PointAABSPTree [expiring_frog]
+ <li> Fix: System::findDataFile uses data directory set by GApp
+ <li> Fix: AtomicInt32 decrement returns int32 instead of uint32
+ <li> Fix: OS X function keys now work correctly under CarbonWindow
+ <li> Fix: OS X modifier keys now work correctly under CarbonWindow
+ <li> Fix: OS X arrow keys now work correctly under CarbonWindow
+ <li> Fix: Rewrote buildg3d to fix many longstanding bugs, including mismatched 'bin' directories and confusion about the 'install' target
+ <li> Fix: gfxmeter reports now format correctly regardless of monitor width
+ <li> Fix: patch to initialize correctly on the Mesa library, which crashes when requesting DEPTH24_STENCIL8
+ <li> Fix: stringSplit now works correctly for adjacent split characters [Akita Noek]
+ <li> Fix: Draw::axes labels now obey current viewport
+ <li> Fix: GuiWindow now loses focus when hidden
+ <li> Fix: GFont::draw2D now computes correct horizontal bounds on text
+ <li> Fix: GuiPane no longer renders when invisible
+ <li> Fix: Clicking off of all GuiWindows makes none of them have focus
+ <li> Fix: Win32Window now allows windows programmatically positioned anywhere on a multiple monitor screen
+ <li> Fix: Win32Window now does not fail when dragging a GL context between multiple monitors
+ <li> Fix: SuperShader now correctly lights bump-mapped surfaces in tangent space [Kyle Whitson]
+ <li> Fix: GuiPane now renders its caption
+ <li> Fix: Rect2D::border now grows the correct way (positive = grow)
+ <li> Fix: Added % operator to TextInput
+ <li> Fix: Added multi-line printing to GConsole
+ <li> Fix: G3D::Texture can now create empty cube maps
+ <li> Fix: G3D::Table iterator now correctly parameterized on hashfunction and equality function as well as key and value
+ <li> Fix: G3D::Table now passes Values by reference when setting them, avoiding one copy
+ <li> Fix: various Framebuffer/empty texture initialization bugs on ATI cards
+ <li> Fix: uniform arrays for GLSL
+ <li> Fix: All aliasing warnings have been fixed no longer needs -fno-strict-aliasing [Corey Taylor]
+ <li> Fix: [ 1829704 ] debugAssert in Array::operator[](unsigned int n) wrong
+ <li> Fix: GuiWindow::pack now recursively packs all child panes
+ <li> Fix: patch to continue build when javac isn't found on both windows and linux [Corey Taylor]
+ <li> Fix: [ 1599139 ] Fixes to make buildg3d work on non C:\ windows systems [Patrick Burke]
+ <li> Fix: Added faster overloads of GImage::stripAlpha() and GImage::insertRedAsAlpha() [Corey Taylor]
+ <li> Fix: GImage::save() with odd-widths bmp files [Corey Taylor]
+ <li> Fix: Draw::capule renders properly (capule was not visible) [Corey Taylor]
+ <li> Fix: Patched ShadowMap to work around ATI and OS X driver shadow map bugs.
+ <b>Incompatible change:</b>Required changing several interfaces to take ShadowMapRef arguments.
+ <li> Fix: GCamera::Frustum was facing backwards
+ <li> Fix: [ 1774479 ] texture glformats wrong (caused incorrect font rendering on Intel)
+ <li> Fix: ArticulatedModel static methods do not force loading of shaders unless an ArticulatedModel has actually been loaded.
+ <li> Fix: RenderDevice::setAlphaWrite/setColorWrite implemented correctly
+ <li> Fix: Implemented ImageFormat::fromCode
+ <li> Fix: [ 1766509 ] Texture doesn't handle 3D textures correctly
+ <li> Fix: Separate bool, float, and int back ends for GLSL shaders
+ </ul>
+
+<hr>
+ <P>
+ Changes in 7.00:
+ <ul>
+ <li> Upgraded to iCompile 0.5.3. Requires users to delete their old ice.txt and ~/.icompile files
+ <li> Key to toggle the user camera is now F2 (was Tab)
+ <li> Support for g++ 4.1 [Corey]
+ <li> Patches for 64-bit compilation [Corey]
+ <li> WinMain is now compiled as C++ code (fixed some "manifest" errors on certain VC8 installations)
+ <li> G3D::ShadowMap
+ <li> Cleaned up and documented G3D::GCamera project/unproject API [Jeff Marsceill]
+ <li> Made G3D::MD2Model::Pose easier to use
+ <li> G3D::Matrix
+ <li> Added G3D::Texture methods for reading back color and depth textures
+ <li> G3D::RenderDevice::getFixedFunctionLighting
+ <li> G3D::debugPrintf -> G3D::consolePrintf
+ <li> G3D::GApp::debugPrintf -> G3D::screenPrintf
+ <li> Reduced ArticulatedModel and MD2Model push/popState calls
+ <li> G3D::Shader now defines platform and graphics vendor macros
+ <li> User camera control requires right mouse click to move
+ <li> G3D::AnyVal::fromFile, G3D::AnyVal::load, G3D::AnyVal::save
+ <li> G3D::AnyVal now uses copy-on-mutate for fast Array/Table copies
+ <li> G3D::AnyVal no longer separates table entries with ";"
+ <li> G3D::AnyVal now provides casts to basic data types
+ <li> G3D::AnyVal G3D::Rect2D and G3D::AABox types
+ <li> G3D::NetListener, G3D::ReliableConduit, and G3D::LightweightConduit can now be created without an explicit NetworkDevice pointer
+ <li> GApp::onGraphics now takes posed model arguments
+ <li> G3D::RenderDevice::beginFrame no longer executes a pushState-- state will carry over between frames
+ <li> G3D::GUniqueID
+ <li> GApp::onPose
+ <li> All classes that read from files can now read data inside zipfiles.
+ <li> Changed G3D::hashCode(T) to ::GHashCode<T>(T) [Corey]
+ <li> Removed "G3D::" from the printed portion of the documentation index to make it easier to read.
+ <li> OSWindow::fireEvent for inserting user events into the queue
+ <li> G3D::logPrintf
+ <li> System::findDataFile
+ <li> On OS X, G3D::FirstPersonManipulator now treats ctrl-click as right click
+ <li> Increased mouse sensitivity of G3D::FirstPersonManipulator on OS X
+ <li> System::getClipboardText, System::setClipboardText
+ <li> Widget::window() accessor
+ <li> Replaced SDLEvent with G3D::GEvent
+ <li> Increased GFont rendering performance, added GFont::send2DQuads for fast rendering of large amounts of text
+ <li> G3D::GFont now supports fonts with 256 characters (.fnt file format version 2)
+ <li> Upgraded to the OpenGL 2.0 glext/glxext/wglext headers
+ <li> G3D::GuiTheme, G3D::GuiText
+ <li> G3D::UprightFrame, G3D::UprightSpline
+ <li> G3D::Spline
+ <li> G3D::RenderDevice::clip2D
+ <li> G3D::Win32Window now returns events for up to 5 mouse buttons
+ <li> Significantly changed GEvent delivery and Widget mechanisms to incorporate notions of focus--see upgrade.html
+ <li> Motion events (joystick/mouse) can no longer be cancelled by Widget::onEvent
+ <li> Mouse motion events for all platforms
+ <li> G3D::PosedModel::Ref::sendGeometry
+ <li> G3D::RenderDevice::pushState(FramebufferRef)
+ <li> G3D::Texture::PreProcess::computeNormalMap
+ <li> direct.h is now included by fileutils.h on Win32 to ensure that chdir, mkdir, etc. are available
+ <li> Changed distribution directories to place include, lib, and bin under a directory named after the platform
+ and all other files one directory up.
+ <li> G3D::GCamera::unproject
+ <li> Made G3D::Ray non-virtual for efficiency
+ <li> RenderDevice::alphaWrite now defaults to true
+ <li> Changed G3D install directory from g3d-7_00 to g3d-7.00
+ <li> On a GL 2.0 or greater driver, G3D::GLCaps now assumes the presence of all GL2 extensions even if they are not explicitly listed by the card [Corey Taylor]
+ <li> debugPrintf now flushes stderr on Unix systems
+ <li> G3D::Lighting::fromSky
+ <li> G3D::Texture::newGLTexture2D [Corey Taylor]
+ <li> Added 10-bit cinema texture formats [Corey Taylor]
+ <li> Added G3D::ArticulatedModel to the core (includes 3DS loading)
+ <li> Added G3D::SuperShader to the core
+ <li> Merged GApp and GApplet into GApp to make the common case easier to implement
+ <li> Removed GApp::debugMode options, removed "debug" prefix from most fields.
+ <li> G3D::zipfileExists for testing if a filename path contains a .zip to be opened [Eric]
+ <li> G3D::isZipfile tests the header of a file to see if it is a .zip [Eric]
+ <li> G3D::zipRead and G3D::zipClose to open and close .zip files [Eric]
+ <li> G3D::fileExists supports filename paths that contain .zip [Eric]
+ <li> G3D::fileLength supports filename paths that contain .zip [Eric]
+ <li> G3D::getFiles and G3D::getDirs support filename paths that contain .zip [Eric]
+ <li> Texture::isSupportedImage - static method, returns true if a filename exists and is compatible with Texture [Eric]
+ <li> Viewer (tool) - Allows drag and drop viewing of many supported file formats [Eric]
+ <li> GFXMeter (tool) - Updated for 7.00 compatibility: GApplet structure removed [Eric]
+ <li> OSWindow::Settings has an allowMaximize field. Win32Window will have an activated 'Maximize' button if true [Eric]
+ <li> GApp2::debugCamera -> GApp2::defaultCamera
+ <li> GApp2::debugController -> GApp2::defaultController
+ <li> Made GApp2::debugText private
+ <li> Added g3d_WorldLight0 to the G3D shader extensions
+ <li> Added Shader support for GL_FLOAT_MAT3_ARB uniforms
+ <li> Tab (command completion) no longer auto-repeats in GConsole
+ <li> GConsole now limits the key repeat rate to the framerate
+ <li> Removed GApp::Settings::useNetwork because it was no longer needed--Win32 does not trigger firewall checks anymore
+ <li> Optimized AABSPTree balance and queries; now about 20% faster than 6.10, but requires 30 bytes more memory per member
+ <li> G3D::Array::pop now shrinks the underlying array by default (Array::popRemove does not shrink the array and is faster)
+ <li> Fast O(<i>n</i>), non-destructive G3D::Array::partition and G3D::Array::medianPartition
+ <li> Increased ReliableConduit read attempts before timeout from 10 to 100
+ <li> G3D::GImage::pixel1 now returns G3D::Color1uint8*
+ <li> G3D::Color1, G3D::Color1uint8
+ <li> G3D::Image1, G3D::Image1uint8
+ <li> G3D::Image3, G3D::Image3uint8
+ <li> G3D::Image4, G3D::Image4uint8
+ <li> BinaryInput::flipEndian32, BinaryInput::flipEndian16
+ <li> Texture::createEmpty now intializes invertY = true, which is usually desirable for Framebuffer rendering.
+ <li> G3D::Map2D
+ <li> G3D::Vector4int8
+ <li> G3D::PointShape
+ <li> G3D::GImage::RGBAtoRGB
+ <li> Added GLEW compatibility [nico]
+ <li> Added data-files directory to the locations searched for G3D demo data
+ <li> On Win32, assertions now print ot the Output window as well as the dialog box.
+ <li> On Win32, $TEMP is now used for the logfile location instead of c:\tmp
+ <li> G3D::GKey replaces old SDL key enumeration
+ <li> Decreased memory requirements and increased balance speed of G3D::AABSPTree by adding a level of indirection
+ between tree nodes and the data stored in them.
+ <li> Added OSWindow::Settings::caption
+ <li> <b>Win32 programs must call the macro <b><code>G3D_START_AT_MAIN();</code></b> at top-level if they do not define WinMain themselves.</b>
+ <li> Win32 switched from MBCS to UNICODE for the binaries (G3D sources compile under either, but UNICODE is the VC8 default)
+ <li> Replaced SDL event types with G3D event types
+ <li> Added support for GL_ARB_point_sprite
+ <li> Win32 programs must call the macro <b><code>G3D_START_AT_MAIN();</code></b> at top-level if they do not define WinMain themselves.
+ <li> Win32 switched from MBCS to UNICODE for the binaries (G3D sources compile under either)
+ <li> Changed G3D::GApp::main to return an int
+ <li> Added header support for GL_EXT_geometry_shader4 ("Geometry shaders")
+ <li> Changed IFSModel::create to IFSModel::fromFile
+ <li> G3D::BSPMap for loading Quake3 .bsp files
+ <li> G3D::TextInput::Settings::caseSensitive
+ <li> G3D::TextInput::readBoolean, G3D::TextInput::Settings::trueSymbols, G3D::TextInput::Settings::falseSymbols, G3D::TextOutput::Settings::trueSymbol, G3D::TextOutput::Settings::falseSymbol, G3D::TextOutput::writeBoolean, G3D::Token::BOOLEAN_TYPE
+ <li> G3D::TextInput::Settings::msvcSpecials now defaults to true
+ <li> Made the input to G3D::tessellateComplexPolygon a constant array reference
+ <li> Removed SDL from Win32 build
+ <li> Increased maximum ReliableConduit message size to 60 MB
+ <li> Removed error macro
+ <li> G3D::GConsole
+ <li> Rect2D::lerp
+ <li> Added GL_EXT_packed_depth_stencil
+ <li> Added G3D::ImageFormat::DEPTH24_STENCIL8 packed stencil mode
+ <li> getOpenGLState now includes GL_LIGHT_MODEL_TWO_SIDE value
+ <li> Made ThirdPersonManipulator constructor protected (use ThirdPersonManipulator::create now)
+ <li> G3D::ToneMap
+ <li> Changed Renderbuffer::createEmpty argument order to match Texture::createEmpty
+ <li> G3D::IFSModel can now remove degenerate faces on load
+ <li> G3D::MD2Model::textureFromFile
+ <li> Replaced AABSPTree::beginRayIntersection with simpler AABSPTree::intersectRay interface.
+ <li> G3D::MD2Model now uses floating point texture coordinates, which makes it easier to
+ write pixel shaders for MD2Models
+ <li> G3D::GImage::computeNormalMap now accepts a scale factor indicating how steep the input bump map is
+ <li> Added G3D::Shader support for the GLSL #version directive
+ <li> Optimized G3D::GImage::computeNormalMap to use primarily integer math
+ <li> G3D::AABox::contains(AABox)
+ <li> Added large file (>2GB) support to BinaryInput [Peter]
+ <li> Renamed graphics3d.h to G3D/G3D.h
+ <li> Renamed GLG3D.h to GLG3D/GLG3D.h
+ <li> Renamed G3DAll.h to G3D/G3DAll.h
+ <li> G3D::Quat::unitize now normalizes in place
+ <li> Added G3D::Quat::operator*=
+ <li> Removed G3D::FirstPersonManipulator's constructor--use the static G3D::FirstPersonManipulator::create method now.
+ <li> Changed BSPMap leaf bounds from Box to AABox--50% improvement in frustum culling speed
+ <li> Optimized BSPMap rendering performance
+ <li> Removed all deprecated APIs
+ <li> All accessors of the form "getXXX" that take no arguments are now named just "xxx"
+ <li> G3D::Sky::create is now named G3D::Sky::fromFile and no longer accepts a G3D::RenderDevice. G3D::Sky::render now requires a RenderDevice.
+ <li> G3D::GFont::fromFile no longer accepts a G3D::RenderDevice and G3D::GFont::draw2D now requires a RenderDevice.
+ <li> Removed an assertion in BinaryInput requiring that compressed buffers be copied on construction [Nick Bray]
+ <li> Removed CoordinateFrame::zLookDirection (use -1)
+ <li> Removed Capsule::endPoint (use point)
+ <li> Removed CoordinateFrame::getStrafeVector (use rightVector)
+ <li> Removed static constants (use equivalent lower-case methods)
+ <li> Removed Cylinder::getPoint1 (use point)
+ <li> Fix: BSPMap::getStartingPosition now works correctly on all maps [Jeff]
+ <li> Fix: Workaround for ATI drivers that do not support zero-area scissor region
+ <li> Fix: GL_EXT_texture_3D -> GL_EXT_texture3D
+ <li> Fix: ARB_texture_cube_map and EXT_texture_cube_map are now aliases on all cards
+ <li> Fix: EXT_texture_edge_clamp and SGIS_texture_edge_clamp are now aliases on all cards
+ <li> Fix: G3D::Sky now turns off lighting
+ <li> Fix: Win32Window now returns mousebutton and motion events according to the GEvent spec
+ <li> Fix: Win32Window now operates correctly in fullscreen mode
+ <li> Fix: Fixed TGA decode to load from the middle of a binaryinput
+ <li> Fix: Added texture coordinates to posed MD2Models
+ <li> Fix: prompt/debugAssert now correctly responds to button presses on OS X
+ <li> Fix: GConsole now filename completes after the first word
+ <li> Fix: RenderDevice::getDepthBufferValue now checks for the presence of a depth buffer.
+ <li> Fix: G3D::AABSPTree now correctly handles members with infinite bounds on ray intersection tests
+ <li> Fix: Removed use of tmpfile on Unix
+ <li> Fix: Java ReliableConduit now properly waits if the buffer is full when sending
+ <li> Fix: [ 1607693 ] Triangles/Second display is now correct (rates were too low in 6.10)
+ <li> Fix: NetworkDevice now does not perform a test broadcast during initialization
+ <li> Fix: G3D::LightweightConduit::ok is now false if any error occurs during initialization
+ <li> Fix: BSPMAP for cards without glMultiDrawElementsEXT
+ <li> Fix: [ 1584335 ] ReliableConduit incorrectly assumes it's ok
+ <li> Fix: RenderDevide::push2D no longer resets the frameBuffer.
+ <li> Fix: Framebuffer logic for counting number of attachments was broken
+ <li> Fix: [ 1581986 ] Matrix3::fromAxisAngle now normalizes the input vector
+ <li> Fix: [1490643] Linux/FreeBSD/OS X binaries are now compiled with -fno-strict-aliasing, which fixes some
+ memory corruption problems that occurred with full optimizations.
+ <li> Fix: Fixed all warnings on gcc-4.1 and VC8.
+ <li> Fix: Matrix and CoordinateFrame serializers inside G3D::AnyVal dropped data
+ <li> Fix: [ 1535759 ] valgrind finds initialization/deletion errors in TextOutput, Matrix [Chris Demetriou]
+ <li> Fix: Patched MD2Model to automatically reduce animation times to less than 100000; large time were overflowing double->int conversion and causing animations to appear scrambled.
+ <li> Fix: [ 1535292 ] global Table hashCode overloads broken [Chris Demetriou]
+ <li> Fix: [ 1535736 ] Fixed System.cpp memory allocator to compile on 64-bit machines correctly [Chris Demetriou]
+ </ul>
+
+ <hr>
+ <P>
+ Changes in 6.10:
+ <UL>
+ <LI> Optimized G3D::CoordinateFrame::pointToObjectSpace to be fully inlined via left-multiplication
+ <LI> G3D::Matrix4::orthogonalProjection from a G3D::Rect2D
+ <LI> G3D::RenderDevice::swapBuffers
+ <LI> G3D::AnyVal
+ <LI> G3D::ThirdPersonManipulator
+ <LI> Added support for GL_SGIS_texture_lod in Texture.
+ <LI> Fix: [ 1490655 ] MeshAlg::Edge::containVertex goes off the end of the array
+ <LI> Fix: [ 1511729 ] NVIDIA rectangle generates errors in mipmap code
+ <LI> Fix: [ 1507296 ] RenderDevice must swapBuffer on resize
+ </UL>
+<hr>
+ <P>
+ Changes in 6.09:
+ <UL>
+ <LI> glDepthBoundsEXT
+ <LI> G3D::Quat::sameRotation
+ <LI> Full loading of the GL_ATI_separate_stencil extension, support within RenderDevice
+ <LI> platform.h undefines WIN32_LEAN_AND_MEAN, NOMINMAX after it has defined them
+ <LI> G3D::Texture::Settings::maxMipMap
+ <LI> Renamed Texture::Parameters to Texture::Settings (backwards compatible typedef added)
+ <LI> Optimized IFSModel rendering by increasing internal VAR cache size and reducing the number of state changes.
+ Can now render more than 1000 IFSModels at 30 fps on GeForce 7800.
+ <LI> G3D::System::mallocStatus
+ <LI> Range checking on Vector2int16::operator[]
+ <li> GImage::BAYER_G8R8_B8G8_to_R8G8B8_MHC, GImage::BAYER_B8G8_G8R8_to_R8G8B8_MHC
+ <LI> IFSModel and MD2Model now allocated their posed models using System::malloc
+ <LI> Increased the memory maintained by G3D::System for buffer pools up to a total of 13 MB:
+ 8 MB tiny (preallocated), 1 MB small (on demand), 4 MB medium (on demand). This was observed to
+ dramatically increase performance (15x) in real programs that were
+ performance limited by memory allocation time.
+ <LI> NetworkDevice now uses Winsock2.0 on Windows (controlled by the G3D_WINSOCK_MAJOR_VERSION/G3D_WINSOCK_MINOR_VERSION settings in NetAddress.h)
+ <LI> G3D::Manipulator
+ <LI> G3D::GApplet now runs installed G3D::GModules (except for graphics, which is left to the progrmamer)
+ <LI> G3D::GApp::addWidget, G3D::GApplet::addWidget, G3D::GApp::removeWidget, G3D::GApplet::removeWidget
+ <LI> G3D::Widget, G3D::WidgetManager
+ <LI> G3D::System::getEnv()
+ <LI> G3D::PosedModel2D
+ <LI> G3D::DXCaps
+ <LI> Increased precision of several Quat operations
+ <LI> G3D::Quat::fuzzyEq
+ <LI> G3D::Quat::operator-
+ <LI> G3D::LineSegment::length, G3D::LineSegment::point
+ <LI> Increased fuzzyEpsilon by a factor of 10 to take into account the new float32 focus of the APIs
+ <LI> G3D::RegistryUtil
+ <LI> G3D::LineSegment2D
+ <LI> G3D::ConvexPolygon2D
+ <LI> G3D::AxesShape
+ <LI> contrib/shaders/showDepth
+ <LI> G3D::Crypto with MD5 and CRC32 hashes
+ <LI> TextureManager::findTexture, TextureManager::cacheTexture [Erik]
+ <LI> Win32Window::_directInput created on-demand [Erik]
+ <LI> WeakReferenceCountedPointer has more comparison operators [Erik]
+ <LI> GImage::resolveFormat utility function [Erik]
+ <LI> GLCaps supports MESA
+ <LI> G3D::Win32Window and G3D::SDLWindow now release input capture and make the mouse visible on destruction
+ <LI> G3D::OSWindow::setInputCaptureCount, G3D::OSWindow::inputCaptureCount, G3D::OSWindow::incInputCaptureCount, G3D::OSWindow::decInputCaptureCount
+ <LI> GImage::makeCheckerboard
+ <LI> G3D::Vector3::one()
+ <LI> G3D::Shader now supports g3d_size(sampler2D) and g3d_invSize(sampler2D) extensions in GLSL shaders.
+ <LI> Renamed GAppSettings to G3D::GApp::Settings (old name is supported but deprecated)
+ <LI> Renamed GWindowSettings to G3D::OSWindow::Settings (old name is supported but deprecated)
+ <LI> Renamed TextInput::Options to G3D::TextInput::Options (old name is supported but deprecated)
+ <LI> G3D::FPManualController::setAutoActive for World of Warcraft style controller
+ <LI> G3D::isSlash, G3D::isQuote
+ <LI> G3D::GApplet::onEvent can now consume (i.e., prevent G3D::GApp from seeing) the event
+ <LI> G3D::CoordinateFrame::fuzzyIsIdentity, G3D::CoordinateFrame::isIdentity, G3D::CoordinateFrame::fuzzyEq
+ <LI> Matrix3::isOrthonormal
+ <LI> [1421201] Removed excess gl (NVIDIA) headers
+ <LI> Win32Window destructor now releases the mouse if it was captured and the current GL context is that window and the window was not created from an existing HDC/HWND
+ <LI> Fix: com.graphics3d.g3d.ReliableConduit now correctly selects on the waiting socket
+ <LI> Fix: [ 1166057 ] AABSPTree::beginBoxIntersection
+ <LI> Fix: GLCaps::supports(ImageFormat) now returns correct results on all cards
+ <LI> Fix: Shadow map rendering of default PosedModels now enables lighting
+ <LI> Fix: G3D::UserInput now restores the mouse position after pureDeltaMouse is turned off
+ <LI> Fix: G3D::Win32Window now clips precisely to the client area during an input grab.
+ <LI> Fix: [ 1383042 ] free static variables on shutdown
+ <LI> Fix: [1449115 ] Texture loading for odd-byte rows
+ <LI> Fix: G3D::Win32Window now produces correct character and scan codes for key events
+ <LI> Fix: G3D::GApplet::onEvent calls GApplet::processEvent by default
+ <LI> Fix: [ 1444320 ] TextInput parsed ".1" as "1" instead of "0.1"
+ <LI> Fix: G3D::Shape::type is now const
+ <LI> Fix: 0 --> 0.0f FrameBuffer.h [Erik]
+ <LI> Fix: Fixed Texture read-back dimensions for cube-map
+ <LI> Fix: Missing #include in SkyParameters.h [Erik]
+ <LI> Fix: Quad triangle counts are now accurate (were off by factor of 4 in 6.08)
+ <LI> Fix: contrib/ArticulatedModel now correctly masks all components using the diffuse alpha in fixed function mode
+ <LI> Fix: G3D::CoordinateFrame::getHeading was flipped front-to-back
+ <LI> Fix: [ 1404487 ] Missing Alt key up/down events on Win32
+ <LI> Fix: [ 1484924 ] collisionTimeForMovingPointFixedBox normals
+ </UL>
+ <P>
+<hr>
+ Changes in 6.08:
+ <UL>
+ <LI> Moved Win32 linker statements out of platform.h for IncrediBuild compatibility.
+ <LI> G3D::Texture and G3D::Sky now accept a rescaling factor
+ <LI> Added GFont::fromMemory() [Corey]
+ <LI> Added optional argument to Quat::slerp() for slerp/lerp angle threshold. [Corey]
+ <LI> Across-the-board performance optimizations. Most apps should render 10% faster.
+ Includes removal of Milestones when using VBO VAR [Nick Bray], GFont::draw2D and
+ Draw::rect2D stripped down to raw OpenGL, consistent internal use of float,
+ increased RenderDevice state change optimization.
+ <LI> Minimized header interdependencies (GLG3D headers no longer include all of G3D)
+ <LI> Added GThread and GMutex classes. [Corey]
+ <LI> Added ImageFormat::fromCode(). [Corey]
+ <LI> Added Plane::distance() and Plane::closestPoint() helper methods. [Corey]
+ <LI> G3D::ImageFormat::code, G3D::ImageFormat::colorSpace
+ <LI> <B>incompatible change</B> G3D::MeshAlg::computeTangentSpace basis now computes a right-handed coordinate frame,
+ where the binormal direction is the negative of the direction it faced in G3D 6.07.
+ <LI> Exposed G3D::RenderDevice::beforePrimitive and G3D::RenderDevice::afterPrimitive to end-user code for
+ integrating raw OpenGL calls.
+ <LI> G3D::Framebuffer and G3D::Renderbuffer to implement the Framebuffer_object extension [Dan Hilferty]
+ <LI> G3D::Shader::hasArgument
+ <LI> G3D::Texture::getImage
+ <LI> Changed SECOND, MINUTE, DAY, HOUR, SUNRISE, SUNSET, MIDNIGHT, METER, KILOMETER to enum values instead of #defines
+ <LI> G3D::Texture::Parameters; deprecated most Texture constructors in favor of ones that use this class
+ <LI> Moved most image manipulation routines into GImage.
+ <LI> G3D::GImage now allocates the underlying buffer in multiples of bytes to allow slight overflor for MMX algorithms
+ <LI> G3D::GImage::BAYER_R8G8_G8R8_to_R8G8B8_MHC
+ <LI> G3D::GImage::R8G8B8_to_Y8U8V8
+ <LI> G3D::GImage::Y8U8V8_to_R8G8B8
+ <LI> G3D::GImage now supports PPM binary
+ <LI> Various Rect2D helpers [Nick Bray]
+ <LI> ConvexPolyhedron improved clipping [Nick Bray]
+ <LI> G3D::System::build
+ <LI> G3D::System::calloc
+ <LI> G3D::GImage::convertToRGBA
+ <LI> contrib/AVI can read most AVI files on Windows.
+ <LI> contrib/wxGWindow now uses wxWidgets 2.6.2
+ <LI> G3D_DEBUG now controls whether debug code is enabled; it defaults to the value of _DEBUG
+ <LI> zlib upgraded to 1.2.3 [Corey]
+ <LI> zlib now statically linked on Win32 (no longer requires zlib1.dll at runtime) [Corey]
+ <LI> G3D::MeshShape
+ <LI> Changed std::string hashCode to use CRC32 to reduce collisions
+ <LI> G3D::crc32
+ <LI> Added occlusion query #defines [Nick Bray]
+ <LI> G3D::Win32Window now shares textures and vertex buffers across all GL contexts
+ <LI> G3D::Win32Window now enforces single-threading among GL contexts
+ <LI> G3D::GLCaps::slowVBO
+ <LI> G3D::VARArea now uses main memory vertex buffers on cards with slow VBO implementations.
+ <LI> G3D::Matrix3::toString [Peter]
+ <LI> G3D::Matrix4::toString [Peter]
+ <LI> G3D::Color3::fromHSV [Peter]
+ <LI> G3D::Color3::toHSV [Peter]
+ <LI> G3D::Color3::jetColorMap [Peter]
+ <LI> Optimized G3D::iRound (now faster than casting!)
+ <LI> G3D::MD2Model::create now accepts a scale factor
+ <LI> #G3D_DEPRECATED macro
+ <LI> #G3D_CHECK_PRINTF_ARGS, #G3D_CHECK_VPRINTF_ARGS macros to allow
+ checking of printf argument strings under gcc at compile time with
+ -Wformat.
+ <LI> G3D::TextInput::filename
+ <LI> G3D::TextInput::Options::msvcSpecials
+ <LI> G3D::TextInput::Options::startingLineNumberOffset
+ <LI> G3D::TextInput::readSymbolToken [cgd]
+ <LI> G3D::TextInput::readStringToken [cgd]
+ <LI> G3D_DEPRECATED macro
+ <LI> Threadsafe G3D::ReferenceCountedPointer
+ <LI> G3D::AtomicInt32
+ <LI> G3D::GThread [Corey]
+ <LI> G3D::Array::popDiscard
+ <LI> Optimized multi-argument Array::append
+ <LI> G3D::GFont 2x faster than in G3D 6.07
+ <LI> G3D::RenderDevice::pushState 2x faster than in G3D 6.07
+ <LI> G3D::RenderDevice::pushState no longer stores GL texgen and fog information
+ <LI> G3D::Draw::fastRect2D
+ <LI> G3D::System::outOfMemoryCallback
+ <LI> G3D::Queue::fastClear [Chris Demetriou]
+ <LI> G3D::Rect2D::x0y1 and x1y0
+ <LI> G3D::GLCaps bug tests now run in a separate GL context [Erik Cassel]
+ <LI> G3D::GApplet tracks real and simulation time.
+ <LI> contrib/Q3Map updated to correctly render instanced objects [Alex Rice]
+ <LI> G3D::OSWindow subclasses now required to invoke OSWindow::loadExtensions
+ <LI> G3D::Quat::log for non-unit quats and for real-only quats.
+ <LI> G3D::GApplet::doUserInput
+ <LI> G3D::GApp prints time for each component
+ <LI> G3D::Stopwatch
+ <LI> G3D::OSWindow::renderDevice()
+ <LI> G3D::OSWindow::current()
+ <LI> G3D::GLCaps::hasBug_redBlueMipmapSwap and workaround for G3D::Texture on Radeon 7500
+ <LI> Fix: CollisionDetection::penetrationDepthForFixedSphereFixedPlane() contact point and normal values. [Corey]
+ <LI> Fix: Quat::slerp has invalid shortest path [Corey]
+ <LI> Fix: G3D::drawFeatureEdges now uses correctly normalized face edges (and offers a crease angle)
+ <LI> Fix: G3D::SDLWindow now releases the mouse on Linux during an assertion.
+ <LI> Fix: All keys are reset to up when Win32Window loses focus. [Corey]
+ <LI> Fix: gaussRandom is unit gaussian [Corey]
+ <LI> Fix: [ 1418276 ] 6.08: Unsupported format for depth texture
+ <LI> Fix: Ignoring extra/unused set Shader arguments. [Corey]
+ <LI> Fix: [ 1229205 ] uniform texture array (Could not set indexed array uniforms). [Corey]
+ <LI> Fix: <B>incompatible change</B> BinaryInput/BinaryOutput copy constructors and assignments were accessible. [Corey]
+ <LI> Fix: RenderDevice::screenshotPic would corrupt GImage's heap. [Corey]
+ <LI> Fix: Alt-Tab window switching caused an invalid Alt key state. [Corey]
+ <LI> Fix: Incorrect window size event in Win32Window sent to OpenGL. [Corey]
+ <LI> Fix: [ 1227915 ] Textures don't bind on ATI under GLSL.
+ <LI> Fix: [ 1358477 ] ray-plane intersection bug [Dan Keefe]
+ <LI> Fix: [ 1370665 ] hash_map moved to stdext in VC8 (2005)
+ <LI> Fix: ToneMap extended to use DIM_2D_NPOT instead of DIM_2D_RECT
+ <LI> Fix: Texture::copyFromScreen now works with DIM_2D_NPOT textures
+ <LI> Fix: Wrapped debugAssertM in do {} while (0) to ensure correct compilation in single-line statements [ERik Cassel]
+ <LI> Fix: G3D::Draw::cylinder now renders the bottom correctly
+ <LI> Fix: Array::front now compiles under gcc
+ <LI> Fix: G3D::Ray::distance used to measure against the origin [David]
+ <LI> Fix: [ 1293151 ] ArticulatedModel clipping on Radeon -- disabled auto-mipmap generation on mobile radeon 9xxx
+ <LI> Fix: G3D::TextInput now parses ^=, character 255 correctly [cgd]
+ <LI> Fix: G3D::TextInput now reports line numbers correctly with raw newlines [cgd]
+ <LI> Fix: .ICO files with transparency loaded incorrectly [Corey]
+ <LI> Fix: G3D::Draw::rect2DBorder inner border was 1 pixel too thick.
+ <LI> Fix: [ 1326173 ] Win32Window::init should call makeCurrent.[Erik Cassel]
+ <LI> Fix: [ 1326423 ] G3D::Queue::_copy broken [Chris Demetriou]
+ <LI> Fix: [ 1313293 ] 6.08: TextInput gets symbol extendedType() wrong [Chris Demetriou]
+ <LI> Fix: IFSModel::save, for PLY2 forgot newlines [Peter]
+ <LI> Fix: Quat(Matrix3) now computes trace correctly (gave negative quats in some cases)
+ <LI> Fix: Setting RenderDevice::polygonOffset now always produces a depth shift,
+ even for faces perpendicular to the view axis.
+ <LI> Fix: GImage now auto-resolves formats for files with 1 character base names
+ <LI> Fix: WeakReferenceCountedPointer cycle bug
+ <LI> Fix: Corrected lag encountered when using some ReliableConduit constructors [Dan Keefe]
+ </UL>
+
+ <P>
+<hr>
+ Changes in 6.07:
+ <UL>
+ <LI> G3D::OSWindow::makeCurrent
+ <LI> Win32 release binaries now built with no debug information (used to have line numbers)
+ <LI> AABox::AABox enforces the constraint low <= high
+ <LI> Optimized G3D::Array, Table, Queue, and Set for performance. Now significantly (up to 10x) faster
+ than their std::counterparts.
+ <LI> G3D::Vector3(Vector2, float) constructor
+ <LI> G3D::Vector2::fastDirection
+ <LI> G3D::TextInput::Options::cComments
+ <LI> G3D::TextInput::Options::escapeSequencesInStrings
+ <LI> G3D::TextInput::Options::otherCommentCharacter2
+ <LI> G3D::TextInput::WrongString
+ <LI> GLCaps::supports_GL_ATI_separate_stencil
+ <LI> GLCaps can now test a card/driver and detect specific bugs:
+ <ul><li>G3D::GLCaps::hasBug_glMultiTexCoord3fvARB
+ <LI> G3D::GLCaps::hasBug_normalMapTexGen
+ </ul>
+ <LI> G3D::ReferenceCountedPointer::downcast for non VC6 compilers
+ <LI> Improved G3D::ReferenceCountedPointer documentation to make subclassing features clearer
+ <LI> Moved typedef for uint into G3D namespace and into g3d (was in glg3d)
+ <LI> G3D::Shape
+ <LI> G3D::Cylinder
+ <LI> G3D::System::malloc, G3D::System::realloc, G3D::System::free for fast allocation of small objects
+ <LI> G3D::Draw::plane
+ <LI> G3D::Draw::cylinder
+ <LI> G3D::gaussRandom
+ <LI> GCamera deserialize(BinaryInput) & serialize(BinaryOutput) functions [Peter]
+ <LI> G3D::GApp now writes a description of the whole system to the log to aid debugging.
+ <LI> [ 1217928 ] OpenGL occlusion query entry points are loaded on initialization
+ <LI> New texture interpolation modes: BILINEAR_MIPMAP, NEAREST_MIPMAP, NEAREST_NO_MIPMAP
+ <LI> New texture formats:
+ <UL>
+ <LI> G3D::ImageFormat::L16;
+ <LI> G3D::ImageFormat::L16F;
+ <LI> G3D::ImageFormat::L32F;
+ <LI> G3D::ImageFormat::A16;
+ <LI> G3D::ImageFormat::A16F;
+ <LI> G3D::ImageFormat::A32F;
+ <LI> G3D::ImageFormat::LA4;
+ <LI> G3D::ImageFormat::LA16;
+ <LI> G3D::ImageFormat::LA16F;
+ <LI> G3D::ImageFormat::LA32F;
+ <LI> G3D::ImageFormat::RGB16;
+ <LI> G3D::ImageFormat::RGB16F;
+ <LI> G3D::ImageFormat::RGB32F;
+ <LI> G3D::ImageFormat::RGBA16;
+ <LI> G3D::ImageFormat::RGBA16F;
+ <LI> G3D::ImageFormat::RGBA32F;
+ </UL>
+ <LI> isValidPointer and isValidHeapPointer no longer check the Win32 debug heap in order to support offset and padded memory blocks.
+ <LI> Restructured unit tests
+ <LI> G3D::CoordinateFrame::lookRay [David Baszucki]
+ <LI> G3D::System::describeSystem, G3D::NetworkDevice::describeSystem, G3D::RenderDevice::describeSystem
+ <LI> G3D::Array performance tuning for short arrays and arrays of small objects
+ <LI> Added glext.h entries for GL_ARB_draw_buffers, GL_ARB_texture_rectangle,
+ GL_ARB_color_buffer_float, GL_ARB_half_float_pixel, GL_ARB_texture_float,
+ and GL_ARB_pixel_buffer_object extensions
+ <LI> IFSModel::create added weld option, defaults to true (to keep compatibility). [Peter]
+ <LI> G3D::RenderDevice::alphaTestReference, RenderDevice::alphaTest
+ <LI> G3D::VAR::set
+ <LI> G3D::Log::vprintf
+ <LI> G3D::WeakReferenceCountedPointer
+ <LI> GCC 4.0 build support added [Corey]
+ <LI> G3D::CoordinateFrame::lookAt now gives a valid output even when look == up
+ <LI> contrib/GChunk
+ <LI> GLCaps now loads GL_EXT_framebuffer_object functions
+ <LI> Added MSVC 6 support for C99 restrict keyword
+ <LI> G3D::Win32Window properly resizes viewport on window resize [Corey]
+ <LI> G3D::BinaryFormat, G3D::byteSize, G3D::binaryFormatOf
+ <LI> Removed dead ManualCameraControllerHelper code
+ <li> Added consistent area and volume methods to geometric primitives, deprecated old methods.
+ <LI> Fast G3D::BinaryInput::read / G3D::BinaryOutput::write methods for arrays
+ <LI> Enabled cube mapping on Radeon mobility cards and added a workaround to the known problems with texcoords on those cards.
+ <LI> Can now create G3D::Win32Window with existing HWND and HDC [Corey]
+ <LI> G3D::VertexAndPixelShader::ArgList::set(std::string, Array<T>)-- [ 1192401 ] Shader support arrays
+ <LI> Fix: SDLWindow used std::string's instead of C strings in printf and format inside some exception handling code. [Peter]
+ <LI> G3D::X11Window (same as SDLWindow in this release)
+ <LI> Fix: [ 1277854 ] Win32Window fails on 24-bit modes
+ <LI> RFE: [ 1242466 ] Inline Matrix3 methods
+ <LI> Fix: [ 1226272 ] end caps of capsules in wrong position
+ <LI> Fix: G3D::ImageFormat::LA8 now has 8-bits per channel
+ <LI> Fix: [ 1124491 ] Remove GL_SAMPLER_2DRECT_ARB
+ <LI> Fix: [ 1257113 ] G3D::Queue problems comining pushFront and pushBack
+ <LI> Fix: MeshAlg::Weld now linear time (was O(n^2) due to a bug)
+ <LI> Fix: [ 1298873 ] fast & correct CoordinateFrame::lerp
+ </UL>
+
+ <P>
+<hr>
+ Changes in 6.06:
+ <UL>
+ <LI> G3D::Lighting::emissiveScale
+ <LI> G3D::RenderDevice::drawBuffer
+ <LI> G3D::RenderDevice::debugNumMinorStateChanges, debugNumMinorOpenGLStateChanges, debugNumMajorStateChanges, debugNumMajorOpenGLStateChanges.
+ <LI> In stereo mode, Texture::copyFromScreen automatically chooses the left/right buffer to read based on the current glDrawBuffer
+ <LI> contrib/ArticulatedModel/ToneMap
+ <LI> Lazy state changes for shaders
+ <LI> 50% performance improvement for G3D::BinaryInput, G3D::BinaryOutput when machine endian matches file endian
+ <LI> Textures load with default of maxAnisotroy = 2.0
+ <LI> maxAnisotropy argument to G3D::Texture constructors.
+ <LI> GLCaps now loads GL_ATI_fragment_shader extension
+ <LI> contrib/ArticulatedModel now supports rigid body hierarchies
+ <LI> Added TEX_SUBTRACT, TEX_ADD_SIGNED, TEX_DOT3, TEX_DOT3_RGBA modes for G3D::RenderDevice::setTextureCombineMode
+ <LI> G3D::RenderDevice now cleans up all static G3D::VARArea s when it shuts down
+ <LI> FIX: [ 1208157 ] GLSL slow on ATI
+ <LI> FIX: Off-by-one on viewport scale for 2D rendering
+ <LI> FIX: MeshAlg::computeTangentSpaceBasis now works correctly
+ <LI> FIX: 6.05 enabled all fixed function lights by default. This caused major performance problems on some cards.
+ <LI> FIX: Extended cube map workaround to all Radeon Mobility cards
+ <LI> FIX: Added check for glBlendEq before calling in RenderDevice
+ <LI> FIX: Added a test for GL_EXT_texture_env_add in RenderDevice
+ <LI> FIX: [ 1191817 ] unsigned warnings in BinaryInput
+ </UL>
+
+<hr>
+ <P>
+ Changes in 6.05:
+ <UL>
+ <LI> G3D::BAYER_G8B8_R8G8_to_R8G8B8_MHC
+ <LI> G3D::Quarter_R8G8B8_to_BAYER_G8B8_R8G8
+ <LI> G3D::BAYER_G8B8_R8G8_to_Quarter_R8G8B8
+ <LI> contrib/Matrix
+ <LI> contrib/Java
+ <LI> Texture::alphaOnlyVersion
+ <LI> Draw::sphere speed improved over 25% with single quad strip (improves Draw::capsule) [Corey]
+ <LI> Allow 1-channel GImage saving - BMP (expanded to RGB), PNG [Corey]
+ <LI> Allow 1-channel GImage loading - PNG [Corey]
+ <LI> Added shader and framebuffer extensions to glext.h
+ <LI> All files used during current execution are available via G3D::getFiles() [Corey]
+ <LI> Implemented OSX version of glGetCurrentContext with CGL. [Corey + Derek]
+ <LI> ReferenceCountedObject is-in-heap checks were removed to allow better multiple and virtual inheritance for reference counted objects. ReferenceCountedPointer still appropriately checks does an is-in-heap check on assignment. [Corey]
+ <LI> Added Dev C++ compatability
+ <LI> glGetAttribLocationARB
+ <LI> Changed GLight == operator to not use memcpy (was causing issues due to byte padding on some compilers)
+ <LI> Made CoordinateFrame destructor non-virtual (eliminates vtable)
+ <LI> Added new FAQ documentation
+ <LI> Added support to G3D::BinaryInput and G3D::BinaryOutput
+ reading and writing huge (larger than available memory) files.
+ Files are still restricted to about 2 GB total, and compressed
+ files must fit entirely in memory.
+ <LI> Tweaked allocation strategy for small G3D::Array
+ <LI> G3D::Texture::rect2DBounds, G3D::Texture::vector2Bounds
+ <LI> G3D::Vector4 * G3D::Vector4, Vector4 / Vector4
+ <LI> G3D::Array::operator=(std::vector)
+ <LI> G3D::Sky::getEnvironmentMap now returns the top texture on machines
+ that don't support cube maps.
+ <LI> glDisableAllTextures()
+ <LI> G3D::setFailureHook
+ <LI> G3D::Shader::fromStrings now accepts optional names for the vertex and pixel shader
+ <LI> G3D::Shader no longer requires values for declared but unused uniform variables
+ <LI> G3D::RenderDevice now stores texture matrix at 32-bit precision (for faster push/popState)
+ <LI> G3D::RenderDevice::setTextureLODBias
+ <LI> G3D::Shader now supports shadow map arguments
+ <LI> G3D::Shader::ArgList checks to see if Texture arguments are null
+ <LI> G3D::RenderDevice::setAlphaWrite now defaults to true if the OSWindow has an alpha channel.
+ <LI> G3D::RenderDevice::screenshotPic now supports alpha
+ <LI> contrib/VideoSerializer
+ <LI> G3D::BinaryOutput::writeBits, G3d::BinaryInput::readBits
+ <LI> G3D::Sky can now be initialized with a NULL renderDevice, provided a non-null one
+ is used with the G3D::Sky::render method.
+ <LI> G3D::pi(), G3D::halfPi(), G3D::twoPi() added to replace defines [Corey]
+ <LI> contrib/Q3Map
+ <LI> Increased G3D::Draw::sphere performance using vertex arrays.
+ <LI> G3D::Array::fastClear
+ <LI> G3D::AABSPTree::insert(Array<T>)
+ <LI> G3D::Texture::sizeOfAllTexturesInMemory
+ <LI> G3D::VARArea::sizeOfAllVARAreasInMemory
+ <LI> G3D::RenderDevice stores cameraToWorldMatrixInverse for faster coordinate system changes.
+ <LI> inlined G3D::Matrix3::operator= for performance
+ <LI> Created installer for Windows install [Corey]
+ <LI> Reorganized the documentation topic index based on abstraction level, added hyperlinks to demo/contrib code
+ <LI> G3D::ReliableConduit and G3D::LightweightConduit now send and receive
+ objects directly; no need to make a G3D::NetMessage. G3D::NetMessage
+ and associated methods are now deprecated.
+ <LI> Win32 GUI G3D::prompt now auto-expands \\n to \\r\\n in prompt string [Corey]
+ <LI> G3D::Draw::frustum
+ <LI> Increased timeout and attempts for G3D::ReliableConduit to handle huge (1 MB) packets
+ <LI> G3D::BinaryOutput::reset (memory writing only; not supported for disk)
+ <LI> Reduced overhead for G3D::ReliableConduit and
+ G3D::LightWeightConduit send routines
+ <LI> Added PPM/PGM/PBM ASCII encode/decode support to G3D::GImage [Corey]
+ <LI> New G3D::PosedModel rendering methods appropriate for shadow casting
+ (with efficient default implementations).
+ <LI> G3D::Lighting
+ <LI> Changed RenderDevice::TEX_INTERPOLATE to mean GL_DECAL and added TEX_BLEND for GL_BLEND
+ <LI> G3D::CoordinateFrame::upVector
+ <LI> G3D::GLight::diffuse
+ <LI> G3D::Rect2D::contains is now const
+ <LI> Rewrote G3D::BinaryOutput to not use G3D::Array
+ <LI> G3D::MD2Model::textureMatrix
+ <LI> G3D::MeshAlg::computeBounds(vertex, index, ...)
+ <LI> G3D::RenderDevice::colorWriteEnabled(), depthWriteEnabled, alphaWriteEnabled
+ <LI> G3D::RenderDevice::setSpecularCoefficient(Color3)
+ <LI> G3D::VAR::maxSize
+ <LI> G3D::RenderDevice::enableTwoSidedLighting
+ <LI> G3D::PosedModel::hasTransparency
+ <LI> G3D::PosedModel::sort
+ <LI> G3D::RenderDevice::renderMode
+ <LI> G3D::MeshAlg::computeNormals(geometry, indexArray);
+ <LI> contrib/ArticulatedModel (beta 3DS support)
+ <LI> G3D::RenderDevice::swapBuffersAutomatically allows caller to suppress page flip.
+ <LI> Added coordinate system documentation.
+ <LI> RenderDevice::enableClip2D, RenderDevice::disableClip2D (scissor region)
+ <LI> contrib/wxGWindow is stable and full featured-- use wxWidgets 2.5.3 with G3D!
+ <LI> G3D::fileIsNewer
+ <LI> G3D::isDirectory
+ <LI> G3D::filenameContainsWildcards
+ <LI> G3D::filenamePath
+ <LI> G3D::Draw::lineSegment now accepts a scale (allowing arrows and axes to thicken appropriately)
+ <LI> G3D::Rect2D::largestCenteredSubRect
+ <LI> G3D::Matrix4::serialize, G3D::Matrix4::deserialize
+ <LI> glTexImage3DEXT
+ <LI> Removed glut.lib and glut.dll from the win32-lib directory.
+ <LI> G3D::writeStringToFile, G3D::TextOutput, and G3D::BinaryOutput now flush by default (safe, not fast).
+ <LI> Shifted push2D by 0.375 pixels as recommended in the OpenGL guide to bias integer coords towards pixel centers
+ <LI> G3D::Draw::rect2DBorder
+ <LI> G3D::Rect2D::border
+ <LI> G3D::RenderDevice now creates a G3D::Win32Window on Windows instead of a G3D::SDLWindow. SDLWindow is now
+ deprecated on Windows.
+ <LI> G3D::VARArea now updates allocation sizes instead of G3D::VAR internally. Added
+ more accessor methods to VARArea to futher remove VAR from VARArea internals. [Corey]
+ <LI> VARSystem.cpp moved to VARArea.cpp - filename change only! [Corey]
+ <LI> Linux build system updated:
+ Builds only static libraries, Does not require libtool/libtoolize anymore,
+ Does not check for or require libraries that normally linked with the .so files,
+ Automatically builds Test project with iCompile during install. [Corey]
+ <LI> G3D::Quat::deserialize, G3D::Quat::serialize
+ <LI> G3D::PhysicsFrame::deserialize, G3D::PhysicsFrame::serialize
+ <LI> G3D::TextInput::Options::singleQuotedStrings (defaults to true, changing the behavior
+ from previous versions).
+ <LI> G3D::Token::extendedType returns information disambiguating characters and strings
+ and floats and ints.
+ <LI> Added data/ah64-body and ah64-rotor
+ <LI> demos/Network_Demo now uses a helicopter model instead of a plane
+ <LI> G3D::VARArea::gl_vertexBufferObject and G3D::VARArea::gl_basePointer for breaking
+ the VARArea abstraction.
+ <LI> GLG3D.h no longer links against SDLMain.lib on Windows if _CONSOLE is defined
+ (since console programs have no WinMain).
+ <LI> SDL's redefinition of main is cleared when not linking sdlmain.lib [Corey]
+ <LI> Moved contrib/Win32Window to G3D::Win32Window
+ <LI> G3D::TextInput::readSymbols
+ <LI> contrib/Image [Morgan]
+ <LI> contrib/wxGWindow [Morgan]
+ <LI> Added support for full-screen antialiasing to contrib/Win32Window
+ <LI> Added joystick support to contrib/Win32Window [Corey]
+ <LI> Win32Window fully-implements OSWindow [Corey]
+ <LI> Texture now supports DDS(2D/CubeMap) and PNG files [Corey]
+ <LI> Added Win32 pbuffer routines (no G3D wrapper, though-- we're waiting for the new ARB API).
+ <LI> G3D::PosedModel::texCoords
+ <LI> G3D::IFSModel now loads IFS 1.1 [Peter]
+ <LI> G3D::IFSModel now loads and saves PLY2 files (plain text IFS format) [Peter]
+ <LI> Automatically switch to glCompressedTexImage2D in G3D::Texture::fromMemory [Corey]
+ <LI> Added G3D::Sky::fromCubeMap for preloaded CubeMap Texture::Ref's [Corey]
+ <LI> Added G3D::Sky::fromFile and deprecated Sky::create [Corey]
+ <LI> Demo and Test projects now build with iCompile, which is included [Corey]
+ <LI> Fix: TextOutput::writeString now escapes special characters
+ <LI> Fix: AABSPTree::serializeStructure
+ <LI> Fix: Properly handle gl_ uniforms on Radeon for Shader
+ <LI> Fix: [ 875467 ] OS X debugBreak (requires default XCode debug menu item 'Break on DebugStr()') [Corey + Derek]
+ <LI> Fix: Can make a G3D::Texture::fromGImage with one channel (defaults to L8 format)
+ <LI> Fix: [ 1149972 ] 6.05: Make Sky render correctly on low-end cards (no Cube mapping) [Corey]
+ <LI> Fix: [ 1032742 ] OS X _DEBUG not defined [Derek]
+ <LI> Fix: 16-bit integer reads in BinaryInput that always reversed endianness. (OSX file reading) [Corey + Derek]
+ <LI> Fix: Matrix4 operator[] was returning a matrix value cast to a pointer [Corey]
+ <LI> Fix: Matrix3 and Matrix4 had missing float* / const float* operators [Corey]
+ <LI> Fix: Rect2D::clip broken for types other than Vector2
+ <LI> Fix: RenderDevice::configureShadowMap result depends on objectToWorldMatrix
+ <LI> Fix: [ 1150650 ] DebugBreak() undefined
+ <LI> Fix: [ 1111534 ] Network Demo crashes starting 2nd server on same machine
+ <LI> Fix: [ 1102091 ] ReliableConduit::receive times out
+ <LI> Fix: Implemented MD2Model::objectSpaceBoundingX methods.
+ <LI> Fix: G3D::Triangle::area is now zero for zero-area triangles (was inf)
+ <LI> Fix: AABSPTree with extent on MSVC 6 no longer enters infinite loop in std::sort
+ <LI> Fix: [ 1105641 ] Does not build with g++ 3.4.x [Corey]
+ <LI> Fix: [ 1103619 ] RenderDevice::countPrimitive is wrong (changed to RenderDevice::countTriangles) [Corey]
+ <LI> Fix: AABSPTree::BoxIntersectionIterator doesn't compile
+ <LI> Fix: [ 1101680 ] copyfile won't overwrite (on Windows now overwrites) [Corey]
+ <LI> Fix: [ 1101646 ] GCamera::frustum incorrect for non-square viewport
+ <LI> Fix: Ultra bright lens flare at sunset [Nicholas Bray]
+ <LI> Fix: IP address strings were reversed by NetAddress(std::string)
+ <LI> Fix: TextInput now returns end of file token for files without trailing whitespace
+ <LI> Fix: [ 1094166 ] 6.05: Release mouse stuck on x-axis [Corey + Morgan]
+ <LI> Fix: Recognize buggy ATI Radeon Mobility cube maps and work around
+ <LI> Fix: Textures now initialize without setting error bit on cards without GL_ARB_shadow
+ <LI> Fix: filenameBaseExt now operates correctly on strings with both \ and / slashes.
+ <LI> Fix: [ 1062659 ] BinaryInput::BinaryInput() memory leak
+ <LI> Fix: Removed RenderDevice::polygonCount, which was never used.
+ <LI> Fix: TextInput::readNumber no longer accepts double preceeding +/- on numbers when Options::signedNumbers is true
+ <LI> Fix: [ 1038733 ] OSWindow cannot set icon properly [Corey]
+ <LI> Fix: [ 939400 ] Linux mouse set position (Wild camera swinging on startup) [Corey]
+ <LI> Fix: [ 1042591 ] Software GL Causes Assertion [Corey]
+ <LI> Fix: [ 1036634 ] debugAssert doesn't work on MSVC 7 [Corey]
+ <LI> Fix: [ 1049024 ] Fix compile warnings from gcc/Linux build [Corey]
+ <LI> Fix: [ 1051272 ] Win32Window doesn't use GWindowSettings properly. [Corey]
+ <LI> Fix: Win32Window clips the proper cursor region during input capture. [Corey]
+ <LI> Fix: GWindows now center and maximize on the primary monitor for Windows.
+ <LI> Fix: [ 1052945 ] TextOutput wordWrap starts on newlines
+ <LI> Fix: [ 1050957 ] TextInput readNumber support for capital 'E' numbers.
+ <LI> Fix: [ 1049674 ] TextInput failes on X. numbers.
+ <LI> Fix: [ 1044028 ] Linux TextOutput Warning
+ <LI> Fix: [ 1032750 ] Grayscale JPG errors [Corey]
+ <LI> Fix: [ 1036225 ] Encode TGA support strips alpha channel [Corey]
+ <LI> Fix: [ 1038631 ] CoordinateFrame::slerp (Quat::slerp has fix) [Corey]
+ <LI> Fix: [ 1033686 ] GImage::GImage(filename) dies on certain (BMP) images [Corey]
+ <LI> Fix: Texture mapping modes for pre-OpenGL 1.3 cards [Dan & Morgan]
+ </UL>
+
+<hr>
+ <P>
+ Changes in 6.04:
+ <UL>
+ <LI> G3D Manual! [ Morgan and Sascha ]
+ <LI> Initial MSVC7 build script. MSVC7 is not an officially supported platform
+ however the release contains MSVC7 precompiled binaries and the build script
+ will automatically build on both 6 and 7.
+ <LI> Improved performance of G3D::writeStringToFile
+ <LI> G3D::ReferenceCountedPointer assignment now allows compile time subtyping
+ <LI> G3D::ReferenceCountedPointer != operator
+ <LI> G3D::ReferenceCountedPointer::notNull
+ <LI> G3D::GLight::directional now normalizes the light vector
+ <LI> G3D::setAssertionHook
+ <LI> [ 1029256 ] G3D::Shader / G3D::VertexAndPixelShader define g3d_ uniforms inside shaders
+ <LI> static G3D::IFSModel::save/load for writing/reading IFS files
+ <LI> G3D::TextInput allows ' inside quoted strings
+ <LI> G3D::TextInput allows \ as a symbol token
+ <LI> G3D::TextInput supports an arbitrary comment character (e.g. '#')
+ <LI> Precompiled binaries for VisualC++ 7 (.NET 2002/2003)
+ <LI> VisualC++ 7 (.NET 2002/2003) supported by build script
+ <LI> Build now MOVEs binaries instead of COPYing them on Windows (allows
+ two compilers to output to the same location)
+ <LI> G3D Guide overview documentation
+ <LI> Changelog and Error FAQ moved under Doxygen
+ <LI> Build scripts and documentation now under the 'doc' .dsp on Windows
+ <LI> Textures now support a DepthReadMode that can be used to perform hardware
+ shadow map comparisions. <B>RenderDevice::configureShadowMap now requires
+ an appropriately configured texture-- in previous releases it would
+ reconfigure the texture for you.</B>
+ <LI> G3D::UserInput::keyReleased, G3D::UserInput::ReleasedKeys
+ <LI> G3D::Array::randomElement
+ <LI> G3D::Array::insert
+ <LI> G3D::RenderDevice::getObjectToWorldMatrix and getCameraToWorldMatrix now return
+ const CoordinateFrame&
+ <LI> Optimized G3D::Array::randomize
+ <LI> G3D::cyclicCatmullRomSpline
+ <LI> G3D::wrap
+ <LI> contrib/AudioDevice
+ <LI> G3D::System::time();
+ <LI> More precise System::sleep
+ <LI> G3D::IFSModel::pose with no arguments
+ <LI> G3D::AABSPTree::serializeStructure, deserializeStructure,
+ <LI> serialize(Vector3::Axis, BinaryOutput), deserialize(Vector3::Axis, BinaryInput),
+ <LI> "glslc" GLSL compiler in the tools directory for getting compile-time errors from shaders
+ <LI> GLCaps::init now takes optional debug log
+ <LI> G3D::VertexAndPixelShader static constructors take optional 'debug' argument
+ <LI> GWindowSettings::visible; Win32Window can now start invisible
+ <LI> [ 991147 ] glBlendEquationEXT, RenderDevice::BlendEq, min, max, subtract, reverse subtract alpha blending
+ <LI> [ 989785 ] Draw::rect2D
+ <LI> GLCaps::numTextureCoords, GLCaps::numTextureUnits, GLCaps::numTextures
+ <LI> GLCaps::G3D_MAX_TEXTURE_UNITS
+ <LI> Rect2D::corner
+ <LI> GCamera::getFrustum, GCamera::frustum, GCamera::Frustum, GCamera::Frustum::Face
+ <LI> Plane constructor that accepts Vector4s (possibly at infinity)
+ <LI> AABox::inf, AABox::zero, AABox::maxFinite
+ <LI> AABox::intersects(Sphere)
+ <LI> Vector3::minFinite, Vector3::maxFinite
+ <LI> Plane::halfSpaceContainsFinite
+ <LI> Plane::halfSpaceContains(Vector4)
+ <LI> AABSPTree::getIntersectingMembers(Array<Plane>)
+ <LI> AABSPTree::getIntersectingMembers(GCamera::Frustum) for view-frustum culling
+ <LI> AABSPTree::getIntersectingMembers(Sphere)
+ <LI> AABox::split
+ <LI> Extended AABox::culledBy, Box::culledBy, and Sphere::culledBy with extra
+ information for bounding volume hierarchies
+ <LI> G3D::computeNormalMap
+ <LI> Matrix3::fuzzyEq(Matrix3)
+ <LI> Removed System::sleep(0.02) from GLG3D demo to give more accurate performance measure
+ <LI> [ 965824 ] changed link library defaults
+ <LI> serialize/deserialize for int, bool, double, float, std::string
+ <LI> G3D::TextOutput
+ <LI> [ 976924 ] Texture::texelWidth
+ <LI> [ 973413 ] VertexAndPixelShader::ArgList::set can be called more than once per variable
+ <LI> OSWindow::setIcon(std::string filename)
+ <LI> Texture::fromMemory that takes a single image (instead of an array of images)
+ <LI> [972604] RenderDevice::setTextureMatrix(uint, Matrix4)
+ <LI> [972747] Rect2D::center
+ <LI> GImage and Texture now load ICO files
+ <LI> GL_SAMPLER_1D_ARB, 2D, 3D, CUBE
+ <LI> Win32Window mouse events
+ <LI> Added normals to AABox collision results
+ <LI> Fix: [ 1026534 ]various cast bugs using Ref types.
+ Removed G3D::ReferenceCountedPointer implicit cast to underlying pointer type
+ This is technically an <B>incompatible change</B>, however we found no occurance
+ in the library or demos using this that was not a bug!
+ <LI> Fix: VAR constructor takes VARAreaRef instead of VARArea* <B>Incompatible change</B>
+ <LI> Fix: ManualCameraController is prevented from looking precisely along the Y-axis, which would cause
+ a singularity.
+ <LI> Fix: Added '?' as a valid symbol Token
+ <LI> Fix: [ 946235 ] GFont::align right w/ fixed_spacing
+ <LI> Fix: [ 1001033 ] RenderDevice with 0 texture units
+ <LI> Fix: GLCaps:: ARB stencil two side -> EXT stencil two side (stencilled shadows were broken)
+ <LI> Fix: [ 993449 ] vsnprintf crashes MSVC 7
+ <LI> Fix: [ 991320 ] Pointer truncation Warnings
+ <LI> Fix: [ 981440 ] AUTO with Texture::fromMemory
+ <LI> Fix: Plane::halfSpaceContains now works for infinite and semi-infinite points
+ <LI> Fix: [ 979032 ] Quat <-> Matrix3 roundtrip inverts
+ <LI> Fix: [ 976743 ] document GLCaps functions
+ <LI> Fix: [ 976746 ] #include GLCaps in g3dall
+ <LI> Fix: [ 973550 ] sampler2DRect now supported in GLSL shaders (NVIDIA only; ATI drivers are broken)
+ <LI> Fix: [ 973490 ] Win32Window width/height off by non-client amount
+ <LI> Fix: [ 961827 ] In debug mode, RenderDevice tries to access
+ GL_MAX_TEXTURE_IMAGE_UNITS_ARB and an assertion fails on cards that
+ don't support it.
+ <LI> Fix: Texture binding for VertexAndPixelShader
+ </UL>
+<hr>
+ <P>
+ Changes in 6.03:
+ <UL>
+ <LI> Matrix4::approxCoordinateFrame
+ <LI> Vector2(const Vector2int16&) [Giulio]
+ <LI> RenderDevice::setObjectShader
+ <LI> RenderDevice::setVertexAndPixelShader
+ <LI> G3D::RenderDevice supports "..._CURRENT" as an option for most settings
+ <LI> inf -> inf(), nan -> nan(), NAN -> NAN()
+ <B>This is an incompatible change-- it was needed to fix a bug with the order
+ of initialization of globals</B>
+ <LI> GImage::sizeInMemory
+ <LI> Defined std::ostream << NetAddress, std::ostream << Vector3
+ <LI> 'build doc' copies the contrib directory to the install directory
+ <LI> LightweightConduit::PacketSizeException
+ <LI> Quat::unitRandom() [Giulio]
+ <LI> Color3::wheelRandom
+ <LI> GImage::save and encode now const [Thanks Arni Mar Jonsson]
+ <LI> LightweightConduit::send that accepts multiple destinations
+ <LI> ReliableConduit::multisend
+ <LI> Moved IFSBuilder from demos to contrib
+ <LI> LightweightConduit and ReliableConduit send/receive can now take references as well as pointers
+ <LI> RenderDevice::clear() that takes no arguments
+ <LI> RenderDevice::setShader
+ <LI> G3D::GApp now catches ShaderGroup::ArgumentError exceptions
+ <LI> System::operatingSystem() now includes a version number on Linux
+ <LI> SDLWindow no longer initializes the audio system; use SDL_InitSubsytem if you need audio.
+ <LI> Extended GLenumToString with GL_SHADER_OBJECTS_ARB types.
+ <LI> NVIDIA p-buffer: GLX_SAMPLE_BUFFERS_ARB, GLX_SAMPLES_ARB, GLX_FLOAT_COMPONENTS_NV,
+ glXDestroyGLXPbufferSGIX, glXChooseFBConfigSGIX, glXCreateGLXPbufferSGIX,
+ glXCreateContextWithConfigSGIX, glXQueryGLXPbufferSGIX
+ <LI> NVIDIA swap lock: glXJoinSwapGroupNV, glXBindSwapBarrierNV, glXQuerySwapGroupNV,
+ glXQueryMaxSwapGroupsNV, glXQueryFrameCountNV, glXResetFrameCountNV
+ <LI> OSWindow::requiresMainLoop, OSWindow::runMainLoop (Beta)
+ <LI> OSWindow::pollEvent, SDLWindow::pollEvent
+ <LI> G3D::GApp accepts an optional OSWindow on construction
+ <LI> G3D::VertexAndPixelShader, G3D::ObjectShader (Beta)
+ <LI> Deprecated GPUProgram, VertexProgram, and PixelProgram (the OpenGL 1.5 shaders
+ follow a different paradigm than the OpenGL 1.3 ones, so the G3D API must change
+ to match it).
+ <LI> Support for GL_ARB_vertex_shader, GL_ARB_fragment_shader, and GL_ARB_shader_objects
+ <LI> G3D::drawFeatureEdges
+ <LI> const Array<Vector3>& G3D::MD2Model::PosedModel::objectSpaceFaceNormals();
+ <LI> G3D::RenderDevice::sendSequentialIndices
+ <LI> Network_Demo
+ <LI> contrib/Win32Window
+ <LI> contrib/pingtest
+ <LI> contrib/GlutWindow [Morgan and Dan Keefe]
+ <LI> contrib/ObjModel [Corey Taylor]
+ <LI> G3D::GLCaps
+ <LI> GAppSettings::logFilename
+ <LI> Deprecated RenderDevice::suportsOpenGLExtension, RenderDevice::supportsImageFormat,
+ other supports shortcuts (use GLCaps instead).
+ <LI> DiscoveryClient::cleanup
+ <LI> Optimized BinaryInput::readUInt32, readUInt16
+ <LI> Extended network documentation
+ <LI> 'fastlib' build target for G3D library developers
+ <LI> glGetVector2, glGetVector3, glGetVector4
+ <LI> float * Quat (double * Quat already existed)
+ <LI> GApp automatically generates g3d-license.txt at runtime ([RFE#856338] CREDIT.TXT)
+ <LI> G3D::license
+ <LI> Removed several large files (tag, ppt, exe) from the source zipfile, bringing it down to 3 MB
+ <LI> Improved CoordinateFrame:pointToObjectSpace() (RFE#715996) [Giulio]
+ <LI> [RFE#945935] Make static constants into functions [Giulio]
+ <LI> Fix: LightweightConduit::send verifies that the packet size is smaller than the UDP limit
+ <LI> Fix: Multitexture on ATI and Wildcat cards
+ <LI> Fix: Incorrect occlusion in GLG3D_Demo (was caused by global constant problem)
+ <LI> Fix: [BUG#949377] Checks for stencil extensions [Giulio]
+ <LI> Fix: [BUG#922725] Non-multitexture implementation for getTextureState() [Giulio]
+ <LI> Fix: Restore ambient light color after RenderDevice::popState
+ <LI> Fix: RenderDevice now initializes OpenGL extensions before testing for multitexture [Erik Cassel, Dan Keefe]
+ <LI> Fix: Bottom clipping plane of GCamera frustum now correct (was slanted incorrectly, making frustum too big)
+ <LI> Fix: GFont::draw2D now returns correct y value (used to be too small)
+ <LI> Fix: NetworkDevice now returns useful hostname on Linux (used to be "localhost")
+ <LI> Fix: The conduit returned from NetworkDevice::createReliableConduit now has ok() == false when connect fails
+ <LI> Fix: Tangent space computation of constant u, v now correct (was missing a factor of 2, leading to slight errors) [Max McGuire]
+ <LI> Fix: [ 925456 ] select broken on Linux (Networking was broken on Linux)
+ <LI> Fix: getDepthBufferValue off by 1 [Andi Fein]
+ </UL>
+
+<hr>
+ <P>
+ Changes in 6.02:
+ <UL>
+ <LI> Default constructor for Line.
+ <LI> Various patches to make G3D work with the CAVE [Dan Keefe]
+ <LI> AABox::set
+ <LI> Made OSWindow::setPosition non-const
+ <LI> VARArea now tests for the presence of all VBO extensions, on the freak chance that
+ a driver has only partial support (due to a bug)
+ <LI> Linux build statically links OpenGL 1.2.1 and loads extensions through OpenGL 1.5
+ to work around Wildcat Linux driver bug (Windows and Mac statically link OpenGL 1.1
+ and load extensions through OpenGL 1.5)
+ <LI> Triangle stores precomputed edge lengths
+ <LI> Ray-triangle with vertex weights
+ <LI> Highly optimized ray-triangle intersection test [Tomas Moller & Ben Trumbore]
+ <LI> Create a texture from 6 different cube-map filenames
+ <LI> Added contrib directory built as part of the 'doc' target
+ <LI> contrib/CoreyGWindow: OSWindow implementations for various platforms
+ <LI> AABSPSet::beginRayIntersection [Pete Hopkins]
+ <LI> AABSPTree::beginBoxIntersection
+ <LI> CollisionDetection::intersectionTimeForMovingPointFixedAABox, Ray::intersectionTime(AABox)
+ [Pierre Terdiman and Andrew Woo]
+ <LI> Triangle::center
+ <LI> Renamed KDTreeSet to AABSPTree, old name is #defined
+ <LI> RenderDevice now works on cards without multitexture
+ <LI> void glTexCoord(const G3D::Vector4& t); [Dan Keefe]
+ <LI> Overloaded float, double, and int * Matrix3
+ <LI> Fix: [ 923944 ] Matrix/Quat ambiguity
+ <LI> Fix: fuzzyEq(inf, inf) is true
+ <LI> Fix: Triangle::randomPoint returns values outside the triangle
+ <LI> Fix: [ 913763 ] tokenTypeToString(Token::END)
+ <LI> Fix: Compute number of texture coordinates before RenderDevice::setVideoMode [Dan Keefe]
+ <LI> Changed the default depth bits to '0' for wider compatibility
+ (Fix: Unable to create OpenGL screen: Couldn't find matching GLX visual)
+ <LI> Fix: [912305] Table, Queue, and Set assignment operators do not free old values
+ <LI> Fix: Separate specular and Multisample on Tablet PC w/ Trident [Dan Keefe]
+ <LI> Fix: Linux debug build now has line numbers
+ <LI> Upgraded to SDL 1.2.7
+ Fix: [ 838030 ] SDL 1.2.6 blocks prompt
+ Fix: FSAA does not work under SDL
+ Fix: Default Win32 refresh rate
+ <LI> Draw::vertexVectors
+ <LI> New meshes from Brown University: hemisphere.ifs, curvy.ifs, head.ifs,
+ closed-low-poly-teapot.ifs, bump.ifs
+ <LI> GLight::specular
+ <LI> SDLWindow::setWindowDimensions and setWindowPosition now work on Win32
+ <LI> GWindowSettings::x, GWindowSettings::y, GWindowSettings::center
+ <LI> System::setEnv
+ <LI> [ 909999 ] OSWindow Joystick interface
+ <LI> double * Quat ([ 909305 ] scalar * {quat, vector, matrix})
+ <LI> Increased the precision of several Vector2 and Vector3 methods
+ <LI> MeshAlg::computeNormals now returns 0 instead of NaN for degenerate normals
+ <LI> Updated main-no-GApp.cpp for 6.02
+ <LI> RenderDevice::screenshotPic can copy from the back buffer
+ <LI> Improved VAR documentation.
+ <LI> If NO_SDL_MAIN is defined, G3D does not attempt to link against sdlmain.lib
+ <LI> UserInput::setPureDeltaMouse
+ <LI> UserInput::mouseXY, mouseX, mouseY
+ <LI> UserInput::mouseDXY
+ <LI> Deprecated UserInput keyMapping constructor argument
+ <LI> RenderDevice::setDrawBuffer [Dan Keefe]
+ <LI> GFont::draw3D [Dan Keefe]
+ <LI> GImage::pixel3(x, y) and GImage::pixel4(x, y)
+ <LI> debugAssert, debugBreak, debugAssertM, etc. all release input grab
+ when an assertion fails (Win32 and Linux) and restore it when the
+ program continues (Win32). This also fixes the DirectInput laggy
+ cursor that occurs after a break.
+ </UL>
+
+<hr>
+ <P>
+ Changes in 6.01:
+ <UL>
+ <LI> Default constructor for G3D::LineSegment
+ <LI> Rect2D::clipPoly (Pete & Morgan)
+ <LI> Draw::poly2D, Draw::poly2DOutline (Pete & Morgan)
+ <LI> Added instructions for rotated text to G3D::GFont::draw2D
+ <LI> Fix: iRandom now compiles correctly under gcc.
+ <LI> Fix: [ 852076 ] Compute better/faster vertex normals in MeshAlg
+ MeshAlg::computeNormals now weighs adjacent faces by their area
+ <LI> Fix: [ 896028 ] Textures broken on Trident TabletPC (Dan Keefe)
+ <LI> Fix: [ 860800 ] ManualCameraController cursor jumps
+ <LI> Fix: G3D::UserInput no longer offsets the mouse position by 1/2 pixel
+ <LI> Fix: Alt-Tab no longer toggles the GApp camera before switching windows
+ <LI> Fix: [ 901248 ] Font bounds y-value incorrect
+ <LI> Fix: G3D::PhysicsFrame::toCoordinateFrame() was rotated by 90 degrees
+ <LI> Fix: [ 895493 ] Radeon 7500 Cube Map
+ <LI> Fix: G3D::MeshAlg::computeWeld produces linker errors on Linux
+ <LI> G3D::TextInput::peekLineNumber(), G3D::TextInput::peekCharacterNumber()
+ <LI> G3D::GAppSettings::dataDir
+ <LI> html/gettingstarted.html
+ <LI> G3D::MeshAlg::debugCheckConsistency
+ <LI> G3D::MD2Model and G3D::IFSModel now weld their adjacency information
+ <LI> Renamed/retyped G3D::PosedModel::adjacentFaces to G3D::PosedModel::vertices
+ (most programs can be fixed by changing the type from Array< Array<int> > to
+ Array<MeshAlg::Vertex> and adjacentVertexArray[v] to vertexArray[v].faceIndex)
+ <LI> Shadow volumes now use the welded adjacency information
+ <LI> G3D::PosedModel now offers both welded and non-welded adjacency information
+ <LI> G3D::contains for C-Arrays
+ <LI> Generate .tag files in the build
+ <LI> G3D::MeshAlg::computeAdjacency does not merge colocated vertices
+ <LI> G3D::MeshAlg::computeAdjacency does not remove degenerate faces and edges
+ <LI> G3D::MeshAlg::Vertex
+ <LI> G3D::Vector3::directionOrZero
+ <LI> G3D::GMaterial
+ <LI> ManualCameraController renamed to G3D::FPCameraController
+ <LI> glGetCurrentContext (beta)
+ <LI> G3D::RenderDevice::supportsImageFormat
+ <LI> G3D::Vector3::magnitude
+ <LI> G3D::Vector3::cross() [returns Matrix3]
+ <LI> G3D::Quat changes (API is still in beta)
+ <LI> G3D::Quat::norm now returns the 2-norm, not the function Dave Eberly uses.
+ <LI> Matrix3 default constructor
+ <LI> Switched UserInput to use SDLWindow internally
+ <LI> Switched RenderDevice to use SDLWindow internally
+ <LI> G3D::Window
+ <LI> G3D::SDLWindow
+ <LI> Renamed G3D::RenderDeviceSettings to G3D::WindowSettings (with a typedef for the old name)
+ <LI> IFSModel now loads models with up to 10 million polygons (like the buddha).
+ <LI> Internal G3D::KDTreeSet state now private.
+ </UL>
+
+<hr>
+ <P>
+ Changes in 6.00:
+ <UL>
+ <LI> FIX: warning: passing `double' for argument 1 of `void G3D::Queue<T>::repackAndRealloc(int)'
+ <LI> Optimized static Matrix3::transpose (36 cycle) and
+ Matrix3::mul (52 cycle) variations.
+ <LI> Changed some lerp arguments from float to double
+ <LI> MeshAlg::computeTangentSpaceBasis
+ <LI> Draw::axes now uses scale to compute axis length
+ <LI> New ParallaxBump demo
+ <LI> Changed several Vector3 return values from float to double
+ <LI> Real-world stars, sun, and moon path (Nick Musurca)
+ <LI> Now compiles under MSVC++ 7.0 (David Baszucki)
+ <LI> Now compiles under g++ OS/X (Ben Landon)
+ <LI> Changed the default RenderDeviceSettings::alphaBits to 0 in the hope that it
+ will work with more graphics cards.
+ <LI> Matrix3::fromX methods became factory methods
+ <LI> G3D::sinc
+ <LI> Multi-platform lib directories
+ <LI> Vector3::average(), Color3::average(), Vector3::sum(), Color3::sum()
+ <LI> Ray::reflect, Ray::refract
+ <LI> Physically correct sky model
+ <LI> FIX: Older graphics cards can now initialize properly
+ <LI> Increased fuzzyEpsilon to 0.000001
+ <LI> Color3::max, Color3::min, Color4::max, Color4::min
+ <LI> Array::sortSubArray
+ <LI> GCamera::getClipPlanes now takes a G3D::Array
+ <LI> G3D::AABox
+ <LI> Box::randomInteriorPoint, Box::randomSurfacePoint
+ <LI> Vector3::cosRandom, Vector3::hemiRandom, Vector3::reflectAbout, Vector3::reflectionDirection, Vector3::refractionDirection
+ <LI> log(Color3)
+ <LI> Upgraded to zlib 1.2.1
+ <LI> VAR::valid (Peter)
+ <LI> System::getLocalTime, System::getTicks
+ <LI> High-performance cycle count and time queries on Linux
+ <LI> UserInput::anyKeyPressed
+ <LI> G3D::Box now provides axes, center, and extent information
+ (serialization is backwards compatible to 5.xx)
+ <LI> TextInput's exceptions now provide file, line, and character numbers
+ as well as preformatted error messages in the style of MSVC++.
+ <LI> G3D::Texture::fromGImage
+ <LI> G3D::TextInput now parses hex numbers of the form 0x#####
+ <LI> G3D::CollisionDetection::penetrationDepthForFixedSphereFixedPlane
+ <LI> G3D::CollisionDetection::penetrationDepthForFixedSphereFixedBox
+ <LI> G3D::beginMarkShadows, G3D::endMarkShadows, G3D::markShadows
+ <LI> GFont::draw2D now returns the string bounds
+ <LI> Sphere::surfaceArea, Sphere::volume, Box::surfaceArea, Box::volume
+ <LI> Two-sided stencil operations
+ <LI> Removed G3D::Real
+ <LI> FIX: [ 855947 ] Fonts are broken on Radeon
+ <LI> Switched vertex arrays to use the new ARB_vertex_buffer_object extension.
+ Compared to 5.xx rendering speed: NVIDIA/Win32 is the same (fast),
+ ATI and Linux rendering are about 10x faster. The API has changed
+ slightly-- most significant, the vertex, normal, color, etc. arrays
+ must all come from the same VARArea now.
+ <LI> Disabled the "conditional is constant" level 4 warning on Windows
+ that is triggered by the for-loop scoping fix.
+ <LI> G3D::SkyParameters::directionalLight
+ <LI> G3D::TextureManager (Peter S. & Morgan)
+ <LI> Flipped skybox X-axis to match OpenGL cube map coordinates
+ <LI> Texture now uses hardware MIP-map generation
+ <LI> Texture::copyFromScreen for cube map faces
+ <LI> RenderDevice::configureReflectionMap
+ <LI> RenderDevice::configureShadowMap
+ <LI> Renamed CFont to GFont
+ <LI> Renamed CImage to GImage
+ <LI> G3D::Matrix3::getRow
+ <LI> Added optional argument drawCelestialBodies to Sky::create.
+ <LI> RenderDevice::getTextureMatrix
+ <LI> Depth Textures
+ <LI> Texture::createEmpty
+ <LI> RenderDevice::setViewport has flipped the y-axis since version 5.00
+ <LI> ReferenceCountedPointer::isLastReference
+ <LI> Support for textures beyond the number of texture units (which occurs on NVIDIA cards)
+ <LI> G3D::PosedModel
+ <LI> G3D::IFSModel
+ <LI> G3D::CoordinateFrame::normalToObjectSpace, G3D::CoordinateFrame::normalToWorldSpace
+ <LI> Simplified arguments on Texture::copyFromScreen
+ <LI> Moved Camera in GLG3D to GCamera in G3D
+ <LI> Moved setProjectionAndCameraMatrix from Camera to RenderDevice
+ <LI> Moved G3D::Rect2D to G3D from GLG3D, changed interface
+ <LI> G3D::setRenderMode
+ <LI> G3D::RenderDevice::setSpecularCoefficient, G3D::RenderDevice::setShininess
+ <LI> G3D::GLight
+ <LI> Renamed G3D::RenderDevice::configureDirectionalLight, configurePointLight to G3D::RenderDevice::setLight
+ <LI> Changed G3D::Rect2D to use doubles
+ <LI> G3D::Camera::setPosition()
+ <LI> G3D::Camera::lookAt()
+ <LI> G3D::ManualCameraController::setPosition()
+ <LI> G3D::System::getTick, G3D::System::getLocalTime
+ <LI> Fixed [ 839618 ] peak var only updated on reset()
+ <LI> G3D::Array::findIndex (thanks to David Baszucki for the suggestion)
+ <LI> Removed RenderDevice::setProjectionMatrix3D and RenderDevice::setProjectionMatrix2D
+ <LI> RenderDevice::project
+ <LI> RenderDevice::push2D() now uses the current viewport instead of full screen by default
+ <LI> RenderDevice::getViewport
+ <LI> G3D::SimTime
+ <LI> Sky::render no longer needs a camera matrix (it gets it from the render device)
+ <LI> SkyRef, Sky::create()
+ <LI> Removed Sky::getName
+ <LI> Removed RenderDevice::setAmbientLightLevel (duplicated RenderDevice::setAmbientLightColor)
+ <LI> G3D::GApp, G3D::GApplet, G3D::GAppSettings
+ <LI> RenderDevice::getCardDescription
+ <LI> GPUProgram interface for setting program constants [Peter, Morgan & Dan]
+ <LI> RenderDevice::getModelViewMatrix
+ <LI> RenderDevice::getModelViewProjectionMatrix
+ <LI> RenderDevice::getProjectionMatrix
+ <LI> Documented some more common compiler errors.
+ <LI> Moved RenderDevice::debugDraw methods to the Draw class, changed rendering from
+ cylinders to lines for wireframe (for performance)
+ <LI> Ray::direction no longer has unit length
+ <LI> Line::point, Line::direction
+ <LI> LineSegment::endPoint
+ <LI> IFSBuilder loads Brown University Sketch Model (sm) format
+ <LI> New IFS models: angel, distributor-cap, dragon2, duck, elephant, hippo, hub, mech-part, rotor, sandal, trumpet, venus-torso, woman
+ <LI> RenderDevices are now optionally resizable
+ <LI> MeshAlg::computeWeld
+ <LI> Array::randomize
+ <LI> Table now refuses to push the load factor above 19/20 and stops rehashing
+ <LI> Table always keeps an odd number of buckets
+ <LI> Sphere::randomInteriorPoint, Sphere::randomSurfacePoint
+ <LI> LineSegment::randomPoint
+ <LI> Hardcoded some common paths into demoFindData
+ <LI> Deprecated old RenderDevice::init method.
+ <LI> Full screen anti-aliasing (FSAA)
+ <LI> G3D::RenderDeviceSettings
+ <LI> All 2, 3, and 4 character swizzles for Vector2, Vector3, Vector4 are defined.
+ <LI> G3D::rsqrt
+ <LI> Most vector methods are also defined as functions now
+ <LI> sign(Vector2), sign(Vector3), sign(Vector4)
+ <LI> G3D::Matrix4
+ <LI> Changed G3D_VER from double to integer
+ <LI> G3D::lerp
+ <LI> Changed G3D::PI, G3D::HALF_PI, and G3D::TWO_PI to #defines
+ <LI> Vector2::clamp, Vector3::clamp, Vector4::clamp
+ <LI> Changed order of arguments to all lerp methods to match DirectX/Cg
+ <LI> Changed order of arguments to G3D::clamp and G3D::iClamp to match DirectX/Cg
+ <LI> G3D::ManualCameraController::ManualCameraController now requires a G3D::UserInput
+ <LI> G3D::UserInput::appHasFocus
+ <LI> G3D::ManualCameraController now stops tracking the mouse when the app loses focus
+ <LI> G3D::ManualCameraController::setActive
+ <LI> G3D::ManualCameraController now manages the mouse cursor instead of G3D::RenderDevice
+ <LI> G3D::UserInput::getMouseXY, G3D::UserInput::getXY
+ <LI> RenderDevice::debugDrawVertexNormals
+ <LI> GPUProgram, VertexProgram, and PixelProgram now recognize the output of the
+ Cg compiler and automatically bind constants.
+ <LI> RenderDevice now loads glActiveStencilFaceEXT
+ <LI> RenderDevice::numTextureCoords
+ <LI> Moved changelog to a separate page
+ <LI> Reformatted overview to be smaller
+ <LI> Added model debugging info to the IFSBuilder display
+ <LI> Welded some broken vertices in the teapot.ifs file
+ <LI> Renamed Font.* to CFont.*
+ <LI> CFont::draw2DString renamed to CFont::draw2D (use a #define to port old code)
+ <LI> MeshAlg
+ <LI> RenderDevice now enables GL_COLOR_MATERIAL by default
+ <LI> msgBox
+ <LI> MD2 model gallery in documentation (Kevin)
+ <LI> MD2Documentor (Kevin)
+ <LI> debugAssertGLOk macro
+ <LI> VertexProgram now supports NVIDIA Vertex Program 2.0
+ <LI> RenderDevice now loads glGenProgramsNV, glDeleteProgramsNV, glBindProgramNV, glLoadProgramNV, glTrackMatrixNV, glProgramParameter4fvNV, glGetProgramParameterfvNV, glGetProgramParameterdvNV extensions
+ <LI> VertexProgram and PixelProgram static factory methods now return reference counted values.
+ <LI> Split the reference value from RenderDevice::setStencilTest into setStencilConstant
+ <LI> RenderDevice::STENCIL_INVERT, RenderDevice::STENCIL_REPLACE, RenderDevice::STENCIL_ZERO
+ <LI> Added brighten argument to Texture::fromFile
+ <LI> Increased CImage JPEG save quality
+ <LI> RenderDevice::screenshot now returns the name of the file that was written
+ <LI> nextPowerOf2 renamed to ceilPow2
+ <LI> System::alignedMalloc, System::alignedFree
+ <LI> Carbon, Crackman, Edenmill, Futurist, Interplanetary,
+ Iomanoid, Starlight, Lesser, and Wild fonts by Ray Larabie.
+ Like all of our fonts, they are free, but please consider a
+ donation to him if you like them. http://www.larabiefonts.com/
+ <LI> MD2Model_Demo
+ <LI> G3D::MD2Model
+ <LI> FIX: Fixed a bug in Array shrinking that could cause memory corruption
+ <LI> FIX: RenderDevice windows with an aspect ratio of less than 1 now allowed.
+ <LI> FIX: TextInput now parses '#', '~', '~=', '&', '&&', '|', '||' correctly
+ <LI> VARArea::reset() now waits for rendering calls using its vertex
+ arrays to complete before wiping the memory.
+ <LI> G3D::filenameBaseExt, G3D::filenameExt
+ <LI> VARArea::finish()
+ <LI> Milestone
+ <LI> TextInput::Options::signedNumbers
+ <LI> RenderDevice now loads glFlushVertexArrayRangeNV
+ <LI> Vector2int16
+ <LI> RenderDevice::freeVARSize()
+ <LI> Array now allocates 16-byte aligned pointers.
+ <LI> Decreased the default camera movement rate by 50% for better resolution.
+ <LI> RenderDevice enables GL_NORMALIZE by default
+ <LI> Improved the performance of Array::append/Array::push/Array::next
+ <LI> Fix: [ 875219 ] Array::sort must use std::sort
+ <LI> Array::next
+ <LI> Array::reverse
+ <LI> PCX file loading
+ <LI> Test images
+ <LI> Color3uint8 as uint8[] addressing
+ <LI> Color4uint8 as uint8[] addressing
+ <LI> Removed const from VAR::pointer
+ <LI> ReferenceCountedPointer::isNull
+ <LI> alwaysAssertM
+ <LI> Log::common, Log::getCommonLogFilename
+ <LI> Switched from static to dynamic linking of zlib
+ <LI> Upgraded to zlib 1.1.3
+ <LI> On Win32 the lib list is automatically updated through pragmas
+ (5.xx programs should revert to linking against default libraries)
+ <LI> Increased default sky quality to 1.00
+ <LI> G3D::CFontRef
+ <LI> RenderDevice now loads all register combiner extensions (NVIDIA only)
+ <LI> Sky::getEnvironmentMap
+ <LI> Sky implementation now uses a cube map (when one is available)
+ <LI> G3D::Sky constructor now takes a render device
+ <LI> Rotated Sky box 90 degrees to match environment maps
+ <LI> G3D::Sky now takes the environment filenames as "sky_*.jpg" instead of "sky_ft.jpg"
+ <LI> Added default filename for Sky constructor
+ <LI> Added caustics textures created with Kjell Andersson's generator http://www.lysator.liu.se/~kand/caustics/
+ <LI> #defined "for" under MSVC so that it obeys C99 scoping rules
+ <LI> System::consoleKeyPressed
+ <LI> System::consoleClearScreen
+ <LI> System::consoleReadKey
+ <LI> NetMessage::type()
+ <LI> Changed the Conduit message protocol to include a message type.
+ The API is backwards compatible to 5.01 even though the protocol is not.
+ <LI> Removed optional argument maxSize from LightweightConduit::receive.
+ <LI> NetAddress::serialize
+ <LI> NetAddress::deserialize
+ <LI> NetAddress == NetAddress
+ <LI> hashCode(NetAddress)
+ <LI> RenderDevice::init now prints ATI or NVIDIA driver version to the log under Windows
+ <LI> readme.html library build instructions now have downloads for required libraries
+ <LI> Library list has changed for Win32 (added version.lib)
+ <LI> System::cpuArchitecture
+ <LI> System::operatingSystem
+ <LI> double-precision Plane::getEquation
+ <LI> Vector2::lerp
+ <LI> Platform specific #defines G3D_WIN32, G3D_LINUX, G3D_OSX
+ <LI> G3D::Array::contains
+ <LI> G3D::Queue::contains
+ <LI> G3D::ImageFormat
+ <LI> G3D::Texture::DIM_CUBE_MAP
+ <LI> G3D::Texture resizes non-power of two textures
+ <LI> G3D::Texture constructors are completely changed from 5.01 (and hopefully easier to use)
+ <LI> G3D::CImage now supports images with alpha
+ <LI> Removed most of the width/height arguments from G3D::Camera methods
+ <LI> BinaryInput::readBytes and BinaryOutput::writeBytes now take void* as an argument to avoid casting
+ <LI> Plane::fromEquation
+ <LI> Removed Plane::getNormal (use Plane::normal instead)
+ <LI> Removed CDTriangle (use G3D::Triangle instead)
+ <LI> Removed Font (use G3D::CFont instead)
+ <LI> FIX: Camera::getClipPlanes now transforms infinite planes correctly.
+ <LI> FIX: The last reference of an RGC pointer assigned to itself no
+ longer tries to collect before re-assigning
+ </UL>
+
+<hr>
+ <P>
+ Changes in 5.01
+ <UL>
+ <LI> G3D::tesselateComplexPolygon
+ <LI> G3D::ConvexPolygon
+ <LI> G3D::ConvexPolyhedron
+ <LI> G3D::iClamp, G3D::clamp
+ <LI> G3D::iWrap
+ <LI> G3D::iRandom, G3D::random
+ <LI> G3D::getFiles
+ <LI> G3D::getDirs
+ <LI> G3D::VAR::pointer
+ <LI> G3D::realWorldLocalTime
+ <LI> G3D::Texture::TRANSPARENT_BORDER
+ <LI> DECLARE_GLFORMATOF
+ <LI> G3D::System::machineEndian
+ <LI> G3D::VertexProgram, G3D::VertexProgramRef, G3D::RenderDevice::setVertexProgram
+ <LI> G3D::PixelProgram, G3D::PixelProgramRef, G3D::RenderDevice::setPixelProgram
+ <LI> G3D::GPUProgram, G3D::GPUProgramRef
+ <LI> G3D::sizeOfGLFormat
+ <LI> G3D::RenderDevice::setVertexAttrib
+ <LI> G3D::Vector2*=Vector2, /= Vector2, * Vector2, / Vector2
+ <LI> glFormatOf
+ <LI> G3D::Color4uint8
+ <LI> G3D::Color3uint8
+ <LI> G3D::Vector3int16
+ <LI> G3D::System::currentProgramFilename
+ <LI> CImage::insertRedAsAlpha
+ <LI> CImage::stripAlpha
+ <LI> Texture::hasAlpha
+ <LI> Added support for TGA with alpha channel
+ <LI> Re-implemented Texture to support a broader range of formats and cleaner implementation.
+ <LI> Fix: Improved Texture::LUMINANCE support
+ <LI> Added == and != overloads for Texture::Ref so that "a != NULL" is now legal and does not require a cast to Texture::Ref.
+ <LI> G3D::CFont is a typedef for G3D::Font to avoid name conflicts with X11 Font under Linux. In future releases, the name Font will be deprecated.
+ <LI> RenderDevice::setPointSize
+ <LI> Added a new teapot (teapot.ifs) that is closed, with a properly fitting top. The classic teapot is now called "utah-teapot.ifs" (Sebastian Schuberth and Simon Winkelbach)
+ <LI> RenderDevice::init now loads glPointParameterfvARB, glPointParameterfARB,
+ glMultiDrawArraysEXT, and glMultiDrawElementsEXT functions.
+ <LI> GLenumToString(4) now returns "GL_TRIANGLES" instead of "GL_LINE_BIT" (both are correct)
+ <LI> Added TextInput::Options to optionally allow C++ comments to
+ be treated as two slashes instead of a comment
+ <LI> Added data/image/meter.jpg, a meter stick texture convenient for testing
+ <LI> Added sansserif, news, and terminal fonts based on Bitstream's <A HREF="http://www.gnome.org/fonts/">free fonts</A>
+ <LI> RenderDevice::numTextureUnits
+ <LI> Added stars to night Sky
+ <LI> Added classic GL dinosaur model as data/ifs/dinosaur.ifs
+ <LI> Documented G3D::glGetProcAddress
+ <LI> Fix: Texture now restored GL_ENABLE bits properly after creation
+ <LI> Fix: Texture::sizeInMemory now accounts for MIP-map levels
+ <LI> Fix: Fonts and skies now adjust their brightness for the screen gamma level
+ <LI> Fix: Strange compilation bug was causing Sky to be black for some programs
+ <LI> resolveFilename
+ <LI> GLProgram_Demo to show how to use vertex programs in G3D
+ <LI> Support for GL_ARB_vertex_program
+ <LI> Modified ManualCameraController so that diagonal movement does not exceed
+ maximum rate.
+ <LI> Added support for non-GL_FLOAT vertex arrays to RenderDevice
+ <LI> Added support for Wavefront OBJ files to IFSBuilder
+ <LI> Removed duplicate copies of SDL.dll from the source tree
+ <LI> Renamed G3D::CDTriangle to G3D::Triangle
+ <LI> Added several G3D::Triangle methods
+ <LI> Moved CollisionDetection::primaryAxis to Vector3::primaryAxis
+ <LI> Fix: Texture::sizeInMemory now returns correct results for RGB8 textures.
+ <LI> Changed texture constructors in ways that slightly break backwards compatibility
+ <LI> Deprecated several arguments to the texture constructors.
+ </UL>
+
+<hr>
+ Changes in 5.00
+ <UL>
+ <LI> Color3::operator*=(const Color3&)
+ <LI> Color3::operator*(const Color3&)
+ <LI> Eliminated duplicate GL headers [James O'Sullivan]
+ <LI> Linux Makefiles [James O'Sullivan, Jordan Parker]
+ <LI> RenderDevice::getProjectionMatrixParams
+ <LI> RenderDevice::debugDrawCylinder
+ <LI> Added an option to not copy input memory for BinaryInput
+ <LI> Added data/ifs/sphere.ifs
+ <LI> Added data/ifs/spikeball.ifs
+ <LI> Added a new (imperfect) demo/tool that converts 3DS and MD2 to IFS.
+ <LI> Added RenderDevice to the Font constructor
+ <LI> Removed RenderDevice from Font::drawString
+ <LI> Included glut32.lib, .dll, and .h (Version 3.7.6) in the distribution.
+ The windows glut port is by Nate Robbins and is from
+ http://www.xmission.com/~nate/glut.html.
+ glut was originally written by Mark Kilgard.
+ <LI> Modified OpenGL headers to work cross platform, with the latest NVIDIA extensions
+ <LI> Changed library name from graphics3D.lib to G3D.lib, same for
+ debug version.
+ <LI> Changed directory structure and added readme.html to explain
+ the new setup.
+ <LI> Changed BinaryInput::readBytes to allow reading onto the stack
+ <LI> Added Vector4::isFinite
+ <LI> G3D::CDTriangle (for 35% faster collision detection)
+ <LI> CollisionDetection::closestPointToRectangle
+ <LI> CollisionDetection::movingSpherePassesThroughFixedBox
+ <LI> CollisionDetection::movingSpherePassesThroughFixedSphere
+ <LI> Changed CollisionDetection::movingXFixedTriangle arguments
+ <LI> CollisionDetection::collisionTimeForMovingSphereFixedSphere
+ <LI> Changed CollisionDetection::distanceToX methods to closestPointToX
+ <LI> Vector3::NAN3
+ <LI> Made Vector3::isUnit fuzzy
+ <LI> Made Vector3::isZero fuzzy
+ <LI> Fix: Texture(std::string, std::string) constructor now works for alpha-only textures.
+ <LI> FIX: Array now calls copy constructor when resizing
+ <LI> FIX: Triangle-sphere and rectangle-sphere collision detection
+ returned an incorrect collision location; now fixed.
+ <LI> FIX: changed VectorX::isFinite to call isFinite (used to give bad result for NaNs)
+ <LI> FIX: Used the normalized edge to compute intersection in
+ CollisionDetection::distanceToTrianglePerimeter
+ <LI> FIX: Changed the order of corners returned from Box::getFaceCorners so the
+ face is ccw, facing out
+ <LI> FIX: ManualCameraController::lookAt now faces along the -z axis.
+ <LI> FIX: data/ifs/icosa.ifs model is now an icosahedron
+ <LI> Made Set::begin() and Set::end() const
+ <LI> Added ifdef _WIN32 all over for typedefing types from Windows to Linux and vice versa.
+ <LI> G3D::isNaN, G3D::isFinite
+ <LI> Added a single triangle triangle.ifs file
+ <LI> G3D::LineSegment
+ <LI> RenderDevice::debugDrawRay
+ <LI> CoordinateFrame::toObjectSpace(Ray&)
+ <LI> CoordinateFrame::toObjectSpace(Box&)
+ <LI> CoordinateFrame::toObjectSpace(Sphere&)
+ <LI> Changed CollisionDetection routines to return the surface normal of the
+ surface at the collision location.
+ <LI> CollisionDetection::collisionTimeForMovingPointFixedCapsule
+ <LI> CollisionDetection::collisionTimeForMovingSphereFixedCapsule
+ <LI> G3D::Capsule class
+ <LI> Removed e-mail addresses from contributor list to protect them from spammers
+ <LI> Linux port [Hari Khalsa & Chris Kern]
+ <LI> Added serialize and deserialize methods, deserializing constructor to
+ Vector2, Vector3, Vector4, Color3, Color4, Matrix3, CoordinateFrame, Box,
+ Sphere, Plane, Ray, Line, Capsule, LineSegment
+ <LI> Moved parts of Plane.h into Plane.cpp
+ <LI> BinaryInput::readBool8 and BinaryOutput::writeBool8
+ <LI> G3D::System [based on Michael Herf, Rob Wyatt, and Benjamin
+ Jurke's work]
+ <LI> Networking infrastructure: G3D::NetworkDevice, G3D::NetAddress,
+ G3D::ReliableConduit, G3D::LightweightConduit, G3D::NetListener
+ <LI> G3D::Camera
+ <LI> Vector2::toString
+ <LI> G3D::createTempFile
+ <LI> G3D::fileLength
+ <LI> UserInput::setKeyMapping
+ <LI> UserInput::keyCodeToString, UserInput::stringToKeyCode
+ <LI> JPEG library uses createTempFile
+ <LI> JPEG library will allocate up to 6MB before resorting to temp
+ files-- faster and more reliable
+ <LI> Moved SDL initialization to RenderDevice constructor from the init
+ method so extension can be used earlier
+ <LI> Support for up to 8 texture units, no longer crashes on machines
+ that have more than 4 units
+ <LI> Made Arrays allocate at least 32 bytes when resized to improve
+ performance of small char stacks
+ <LI> Added UserInput key codes for mouse wheel buttons
+ <LI> UserInput::keyPressed, UserInput::pressedKeys()
+ <LI> UserInput::GKey
+ <LI> Renamed UserInput::poll() to UserInput::endEvents(), added
+ UserInput::beginEvents()
+ <LI> Moved custom UserInput key codes into an enum so they are
+ compile-time constants
+ <LI> Changed all <io.h> to <stdio.h> for cross-platform [Rob & Chris]
+ <LI> Moved LITTLE_ENDIAN and BIG_ENDIAN constants to an enum and renamed
+ them to G3D_LITTLE_ENDIAN and G3D_BIG_ENDIAN for cross-platform
+ [Rob & Chris]
+ <LI> Permanently fixed the precision of Real to be 32-bit float.
+ <LI> RenderDevice now loads the NVIDIA VAR fence extensions.
+ <LI> Renamed RenderDevice::begin to RenderDevice::beginPrimitive, same
+ for end.
+ <LI> Redesigned the vertex array system; see VAR and VARArea.
+ <LI> Changed GLG3D demo to demonstrate the use of the new VAR and
+ VARArea classes
+ <LI> CoordinateFrame(Vector3) constructor.
+ <LI> Improved the performance of zero-radius sphere [aka point]
+ collision detection
+ </UL>
+
+<hr>
+ <P>
+ Changes in 4.01
+ <UL>
+ <LI> trimWhitespace()
+ <LI> Pointwise multiplication and division for Vector3
+ <LI> Array::sort now uses > operator by default; two alternative sort methods allow qsort style sorting
+ <LI> Texture::copyFromScreen
+ <LI> Texture::invertY
+ <LI> BinaryInput/BinaryOutput compression (via zlib)
+ <LI> Alpha-only G3D::Texture mode
+ <LI> G3D::Font and fonts in data/font
+ <LI> Array::fastRemove
+ <LI> TextInput [Morgan & Aaron]
+ <LI> Color4::CLEAR
+ <LI> Table [] operator now returns a non-const reference
+ <LI> RenderDevice::getFrameRate, RenderDevice::getTriangleRate, RenderDevice::getTriangleCount
+ <LI> ManualCameraController::setMoveRate, ManualCameraController::setTurnRate
+ <LI> SkyParameters default constructor
+ <LI> Vector2, Vector3, Vector4 isZero(), isUnit(), isFinite()
+ <LI> Vector4::length(), Vector4::squaredLength()
+ <LI> isValidPointer now returns false for 0xFEEEFEEE
+ <LI> RenderDevice checks for texture compression extensions
+ <LI> Restructured the directories for the CPP sources (only affects people who build G3D)
+ <LI> Included NVIDIA and SGI OpenGL headers in the distribution, changed install notes
+ <LI> Fixed a bug that previously prevented textures from being garbage collected
+ <LI> Fixed Line::distance returning values too small
+ <LI> Fixed Plane(normal, point) constructor to compute point from normalized direction [Kevin]
+ <LI> LED font by Matthew Welch daffy-duck@worldnet.att.net
+ <LI> VenusRising font by Ray Larabie <A HREF="mailto:drowsy@cheerful.com">drowsy@cheerful.com</A>
+ <LI> VideoFreak font by Jakob Fischer pizzadude@pizzadude.dk
+ </UL>
+
+<hr>
+ <P>
+ Changes in 4.00
+ <UL>
+ <LI> Moved texture combine modes from Textures onto RenderDevice texture units
+ <LI> Documented RenderDevice::getHDC() (Windows only)
+ <LI> Renamed RenderDevice::swapBuffers() to RenderDevice::endFrame(), added corresponding RenderDevice::beginFrame()
+ <LI> Moved getNumJoySticks from RenderDevice to UserInput
+ <LI> Added TEX_ADD combine mode
+ <LI> Table::getKeys and Set::getMembers now have overloads that take an Array as input.
+ <LI> BinaryOutput::getCArray
+ <LI> RenderDevice::getObjectToWorldMatrix(), RenderDevice::getCameraToWorldMatrix()
+ <LI> RenderDevice::debugDrawAxes(), RenderDevice::debugDrawBox(), RenderDevice::debugDrawSphere()
+ <LI> Color3::Color3(const Vector3&) and Color4::Color4(const Vector4&)
+ <LI> Moved hashCode(const Vector3&) and hashCode(const Vector4&) to the global namespace [Kevin]
+ <LI> isValidPointer now returns false for 0xCCCCCCCC and 0xDEADBEEF
+ <LI> Fix: RenderDevice::setPolygonOffset now affects polygons rendered in line and point mode
+ <LI> Fix: Sun is now invisible after it goes below the horizon
+ <LI> Fix: BinaryInput now supports endian-ness correctly in memory read mode
+ <LI> Fix: Table.copyFrom and copy constructor now work
+ </UL>
+
+<hr>
+ <P>
+ Changes in 3.02
+ <UL>
+ <LI> Built libraries using "Multithreaded DLL" [Kevin & Darius]
+ <LI> Added depth, color, and stencil bit depth preferences to G3D::RenderDevice
+ <LI> G3D::Sky (plus sky directory in the data distribution)
+ <LI> Sky cube data [Jauhn Dabz, jauhn@yahoo.com, http://nullpoint.fragland.net]
+ <LI> G3D::UserInput
+ <LI> G3D::ManualCameraController
+ <LI> G3D::SkyParameters
+ <LI> G3D::toSeconds, G3D::AMPM, G3D::GameTime, G3D::RealTime
+ <LI> G3D::RenderDevice::project
+ <LI> G3D::linearSpline
+ <LI> G3D::Color3::fromARGB and G3D::Color4::fromARGB
+ <LI> Added non-const G3D::Array::last() [Kevin]
+ <LI> Modified G3D::RenderDevice::configureDirectionalLight to operate in world space
+ <LI> Fix: Flipped the y-axis of G3D::RenderDevice::getDepthBufferValue so it matches the documentation.
+ <LI> Removed brief descriptions from documentation
+ <LI> Removed sqrt, sin, cos, etc. that conflict with standard library names
+ <LI> Removed TWO_PI constant
+ <LI> Removed G3D::Matrix3 virtual destructor
+ <LI> Removed G3D::Quat virtual destructor [Kevin]
+ </UL>
+
+ <hr>
+ Changes in 3.01
+ <UL>
+ <LI> Changed an assert() to debugAssert() in Queue.h
+ <LI> G3D::Table doesn't grow the number of buckets under bad hash codes [Morgan & Darius]
+ <LI> G3D::Table allocates only 10 initial buckets
+ <LI> G3D::Table::debugGetLoad()
+ <LI> G3D::CollisionDetection::collisionTimeForMovingPointFixedRectangle
+ <LI> G3D::CollisionDetection::collisionTimeForMovingPointFixedBox
+ <LI> G3D::Ray::intersectionTime, G3D::Ray::unit()
+ <LI> G3D::Log [Morgan & Aaron]
+ <LI> G3D::RenderDevice (OpenGL state abstraction. VertexBuffer support is beta only)
+ <LI> G3D::Texture (includes texture compression, image loading, and texture rectangle)
+ <LI> Added a comment to the vector classes noting that they can't be sublcassed [Kevin Egan]
+ </UL>
+<hr>
+ Changes in 3.00
+ <UL>
+ <LI> G3D::NEWLINE
+ <LI> writeStringToFile
+ <LI> Fixed empty stringJoin bug
+ <LI> Fixed parseFilename with no path bug
+ <LI> Vector3::INF3, Vector3::ZERO3
+ <LI> G3D::PhysicsFrame (beta-- this interface is going to change in 4.00)
+ <LI> G3D::Vector4
+ <LI> G3D::Queue
+ <LI> Default constructor for G3D::CImage
+ <LI> G3D::isValidHeapPointer, G3D::isValidPointer
+ <LI> G3D::Ray
+ <LI> CImage copy constructor, CImage::load
+ <LI> Removed \#pragma once for gcc compatibility
+ <LI> Renamed several hashcode methods to hashCode
+ <LI> Fixed fuzzy math to work with infinite numbers
+ <LI> Fixed Table::remove(), Set::remove() bug [Darius Jazayeri]
+ <LI> G3D::CoordinateFrame.toObjectSpace(Vector4), G3D::CoordinateFrame.toWorldSpace(Vector4)
+ <LI> Added the data directory
+ <LI> G3D::CollisionDetection
+ <LI> G3D::Sphere::culledBy()
+ <LI> Added the GLG3D library [Morgan McGuire & Seth Block]
+ <LI> Changed SDL_GL_Demo to use GLG3D, rotate triangle, and use color blending
+ <LI> Fixed debugPrintf to handle long strings on Win32
+ <LI> Wrapped the MMX headers with \#ifdefs [Nate Miller]
+ <LI> Moved OpenGL code out of CoordinateFrame.h/cpp
+ <LI> Fixed BinaryInput readVector*, readColor* to read in correct order [Nate Miller]
+ <LI> BinaryInput::readVector4, BinaryInput::readColor4, BinaryOutput::writeVector4, BinaryOutput::writeColor4
+ <LI> IFS_Demo for loading IFS files, dealing with models in OpenGL [Nate Miller]
+ </UL>
+
+<hr>
+ <P>
+ Changes in 2.00
+ <UL>
+ <LI> Vector2 members renamed to x,y from s,t
+ <LI> Added SDL_GL_Demo and Win32_Demo
+ <LI> Removed Group
+ </UL>
+
+<hr>
+ <P>
+ Changes in 1.10
+ <UL>
+ <LI> CImage, color conversion routines [Morgan McGuire, John Chisholm, and Edward Resnick]
+ <LI> Array dereference for BinaryInput
+ <LI> BinaryInput from memory
+ <LI> BinaryOutput to memory
+ <LI> toUpper(std::string), toLower(std::string)
+ <LI> Group::clear()
+ <LI> inf, nan as global constants (double precision)
+ <LI> Can iterate over const Tables
+ <LI> Table::deleteValues()
+ <LI> Fixed an off-by-one bug in BinaryInput::readString()
+ <LI> beginsWith() and wordWrap() string utilities
+ <LI> prompt dialogs have fixed width font [Kurt Miller]
+ <LI> iMax(), iMin()
+ <LI> Array::sort()
+ <LI> stringCompare(), stringPtrCompare()
+ <LI> readFileAsString()
+ <LI> Fixed textPrompt() to wait for input
+ <LI> BinaryInput.getFilename(), BinaryOutput.getFilename()
+ <LI> ReferenceCount [Justin Miller]
+ <LI> endsWith()
+ <LI> stringSplit(), stringJoin()
+ <LI> Renamed format.* to stringutils.*
+ <LI> fileExists(), parseFilename(), createDirectory(), copyFile()
+ <LI> highestBit() [Jukka Liimatta]
+ <LI> flipRGBVertical()
+ <LI> Changed all header guards to use G3D_ prefix
+ <LI> ConvexPolyhedron
+ <LI> Virtual destructors on almost all objects.
+ <LI> RGBtoBGR()
+ <LI> Color4
+ <LI> Array::pop(bool shrinkArray=true)
+ <LI> Vector2::isFinite, Vector2::fuzzyEq, Vector::fuzzyNe
+ </UL>
+ <P>
+
+<hr>
+ Changes in 1.09
+ <UL>
+ <LI> Removed pointer hash [Aaron Orenstein]
+ <LI> Changed some includes from quotes to pointy brackets [Aaron Orenstein]
+ <LI> Sphere::toString()
+ <LI> Plane::toString()
+ <LI> Added a change log
+ </UL>
+
+ */
diff --git a/externals/g3dlite/doc-files/contributors.dox b/externals/g3dlite/doc-files/contributors.dox
new file mode 100644
index 00000000000..3cd946a0bf1
--- /dev/null
+++ b/externals/g3dlite/doc-files/contributors.dox
@@ -0,0 +1,85 @@
+/**
+@page contributors G3D Developer Credits
+
+ <b>Team Members</b>
+ <br><i>7.01 Release Team</i>
+ <br>Morgan McGuire [Williams College] - Project Manager
+ <br>Corey Taylor [EA] - Assistant Project Manager and Linux Lead
+ <br>Casey O'Donnell [RPI] - OS X Lead
+ <br>Dan Keefe [Brown University] - Developer
+ <br>Kyle Whitson [Williams College] - Developer
+ <br>Danny Yuxing Huang [Williams College] - Developer
+
+ <b>Previous Contributors and Cited Sources</b> <br>This library
+ contains code and resources contributed by the following people, or
+ based open code and articles by them. Starred (*) developers are
+ previous members of the %G3D team.
+
+ <p>David Baszucki
+ <BR>Seth Block
+ <BR>Nicholas Bray
+ <BR>Nick Capens
+ <BR>Erik Cassel
+ <BR>John Chisholm*
+ <BR>Jauhn Dabz
+ <BR>Erik de Castro Lopo
+ <br>Rich Deeson*
+ <BR>Chris Demetriou
+ <BR>L. Peter Deutsch
+ <br>Dmitri*
+ <BR>Dave Eberly
+ <BR>Kevin Egan*
+ <BR>Cass Everitt
+ <BR>Dan Fast*
+ <BR>Andi Fein
+ <BR>Jakob Fischer
+ <BR>Dan Keefe*
+ <BR>Harishabd Khalsa
+ <BR>Nicolai Haehnle
+ <BR>Michael Herf
+ <br>Daniel Hilferty*
+ <BR>Pete Hopkins
+ <br>Danny Yuxing Huang
+ <BR>Peter Hunt
+ <BR>Robert Hunter
+ <BR>Ed Johnson
+ <BR>Benjamin Jurke
+ <BR>Chris Kern
+ <BR>Independent JPEG Group
+ <BR>Darius Jazayeri
+ <BR>Ben Landon*
+ <BR>Thomas G. Lane
+ <BR>Ray Larabie
+ <BR>Jukka Liimatta
+ <BR>Giulio Mainardi
+ <BR>Jeff Marsceill
+ <BR>Max McGuire
+ <BR>Morgan McGuire
+ <BR>Justin Miller
+ <BR>Kurt Miller
+ <BR>Nate Miller
+ <BR>Tomas Moller
+ <BR>Eric Muller*
+ <BR>Nick Musurca
+ <BR>Akita Noek
+ <BR>James O'Sullivan*
+ <BR>Aaron Orenstein
+ <BR>Jordan Parker
+ <BR>Edward Resnick
+ <BR>Alex Rice
+ <BR>Jack Ritter
+ <BR>Nate Robbins
+ <BR>Joshua Schpok*
+ <BR>Sebastian Schubert
+ <BR>SGI
+ <BR>Ben Shine*
+ <BR>Peter Sibley*
+ <br>Gabe Taubman*
+ <BR>Corey Taylor*
+ <BR>Pierre Terdiman
+ <BR>Ben Trumbore
+ <BR>Matthew Welch
+ <BR>Simon Winkelbach
+ <BR>Laura Wollstadt
+ <BR>Andrew Woo
+*/
diff --git a/externals/g3dlite/doc-files/license.dox b/externals/g3dlite/doc-files/license.dox
new file mode 100644
index 00000000000..b4302651fbf
--- /dev/null
+++ b/externals/g3dlite/doc-files/license.dox
@@ -0,0 +1,120 @@
+/** @page license License
+
+\htmlonly
+<TABLE BORDER=0 WIDTH=80%><TR><TD><I><FONT FACE="Arial">
+<A HREF="guideintro.html"><IMG SRC="backarrow.gif" BORDER=0 ALIGN=MIDDLE>
+Introduction</A></I></FONT></TD><TD ALIGN=RIGHT><FONT FACE="Arial"><I>
+<A HREF="guideinstall.html">
+Installation <IMG SRC="forwardarrow.gif" BORDER=0 ALIGN=MIDDLE></A></I></FONT></TD></TR></TABLE>
+\endhtmlonly
+
+@section intent Intent of License
+ (This section is informal and not legally binding.)
+
+ <BR> This library is free code-- you can use it without charge and
+ it is minimally legally encumbered. Unlike some other free libraries,
+ we <B>do not</B> require you to release
+ your source code or make your own program open source.
+
+ <P> I intend the license (below) to protect me and the other
+ contributors from liability and allow you to use the source however
+ you want. You can make your own closed or open-source programs,
+ sell them, give them away, whatever.
+
+ <P>
+ You have an obligation to say "this software is based in part on
+ the work of the Independent JPEG Group" in your documentation or
+ application help if you use the G3D::GImage class because it is based on
+ the IJG library. The OpenGL headers and ZLib headers included may
+ be freely distributed provided their copyright notices remain
+ intact.
+
+ <P>
+ For convenience, G3D::license is a function that returns the license
+ string you must put in your documentation. G3D::GApp will automatically
+ write a file (g3d-license.txt) to disk with the contents of this
+ license unless you tell it not to.
+
+ <P>
+ Most of the data resources have either entered the public domain and have
+ been in several published papers or are data that I have explicitly
+ received permission to distribute with G3D. The G3D fonts are actually font
+ images, not TrueType font descriptions and may be freely
+ distributed. As a rule of thumb, you can freely use and distribute
+ anything you find in the data directory but may need permission to use
+ it in a commercial product. Check the various copyright.txt files
+ in the data directories for specific information.
+
+ <P>
+
+ You are required by the BSD license to acknowledge G3D in your
+ documentation. This can be as minimal as a note buried in the
+ fine print at the end of a manual or a text file accompanying
+ your program. I appreciate it if you acknowledged the library
+ more publicly but you aren't required to.
+
+ <P>
+
+ Likewise, you are encouraged but not required to submit patches to
+ improve the library for the benefit of all. E-mail me with bugs,
+ patches, and questions. <P>
+
+ -Morgan McGuire
+ &lt;<I><A HREF="mailto:matrix@graphics3d.com">matrix@graphics3d.com</A></I>&gt;
+
+ <HR>
+
+ @section reallicense License
+
+ <I>G3D is licensed under the <A HREF="http://www.opensource.org/licenses/bsd-license.php">BSD license</A>,
+ with portions controlled by the <A HREF="IJG-README.TXT">IJG license</A>,
+ <A HREF="libpng-LICENSE.txt">PNG Reference Library license</A> and
+ <A HREF="http://www.gnu.org/copyleft/lesser.html">GNU Lesser General Public License (LGPL)</A>.</I>
+
+ <CODE>
+ <IMG SRC="http://opensource.org/trademarks/osi-certified/web/osi-certified-120x100.gif">
+ <DT>This product uses software from the G3D project (http://g3d-cpp.sf.net)
+ <DT>Copyright &copy; 2000-2008, Morgan McGuire
+ <DT>All rights reserved.
+ <P>
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions
+ are met:
+ <P>
+ Redistributions of source code must retain the above copyright notice,
+ this list of conditions and the following disclaimer.
+ <P>
+ 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.
+ <P>
+ Neither the name of Morgan McGuire, Williams College, Brown University, nor the names of
+ its contributors may be used to endorse or promote products derived from
+ this software without specific prior written permission.
+ <P>
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ THE POSSIBILITY OF SUCH DAMAGE.
+ </CODE>
+
+ <P>
+ You must also agree to be bound by the terms of the Independent JPEG
+ Group license for the portions of this library that are based
+ on the work of the Independent JPEG Group, <B>if you use those
+ portions</B>. Note: if you do not use the G3D::GImage class,
+ this clause does not apply to you because the linker will
+ strip that code from your project. The <A
+ HREF="IJG-README.TXT">IJG-README.TXT</A> file contains the
+ Independent JPEG Group license.
+
+ </OL>
+
+ */ \ No newline at end of file
diff --git a/externals/g3dlite/win/VC90/g3dlite.vcproj b/externals/g3dlite/win/VC90/g3dlite.vcproj
new file mode 100644
index 00000000000..e76d610ab69
--- /dev/null
+++ b/externals/g3dlite/win/VC90/g3dlite.vcproj
@@ -0,0 +1,463 @@
+<?xml version="1.0" encoding="Windows-1252"?>
+<VisualStudioProject
+ ProjectType="Visual C++"
+ Version="9,00"
+ Name="g3dlite"
+ ProjectGUID="{8072769E-CF10-48BF-B9E1-12752A5DAC6E}"
+ RootNamespace="sockets"
+ Keyword="Win32Proj"
+ TargetFrameworkVersion="196613"
+ >
+ <Platforms>
+ <Platform
+ Name="Win32"
+ />
+ <Platform
+ Name="x64"
+ />
+ </Platforms>
+ <ToolFiles>
+ </ToolFiles>
+ <Configurations>
+ <Configuration
+ Name="Debug|Win32"
+ OutputDirectory="..\..\bin\$(PlatformName)_$(ConfigurationName)"
+ IntermediateDirectory=".\g3dlite__$(PlatformName)_$(ConfigurationName)"
+ ConfigurationType="4"
+ CharacterSet="2"
+ >
+ <Tool
+ Name="VCPreBuildEventTool"
+ />
+ <Tool
+ Name="VCCustomBuildTool"
+ />
+ <Tool
+ Name="VCXMLDataGeneratorTool"
+ />
+ <Tool
+ Name="VCWebServiceProxyGeneratorTool"
+ />
+ <Tool
+ Name="VCMIDLTool"
+ />
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalOptions="/MP"
+ Optimization="0"
+ AdditionalIncludeDirectories="..\..\zip.lib\include;..\..\G3D.lib\include;..\..\..\zlib;"
+ AdditionalUsingDirectories=""
+ PreprocessorDefinitions="WIN32;_DEBUG;_CONSOLE"
+ MinimalRebuild="false"
+ BasicRuntimeChecks="3"
+ RuntimeLibrary="3"
+ BufferSecurityCheck="true"
+ EnableFunctionLevelLinking="true"
+ UsePrecompiledHeader="0"
+ AssemblerListingLocation=".\g3dlite__$(PlatformName)_$(ConfigurationName)\"
+ ObjectFile=".\g3dlite__$(PlatformName)_$(ConfigurationName)\"
+ ProgramDataBaseFileName=".\g3dlite__$(PlatformName)_$(ConfigurationName)\"
+ WarningLevel="3"
+ Detect64BitPortabilityProblems="false"
+ DebugInformationFormat="3"
+ CallingConvention="0"
+ />
+ <Tool
+ Name="VCManagedResourceCompilerTool"
+ />
+ <Tool
+ Name="VCResourceCompilerTool"
+ />
+ <Tool
+ Name="VCPreLinkEventTool"
+ />
+ <Tool
+ Name="VCLibrarianTool"
+ OutputFile="$(OutDir)/g3dlite.lib"
+ IgnoreAllDefaultLibraries="true"
+ />
+ <Tool
+ Name="VCALinkTool"
+ />
+ <Tool
+ Name="VCXDCMakeTool"
+ />
+ <Tool
+ Name="VCBscMakeTool"
+ />
+ <Tool
+ Name="VCFxCopTool"
+ />
+ <Tool
+ Name="VCPostBuildEventTool"
+ />
+ </Configuration>
+ <Configuration
+ Name="Debug|x64"
+ OutputDirectory="..\..\bin\$(PlatformName)_$(ConfigurationName)"
+ IntermediateDirectory=".\g3dlite__$(PlatformName)_$(ConfigurationName)"
+ ConfigurationType="4"
+ CharacterSet="2"
+ >
+ <Tool
+ Name="VCPreBuildEventTool"
+ />
+ <Tool
+ Name="VCCustomBuildTool"
+ />
+ <Tool
+ Name="VCXMLDataGeneratorTool"
+ />
+ <Tool
+ Name="VCWebServiceProxyGeneratorTool"
+ />
+ <Tool
+ Name="VCMIDLTool"
+ TargetEnvironment="3"
+ />
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalOptions="/MP"
+ Optimization="0"
+ AdditionalIncludeDirectories="..\..\zip.lib\include;..\..\G3D.lib\include;..\..\..\zlib;"
+ AdditionalUsingDirectories=""
+ PreprocessorDefinitions="WIN32;_DEBUG;_CONSOLE"
+ MinimalRebuild="false"
+ BasicRuntimeChecks="3"
+ RuntimeLibrary="3"
+ BufferSecurityCheck="true"
+ EnableFunctionLevelLinking="true"
+ UsePrecompiledHeader="0"
+ AssemblerListingLocation=".\g3dlite__$(PlatformName)_$(ConfigurationName)\"
+ ObjectFile=".\g3dlite__$(PlatformName)_$(ConfigurationName)\"
+ ProgramDataBaseFileName=".\g3dlite__$(PlatformName)_$(ConfigurationName)\"
+ WarningLevel="3"
+ Detect64BitPortabilityProblems="false"
+ DebugInformationFormat="3"
+ CallingConvention="0"
+ />
+ <Tool
+ Name="VCManagedResourceCompilerTool"
+ />
+ <Tool
+ Name="VCResourceCompilerTool"
+ />
+ <Tool
+ Name="VCPreLinkEventTool"
+ />
+ <Tool
+ Name="VCLibrarianTool"
+ OutputFile="$(OutDir)/g3dlite.lib"
+ IgnoreAllDefaultLibraries="true"
+ />
+ <Tool
+ Name="VCALinkTool"
+ />
+ <Tool
+ Name="VCXDCMakeTool"
+ />
+ <Tool
+ Name="VCBscMakeTool"
+ />
+ <Tool
+ Name="VCFxCopTool"
+ />
+ <Tool
+ Name="VCPostBuildEventTool"
+ />
+ </Configuration>
+ <Configuration
+ Name="Release|Win32"
+ OutputDirectory="..\..\bin\$(PlatformName)_$(ConfigurationName)"
+ IntermediateDirectory=".\g3dlite__$(PlatformName)_$(ConfigurationName)"
+ ConfigurationType="4"
+ CharacterSet="2"
+ >
+ <Tool
+ Name="VCPreBuildEventTool"
+ />
+ <Tool
+ Name="VCCustomBuildTool"
+ />
+ <Tool
+ Name="VCXMLDataGeneratorTool"
+ />
+ <Tool
+ Name="VCWebServiceProxyGeneratorTool"
+ />
+ <Tool
+ Name="VCMIDLTool"
+ />
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalOptions="/MP"
+ AdditionalIncludeDirectories="..\..\zip.lib\include;..\..\G3D.lib\include;..\..\..\zlib;"
+ AdditionalUsingDirectories=""
+ PreprocessorDefinitions="WIN32;NDEBUG;_CONSOLE;_SECURE_SCL=0"
+ RuntimeLibrary="2"
+ EnableEnhancedInstructionSet="1"
+ UsePrecompiledHeader="0"
+ AssemblerListingLocation=".\g3dlite__$(PlatformName)_$(ConfigurationName)\"
+ ObjectFile=".\g3dlite__$(PlatformName)_$(ConfigurationName)\"
+ ProgramDataBaseFileName=".\g3dlite__$(PlatformName)_$(ConfigurationName)\"
+ WarningLevel="3"
+ Detect64BitPortabilityProblems="false"
+ DebugInformationFormat="3"
+ CallingConvention="0"
+ />
+ <Tool
+ Name="VCManagedResourceCompilerTool"
+ />
+ <Tool
+ Name="VCResourceCompilerTool"
+ />
+ <Tool
+ Name="VCPreLinkEventTool"
+ />
+ <Tool
+ Name="VCLibrarianTool"
+ OutputFile="$(OutDir)/g3dlite.lib"
+ IgnoreAllDefaultLibraries="true"
+ />
+ <Tool
+ Name="VCALinkTool"
+ />
+ <Tool
+ Name="VCXDCMakeTool"
+ />
+ <Tool
+ Name="VCBscMakeTool"
+ />
+ <Tool
+ Name="VCFxCopTool"
+ />
+ <Tool
+ Name="VCPostBuildEventTool"
+ />
+ </Configuration>
+ <Configuration
+ Name="Release|x64"
+ OutputDirectory="..\..\bin\$(PlatformName)_$(ConfigurationName)"
+ IntermediateDirectory=".\g3dlite__$(PlatformName)_$(ConfigurationName)"
+ ConfigurationType="4"
+ CharacterSet="2"
+ >
+ <Tool
+ Name="VCPreBuildEventTool"
+ />
+ <Tool
+ Name="VCCustomBuildTool"
+ />
+ <Tool
+ Name="VCXMLDataGeneratorTool"
+ />
+ <Tool
+ Name="VCWebServiceProxyGeneratorTool"
+ />
+ <Tool
+ Name="VCMIDLTool"
+ TargetEnvironment="3"
+ />
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalOptions="/MP"
+ AdditionalIncludeDirectories="..\..\zip.lib\include;..\..\G3D.lib\include;..\..\..\zlib;"
+ AdditionalUsingDirectories=""
+ PreprocessorDefinitions="WIN32;NDEBUG;_CONSOLE;_SECURE_SCL=0"
+ RuntimeLibrary="2"
+ EnableEnhancedInstructionSet="0"
+ UsePrecompiledHeader="0"
+ AssemblerListingLocation=".\g3dlite__$(PlatformName)_$(ConfigurationName)\"
+ ObjectFile=".\g3dlite__$(PlatformName)_$(ConfigurationName)\"
+ ProgramDataBaseFileName=".\g3dlite__$(PlatformName)_$(ConfigurationName)\"
+ WarningLevel="3"
+ Detect64BitPortabilityProblems="false"
+ DebugInformationFormat="3"
+ CallingConvention="0"
+ />
+ <Tool
+ Name="VCManagedResourceCompilerTool"
+ />
+ <Tool
+ Name="VCResourceCompilerTool"
+ />
+ <Tool
+ Name="VCPreLinkEventTool"
+ />
+ <Tool
+ Name="VCLibrarianTool"
+ OutputFile="$(OutDir)/g3dlite.lib"
+ IgnoreAllDefaultLibraries="true"
+ />
+ <Tool
+ Name="VCALinkTool"
+ />
+ <Tool
+ Name="VCXDCMakeTool"
+ />
+ <Tool
+ Name="VCBscMakeTool"
+ />
+ <Tool
+ Name="VCFxCopTool"
+ />
+ <Tool
+ Name="VCPostBuildEventTool"
+ />
+ </Configuration>
+ </Configurations>
+ <References>
+ </References>
+ <Files>
+ <Filter
+ Name="Header Files"
+ >
+ </Filter>
+ <Filter
+ Name="Source Files"
+ >
+ <File
+ RelativePath="..\..\G3D.lib\source\AABox.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\..\G3D.lib\source\AnyVal.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\..\G3D.lib\source\BinaryFormat.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\..\G3D.lib\source\BinaryInput.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\..\G3D.lib\source\BinaryOutput.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\..\G3D.lib\source\Box.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\..\G3D.lib\source\Capsule.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\..\G3D.lib\source\CollisionDetection.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\..\G3D.lib\source\CoordinateFrame.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\..\G3D.lib\source\Crypto.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\..\G3D.lib\source\Cylinder.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\..\G3D.lib\source\debugAssert.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\..\G3D.lib\source\fileutils.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\..\G3D.lib\source\format.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\..\G3D.lib\source\g3dmath.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\..\G3D.lib\source\Line.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\..\G3D.lib\source\LineSegment.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\..\G3D.lib\source\Log.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\..\G3D.lib\source\Matrix3.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\..\G3D.lib\source\Matrix4.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\..\G3D.lib\source\Plane.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\..\G3D.lib\source\prompt.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\..\G3D.lib\source\Quat.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\..\G3D.lib\source\Ray.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\..\G3D.lib\source\RegistryUtil.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\..\G3D.lib\source\Sphere.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\..\G3D.lib\source\stringutils.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\..\G3D.lib\source\System.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\..\G3D.lib\source\TextInput.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\..\G3D.lib\source\TextOutput.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\..\G3D.lib\source\Triangle.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\..\G3D.lib\source\UprightFrame.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\..\G3D.lib\source\Vector2.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\..\G3D.lib\source\Vector3.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\..\G3D.lib\source\Vector4.cpp"
+ >
+ </File>
+ </Filter>
+ </Files>
+ <Globals>
+ </Globals>
+</VisualStudioProject>
diff --git a/externals/g3dlite/win/delme b/externals/g3dlite/win/delme
deleted file mode 100644
index e69de29bb2d..00000000000
--- a/externals/g3dlite/win/delme
+++ /dev/null
diff --git a/externals/g3dlite/win/g3dlite.sln b/externals/g3dlite/win/g3dlite.sln
new file mode 100644
index 00000000000..875374e3944
--- /dev/null
+++ b/externals/g3dlite/win/g3dlite.sln
@@ -0,0 +1,25 @@
+Microsoft Visual Studio Solution File, Format Version 10.00
+# Visual Studio 2008
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "g3dlite", "VC90\g3dlite.vcproj", "{8072769E-CF10-48BF-B9E1-12752A5DAC6E}"
+EndProject
+Global
+ GlobalSection(SolutionConfigurationPlatforms) = preSolution
+ Debug|Win32 = Debug|Win32
+ Debug|x64 = Debug|x64
+ Release|Win32 = Release|Win32
+ Release|x64 = Release|x64
+ EndGlobalSection
+ GlobalSection(ProjectConfigurationPlatforms) = postSolution
+ {8072769E-CF10-48BF-B9E1-12752A5DAC6E}.Debug|Win32.ActiveCfg = Debug|Win32
+ {8072769E-CF10-48BF-B9E1-12752A5DAC6E}.Debug|Win32.Build.0 = Debug|Win32
+ {8072769E-CF10-48BF-B9E1-12752A5DAC6E}.Debug|x64.ActiveCfg = Debug|x64
+ {8072769E-CF10-48BF-B9E1-12752A5DAC6E}.Debug|x64.Build.0 = Debug|x64
+ {8072769E-CF10-48BF-B9E1-12752A5DAC6E}.Release|Win32.ActiveCfg = Release|Win32
+ {8072769E-CF10-48BF-B9E1-12752A5DAC6E}.Release|Win32.Build.0 = Release|Win32
+ {8072769E-CF10-48BF-B9E1-12752A5DAC6E}.Release|x64.ActiveCfg = Release|x64
+ {8072769E-CF10-48BF-B9E1-12752A5DAC6E}.Release|x64.Build.0 = Release|x64
+ EndGlobalSection
+ GlobalSection(SolutionProperties) = preSolution
+ HideSolutionNode = FALSE
+ EndGlobalSection
+EndGlobal
diff --git a/externals/g3dlite/zip.lib/include/zip/ioapi.h b/externals/g3dlite/zip.lib/include/zip/ioapi.h
new file mode 100644
index 00000000000..7d457baab34
--- /dev/null
+++ b/externals/g3dlite/zip.lib/include/zip/ioapi.h
@@ -0,0 +1,75 @@
+/* ioapi.h -- IO base function header for compress/uncompress .zip
+ files using zlib + zip or unzip API
+
+ Version 1.01e, February 12th, 2005
+
+ Copyright (C) 1998-2005 Gilles Vollant
+*/
+
+#ifndef _ZLIBIOAPI_H
+#define _ZLIBIOAPI_H
+
+
+#define ZLIB_FILEFUNC_SEEK_CUR (1)
+#define ZLIB_FILEFUNC_SEEK_END (2)
+#define ZLIB_FILEFUNC_SEEK_SET (0)
+
+#define ZLIB_FILEFUNC_MODE_READ (1)
+#define ZLIB_FILEFUNC_MODE_WRITE (2)
+#define ZLIB_FILEFUNC_MODE_READWRITEFILTER (3)
+
+#define ZLIB_FILEFUNC_MODE_EXISTING (4)
+#define ZLIB_FILEFUNC_MODE_CREATE (8)
+
+
+#ifndef ZCALLBACK
+
+#if (defined(WIN32) || defined (WINDOWS) || defined (_WINDOWS)) && defined(CALLBACK) && defined (USEWINDOWS_CALLBACK)
+#define ZCALLBACK CALLBACK
+#else
+#define ZCALLBACK
+#endif
+#endif
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef voidpf (ZCALLBACK *open_file_func) OF((voidpf opaque, const char* filename, int mode));
+typedef uLong (ZCALLBACK *read_file_func) OF((voidpf opaque, voidpf stream, void* buf, uLong size));
+typedef uLong (ZCALLBACK *write_file_func) OF((voidpf opaque, voidpf stream, const void* buf, uLong size));
+typedef long (ZCALLBACK *tell_file_func) OF((voidpf opaque, voidpf stream));
+typedef long (ZCALLBACK *seek_file_func) OF((voidpf opaque, voidpf stream, uLong offset, int origin));
+typedef int (ZCALLBACK *close_file_func) OF((voidpf opaque, voidpf stream));
+typedef int (ZCALLBACK *testerror_file_func) OF((voidpf opaque, voidpf stream));
+
+typedef struct zlib_filefunc_def_s
+{
+ open_file_func zopen_file;
+ read_file_func zread_file;
+ write_file_func zwrite_file;
+ tell_file_func ztell_file;
+ seek_file_func zseek_file;
+ close_file_func zclose_file;
+ testerror_file_func zerror_file;
+ voidpf opaque;
+} zlib_filefunc_def;
+
+
+
+void fill_fopen_filefunc OF((zlib_filefunc_def* pzlib_filefunc_def));
+
+#define ZREAD(filefunc,filestream,buf,size) ((*((filefunc).zread_file))((filefunc).opaque,filestream,buf,size))
+#define ZWRITE(filefunc,filestream,buf,size) ((*((filefunc).zwrite_file))((filefunc).opaque,filestream,buf,size))
+#define ZTELL(filefunc,filestream) ((*((filefunc).ztell_file))((filefunc).opaque,filestream))
+#define ZSEEK(filefunc,filestream,pos,mode) ((*((filefunc).zseek_file))((filefunc).opaque,filestream,pos,mode))
+#define ZCLOSE(filefunc,filestream) ((*((filefunc).zclose_file))((filefunc).opaque,filestream))
+#define ZERROR(filefunc,filestream) ((*((filefunc).zerror_file))((filefunc).opaque,filestream))
+
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
+
diff --git a/externals/g3dlite/zip.lib/include/zip/unzip.h b/externals/g3dlite/zip.lib/include/zip/unzip.h
new file mode 100644
index 00000000000..b247937c807
--- /dev/null
+++ b/externals/g3dlite/zip.lib/include/zip/unzip.h
@@ -0,0 +1,354 @@
+/* unzip.h -- IO for uncompress .zip files using zlib
+ Version 1.01e, February 12th, 2005
+
+ Copyright (C) 1998-2005 Gilles Vollant
+
+ This unzip package allow extract file from .ZIP file, compatible with PKZip 2.04g
+ WinZip, InfoZip tools and compatible.
+
+ Multi volume ZipFile (span) are not supported.
+ Encryption compatible with pkzip 2.04g only supported
+ Old compressions used by old PKZip 1.x are not supported
+
+
+ I WAIT FEEDBACK at mail info@winimage.com
+ Visit also http://www.winimage.com/zLibDll/unzip.htm for evolution
+
+ Condition of use and distribution are the same than zlib :
+
+ 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.
+
+
+*/
+
+/* for more info about .ZIP format, see
+ http://www.info-zip.org/pub/infozip/doc/appnote-981119-iz.zip
+ http://www.info-zip.org/pub/infozip/doc/
+ PkWare has also a specification at :
+ ftp://ftp.pkware.com/probdesc.zip
+*/
+
+#ifndef _unz_H
+#define _unz_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#ifndef _ZLIB_H
+#include "zlib.h"
+#endif
+
+#ifndef _ZLIBIOAPI_H
+#include "ioapi.h"
+#endif
+
+#if defined(STRICTUNZIP) || defined(STRICTZIPUNZIP)
+/* like the STRICT of WIN32, we define a pointer that cannot be converted
+ from (void*) without cast */
+typedef struct TagunzFile__ { int unused; } unzFile__;
+typedef unzFile__ *unzFile;
+#else
+typedef voidp unzFile;
+#endif
+
+
+#define UNZ_OK (0)
+#define UNZ_END_OF_LIST_OF_FILE (-100)
+#define UNZ_ERRNO (Z_ERRNO)
+#define UNZ_EOF (0)
+#define UNZ_PARAMERROR (-102)
+#define UNZ_BADZIPFILE (-103)
+#define UNZ_INTERNALERROR (-104)
+#define UNZ_CRCERROR (-105)
+
+/* tm_unz contain date/time info */
+typedef struct tm_unz_s
+{
+ uInt tm_sec; /* seconds after the minute - [0,59] */
+ uInt tm_min; /* minutes after the hour - [0,59] */
+ uInt tm_hour; /* hours since midnight - [0,23] */
+ uInt tm_mday; /* day of the month - [1,31] */
+ uInt tm_mon; /* months since January - [0,11] */
+ uInt tm_year; /* years - [1980..2044] */
+} tm_unz;
+
+/* unz_global_info structure contain global data about the ZIPfile
+ These data comes from the end of central dir */
+typedef struct unz_global_info_s
+{
+ uLong number_entry; /* total number of entries in
+ the central dir on this disk */
+ uLong size_comment; /* size of the global comment of the zipfile */
+} unz_global_info;
+
+
+/* unz_file_info contain information about a file in the zipfile */
+typedef struct unz_file_info_s
+{
+ uLong version; /* version made by 2 bytes */
+ uLong version_needed; /* version needed to extract 2 bytes */
+ uLong flag; /* general purpose bit flag 2 bytes */
+ uLong compression_method; /* compression method 2 bytes */
+ uLong dosDate; /* last mod file date in Dos fmt 4 bytes */
+ uLong crc; /* crc-32 4 bytes */
+ uLong compressed_size; /* compressed size 4 bytes */
+ uLong uncompressed_size; /* uncompressed size 4 bytes */
+ uLong size_filename; /* filename length 2 bytes */
+ uLong size_file_extra; /* extra field length 2 bytes */
+ uLong size_file_comment; /* file comment length 2 bytes */
+
+ uLong disk_num_start; /* disk number start 2 bytes */
+ uLong internal_fa; /* internal file attributes 2 bytes */
+ uLong external_fa; /* external file attributes 4 bytes */
+
+ tm_unz tmu_date;
+} unz_file_info;
+
+extern int ZEXPORT unzStringFileNameCompare OF ((const char* fileName1,
+ const char* fileName2,
+ int iCaseSensitivity));
+/*
+ Compare two filename (fileName1,fileName2).
+ If iCaseSenisivity = 1, comparision is case sensitivity (like strcmp)
+ If iCaseSenisivity = 2, comparision is not case sensitivity (like strcmpi
+ or strcasecmp)
+ If iCaseSenisivity = 0, case sensitivity is defaut of your operating system
+ (like 1 on Unix, 2 on Windows)
+*/
+
+
+extern unzFile ZEXPORT unzOpen OF((const char *path));
+/*
+ Open a Zip file. path contain the full pathname (by example,
+ on a Windows XP computer "c:\\zlib\\zlib113.zip" or on an Unix computer
+ "zlib/zlib113.zip".
+ If the zipfile cannot be opened (file don't exist or in not valid), the
+ return value is NULL.
+ Else, the return value is a unzFile Handle, usable with other function
+ of this unzip package.
+*/
+
+extern unzFile ZEXPORT unzOpen2 OF((const char *path,
+ zlib_filefunc_def* pzlib_filefunc_def));
+/*
+ Open a Zip file, like unzOpen, but provide a set of file low level API
+ for read/write the zip file (see ioapi.h)
+*/
+
+extern int ZEXPORT unzClose OF((unzFile file));
+/*
+ Close a ZipFile opened with unzipOpen.
+ If there is files inside the .Zip opened with unzOpenCurrentFile (see later),
+ these files MUST be closed with unzipCloseCurrentFile before call unzipClose.
+ return UNZ_OK if there is no problem. */
+
+extern int ZEXPORT unzGetGlobalInfo OF((unzFile file,
+ unz_global_info *pglobal_info));
+/*
+ Write info about the ZipFile in the *pglobal_info structure.
+ No preparation of the structure is needed
+ return UNZ_OK if there is no problem. */
+
+
+extern int ZEXPORT unzGetGlobalComment OF((unzFile file,
+ char *szComment,
+ uLong uSizeBuf));
+/*
+ Get the global comment string of the ZipFile, in the szComment buffer.
+ uSizeBuf is the size of the szComment buffer.
+ return the number of byte copied or an error code <0
+*/
+
+
+/***************************************************************************/
+/* Unzip package allow you browse the directory of the zipfile */
+
+extern int ZEXPORT unzGoToFirstFile OF((unzFile file));
+/*
+ Set the current file of the zipfile to the first file.
+ return UNZ_OK if there is no problem
+*/
+
+extern int ZEXPORT unzGoToNextFile OF((unzFile file));
+/*
+ Set the current file of the zipfile to the next file.
+ return UNZ_OK if there is no problem
+ return UNZ_END_OF_LIST_OF_FILE if the actual file was the latest.
+*/
+
+extern int ZEXPORT unzLocateFile OF((unzFile file,
+ const char *szFileName,
+ int iCaseSensitivity));
+/*
+ Try locate the file szFileName in the zipfile.
+ For the iCaseSensitivity signification, see unzStringFileNameCompare
+
+ return value :
+ UNZ_OK if the file is found. It becomes the current file.
+ UNZ_END_OF_LIST_OF_FILE if the file is not found
+*/
+
+
+/* ****************************************** */
+/* Ryan supplied functions */
+/* unz_file_info contain information about a file in the zipfile */
+typedef struct unz_file_pos_s
+{
+ uLong pos_in_zip_directory; /* offset in zip file directory */
+ uLong num_of_file; /* # of file */
+} unz_file_pos;
+
+extern int ZEXPORT unzGetFilePos(
+ unzFile file,
+ unz_file_pos* file_pos);
+
+extern int ZEXPORT unzGoToFilePos(
+ unzFile file,
+ unz_file_pos* file_pos);
+
+/* ****************************************** */
+
+extern int ZEXPORT unzGetCurrentFileInfo OF((unzFile file,
+ unz_file_info *pfile_info,
+ char *szFileName,
+ uLong fileNameBufferSize,
+ void *extraField,
+ uLong extraFieldBufferSize,
+ char *szComment,
+ uLong commentBufferSize));
+/*
+ Get Info about the current file
+ if pfile_info!=NULL, the *pfile_info structure will contain somes info about
+ the current file
+ if szFileName!=NULL, the filemane string will be copied in szFileName
+ (fileNameBufferSize is the size of the buffer)
+ if extraField!=NULL, the extra field information will be copied in extraField
+ (extraFieldBufferSize is the size of the buffer).
+ This is the Central-header version of the extra field
+ if szComment!=NULL, the comment string of the file will be copied in szComment
+ (commentBufferSize is the size of the buffer)
+*/
+
+/***************************************************************************/
+/* for reading the content of the current zipfile, you can open it, read data
+ from it, and close it (you can close it before reading all the file)
+ */
+
+extern int ZEXPORT unzOpenCurrentFile OF((unzFile file));
+/*
+ Open for reading data the current file in the zipfile.
+ If there is no error, the return value is UNZ_OK.
+*/
+
+extern int ZEXPORT unzOpenCurrentFilePassword OF((unzFile file,
+ const char* password));
+/*
+ Open for reading data the current file in the zipfile.
+ password is a crypting password
+ If there is no error, the return value is UNZ_OK.
+*/
+
+extern int ZEXPORT unzOpenCurrentFile2 OF((unzFile file,
+ int* method,
+ int* level,
+ int raw));
+/*
+ Same than unzOpenCurrentFile, but open for read raw the file (not uncompress)
+ if raw==1
+ *method will receive method of compression, *level will receive level of
+ compression
+ note : you can set level parameter as NULL (if you did not want known level,
+ but you CANNOT set method parameter as NULL
+*/
+
+extern int ZEXPORT unzOpenCurrentFile3 OF((unzFile file,
+ int* method,
+ int* level,
+ int raw,
+ const char* password));
+/*
+ Same than unzOpenCurrentFile, but open for read raw the file (not uncompress)
+ if raw==1
+ *method will receive method of compression, *level will receive level of
+ compression
+ note : you can set level parameter as NULL (if you did not want known level,
+ but you CANNOT set method parameter as NULL
+*/
+
+
+extern int ZEXPORT unzCloseCurrentFile OF((unzFile file));
+/*
+ Close the file in zip opened with unzOpenCurrentFile
+ Return UNZ_CRCERROR if all the file was read but the CRC is not good
+*/
+
+extern int ZEXPORT unzReadCurrentFile OF((unzFile file,
+ voidp buf,
+ unsigned len));
+/*
+ Read bytes from the current file (opened by unzOpenCurrentFile)
+ buf contain buffer where data must be copied
+ len the size of buf.
+
+ return the number of byte copied if somes bytes are copied
+ return 0 if the end of file was reached
+ return <0 with error code if there is an error
+ (UNZ_ERRNO for IO error, or zLib error for uncompress error)
+*/
+
+extern z_off_t ZEXPORT unztell OF((unzFile file));
+/*
+ Give the current position in uncompressed data
+*/
+
+extern int ZEXPORT unzeof OF((unzFile file));
+/*
+ return 1 if the end of file was reached, 0 elsewhere
+*/
+
+extern int ZEXPORT unzGetLocalExtrafield OF((unzFile file,
+ voidp buf,
+ unsigned len));
+/*
+ Read extra field from the current file (opened by unzOpenCurrentFile)
+ This is the local-header version of the extra field (sometimes, there is
+ more info in the local-header version than in the central-header)
+
+ if buf==NULL, it return the size of the local extra field
+
+ if buf!=NULL, len is the size of the buffer, the extra header is copied in
+ buf.
+ the return value is the number of bytes copied in buf, or (if <0)
+ the error code
+*/
+
+/***************************************************************************/
+
+/* Get the current file offset */
+extern uLong ZEXPORT unzGetOffset (unzFile file);
+
+/* Set the current file offset */
+extern int ZEXPORT unzSetOffset (unzFile file, uLong pos);
+
+
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _unz_H */
diff --git a/externals/g3dlite/zip.lib/include/zip/zip.h b/externals/g3dlite/zip.lib/include/zip/zip.h
new file mode 100644
index 00000000000..acacce83b9b
--- /dev/null
+++ b/externals/g3dlite/zip.lib/include/zip/zip.h
@@ -0,0 +1,235 @@
+/* zip.h -- IO for compress .zip files using zlib
+ Version 1.01e, February 12th, 2005
+
+ Copyright (C) 1998-2005 Gilles Vollant
+
+ This unzip package allow creates .ZIP file, compatible with PKZip 2.04g
+ WinZip, InfoZip tools and compatible.
+ Multi volume ZipFile (span) are not supported.
+ Encryption compatible with pkzip 2.04g only supported
+ Old compressions used by old PKZip 1.x are not supported
+
+ For uncompress .zip file, look at unzip.h
+
+
+ I WAIT FEEDBACK at mail info@winimage.com
+ Visit also http://www.winimage.com/zLibDll/unzip.html for evolution
+
+ Condition of use and distribution are the same than zlib :
+
+ 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.
+
+
+*/
+
+/* for more info about .ZIP format, see
+ http://www.info-zip.org/pub/infozip/doc/appnote-981119-iz.zip
+ http://www.info-zip.org/pub/infozip/doc/
+ PkWare has also a specification at :
+ ftp://ftp.pkware.com/probdesc.zip
+*/
+
+#ifndef _zip_H
+#define _zip_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#ifndef _ZLIB_H
+#include "zlib.h"
+#endif
+
+#ifndef _ZLIBIOAPI_H
+#include "ioapi.h"
+#endif
+
+#if defined(STRICTZIP) || defined(STRICTZIPUNZIP)
+/* like the STRICT of WIN32, we define a pointer that cannot be converted
+ from (void*) without cast */
+typedef struct TagzipFile__ { int unused; } zipFile__;
+typedef zipFile__ *zipFile;
+#else
+typedef voidp zipFile;
+#endif
+
+#define ZIP_OK (0)
+#define ZIP_EOF (0)
+#define ZIP_ERRNO (Z_ERRNO)
+#define ZIP_PARAMERROR (-102)
+#define ZIP_BADZIPFILE (-103)
+#define ZIP_INTERNALERROR (-104)
+
+#ifndef DEF_MEM_LEVEL
+# if MAX_MEM_LEVEL >= 8
+# define DEF_MEM_LEVEL 8
+# else
+# define DEF_MEM_LEVEL MAX_MEM_LEVEL
+# endif
+#endif
+/* default memLevel */
+
+/* tm_zip contain date/time info */
+typedef struct tm_zip_s
+{
+ uInt tm_sec; /* seconds after the minute - [0,59] */
+ uInt tm_min; /* minutes after the hour - [0,59] */
+ uInt tm_hour; /* hours since midnight - [0,23] */
+ uInt tm_mday; /* day of the month - [1,31] */
+ uInt tm_mon; /* months since January - [0,11] */
+ uInt tm_year; /* years - [1980..2044] */
+} tm_zip;
+
+typedef struct
+{
+ tm_zip tmz_date; /* date in understandable format */
+ uLong dosDate; /* if dos_date == 0, tmu_date is used */
+/* uLong flag; */ /* general purpose bit flag 2 bytes */
+
+ uLong internal_fa; /* internal file attributes 2 bytes */
+ uLong external_fa; /* external file attributes 4 bytes */
+} zip_fileinfo;
+
+typedef const char* zipcharpc;
+
+
+#define APPEND_STATUS_CREATE (0)
+#define APPEND_STATUS_CREATEAFTER (1)
+#define APPEND_STATUS_ADDINZIP (2)
+
+extern zipFile ZEXPORT zipOpen OF((const char *pathname, int append));
+/*
+ Create a zipfile.
+ pathname contain on Windows XP a filename like "c:\\zlib\\zlib113.zip" or on
+ an Unix computer "zlib/zlib113.zip".
+ if the file pathname exist and append==APPEND_STATUS_CREATEAFTER, the zip
+ will be created at the end of the file.
+ (useful if the file contain a self extractor code)
+ if the file pathname exist and append==APPEND_STATUS_ADDINZIP, we will
+ add files in existing zip (be sure you don't add file that doesn't exist)
+ If the zipfile cannot be opened, the return value is NULL.
+ Else, the return value is a zipFile Handle, usable with other function
+ of this zip package.
+*/
+
+/* Note : there is no delete function into a zipfile.
+ If you want delete file into a zipfile, you must open a zipfile, and create another
+ Of couse, you can use RAW reading and writing to copy the file you did not want delte
+*/
+
+extern zipFile ZEXPORT zipOpen2 OF((const char *pathname,
+ int append,
+ zipcharpc* globalcomment,
+ zlib_filefunc_def* pzlib_filefunc_def));
+
+extern int ZEXPORT zipOpenNewFileInZip OF((zipFile file,
+ const char* filename,
+ const zip_fileinfo* zipfi,
+ const void* extrafield_local,
+ uInt size_extrafield_local,
+ const void* extrafield_global,
+ uInt size_extrafield_global,
+ const char* comment,
+ int method,
+ int level));
+/*
+ Open a file in the ZIP for writing.
+ filename : the filename in zip (if NULL, '-' without quote will be used
+ *zipfi contain supplemental information
+ if extrafield_local!=NULL and size_extrafield_local>0, extrafield_local
+ contains the extrafield data the the local header
+ if extrafield_global!=NULL and size_extrafield_global>0, extrafield_global
+ contains the extrafield data the the local header
+ if comment != NULL, comment contain the comment string
+ method contain the compression method (0 for store, Z_DEFLATED for deflate)
+ level contain the level of compression (can be Z_DEFAULT_COMPRESSION)
+*/
+
+
+extern int ZEXPORT zipOpenNewFileInZip2 OF((zipFile file,
+ const char* filename,
+ const zip_fileinfo* zipfi,
+ const void* extrafield_local,
+ uInt size_extrafield_local,
+ const void* extrafield_global,
+ uInt size_extrafield_global,
+ const char* comment,
+ int method,
+ int level,
+ int raw));
+
+/*
+ Same than zipOpenNewFileInZip, except if raw=1, we write raw file
+ */
+
+extern int ZEXPORT zipOpenNewFileInZip3 OF((zipFile file,
+ const char* filename,
+ const zip_fileinfo* zipfi,
+ const void* extrafield_local,
+ uInt size_extrafield_local,
+ const void* extrafield_global,
+ uInt size_extrafield_global,
+ const char* comment,
+ int method,
+ int level,
+ int raw,
+ int windowBits,
+ int memLevel,
+ int strategy,
+ const char* password,
+ uLong crcForCtypting));
+
+/*
+ Same than zipOpenNewFileInZip2, except
+ windowBits,memLevel,,strategy : see parameter strategy in deflateInit2
+ password : crypting password (NULL for no crypting)
+ crcForCtypting : crc of file to compress (needed for crypting)
+ */
+
+
+extern int ZEXPORT zipWriteInFileInZip OF((zipFile file,
+ const void* buf,
+ unsigned len));
+/*
+ Write data in the zipfile
+*/
+
+extern int ZEXPORT zipCloseFileInZip OF((zipFile file));
+/*
+ Close the current file in the zipfile
+*/
+
+extern int ZEXPORT zipCloseFileInZipRaw OF((zipFile file,
+ uLong uncompressed_size,
+ uLong crc32));
+/*
+ Close the current file in the zipfile, for fiel opened with
+ parameter raw=1 in zipOpenNewFileInZip2
+ uncompressed_size and crc32 are value for the uncompressed size
+*/
+
+extern int ZEXPORT zipClose OF((zipFile file,
+ const char* global_comment));
+/*
+ Close the zipfile
+*/
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _zip_H */
diff --git a/externals/g3dlite/zip.lib/source/crypt.h b/externals/g3dlite/zip.lib/source/crypt.h
new file mode 100644
index 00000000000..622f4bc2ec4
--- /dev/null
+++ b/externals/g3dlite/zip.lib/source/crypt.h
@@ -0,0 +1,132 @@
+/* crypt.h -- base code for crypt/uncrypt ZIPfile
+
+
+ Version 1.01e, February 12th, 2005
+
+ Copyright (C) 1998-2005 Gilles Vollant
+
+ This code is a modified version of crypting code in Infozip distribution
+
+ The encryption/decryption parts of this source code (as opposed to the
+ non-echoing password parts) were originally written in Europe. The
+ whole source package can be freely distributed, including from the USA.
+ (Prior to January 2000, re-export from the US was a violation of US law.)
+
+ This encryption code is a direct transcription of the algorithm from
+ Roger Schlafly, described by Phil Katz in the file appnote.txt. This
+ file (appnote.txt) is distributed with the PKZIP program (even in the
+ version without encryption capabilities).
+
+ If you don't need crypting in your application, just define symbols
+ NOCRYPT and NOUNCRYPT.
+
+ This code support the "Traditional PKWARE Encryption".
+
+ The new AES encryption added on Zip format by Winzip (see the page
+ http://www.winzip.com/aes_info.htm ) and PKWare PKZip 5.x Strong
+ Encryption is not supported.
+*/
+
+#define CRC32(c, b) ((*(pcrc_32_tab+(((int)(c) ^ (b)) & 0xff))) ^ ((c) >> 8))
+
+/***********************************************************************
+ * Return the next byte in the pseudo-random sequence
+ */
+static int decrypt_byte(unsigned long* pkeys, const unsigned long* pcrc_32_tab)
+{
+ unsigned temp; /* POTENTIAL BUG: temp*(temp^1) may overflow in an
+ * unpredictable manner on 16-bit systems; not a problem
+ * with any known compiler so far, though */
+
+ temp = ((unsigned)(*(pkeys+2)) & 0xffff) | 2;
+ return (int)(((temp * (temp ^ 1)) >> 8) & 0xff);
+}
+
+/***********************************************************************
+ * Update the encryption keys with the next byte of plain text
+ */
+static int update_keys(unsigned long* pkeys,const unsigned long* pcrc_32_tab,int c)
+{
+ (*(pkeys+0)) = CRC32((*(pkeys+0)), c);
+ (*(pkeys+1)) += (*(pkeys+0)) & 0xff;
+ (*(pkeys+1)) = (*(pkeys+1)) * 134775813L + 1;
+ {
+ register int keyshift = (int)((*(pkeys+1)) >> 24);
+ (*(pkeys+2)) = CRC32((*(pkeys+2)), keyshift);
+ }
+ return c;
+}
+
+
+/***********************************************************************
+ * Initialize the encryption keys and the random header according to
+ * the given password.
+ */
+static void init_keys(const char* passwd,unsigned long* pkeys,const unsigned long* pcrc_32_tab)
+{
+ *(pkeys+0) = 305419896L;
+ *(pkeys+1) = 591751049L;
+ *(pkeys+2) = 878082192L;
+ while (*passwd != '\0') {
+ update_keys(pkeys,pcrc_32_tab,(int)*passwd);
+ passwd++;
+ }
+}
+
+#define zdecode(pkeys,pcrc_32_tab,c) \
+ (update_keys(pkeys,pcrc_32_tab,c ^= decrypt_byte(pkeys,pcrc_32_tab)))
+
+#define zencode(pkeys,pcrc_32_tab,c,t) \
+ (t=decrypt_byte(pkeys,pcrc_32_tab), update_keys(pkeys,pcrc_32_tab,c), t^(c))
+
+#ifdef INCLUDECRYPTINGCODE_IFCRYPTALLOWED
+
+#define RAND_HEAD_LEN 12
+ /* "last resort" source for second part of crypt seed pattern */
+# ifndef ZCR_SEED2
+# define ZCR_SEED2 3141592654UL /* use PI as default pattern */
+# endif
+
+static int crypthead(passwd, buf, bufSize, pkeys, pcrc_32_tab, crcForCrypting)
+ const char *passwd; /* password string */
+ unsigned char *buf; /* where to write header */
+ int bufSize;
+ unsigned long* pkeys;
+ const unsigned long* pcrc_32_tab;
+ unsigned long crcForCrypting;
+{
+ int n; /* index in random header */
+ int t; /* temporary */
+ int c; /* random byte */
+ unsigned char header[RAND_HEAD_LEN-2]; /* random header */
+ static unsigned calls = 0; /* ensure different random header each time */
+
+ if (bufSize<RAND_HEAD_LEN)
+ return 0;
+
+ /* First generate RAND_HEAD_LEN-2 random bytes. We encrypt the
+ * output of rand() to get less predictability, since rand() is
+ * often poorly implemented.
+ */
+ if (++calls == 1)
+ {
+ srand((unsigned)(time(NULL) ^ ZCR_SEED2));
+ }
+ init_keys(passwd, pkeys, pcrc_32_tab);
+ for (n = 0; n < RAND_HEAD_LEN-2; n++)
+ {
+ c = (rand() >> 7) & 0xff;
+ header[n] = (unsigned char)zencode(pkeys, pcrc_32_tab, c, t);
+ }
+ /* Encrypt random header (last two bytes is high word of crc) */
+ init_keys(passwd, pkeys, pcrc_32_tab);
+ for (n = 0; n < RAND_HEAD_LEN-2; n++)
+ {
+ buf[n] = (unsigned char)zencode(pkeys, pcrc_32_tab, header[n], t);
+ }
+ buf[n++] = zencode(pkeys, pcrc_32_tab, (int)(crcForCrypting >> 16) & 0xff, t);
+ buf[n++] = zencode(pkeys, pcrc_32_tab, (int)(crcForCrypting >> 24) & 0xff, t);
+ return n;
+}
+
+#endif
diff --git a/externals/g3dlite/zip.lib/source/ioapi.c b/externals/g3dlite/zip.lib/source/ioapi.c
new file mode 100644
index 00000000000..f1bee23e64b
--- /dev/null
+++ b/externals/g3dlite/zip.lib/source/ioapi.c
@@ -0,0 +1,177 @@
+/* ioapi.c -- IO base function header for compress/uncompress .zip
+ files using zlib + zip or unzip API
+
+ Version 1.01e, February 12th, 2005
+
+ Copyright (C) 1998-2005 Gilles Vollant
+*/
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "zlib.h"
+#include "ioapi.h"
+
+
+
+/* I've found an old Unix (a SunOS 4.1.3_U1) without all SEEK_* defined.... */
+
+#ifndef SEEK_CUR
+#define SEEK_CUR 1
+#endif
+
+#ifndef SEEK_END
+#define SEEK_END 2
+#endif
+
+#ifndef SEEK_SET
+#define SEEK_SET 0
+#endif
+
+voidpf ZCALLBACK fopen_file_func OF((
+ voidpf opaque,
+ const char* filename,
+ int mode));
+
+uLong ZCALLBACK fread_file_func OF((
+ voidpf opaque,
+ voidpf stream,
+ void* buf,
+ uLong size));
+
+uLong ZCALLBACK fwrite_file_func OF((
+ voidpf opaque,
+ voidpf stream,
+ const void* buf,
+ uLong size));
+
+long ZCALLBACK ftell_file_func OF((
+ voidpf opaque,
+ voidpf stream));
+
+long ZCALLBACK fseek_file_func OF((
+ voidpf opaque,
+ voidpf stream,
+ uLong offset,
+ int origin));
+
+int ZCALLBACK fclose_file_func OF((
+ voidpf opaque,
+ voidpf stream));
+
+int ZCALLBACK ferror_file_func OF((
+ voidpf opaque,
+ voidpf stream));
+
+
+voidpf ZCALLBACK fopen_file_func (opaque, filename, mode)
+ voidpf opaque;
+ const char* filename;
+ int mode;
+{
+ FILE* file = NULL;
+ const char* mode_fopen = NULL;
+ if ((mode & ZLIB_FILEFUNC_MODE_READWRITEFILTER)==ZLIB_FILEFUNC_MODE_READ)
+ mode_fopen = "rb";
+ else
+ if (mode & ZLIB_FILEFUNC_MODE_EXISTING)
+ mode_fopen = "r+b";
+ else
+ if (mode & ZLIB_FILEFUNC_MODE_CREATE)
+ mode_fopen = "wb";
+
+ if ((filename!=NULL) && (mode_fopen != NULL))
+ file = fopen(filename, mode_fopen);
+ return file;
+}
+
+
+uLong ZCALLBACK fread_file_func (opaque, stream, buf, size)
+ voidpf opaque;
+ voidpf stream;
+ void* buf;
+ uLong size;
+{
+ uLong ret;
+ ret = (uLong)fread(buf, 1, (size_t)size, (FILE *)stream);
+ return ret;
+}
+
+
+uLong ZCALLBACK fwrite_file_func (opaque, stream, buf, size)
+ voidpf opaque;
+ voidpf stream;
+ const void* buf;
+ uLong size;
+{
+ uLong ret;
+ ret = (uLong)fwrite(buf, 1, (size_t)size, (FILE *)stream);
+ return ret;
+}
+
+long ZCALLBACK ftell_file_func (opaque, stream)
+ voidpf opaque;
+ voidpf stream;
+{
+ long ret;
+ ret = ftell((FILE *)stream);
+ return ret;
+}
+
+long ZCALLBACK fseek_file_func (opaque, stream, offset, origin)
+ voidpf opaque;
+ voidpf stream;
+ uLong offset;
+ int origin;
+{
+ int fseek_origin=0;
+ long ret;
+ switch (origin)
+ {
+ case ZLIB_FILEFUNC_SEEK_CUR :
+ fseek_origin = SEEK_CUR;
+ break;
+ case ZLIB_FILEFUNC_SEEK_END :
+ fseek_origin = SEEK_END;
+ break;
+ case ZLIB_FILEFUNC_SEEK_SET :
+ fseek_origin = SEEK_SET;
+ break;
+ default: return -1;
+ }
+ ret = 0;
+ fseek((FILE *)stream, offset, fseek_origin);
+ return ret;
+}
+
+int ZCALLBACK fclose_file_func (opaque, stream)
+ voidpf opaque;
+ voidpf stream;
+{
+ int ret;
+ ret = fclose((FILE *)stream);
+ return ret;
+}
+
+int ZCALLBACK ferror_file_func (opaque, stream)
+ voidpf opaque;
+ voidpf stream;
+{
+ int ret;
+ ret = ferror((FILE *)stream);
+ return ret;
+}
+
+void fill_fopen_filefunc (pzlib_filefunc_def)
+ zlib_filefunc_def* pzlib_filefunc_def;
+{
+ pzlib_filefunc_def->zopen_file = fopen_file_func;
+ pzlib_filefunc_def->zread_file = fread_file_func;
+ pzlib_filefunc_def->zwrite_file = fwrite_file_func;
+ pzlib_filefunc_def->ztell_file = ftell_file_func;
+ pzlib_filefunc_def->zseek_file = fseek_file_func;
+ pzlib_filefunc_def->zclose_file = fclose_file_func;
+ pzlib_filefunc_def->zerror_file = ferror_file_func;
+ pzlib_filefunc_def->opaque = NULL;
+}
diff --git a/externals/g3dlite/zip.lib/source/iowin32.c b/externals/g3dlite/zip.lib/source/iowin32.c
new file mode 100644
index 00000000000..ce911e3636e
--- /dev/null
+++ b/externals/g3dlite/zip.lib/source/iowin32.c
@@ -0,0 +1,272 @@
+#ifdef _MSC_VER
+/* iowin32.c -- IO base function header for compress/uncompress .zip
+ files using zlib + zip or unzip API
+ This IO API version uses the Win32 API (for Microsoft Windows)
+
+ Version 1.01e, February 12th, 2005
+
+ Copyright (C) 1998-2005 Gilles Vollant
+*/
+
+#include <stdlib.h>
+
+#include "zlib.h"
+#include "ioapi.h"
+#include "iowin32.h"
+
+#ifndef INVALID_HANDLE_VALUE
+#define INVALID_HANDLE_VALUE (0xFFFFFFFF)
+#endif
+
+#ifndef INVALID_SET_FILE_POINTER
+#define INVALID_SET_FILE_POINTER ((DWORD)-1)
+#endif
+
+voidpf ZCALLBACK win32_open_file_func OF((
+ voidpf opaque,
+ const char* filename,
+ int mode));
+
+uLong ZCALLBACK win32_read_file_func OF((
+ voidpf opaque,
+ voidpf stream,
+ void* buf,
+ uLong size));
+
+uLong ZCALLBACK win32_write_file_func OF((
+ voidpf opaque,
+ voidpf stream,
+ const void* buf,
+ uLong size));
+
+long ZCALLBACK win32_tell_file_func OF((
+ voidpf opaque,
+ voidpf stream));
+
+long ZCALLBACK win32_seek_file_func OF((
+ voidpf opaque,
+ voidpf stream,
+ uLong offset,
+ int origin));
+
+int ZCALLBACK win32_close_file_func OF((
+ voidpf opaque,
+ voidpf stream));
+
+int ZCALLBACK win32_error_file_func OF((
+ voidpf opaque,
+ voidpf stream));
+
+typedef struct
+{
+ HANDLE hf;
+ int error;
+} WIN32FILE_IOWIN;
+
+voidpf ZCALLBACK win32_open_file_func (opaque, filename, mode)
+ voidpf opaque;
+ const char* filename;
+ int mode;
+{
+ const char* mode_fopen = NULL;
+ DWORD dwDesiredAccess,dwCreationDisposition,dwShareMode,dwFlagsAndAttributes ;
+ HANDLE hFile = 0;
+ voidpf ret=NULL;
+
+ dwDesiredAccess = dwShareMode = dwFlagsAndAttributes = 0;
+
+ if ((mode & ZLIB_FILEFUNC_MODE_READWRITEFILTER)==ZLIB_FILEFUNC_MODE_READ)
+ {
+ dwDesiredAccess = GENERIC_READ;
+ dwCreationDisposition = OPEN_EXISTING;
+ dwShareMode = FILE_SHARE_READ;
+ }
+ else
+ if (mode & ZLIB_FILEFUNC_MODE_EXISTING)
+ {
+ dwDesiredAccess = GENERIC_WRITE | GENERIC_READ;
+ dwCreationDisposition = OPEN_EXISTING;
+ }
+ else
+ if (mode & ZLIB_FILEFUNC_MODE_CREATE)
+ {
+ dwDesiredAccess = GENERIC_WRITE | GENERIC_READ;
+ dwCreationDisposition = CREATE_ALWAYS;
+ }
+
+ if ((filename!=NULL) && (dwDesiredAccess != 0))
+ hFile = CreateFile((LPCTSTR)filename, dwDesiredAccess, dwShareMode, NULL,
+ dwCreationDisposition, dwFlagsAndAttributes, NULL);
+
+ if (hFile == INVALID_HANDLE_VALUE)
+ hFile = NULL;
+
+ if (hFile != NULL)
+ {
+ WIN32FILE_IOWIN w32fiow;
+ w32fiow.hf = hFile;
+ w32fiow.error = 0;
+ ret = malloc(sizeof(WIN32FILE_IOWIN));
+ if (ret==NULL)
+ CloseHandle(hFile);
+ else *((WIN32FILE_IOWIN*)ret) = w32fiow;
+ }
+ return ret;
+}
+
+
+uLong ZCALLBACK win32_read_file_func (opaque, stream, buf, size)
+ voidpf opaque;
+ voidpf stream;
+ void* buf;
+ uLong size;
+{
+ uLong ret=0;
+ HANDLE hFile = NULL;
+ if (stream!=NULL)
+ hFile = ((WIN32FILE_IOWIN*)stream) -> hf;
+ if (hFile != NULL)
+ if (!ReadFile(hFile, buf, size, &ret, NULL))
+ {
+ DWORD dwErr = GetLastError();
+ if (dwErr == ERROR_HANDLE_EOF)
+ dwErr = 0;
+ ((WIN32FILE_IOWIN*)stream) -> error=(int)dwErr;
+ }
+
+ return ret;
+}
+
+
+uLong ZCALLBACK win32_write_file_func (opaque, stream, buf, size)
+ voidpf opaque;
+ voidpf stream;
+ const void* buf;
+ uLong size;
+{
+ uLong ret=0;
+ HANDLE hFile = NULL;
+ if (stream!=NULL)
+ hFile = ((WIN32FILE_IOWIN*)stream) -> hf;
+
+ if (hFile !=NULL)
+ if (!WriteFile(hFile, buf, size, &ret, NULL))
+ {
+ DWORD dwErr = GetLastError();
+ if (dwErr == ERROR_HANDLE_EOF)
+ dwErr = 0;
+ ((WIN32FILE_IOWIN*)stream) -> error=(int)dwErr;
+ }
+
+ return ret;
+}
+
+long ZCALLBACK win32_tell_file_func (opaque, stream)
+ voidpf opaque;
+ voidpf stream;
+{
+ long ret=-1;
+ HANDLE hFile = NULL;
+ if (stream!=NULL)
+ hFile = ((WIN32FILE_IOWIN*)stream) -> hf;
+ if (hFile != NULL)
+ {
+ DWORD dwSet = SetFilePointer(hFile, 0, NULL, FILE_CURRENT);
+ if (dwSet == INVALID_SET_FILE_POINTER)
+ {
+ DWORD dwErr = GetLastError();
+ ((WIN32FILE_IOWIN*)stream) -> error=(int)dwErr;
+ ret = -1;
+ }
+ else
+ ret=(long)dwSet;
+ }
+ return ret;
+}
+
+long ZCALLBACK win32_seek_file_func (opaque, stream, offset, origin)
+ voidpf opaque;
+ voidpf stream;
+ uLong offset;
+ int origin;
+{
+ DWORD dwMoveMethod=0xFFFFFFFF;
+ HANDLE hFile = NULL;
+
+ long ret=-1;
+ if (stream!=NULL)
+ hFile = ((WIN32FILE_IOWIN*)stream) -> hf;
+ switch (origin)
+ {
+ case ZLIB_FILEFUNC_SEEK_CUR :
+ dwMoveMethod = FILE_CURRENT;
+ break;
+ case ZLIB_FILEFUNC_SEEK_END :
+ dwMoveMethod = FILE_END;
+ break;
+ case ZLIB_FILEFUNC_SEEK_SET :
+ dwMoveMethod = FILE_BEGIN;
+ break;
+ default: return -1;
+ }
+
+ if (hFile != NULL)
+ {
+ DWORD dwSet = SetFilePointer(hFile, offset, NULL, dwMoveMethod);
+ if (dwSet == INVALID_SET_FILE_POINTER)
+ {
+ DWORD dwErr = GetLastError();
+ ((WIN32FILE_IOWIN*)stream) -> error=(int)dwErr;
+ ret = -1;
+ }
+ else
+ ret=0;
+ }
+ return ret;
+}
+
+int ZCALLBACK win32_close_file_func (opaque, stream)
+ voidpf opaque;
+ voidpf stream;
+{
+ int ret=-1;
+
+ if (stream!=NULL)
+ {
+ HANDLE hFile;
+ hFile = ((WIN32FILE_IOWIN*)stream) -> hf;
+ if (hFile != NULL)
+ {
+ CloseHandle(hFile);
+ ret=0;
+ }
+ free(stream);
+ }
+ return ret;
+}
+
+int ZCALLBACK win32_error_file_func (opaque, stream)
+ voidpf opaque;
+ voidpf stream;
+{
+ int ret=-1;
+ if (stream!=NULL)
+ {
+ ret = ((WIN32FILE_IOWIN*)stream) -> error;
+ }
+ return ret;
+}
+
+void fill_win32_filefunc (pzlib_filefunc_def)
+ zlib_filefunc_def* pzlib_filefunc_def;
+{
+ pzlib_filefunc_def->zopen_file = win32_open_file_func;
+ pzlib_filefunc_def->zread_file = win32_read_file_func;
+ pzlib_filefunc_def->zwrite_file = win32_write_file_func;
+ pzlib_filefunc_def->ztell_file = win32_tell_file_func;
+ pzlib_filefunc_def->zseek_file = win32_seek_file_func;
+ pzlib_filefunc_def->zclose_file = win32_close_file_func;
+ pzlib_filefunc_def->zerror_file = win32_error_file_func;
+ pzlib_filefunc_def->opaque=NULL;
+}
+#endif
diff --git a/externals/g3dlite/zip.lib/source/iowin32.h b/externals/g3dlite/zip.lib/source/iowin32.h
new file mode 100644
index 00000000000..1978f6152dd
--- /dev/null
+++ b/externals/g3dlite/zip.lib/source/iowin32.h
@@ -0,0 +1,23 @@
+#ifdef _MSC_VER
+/* iowin32.h -- IO base function header for compress/uncompress .zip
+ files using zlib + zip or unzip API
+ This IO API version uses the Win32 API (for Microsoft Windows)
+
+ Version 1.01e, February 12th, 2005
+
+ Copyright (C) 1998-2005 Gilles Vollant
+*/
+
+#include <windows.h>
+
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+void fill_win32_filefunc OF((zlib_filefunc_def* pzlib_filefunc_def));
+
+#ifdef __cplusplus
+}
+#endif
+#endif
diff --git a/externals/g3dlite/zip.lib/source/unzip.c b/externals/g3dlite/zip.lib/source/unzip.c
new file mode 100644
index 00000000000..e80bc5bde7c
--- /dev/null
+++ b/externals/g3dlite/zip.lib/source/unzip.c
@@ -0,0 +1,1604 @@
+/* unzip.c -- IO for uncompress .zip files using zlib
+ Version 1.01e, February 12th, 2005
+
+ Copyright (C) 1998-2005 Gilles Vollant
+
+ Read unzip.h for more info
+*/
+
+/* Decryption code comes from crypt.c by Info-ZIP but has been greatly reduced in terms of
+compatibility with older software. The following is from the original crypt.c. Code
+woven in by Terry Thorsen 1/2003.
+*/
+/*
+ Copyright (c) 1990-2000 Info-ZIP. All rights reserved.
+
+ See the accompanying file LICENSE, version 2000-Apr-09 or later
+ (the contents of which are also included in zip.h) for terms of use.
+ If, for some reason, all these files are missing, the Info-ZIP license
+ also may be found at: ftp://ftp.info-zip.org/pub/infozip/license.html
+*/
+/*
+ crypt.c (full version) by Info-ZIP. Last revised: [see crypt.h]
+
+ The encryption/decryption parts of this source code (as opposed to the
+ non-echoing password parts) were originally written in Europe. The
+ whole source package can be freely distributed, including from the USA.
+ (Prior to January 2000, re-export from the US was a violation of US law.)
+ */
+
+/*
+ This encryption code is a direct transcription of the algorithm from
+ Roger Schlafly, described by Phil Katz in the file appnote.txt. This
+ file (appnote.txt) is distributed with the PKZIP program (even in the
+ version without encryption capabilities).
+ */
+
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include "zlib.h"
+#include "unzip.h"
+
+#ifdef STDC
+# include <stddef.h>
+# include <string.h>
+# include <stdlib.h>
+#endif
+#ifdef NO_ERRNO_H
+ extern int errno;
+#else
+# include <errno.h>
+#endif
+
+
+#ifndef local
+# define local static
+#endif
+/* compile with -Dlocal if your debugger can't find static symbols */
+
+
+#ifndef CASESENSITIVITYDEFAULT_NO
+# if !defined(unix) && !defined(CASESENSITIVITYDEFAULT_YES)
+# define CASESENSITIVITYDEFAULT_NO
+# endif
+#endif
+
+
+#ifndef UNZ_BUFSIZE
+#define UNZ_BUFSIZE (16384)
+#endif
+
+#ifndef UNZ_MAXFILENAMEINZIP
+#define UNZ_MAXFILENAMEINZIP (256)
+#endif
+
+#ifndef ALLOC
+# define ALLOC(size) (malloc(size))
+#endif
+#ifndef TRYFREE
+# define TRYFREE(p) {if (p) free(p);}
+#endif
+
+#define SIZECENTRALDIRITEM (0x2e)
+#define SIZEZIPLOCALHEADER (0x1e)
+
+
+
+
+const char unz_copyright[] =
+ " unzip 1.01 Copyright 1998-2004 Gilles Vollant - http://www.winimage.com/zLibDll";
+
+/* unz_file_info_interntal contain internal info about a file in zipfile*/
+typedef struct unz_file_info_internal_s
+{
+ uLong offset_curfile;/* relative offset of local header 4 bytes */
+} unz_file_info_internal;
+
+
+/* file_in_zip_read_info_s contain internal information about a file in zipfile,
+ when reading and decompress it */
+typedef struct
+{
+ char *read_buffer; /* internal buffer for compressed data */
+ z_stream stream; /* zLib stream structure for inflate */
+
+ uLong pos_in_zipfile; /* position in byte on the zipfile, for fseek*/
+ uLong stream_initialised; /* flag set if stream structure is initialised*/
+
+ uLong offset_local_extrafield;/* offset of the local extra field */
+ uInt size_local_extrafield;/* size of the local extra field */
+ uLong pos_local_extrafield; /* position in the local extra field in read*/
+
+ uLong crc32; /* crc32 of all data uncompressed */
+ uLong crc32_wait; /* crc32 we must obtain after decompress all */
+ uLong rest_read_compressed; /* number of byte to be decompressed */
+ uLong rest_read_uncompressed;/*number of byte to be obtained after decomp*/
+ zlib_filefunc_def z_filefunc;
+ voidpf filestream; /* io structore of the zipfile */
+ uLong compression_method; /* compression method (0==store) */
+ uLong byte_before_the_zipfile;/* byte before the zipfile, (>0 for sfx)*/
+ int raw;
+} file_in_zip_read_info_s;
+
+
+/* unz_s contain internal information about the zipfile
+*/
+typedef struct
+{
+ zlib_filefunc_def z_filefunc;
+ voidpf filestream; /* io structore of the zipfile */
+ unz_global_info gi; /* public global information */
+ uLong byte_before_the_zipfile;/* byte before the zipfile, (>0 for sfx)*/
+ uLong num_file; /* number of the current file in the zipfile*/
+ uLong pos_in_central_dir; /* pos of the current file in the central dir*/
+ uLong current_file_ok; /* flag about the usability of the current file*/
+ uLong central_pos; /* position of the beginning of the central dir*/
+
+ uLong size_central_dir; /* size of the central directory */
+ uLong offset_central_dir; /* offset of start of central directory with
+ respect to the starting disk number */
+
+ unz_file_info cur_file_info; /* public info about the current file in zip*/
+ unz_file_info_internal cur_file_info_internal; /* private info about it*/
+ file_in_zip_read_info_s* pfile_in_zip_read; /* structure about the current
+ file if we are decompressing it */
+ int encrypted;
+# ifndef NOUNCRYPT
+ unsigned long keys[3]; /* keys defining the pseudo-random sequence */
+ const unsigned long* pcrc_32_tab;
+# endif
+} unz_s;
+
+
+#ifndef NOUNCRYPT
+#include "crypt.h"
+#endif
+
+/* ===========================================================================
+ Read a byte from a gz_stream; update next_in and avail_in. Return EOF
+ for end of file.
+ IN assertion: the stream s has been sucessfully opened for reading.
+*/
+
+
+local int unzlocal_getByte OF((
+ const zlib_filefunc_def* pzlib_filefunc_def,
+ voidpf filestream,
+ int *pi));
+
+local int unzlocal_getByte(pzlib_filefunc_def,filestream,pi)
+ const zlib_filefunc_def* pzlib_filefunc_def;
+ voidpf filestream;
+ int *pi;
+{
+ unsigned char c;
+ int err = (int)ZREAD(*pzlib_filefunc_def,filestream,&c,1);
+ if (err==1)
+ {
+ *pi = (int)c;
+ return UNZ_OK;
+ }
+ else
+ {
+ if (ZERROR(*pzlib_filefunc_def,filestream))
+ return UNZ_ERRNO;
+ else
+ return UNZ_EOF;
+ }
+}
+
+
+/* ===========================================================================
+ Reads a long in LSB order from the given gz_stream. Sets
+*/
+local int unzlocal_getShort OF((
+ const zlib_filefunc_def* pzlib_filefunc_def,
+ voidpf filestream,
+ uLong *pX));
+
+local int unzlocal_getShort (pzlib_filefunc_def,filestream,pX)
+ const zlib_filefunc_def* pzlib_filefunc_def;
+ voidpf filestream;
+ uLong *pX;
+{
+ uLong x = 0;
+ int i = 0;
+ int err = 0;
+
+ err = unzlocal_getByte(pzlib_filefunc_def,filestream,&i);
+ x = (uLong)i;
+
+ if (err==UNZ_OK)
+ err = unzlocal_getByte(pzlib_filefunc_def,filestream,&i);
+ x += ((uLong)i)<<8;
+
+ if (err==UNZ_OK)
+ *pX = x;
+ else
+ *pX = 0;
+ return err;
+}
+
+local int unzlocal_getLong OF((
+ const zlib_filefunc_def* pzlib_filefunc_def,
+ voidpf filestream,
+ uLong *pX));
+
+local int unzlocal_getLong (pzlib_filefunc_def,filestream,pX)
+ const zlib_filefunc_def* pzlib_filefunc_def;
+ voidpf filestream;
+ uLong *pX;
+{
+ uLong x = 0;
+ int i = 0;
+ int err = 0;
+
+ err = unzlocal_getByte(pzlib_filefunc_def,filestream,&i);
+ x = (uLong)i;
+
+ if (err==UNZ_OK)
+ err = unzlocal_getByte(pzlib_filefunc_def,filestream,&i);
+ x += ((uLong)i)<<8;
+
+ if (err==UNZ_OK)
+ err = unzlocal_getByte(pzlib_filefunc_def,filestream,&i);
+ x += ((uLong)i)<<16;
+
+ if (err==UNZ_OK)
+ err = unzlocal_getByte(pzlib_filefunc_def,filestream,&i);
+ x += ((uLong)i)<<24;
+
+ if (err==UNZ_OK)
+ *pX = x;
+ else
+ *pX = 0;
+ return err;
+}
+
+
+/* My own strcmpi / strcasecmp */
+local int strcmpcasenosensitive_internal (fileName1,fileName2)
+ const char* fileName1;
+ const char* fileName2;
+{
+ for (;;)
+ {
+ char c1=*(fileName1++);
+ char c2=*(fileName2++);
+ if ((c1>='a') && (c1<='z'))
+ c1 -= 0x20;
+ if ((c2>='a') && (c2<='z'))
+ c2 -= 0x20;
+ if (c1=='\0')
+ return ((c2=='\0') ? 0 : -1);
+ if (c2=='\0')
+ return 1;
+ if (c1<c2)
+ return -1;
+ if (c1>c2)
+ return 1;
+ }
+}
+
+
+#ifdef CASESENSITIVITYDEFAULT_NO
+#define CASESENSITIVITYDEFAULTVALUE 2
+#else
+#define CASESENSITIVITYDEFAULTVALUE 1
+#endif
+
+#ifndef STRCMPCASENOSENTIVEFUNCTION
+#define STRCMPCASENOSENTIVEFUNCTION strcmpcasenosensitive_internal
+#endif
+
+/*
+ Compare two filename (fileName1,fileName2).
+ If iCaseSenisivity = 1, comparision is case sensitivity (like strcmp)
+ If iCaseSenisivity = 2, comparision is not case sensitivity (like strcmpi
+ or strcasecmp)
+ If iCaseSenisivity = 0, case sensitivity is defaut of your operating system
+ (like 1 on Unix, 2 on Windows)
+
+*/
+extern int ZEXPORT unzStringFileNameCompare (fileName1,fileName2,iCaseSensitivity)
+ const char* fileName1;
+ const char* fileName2;
+ int iCaseSensitivity;
+{
+ if (iCaseSensitivity==0)
+ iCaseSensitivity=CASESENSITIVITYDEFAULTVALUE;
+
+ if (iCaseSensitivity==1)
+ return strcmp(fileName1,fileName2);
+
+ return STRCMPCASENOSENTIVEFUNCTION(fileName1,fileName2);
+}
+
+#ifndef BUFREADCOMMENT
+#define BUFREADCOMMENT (0x400)
+#endif
+
+/*
+ Locate the Central directory of a zipfile (at the end, just before
+ the global comment)
+*/
+local uLong unzlocal_SearchCentralDir OF((
+ const zlib_filefunc_def* pzlib_filefunc_def,
+ voidpf filestream));
+
+local uLong unzlocal_SearchCentralDir(pzlib_filefunc_def,filestream)
+ const zlib_filefunc_def* pzlib_filefunc_def;
+ voidpf filestream;
+{
+ unsigned char* buf;
+ uLong uSizeFile;
+ uLong uBackRead;
+ uLong uMaxBack=0xffff; /* maximum size of global comment */
+ uLong uPosFound=0;
+
+ if (ZSEEK(*pzlib_filefunc_def,filestream,0,ZLIB_FILEFUNC_SEEK_END) != 0)
+ return 0;
+
+
+ uSizeFile = ZTELL(*pzlib_filefunc_def,filestream);
+
+ if (uMaxBack>uSizeFile)
+ uMaxBack = uSizeFile;
+
+ buf = (unsigned char*)ALLOC(BUFREADCOMMENT+4);
+ if (buf==NULL)
+ return 0;
+
+ uBackRead = 4;
+ while (uBackRead<uMaxBack)
+ {
+ uLong uReadSize,uReadPos ;
+ int i;
+ if (uBackRead+BUFREADCOMMENT>uMaxBack)
+ uBackRead = uMaxBack;
+ else
+ uBackRead+=BUFREADCOMMENT;
+ uReadPos = uSizeFile-uBackRead ;
+
+ uReadSize = ((BUFREADCOMMENT+4) < (uSizeFile-uReadPos)) ?
+ (BUFREADCOMMENT+4) : (uSizeFile-uReadPos);
+ if (ZSEEK(*pzlib_filefunc_def,filestream,uReadPos,ZLIB_FILEFUNC_SEEK_SET)!=0)
+ break;
+
+ if (ZREAD(*pzlib_filefunc_def,filestream,buf,uReadSize)!=uReadSize)
+ break;
+
+ for (i=(int)uReadSize-3; (i--)>0;)
+ if (((*(buf+i))==0x50) && ((*(buf+i+1))==0x4b) &&
+ ((*(buf+i+2))==0x05) && ((*(buf+i+3))==0x06))
+ {
+ uPosFound = uReadPos+i;
+ break;
+ }
+
+ if (uPosFound!=0)
+ break;
+ }
+ TRYFREE(buf);
+ return uPosFound;
+}
+
+/*
+ Open a Zip file. path contain the full pathname (by example,
+ on a Windows NT computer "c:\\test\\zlib114.zip" or on an Unix computer
+ "zlib/zlib114.zip".
+ If the zipfile cannot be opened (file doesn't exist or in not valid), the
+ return value is NULL.
+ Else, the return value is a unzFile Handle, usable with other function
+ of this unzip package.
+*/
+extern unzFile ZEXPORT unzOpen2 (path, pzlib_filefunc_def)
+ const char *path;
+ zlib_filefunc_def* pzlib_filefunc_def;
+{
+ unz_s us;
+ unz_s *s;
+ uLong central_pos,uL;
+
+ uLong number_disk; /* number of the current dist, used for
+ spaning ZIP, unsupported, always 0*/
+ uLong number_disk_with_CD; /* number the the disk with central dir, used
+ for spaning ZIP, unsupported, always 0*/
+ uLong number_entry_CD; /* total number of entries in
+ the central dir
+ (same than number_entry on nospan) */
+
+ int err=UNZ_OK;
+
+ if (unz_copyright[0]!=' ')
+ return NULL;
+
+ if (pzlib_filefunc_def==NULL)
+ fill_fopen_filefunc(&us.z_filefunc);
+ else
+ us.z_filefunc = *pzlib_filefunc_def;
+
+ us.filestream= (*(us.z_filefunc.zopen_file))(us.z_filefunc.opaque,
+ path,
+ ZLIB_FILEFUNC_MODE_READ |
+ ZLIB_FILEFUNC_MODE_EXISTING);
+ if (us.filestream==NULL)
+ return NULL;
+
+ central_pos = unzlocal_SearchCentralDir(&us.z_filefunc,us.filestream);
+ if (central_pos==0)
+ err=UNZ_ERRNO;
+
+ if (ZSEEK(us.z_filefunc, us.filestream,
+ central_pos,ZLIB_FILEFUNC_SEEK_SET)!=0)
+ err=UNZ_ERRNO;
+
+ /* the signature, already checked */
+ if (unzlocal_getLong(&us.z_filefunc, us.filestream,&uL)!=UNZ_OK)
+ err=UNZ_ERRNO;
+
+ /* number of this disk */
+ if (unzlocal_getShort(&us.z_filefunc, us.filestream,&number_disk)!=UNZ_OK)
+ err=UNZ_ERRNO;
+
+ /* number of the disk with the start of the central directory */
+ if (unzlocal_getShort(&us.z_filefunc, us.filestream,&number_disk_with_CD)!=UNZ_OK)
+ err=UNZ_ERRNO;
+
+ /* total number of entries in the central dir on this disk */
+ if (unzlocal_getShort(&us.z_filefunc, us.filestream,&us.gi.number_entry)!=UNZ_OK)
+ err=UNZ_ERRNO;
+
+ /* total number of entries in the central dir */
+ if (unzlocal_getShort(&us.z_filefunc, us.filestream,&number_entry_CD)!=UNZ_OK)
+ err=UNZ_ERRNO;
+
+ if ((number_entry_CD!=us.gi.number_entry) ||
+ (number_disk_with_CD!=0) ||
+ (number_disk!=0))
+ err=UNZ_BADZIPFILE;
+
+ /* size of the central directory */
+ if (unzlocal_getLong(&us.z_filefunc, us.filestream,&us.size_central_dir)!=UNZ_OK)
+ err=UNZ_ERRNO;
+
+ /* offset of start of central directory with respect to the
+ starting disk number */
+ if (unzlocal_getLong(&us.z_filefunc, us.filestream,&us.offset_central_dir)!=UNZ_OK)
+ err=UNZ_ERRNO;
+
+ /* zipfile comment length */
+ if (unzlocal_getShort(&us.z_filefunc, us.filestream,&us.gi.size_comment)!=UNZ_OK)
+ err=UNZ_ERRNO;
+
+ if ((central_pos<us.offset_central_dir+us.size_central_dir) &&
+ (err==UNZ_OK))
+ err=UNZ_BADZIPFILE;
+
+ if (err!=UNZ_OK)
+ {
+ ZCLOSE(us.z_filefunc, us.filestream);
+ return NULL;
+ }
+
+ us.byte_before_the_zipfile = central_pos -
+ (us.offset_central_dir+us.size_central_dir);
+ us.central_pos = central_pos;
+ us.pfile_in_zip_read = NULL;
+ us.encrypted = 0;
+
+
+ s=(unz_s*)ALLOC(sizeof(unz_s));
+ *s=us;
+ unzGoToFirstFile((unzFile)s);
+ return (unzFile)s;
+}
+
+
+extern unzFile ZEXPORT unzOpen (path)
+ const char *path;
+{
+ return unzOpen2(path, NULL);
+}
+
+/*
+ Close a ZipFile opened with unzipOpen.
+ If there is files inside the .Zip opened with unzipOpenCurrentFile (see later),
+ these files MUST be closed with unzipCloseCurrentFile before call unzipClose.
+ return UNZ_OK if there is no problem. */
+extern int ZEXPORT unzClose (file)
+ unzFile file;
+{
+ unz_s* s;
+ if (file==NULL)
+ return UNZ_PARAMERROR;
+ s=(unz_s*)file;
+
+ if (s->pfile_in_zip_read!=NULL)
+ unzCloseCurrentFile(file);
+
+ ZCLOSE(s->z_filefunc, s->filestream);
+ TRYFREE(s);
+ return UNZ_OK;
+}
+
+
+/*
+ Write info about the ZipFile in the *pglobal_info structure.
+ No preparation of the structure is needed
+ return UNZ_OK if there is no problem. */
+extern int ZEXPORT unzGetGlobalInfo (file,pglobal_info)
+ unzFile file;
+ unz_global_info *pglobal_info;
+{
+ unz_s* s;
+ if (file==NULL)
+ return UNZ_PARAMERROR;
+ s=(unz_s*)file;
+ *pglobal_info=s->gi;
+ return UNZ_OK;
+}
+
+
+/*
+ Translate date/time from Dos format to tm_unz (readable more easilty)
+*/
+local void unzlocal_DosDateToTmuDate (ulDosDate, ptm)
+ uLong ulDosDate;
+ tm_unz* ptm;
+{
+ uLong uDate;
+ uDate = (uLong)(ulDosDate>>16);
+ ptm->tm_mday = (uInt)(uDate&0x1f) ;
+ ptm->tm_mon = (uInt)((((uDate)&0x1E0)/0x20)-1) ;
+ ptm->tm_year = (uInt)(((uDate&0x0FE00)/0x0200)+1980) ;
+
+ ptm->tm_hour = (uInt) ((ulDosDate &0xF800)/0x800);
+ ptm->tm_min = (uInt) ((ulDosDate&0x7E0)/0x20) ;
+ ptm->tm_sec = (uInt) (2*(ulDosDate&0x1f)) ;
+}
+
+/*
+ Get Info about the current file in the zipfile, with internal only info
+*/
+local int unzlocal_GetCurrentFileInfoInternal OF((unzFile file,
+ unz_file_info *pfile_info,
+ unz_file_info_internal
+ *pfile_info_internal,
+ char *szFileName,
+ uLong fileNameBufferSize,
+ void *extraField,
+ uLong extraFieldBufferSize,
+ char *szComment,
+ uLong commentBufferSize));
+
+local int unzlocal_GetCurrentFileInfoInternal (file,
+ pfile_info,
+ pfile_info_internal,
+ szFileName, fileNameBufferSize,
+ extraField, extraFieldBufferSize,
+ szComment, commentBufferSize)
+ unzFile file;
+ unz_file_info *pfile_info;
+ unz_file_info_internal *pfile_info_internal;
+ char *szFileName;
+ uLong fileNameBufferSize;
+ void *extraField;
+ uLong extraFieldBufferSize;
+ char *szComment;
+ uLong commentBufferSize;
+{
+ unz_s* s;
+ unz_file_info file_info;
+ unz_file_info_internal file_info_internal;
+ int err=UNZ_OK;
+ uLong uMagic;
+ long lSeek=0;
+
+ if (file==NULL)
+ return UNZ_PARAMERROR;
+ s=(unz_s*)file;
+ if (ZSEEK(s->z_filefunc, s->filestream,
+ s->pos_in_central_dir+s->byte_before_the_zipfile,
+ ZLIB_FILEFUNC_SEEK_SET)!=0)
+ err=UNZ_ERRNO;
+
+
+ /* we check the magic */
+ if (err==UNZ_OK)
+ {
+ if (unzlocal_getLong(&s->z_filefunc, s->filestream,&uMagic) != UNZ_OK)
+ err=UNZ_ERRNO;
+ else if (uMagic!=0x02014b50)
+ err=UNZ_BADZIPFILE;
+ }
+
+ if (unzlocal_getShort(&s->z_filefunc, s->filestream,&file_info.version) != UNZ_OK)
+ err=UNZ_ERRNO;
+
+ if (unzlocal_getShort(&s->z_filefunc, s->filestream,&file_info.version_needed) != UNZ_OK)
+ err=UNZ_ERRNO;
+
+ if (unzlocal_getShort(&s->z_filefunc, s->filestream,&file_info.flag) != UNZ_OK)
+ err=UNZ_ERRNO;
+
+ if (unzlocal_getShort(&s->z_filefunc, s->filestream,&file_info.compression_method) != UNZ_OK)
+ err=UNZ_ERRNO;
+
+ if (unzlocal_getLong(&s->z_filefunc, s->filestream,&file_info.dosDate) != UNZ_OK)
+ err=UNZ_ERRNO;
+
+ unzlocal_DosDateToTmuDate(file_info.dosDate,&file_info.tmu_date);
+
+ if (unzlocal_getLong(&s->z_filefunc, s->filestream,&file_info.crc) != UNZ_OK)
+ err=UNZ_ERRNO;
+
+ if (unzlocal_getLong(&s->z_filefunc, s->filestream,&file_info.compressed_size) != UNZ_OK)
+ err=UNZ_ERRNO;
+
+ if (unzlocal_getLong(&s->z_filefunc, s->filestream,&file_info.uncompressed_size) != UNZ_OK)
+ err=UNZ_ERRNO;
+
+ if (unzlocal_getShort(&s->z_filefunc, s->filestream,&file_info.size_filename) != UNZ_OK)
+ err=UNZ_ERRNO;
+
+ if (unzlocal_getShort(&s->z_filefunc, s->filestream,&file_info.size_file_extra) != UNZ_OK)
+ err=UNZ_ERRNO;
+
+ if (unzlocal_getShort(&s->z_filefunc, s->filestream,&file_info.size_file_comment) != UNZ_OK)
+ err=UNZ_ERRNO;
+
+ if (unzlocal_getShort(&s->z_filefunc, s->filestream,&file_info.disk_num_start) != UNZ_OK)
+ err=UNZ_ERRNO;
+
+ if (unzlocal_getShort(&s->z_filefunc, s->filestream,&file_info.internal_fa) != UNZ_OK)
+ err=UNZ_ERRNO;
+
+ if (unzlocal_getLong(&s->z_filefunc, s->filestream,&file_info.external_fa) != UNZ_OK)
+ err=UNZ_ERRNO;
+
+ if (unzlocal_getLong(&s->z_filefunc, s->filestream,&file_info_internal.offset_curfile) != UNZ_OK)
+ err=UNZ_ERRNO;
+
+ lSeek+=file_info.size_filename;
+ if ((err==UNZ_OK) && (szFileName!=NULL))
+ {
+ uLong uSizeRead ;
+ if (file_info.size_filename<fileNameBufferSize)
+ {
+ *(szFileName+file_info.size_filename)='\0';
+ uSizeRead = file_info.size_filename;
+ }
+ else
+ uSizeRead = fileNameBufferSize;
+
+ if ((file_info.size_filename>0) && (fileNameBufferSize>0))
+ if (ZREAD(s->z_filefunc, s->filestream,szFileName,uSizeRead)!=uSizeRead)
+ err=UNZ_ERRNO;
+ lSeek -= uSizeRead;
+ }
+
+
+ if ((err==UNZ_OK) && (extraField!=NULL))
+ {
+ uLong uSizeRead ;
+ if (file_info.size_file_extra<extraFieldBufferSize)
+ uSizeRead = file_info.size_file_extra;
+ else
+ uSizeRead = extraFieldBufferSize;
+
+ if (lSeek!=0)
+ {
+ if (ZSEEK(s->z_filefunc, s->filestream,lSeek,ZLIB_FILEFUNC_SEEK_CUR)==0)
+ lSeek=0;
+ else
+ err=UNZ_ERRNO;
+ }
+ if ((file_info.size_file_extra>0) && (extraFieldBufferSize>0))
+ if (ZREAD(s->z_filefunc, s->filestream,extraField,uSizeRead)!=uSizeRead)
+ err=UNZ_ERRNO;
+ lSeek += file_info.size_file_extra - uSizeRead;
+ }
+ else
+ lSeek+=file_info.size_file_extra;
+
+
+ if ((err==UNZ_OK) && (szComment!=NULL))
+ {
+ uLong uSizeRead ;
+ if (file_info.size_file_comment<commentBufferSize)
+ {
+ *(szComment+file_info.size_file_comment)='\0';
+ uSizeRead = file_info.size_file_comment;
+ }
+ else
+ uSizeRead = commentBufferSize;
+
+ if (lSeek!=0)
+ {
+ if (ZSEEK(s->z_filefunc, s->filestream,lSeek,ZLIB_FILEFUNC_SEEK_CUR)==0)
+ lSeek=0;
+ else
+ err=UNZ_ERRNO;
+ }
+ if ((file_info.size_file_comment>0) && (commentBufferSize>0))
+ if (ZREAD(s->z_filefunc, s->filestream,szComment,uSizeRead)!=uSizeRead)
+ err=UNZ_ERRNO;
+ lSeek+=file_info.size_file_comment - uSizeRead;
+ }
+ else
+ lSeek+=file_info.size_file_comment;
+
+ if ((err==UNZ_OK) && (pfile_info!=NULL))
+ *pfile_info=file_info;
+
+ if ((err==UNZ_OK) && (pfile_info_internal!=NULL))
+ *pfile_info_internal=file_info_internal;
+
+ return err;
+}
+
+
+
+/*
+ Write info about the ZipFile in the *pglobal_info structure.
+ No preparation of the structure is needed
+ return UNZ_OK if there is no problem.
+*/
+extern int ZEXPORT unzGetCurrentFileInfo (file,
+ pfile_info,
+ szFileName, fileNameBufferSize,
+ extraField, extraFieldBufferSize,
+ szComment, commentBufferSize)
+ unzFile file;
+ unz_file_info *pfile_info;
+ char *szFileName;
+ uLong fileNameBufferSize;
+ void *extraField;
+ uLong extraFieldBufferSize;
+ char *szComment;
+ uLong commentBufferSize;
+{
+ return unzlocal_GetCurrentFileInfoInternal(file,pfile_info,NULL,
+ szFileName,fileNameBufferSize,
+ extraField,extraFieldBufferSize,
+ szComment,commentBufferSize);
+}
+
+/*
+ Set the current file of the zipfile to the first file.
+ return UNZ_OK if there is no problem
+*/
+extern int ZEXPORT unzGoToFirstFile (file)
+ unzFile file;
+{
+ int err=UNZ_OK;
+ unz_s* s;
+ if (file==NULL)
+ return UNZ_PARAMERROR;
+ s=(unz_s*)file;
+ s->pos_in_central_dir=s->offset_central_dir;
+ s->num_file=0;
+ err=unzlocal_GetCurrentFileInfoInternal(file,&s->cur_file_info,
+ &s->cur_file_info_internal,
+ NULL,0,NULL,0,NULL,0);
+ s->current_file_ok = (err == UNZ_OK);
+ return err;
+}
+
+/*
+ Set the current file of the zipfile to the next file.
+ return UNZ_OK if there is no problem
+ return UNZ_END_OF_LIST_OF_FILE if the actual file was the latest.
+*/
+extern int ZEXPORT unzGoToNextFile (file)
+ unzFile file;
+{
+ unz_s* s;
+ int err;
+
+ if (file==NULL)
+ return UNZ_PARAMERROR;
+ s=(unz_s*)file;
+ if (!s->current_file_ok)
+ return UNZ_END_OF_LIST_OF_FILE;
+ if (s->gi.number_entry != 0xffff) /* 2^16 files overflow hack */
+ if (s->num_file+1==s->gi.number_entry)
+ return UNZ_END_OF_LIST_OF_FILE;
+
+ s->pos_in_central_dir += SIZECENTRALDIRITEM + s->cur_file_info.size_filename +
+ s->cur_file_info.size_file_extra + s->cur_file_info.size_file_comment ;
+ s->num_file++;
+ err = unzlocal_GetCurrentFileInfoInternal(file,&s->cur_file_info,
+ &s->cur_file_info_internal,
+ NULL,0,NULL,0,NULL,0);
+ s->current_file_ok = (err == UNZ_OK);
+ return err;
+}
+
+
+/*
+ Try locate the file szFileName in the zipfile.
+ For the iCaseSensitivity signification, see unzipStringFileNameCompare
+
+ return value :
+ UNZ_OK if the file is found. It becomes the current file.
+ UNZ_END_OF_LIST_OF_FILE if the file is not found
+*/
+extern int ZEXPORT unzLocateFile (file, szFileName, iCaseSensitivity)
+ unzFile file;
+ const char *szFileName;
+ int iCaseSensitivity;
+{
+ unz_s* s;
+ int err;
+
+ /* We remember the 'current' position in the file so that we can jump
+ * back there if we fail.
+ */
+ unz_file_info cur_file_infoSaved;
+ unz_file_info_internal cur_file_info_internalSaved;
+ uLong num_fileSaved;
+ uLong pos_in_central_dirSaved;
+
+
+ if (file==NULL)
+ return UNZ_PARAMERROR;
+
+ if (strlen(szFileName)>=UNZ_MAXFILENAMEINZIP)
+ return UNZ_PARAMERROR;
+
+ s=(unz_s*)file;
+ if (!s->current_file_ok)
+ return UNZ_END_OF_LIST_OF_FILE;
+
+ /* Save the current state */
+ num_fileSaved = s->num_file;
+ pos_in_central_dirSaved = s->pos_in_central_dir;
+ cur_file_infoSaved = s->cur_file_info;
+ cur_file_info_internalSaved = s->cur_file_info_internal;
+
+ err = unzGoToFirstFile(file);
+
+ while (err == UNZ_OK)
+ {
+ char szCurrentFileName[UNZ_MAXFILENAMEINZIP+1];
+ err = unzGetCurrentFileInfo(file,NULL,
+ szCurrentFileName,sizeof(szCurrentFileName)-1,
+ NULL,0,NULL,0);
+ if (err == UNZ_OK)
+ {
+ if (unzStringFileNameCompare(szCurrentFileName,
+ szFileName,iCaseSensitivity)==0)
+ return UNZ_OK;
+ err = unzGoToNextFile(file);
+ }
+ }
+
+ /* We failed, so restore the state of the 'current file' to where we
+ * were.
+ */
+ s->num_file = num_fileSaved ;
+ s->pos_in_central_dir = pos_in_central_dirSaved ;
+ s->cur_file_info = cur_file_infoSaved;
+ s->cur_file_info_internal = cur_file_info_internalSaved;
+ return err;
+}
+
+
+/*
+///////////////////////////////////////////
+// Contributed by Ryan Haksi (mailto://cryogen@infoserve.net)
+// I need random access
+//
+// Further optimization could be realized by adding an ability
+// to cache the directory in memory. The goal being a single
+// comprehensive file read to put the file I need in a memory.
+*/
+
+/*
+typedef struct unz_file_pos_s
+{
+ uLong pos_in_zip_directory; // offset in file
+ uLong num_of_file; // # of file
+} unz_file_pos;
+*/
+
+extern int ZEXPORT unzGetFilePos(file, file_pos)
+ unzFile file;
+ unz_file_pos* file_pos;
+{
+ unz_s* s;
+
+ if (file==NULL || file_pos==NULL)
+ return UNZ_PARAMERROR;
+ s=(unz_s*)file;
+ if (!s->current_file_ok)
+ return UNZ_END_OF_LIST_OF_FILE;
+
+ file_pos->pos_in_zip_directory = s->pos_in_central_dir;
+ file_pos->num_of_file = s->num_file;
+
+ return UNZ_OK;
+}
+
+extern int ZEXPORT unzGoToFilePos(file, file_pos)
+ unzFile file;
+ unz_file_pos* file_pos;
+{
+ unz_s* s;
+ int err;
+
+ if (file==NULL || file_pos==NULL)
+ return UNZ_PARAMERROR;
+ s=(unz_s*)file;
+
+ /* jump to the right spot */
+ s->pos_in_central_dir = file_pos->pos_in_zip_directory;
+ s->num_file = file_pos->num_of_file;
+
+ /* set the current file */
+ err = unzlocal_GetCurrentFileInfoInternal(file,&s->cur_file_info,
+ &s->cur_file_info_internal,
+ NULL,0,NULL,0,NULL,0);
+ /* return results */
+ s->current_file_ok = (err == UNZ_OK);
+ return err;
+}
+
+/*
+// Unzip Helper Functions - should be here?
+///////////////////////////////////////////
+*/
+
+/*
+ Read the local header of the current zipfile
+ Check the coherency of the local header and info in the end of central
+ directory about this file
+ store in *piSizeVar the size of extra info in local header
+ (filename and size of extra field data)
+*/
+local int unzlocal_CheckCurrentFileCoherencyHeader (s,piSizeVar,
+ poffset_local_extrafield,
+ psize_local_extrafield)
+ unz_s* s;
+ uInt* piSizeVar;
+ uLong *poffset_local_extrafield;
+ uInt *psize_local_extrafield;
+{
+ uLong uMagic,uData,uFlags;
+ uLong size_filename;
+ uLong size_extra_field;
+ int err=UNZ_OK;
+
+ *piSizeVar = 0;
+ *poffset_local_extrafield = 0;
+ *psize_local_extrafield = 0;
+
+ if (ZSEEK(s->z_filefunc, s->filestream,s->cur_file_info_internal.offset_curfile +
+ s->byte_before_the_zipfile,ZLIB_FILEFUNC_SEEK_SET)!=0)
+ return UNZ_ERRNO;
+
+
+ if (err==UNZ_OK)
+ {
+ if (unzlocal_getLong(&s->z_filefunc, s->filestream,&uMagic) != UNZ_OK)
+ err=UNZ_ERRNO;
+ else if (uMagic!=0x04034b50)
+ err=UNZ_BADZIPFILE;
+ }
+ if (unzlocal_getShort(&s->z_filefunc, s->filestream,&uData) != UNZ_OK)
+ err=UNZ_ERRNO;
+/*
+ else if ((err==UNZ_OK) && (uData!=s->cur_file_info.wVersion))
+ err=UNZ_BADZIPFILE;
+*/
+ if (unzlocal_getShort(&s->z_filefunc, s->filestream,&uFlags) != UNZ_OK)
+ err=UNZ_ERRNO;
+
+ if (unzlocal_getShort(&s->z_filefunc, s->filestream,&uData) != UNZ_OK)
+ err=UNZ_ERRNO;
+ else if ((err==UNZ_OK) && (uData!=s->cur_file_info.compression_method))
+ err=UNZ_BADZIPFILE;
+
+ if ((err==UNZ_OK) && (s->cur_file_info.compression_method!=0) &&
+ (s->cur_file_info.compression_method!=Z_DEFLATED))
+ err=UNZ_BADZIPFILE;
+
+ if (unzlocal_getLong(&s->z_filefunc, s->filestream,&uData) != UNZ_OK) /* date/time */
+ err=UNZ_ERRNO;
+
+ if (unzlocal_getLong(&s->z_filefunc, s->filestream,&uData) != UNZ_OK) /* crc */
+ err=UNZ_ERRNO;
+ else if ((err==UNZ_OK) && (uData!=s->cur_file_info.crc) &&
+ ((uFlags & 8)==0))
+ err=UNZ_BADZIPFILE;
+
+ if (unzlocal_getLong(&s->z_filefunc, s->filestream,&uData) != UNZ_OK) /* size compr */
+ err=UNZ_ERRNO;
+ else if ((err==UNZ_OK) && (uData!=s->cur_file_info.compressed_size) &&
+ ((uFlags & 8)==0))
+ err=UNZ_BADZIPFILE;
+
+ if (unzlocal_getLong(&s->z_filefunc, s->filestream,&uData) != UNZ_OK) /* size uncompr */
+ err=UNZ_ERRNO;
+ else if ((err==UNZ_OK) && (uData!=s->cur_file_info.uncompressed_size) &&
+ ((uFlags & 8)==0))
+ err=UNZ_BADZIPFILE;
+
+
+ if (unzlocal_getShort(&s->z_filefunc, s->filestream,&size_filename) != UNZ_OK)
+ err=UNZ_ERRNO;
+ else if ((err==UNZ_OK) && (size_filename!=s->cur_file_info.size_filename))
+ err=UNZ_BADZIPFILE;
+
+ *piSizeVar += (uInt)size_filename;
+
+ if (unzlocal_getShort(&s->z_filefunc, s->filestream,&size_extra_field) != UNZ_OK)
+ err=UNZ_ERRNO;
+ *poffset_local_extrafield= s->cur_file_info_internal.offset_curfile +
+ SIZEZIPLOCALHEADER + size_filename;
+ *psize_local_extrafield = (uInt)size_extra_field;
+
+ *piSizeVar += (uInt)size_extra_field;
+
+ return err;
+}
+
+/*
+ Open for reading data the current file in the zipfile.
+ If there is no error and the file is opened, the return value is UNZ_OK.
+*/
+extern int ZEXPORT unzOpenCurrentFile3 (file, method, level, raw, password)
+ unzFile file;
+ int* method;
+ int* level;
+ int raw;
+ const char* password;
+{
+ int err=UNZ_OK;
+ uInt iSizeVar;
+ unz_s* s;
+ file_in_zip_read_info_s* pfile_in_zip_read_info;
+ uLong offset_local_extrafield; /* offset of the local extra field */
+ uInt size_local_extrafield; /* size of the local extra field */
+# ifndef NOUNCRYPT
+ char source[12];
+# else
+ if (password != NULL)
+ return UNZ_PARAMERROR;
+# endif
+
+ if (file==NULL)
+ return UNZ_PARAMERROR;
+ s=(unz_s*)file;
+ if (!s->current_file_ok)
+ return UNZ_PARAMERROR;
+
+ if (s->pfile_in_zip_read != NULL)
+ unzCloseCurrentFile(file);
+
+ if (unzlocal_CheckCurrentFileCoherencyHeader(s,&iSizeVar,
+ &offset_local_extrafield,&size_local_extrafield)!=UNZ_OK)
+ return UNZ_BADZIPFILE;
+
+ pfile_in_zip_read_info = (file_in_zip_read_info_s*)
+ ALLOC(sizeof(file_in_zip_read_info_s));
+ if (pfile_in_zip_read_info==NULL)
+ return UNZ_INTERNALERROR;
+
+ pfile_in_zip_read_info->read_buffer=(char*)ALLOC(UNZ_BUFSIZE);
+ pfile_in_zip_read_info->offset_local_extrafield = offset_local_extrafield;
+ pfile_in_zip_read_info->size_local_extrafield = size_local_extrafield;
+ pfile_in_zip_read_info->pos_local_extrafield=0;
+ pfile_in_zip_read_info->raw=raw;
+
+ if (pfile_in_zip_read_info->read_buffer==NULL)
+ {
+ TRYFREE(pfile_in_zip_read_info);
+ return UNZ_INTERNALERROR;
+ }
+
+ pfile_in_zip_read_info->stream_initialised=0;
+
+ if (method!=NULL)
+ *method = (int)s->cur_file_info.compression_method;
+
+ if (level!=NULL)
+ {
+ *level = 6;
+ switch (s->cur_file_info.flag & 0x06)
+ {
+ case 6 : *level = 1; break;
+ case 4 : *level = 2; break;
+ case 2 : *level = 9; break;
+ }
+ }
+
+ if ((s->cur_file_info.compression_method!=0) &&
+ (s->cur_file_info.compression_method!=Z_DEFLATED))
+ err=UNZ_BADZIPFILE;
+
+ pfile_in_zip_read_info->crc32_wait=s->cur_file_info.crc;
+ pfile_in_zip_read_info->crc32=0;
+ pfile_in_zip_read_info->compression_method =
+ s->cur_file_info.compression_method;
+ pfile_in_zip_read_info->filestream=s->filestream;
+ pfile_in_zip_read_info->z_filefunc=s->z_filefunc;
+ pfile_in_zip_read_info->byte_before_the_zipfile=s->byte_before_the_zipfile;
+
+ pfile_in_zip_read_info->stream.total_out = 0;
+
+ if ((s->cur_file_info.compression_method==Z_DEFLATED) &&
+ (!raw))
+ {
+ pfile_in_zip_read_info->stream.zalloc = (alloc_func)0;
+ pfile_in_zip_read_info->stream.zfree = (free_func)0;
+ pfile_in_zip_read_info->stream.opaque = (voidpf)0;
+ pfile_in_zip_read_info->stream.next_in = (voidpf)0;
+ pfile_in_zip_read_info->stream.avail_in = 0;
+
+ err=inflateInit2(&pfile_in_zip_read_info->stream, -MAX_WBITS);
+ if (err == Z_OK)
+ pfile_in_zip_read_info->stream_initialised=1;
+ else
+ {
+ TRYFREE(pfile_in_zip_read_info);
+ return err;
+ }
+ /* windowBits is passed < 0 to tell that there is no zlib header.
+ * Note that in this case inflate *requires* an extra "dummy" byte
+ * after the compressed stream in order to complete decompression and
+ * return Z_STREAM_END.
+ * In unzip, i don't wait absolutely Z_STREAM_END because I known the
+ * size of both compressed and uncompressed data
+ */
+ }
+ pfile_in_zip_read_info->rest_read_compressed =
+ s->cur_file_info.compressed_size ;
+ pfile_in_zip_read_info->rest_read_uncompressed =
+ s->cur_file_info.uncompressed_size ;
+
+
+ pfile_in_zip_read_info->pos_in_zipfile =
+ s->cur_file_info_internal.offset_curfile + SIZEZIPLOCALHEADER +
+ iSizeVar;
+
+ pfile_in_zip_read_info->stream.avail_in = (uInt)0;
+
+ s->pfile_in_zip_read = pfile_in_zip_read_info;
+
+# ifndef NOUNCRYPT
+ if (password != NULL)
+ {
+ int i;
+ s->pcrc_32_tab = get_crc_table();
+ init_keys(password,s->keys,s->pcrc_32_tab);
+ if (ZSEEK(s->z_filefunc, s->filestream,
+ s->pfile_in_zip_read->pos_in_zipfile +
+ s->pfile_in_zip_read->byte_before_the_zipfile,
+ SEEK_SET)!=0)
+ return UNZ_INTERNALERROR;
+ if(ZREAD(s->z_filefunc, s->filestream,source, 12)<12)
+ return UNZ_INTERNALERROR;
+
+ for (i = 0; i<12; i++)
+ zdecode(s->keys,s->pcrc_32_tab,source[i]);
+
+ s->pfile_in_zip_read->pos_in_zipfile+=12;
+ s->encrypted=1;
+ }
+# endif
+
+
+ return UNZ_OK;
+}
+
+extern int ZEXPORT unzOpenCurrentFile (file)
+ unzFile file;
+{
+ return unzOpenCurrentFile3(file, NULL, NULL, 0, NULL);
+}
+
+extern int ZEXPORT unzOpenCurrentFilePassword (file, password)
+ unzFile file;
+ const char* password;
+{
+ return unzOpenCurrentFile3(file, NULL, NULL, 0, password);
+}
+
+extern int ZEXPORT unzOpenCurrentFile2 (file,method,level,raw)
+ unzFile file;
+ int* method;
+ int* level;
+ int raw;
+{
+ return unzOpenCurrentFile3(file, method, level, raw, NULL);
+}
+
+/*
+ Read bytes from the current file.
+ buf contain buffer where data must be copied
+ len the size of buf.
+
+ return the number of byte copied if somes bytes are copied
+ return 0 if the end of file was reached
+ return <0 with error code if there is an error
+ (UNZ_ERRNO for IO error, or zLib error for uncompress error)
+*/
+extern int ZEXPORT unzReadCurrentFile (file, buf, len)
+ unzFile file;
+ voidp buf;
+ unsigned len;
+{
+ int err=UNZ_OK;
+ uInt iRead = 0;
+ unz_s* s;
+ file_in_zip_read_info_s* pfile_in_zip_read_info;
+ if (file==NULL)
+ return UNZ_PARAMERROR;
+ s=(unz_s*)file;
+ pfile_in_zip_read_info=s->pfile_in_zip_read;
+
+ if (pfile_in_zip_read_info==NULL)
+ return UNZ_PARAMERROR;
+
+
+ if ((pfile_in_zip_read_info->read_buffer == NULL))
+ return UNZ_END_OF_LIST_OF_FILE;
+ if (len==0)
+ return 0;
+
+ pfile_in_zip_read_info->stream.next_out = (Bytef*)buf;
+
+ pfile_in_zip_read_info->stream.avail_out = (uInt)len;
+
+ if ((len>pfile_in_zip_read_info->rest_read_uncompressed) &&
+ (!(pfile_in_zip_read_info->raw)))
+ pfile_in_zip_read_info->stream.avail_out =
+ (uInt)pfile_in_zip_read_info->rest_read_uncompressed;
+
+ if ((len>pfile_in_zip_read_info->rest_read_compressed+
+ pfile_in_zip_read_info->stream.avail_in) &&
+ (pfile_in_zip_read_info->raw))
+ pfile_in_zip_read_info->stream.avail_out =
+ (uInt)pfile_in_zip_read_info->rest_read_compressed+
+ pfile_in_zip_read_info->stream.avail_in;
+
+ while (pfile_in_zip_read_info->stream.avail_out>0)
+ {
+ if ((pfile_in_zip_read_info->stream.avail_in==0) &&
+ (pfile_in_zip_read_info->rest_read_compressed>0))
+ {
+ uInt uReadThis = UNZ_BUFSIZE;
+ if (pfile_in_zip_read_info->rest_read_compressed<uReadThis)
+ uReadThis = (uInt)pfile_in_zip_read_info->rest_read_compressed;
+ if (uReadThis == 0)
+ return UNZ_EOF;
+ if (ZSEEK(pfile_in_zip_read_info->z_filefunc,
+ pfile_in_zip_read_info->filestream,
+ pfile_in_zip_read_info->pos_in_zipfile +
+ pfile_in_zip_read_info->byte_before_the_zipfile,
+ ZLIB_FILEFUNC_SEEK_SET)!=0)
+ return UNZ_ERRNO;
+ if (ZREAD(pfile_in_zip_read_info->z_filefunc,
+ pfile_in_zip_read_info->filestream,
+ pfile_in_zip_read_info->read_buffer,
+ uReadThis)!=uReadThis)
+ return UNZ_ERRNO;
+
+
+# ifndef NOUNCRYPT
+ if(s->encrypted)
+ {
+ uInt i;
+ for(i=0;i<uReadThis;i++)
+ pfile_in_zip_read_info->read_buffer[i] =
+ zdecode(s->keys,s->pcrc_32_tab,
+ pfile_in_zip_read_info->read_buffer[i]);
+ }
+# endif
+
+
+ pfile_in_zip_read_info->pos_in_zipfile += uReadThis;
+
+ pfile_in_zip_read_info->rest_read_compressed-=uReadThis;
+
+ pfile_in_zip_read_info->stream.next_in =
+ (Bytef*)pfile_in_zip_read_info->read_buffer;
+ pfile_in_zip_read_info->stream.avail_in = (uInt)uReadThis;
+ }
+
+ if ((pfile_in_zip_read_info->compression_method==0) || (pfile_in_zip_read_info->raw))
+ {
+ uInt uDoCopy,i ;
+
+ if ((pfile_in_zip_read_info->stream.avail_in == 0) &&
+ (pfile_in_zip_read_info->rest_read_compressed == 0))
+ return (iRead==0) ? UNZ_EOF : iRead;
+
+ if (pfile_in_zip_read_info->stream.avail_out <
+ pfile_in_zip_read_info->stream.avail_in)
+ uDoCopy = pfile_in_zip_read_info->stream.avail_out ;
+ else
+ uDoCopy = pfile_in_zip_read_info->stream.avail_in ;
+
+ for (i=0;i<uDoCopy;i++)
+ *(pfile_in_zip_read_info->stream.next_out+i) =
+ *(pfile_in_zip_read_info->stream.next_in+i);
+
+ pfile_in_zip_read_info->crc32 = crc32(pfile_in_zip_read_info->crc32,
+ pfile_in_zip_read_info->stream.next_out,
+ uDoCopy);
+ pfile_in_zip_read_info->rest_read_uncompressed-=uDoCopy;
+ pfile_in_zip_read_info->stream.avail_in -= uDoCopy;
+ pfile_in_zip_read_info->stream.avail_out -= uDoCopy;
+ pfile_in_zip_read_info->stream.next_out += uDoCopy;
+ pfile_in_zip_read_info->stream.next_in += uDoCopy;
+ pfile_in_zip_read_info->stream.total_out += uDoCopy;
+ iRead += uDoCopy;
+ }
+ else
+ {
+ uLong uTotalOutBefore,uTotalOutAfter;
+ const Bytef *bufBefore;
+ uLong uOutThis;
+ int flush=Z_SYNC_FLUSH;
+
+ uTotalOutBefore = pfile_in_zip_read_info->stream.total_out;
+ bufBefore = pfile_in_zip_read_info->stream.next_out;
+
+ /*
+ if ((pfile_in_zip_read_info->rest_read_uncompressed ==
+ pfile_in_zip_read_info->stream.avail_out) &&
+ (pfile_in_zip_read_info->rest_read_compressed == 0))
+ flush = Z_FINISH;
+ */
+ err=inflate(&pfile_in_zip_read_info->stream,flush);
+
+ if ((err>=0) && (pfile_in_zip_read_info->stream.msg!=NULL))
+ err = Z_DATA_ERROR;
+
+ uTotalOutAfter = pfile_in_zip_read_info->stream.total_out;
+ uOutThis = uTotalOutAfter-uTotalOutBefore;
+
+ pfile_in_zip_read_info->crc32 =
+ crc32(pfile_in_zip_read_info->crc32,bufBefore,
+ (uInt)(uOutThis));
+
+ pfile_in_zip_read_info->rest_read_uncompressed -=
+ uOutThis;
+
+ iRead += (uInt)(uTotalOutAfter - uTotalOutBefore);
+
+ if (err==Z_STREAM_END)
+ return (iRead==0) ? UNZ_EOF : iRead;
+ if (err!=Z_OK)
+ break;
+ }
+ }
+
+ if (err==Z_OK)
+ return iRead;
+ return err;
+}
+
+
+/*
+ Give the current position in uncompressed data
+*/
+extern z_off_t ZEXPORT unztell (file)
+ unzFile file;
+{
+ unz_s* s;
+ file_in_zip_read_info_s* pfile_in_zip_read_info;
+ if (file==NULL)
+ return UNZ_PARAMERROR;
+ s=(unz_s*)file;
+ pfile_in_zip_read_info=s->pfile_in_zip_read;
+
+ if (pfile_in_zip_read_info==NULL)
+ return UNZ_PARAMERROR;
+
+ return (z_off_t)pfile_in_zip_read_info->stream.total_out;
+}
+
+
+/*
+ return 1 if the end of file was reached, 0 elsewhere
+*/
+extern int ZEXPORT unzeof (file)
+ unzFile file;
+{
+ unz_s* s;
+ file_in_zip_read_info_s* pfile_in_zip_read_info;
+ if (file==NULL)
+ return UNZ_PARAMERROR;
+ s=(unz_s*)file;
+ pfile_in_zip_read_info=s->pfile_in_zip_read;
+
+ if (pfile_in_zip_read_info==NULL)
+ return UNZ_PARAMERROR;
+
+ if (pfile_in_zip_read_info->rest_read_uncompressed == 0)
+ return 1;
+ else
+ return 0;
+}
+
+
+
+/*
+ Read extra field from the current file (opened by unzOpenCurrentFile)
+ This is the local-header version of the extra field (sometimes, there is
+ more info in the local-header version than in the central-header)
+
+ if buf==NULL, it return the size of the local extra field that can be read
+
+ if buf!=NULL, len is the size of the buffer, the extra header is copied in
+ buf.
+ the return value is the number of bytes copied in buf, or (if <0)
+ the error code
+*/
+extern int ZEXPORT unzGetLocalExtrafield (file,buf,len)
+ unzFile file;
+ voidp buf;
+ unsigned len;
+{
+ unz_s* s;
+ file_in_zip_read_info_s* pfile_in_zip_read_info;
+ uInt read_now;
+ uLong size_to_read;
+
+ if (file==NULL)
+ return UNZ_PARAMERROR;
+ s=(unz_s*)file;
+ pfile_in_zip_read_info=s->pfile_in_zip_read;
+
+ if (pfile_in_zip_read_info==NULL)
+ return UNZ_PARAMERROR;
+
+ size_to_read = (pfile_in_zip_read_info->size_local_extrafield -
+ pfile_in_zip_read_info->pos_local_extrafield);
+
+ if (buf==NULL)
+ return (int)size_to_read;
+
+ if (len>size_to_read)
+ read_now = (uInt)size_to_read;
+ else
+ read_now = (uInt)len ;
+
+ if (read_now==0)
+ return 0;
+
+ if (ZSEEK(pfile_in_zip_read_info->z_filefunc,
+ pfile_in_zip_read_info->filestream,
+ pfile_in_zip_read_info->offset_local_extrafield +
+ pfile_in_zip_read_info->pos_local_extrafield,
+ ZLIB_FILEFUNC_SEEK_SET)!=0)
+ return UNZ_ERRNO;
+
+ if (ZREAD(pfile_in_zip_read_info->z_filefunc,
+ pfile_in_zip_read_info->filestream,
+ buf,read_now)!=read_now)
+ return UNZ_ERRNO;
+
+ return (int)read_now;
+}
+
+/*
+ Close the file in zip opened with unzipOpenCurrentFile
+ Return UNZ_CRCERROR if all the file was read but the CRC is not good
+*/
+extern int ZEXPORT unzCloseCurrentFile (file)
+ unzFile file;
+{
+ int err=UNZ_OK;
+
+ unz_s* s;
+ file_in_zip_read_info_s* pfile_in_zip_read_info;
+ if (file==NULL)
+ return UNZ_PARAMERROR;
+ s=(unz_s*)file;
+ pfile_in_zip_read_info=s->pfile_in_zip_read;
+
+ if (pfile_in_zip_read_info==NULL)
+ return UNZ_PARAMERROR;
+
+
+ if ((pfile_in_zip_read_info->rest_read_uncompressed == 0) &&
+ (!pfile_in_zip_read_info->raw))
+ {
+ if (pfile_in_zip_read_info->crc32 != pfile_in_zip_read_info->crc32_wait)
+ err=UNZ_CRCERROR;
+ }
+
+
+ TRYFREE(pfile_in_zip_read_info->read_buffer);
+ pfile_in_zip_read_info->read_buffer = NULL;
+ if (pfile_in_zip_read_info->stream_initialised)
+ inflateEnd(&pfile_in_zip_read_info->stream);
+
+ pfile_in_zip_read_info->stream_initialised = 0;
+ TRYFREE(pfile_in_zip_read_info);
+
+ s->pfile_in_zip_read=NULL;
+
+ return err;
+}
+
+
+/*
+ Get the global comment string of the ZipFile, in the szComment buffer.
+ uSizeBuf is the size of the szComment buffer.
+ return the number of byte copied or an error code <0
+*/
+extern int ZEXPORT unzGetGlobalComment (file, szComment, uSizeBuf)
+ unzFile file;
+ char *szComment;
+ uLong uSizeBuf;
+{
+ unz_s* s;
+ uLong uReadThis ;
+ if (file==NULL)
+ return UNZ_PARAMERROR;
+ s=(unz_s*)file;
+
+ uReadThis = uSizeBuf;
+ if (uReadThis>s->gi.size_comment)
+ uReadThis = s->gi.size_comment;
+
+ if (ZSEEK(s->z_filefunc,s->filestream,s->central_pos+22,ZLIB_FILEFUNC_SEEK_SET)!=0)
+ return UNZ_ERRNO;
+
+ if (uReadThis>0)
+ {
+ *szComment='\0';
+ if (ZREAD(s->z_filefunc,s->filestream,szComment,uReadThis)!=uReadThis)
+ return UNZ_ERRNO;
+ }
+
+ if ((szComment != NULL) && (uSizeBuf > s->gi.size_comment))
+ *(szComment+s->gi.size_comment)='\0';
+ return (int)uReadThis;
+}
+
+/* Additions by RX '2004 */
+extern uLong ZEXPORT unzGetOffset (file)
+ unzFile file;
+{
+ unz_s* s;
+
+ if (file==NULL)
+ return UNZ_PARAMERROR;
+ s=(unz_s*)file;
+ if (!s->current_file_ok)
+ return 0;
+ if (s->gi.number_entry != 0 && s->gi.number_entry != 0xffff)
+ if (s->num_file==s->gi.number_entry)
+ return 0;
+ return s->pos_in_central_dir;
+}
+
+extern int ZEXPORT unzSetOffset (file, pos)
+ unzFile file;
+ uLong pos;
+{
+ unz_s* s;
+ int err;
+
+ if (file==NULL)
+ return UNZ_PARAMERROR;
+ s=(unz_s*)file;
+
+ s->pos_in_central_dir = pos;
+ s->num_file = s->gi.number_entry; /* hack */
+ err = unzlocal_GetCurrentFileInfoInternal(file,&s->cur_file_info,
+ &s->cur_file_info_internal,
+ NULL,0,NULL,0,NULL,0);
+ s->current_file_ok = (err == UNZ_OK);
+ return err;
+}
diff --git a/externals/g3dlite/zip.lib/source/zip.c b/externals/g3dlite/zip.lib/source/zip.c
new file mode 100644
index 00000000000..d5f9fe53d26
--- /dev/null
+++ b/externals/g3dlite/zip.lib/source/zip.c
@@ -0,0 +1,1221 @@
+/* zip.c -- IO on .zip files using zlib
+ Version 1.01e, February 12th, 2005
+
+ 27 Dec 2004 Rolf Kalbermatter
+ Modification to zipOpen2 to support globalComment retrieval.
+
+ Copyright (C) 1998-2005 Gilles Vollant
+
+ Read zip.h for more info
+*/
+
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+#include "zlib.h"
+#include "zip.h"
+
+#ifdef STDC
+# include <stddef.h>
+# include <string.h>
+# include <stdlib.h>
+#endif
+#ifdef NO_ERRNO_H
+ extern int errno;
+#else
+# include <errno.h>
+#endif
+
+
+#ifndef local
+# define local static
+#endif
+/* compile with -Dlocal if your debugger can't find static symbols */
+
+#ifndef VERSIONMADEBY
+# define VERSIONMADEBY (0x0) /* platform depedent */
+#endif
+
+#ifndef Z_BUFSIZE
+#define Z_BUFSIZE (16384)
+#endif
+
+#ifndef Z_MAXFILENAMEINZIP
+#define Z_MAXFILENAMEINZIP (256)
+#endif
+
+#ifndef ALLOC
+# define ALLOC(size) (malloc(size))
+#endif
+#ifndef TRYFREE
+# define TRYFREE(p) {if (p) free(p);}
+#endif
+
+/*
+#define SIZECENTRALDIRITEM (0x2e)
+#define SIZEZIPLOCALHEADER (0x1e)
+*/
+
+/* I've found an old Unix (a SunOS 4.1.3_U1) without all SEEK_* defined.... */
+
+#ifndef SEEK_CUR
+#define SEEK_CUR 1
+#endif
+
+#ifndef SEEK_END
+#define SEEK_END 2
+#endif
+
+#ifndef SEEK_SET
+#define SEEK_SET 0
+#endif
+
+#ifndef DEF_MEM_LEVEL
+#if MAX_MEM_LEVEL >= 8
+# define DEF_MEM_LEVEL 8
+#else
+# define DEF_MEM_LEVEL MAX_MEM_LEVEL
+#endif
+#endif
+const char zip_copyright[] =
+ " zip 1.01 Copyright 1998-2004 Gilles Vollant - http://www.winimage.com/zLibDll";
+
+
+#define SIZEDATA_INDATABLOCK (4096-(4*4))
+
+#define LOCALHEADERMAGIC (0x04034b50)
+#define CENTRALHEADERMAGIC (0x02014b50)
+#define ENDHEADERMAGIC (0x06054b50)
+
+#define FLAG_LOCALHEADER_OFFSET (0x06)
+#define CRC_LOCALHEADER_OFFSET (0x0e)
+
+#define SIZECENTRALHEADER (0x2e) /* 46 */
+
+typedef struct linkedlist_datablock_internal_s
+{
+ struct linkedlist_datablock_internal_s* next_datablock;
+ uLong avail_in_this_block;
+ uLong filled_in_this_block;
+ uLong unused; /* for future use and alignement */
+ unsigned char data[SIZEDATA_INDATABLOCK];
+} linkedlist_datablock_internal;
+
+typedef struct linkedlist_data_s
+{
+ linkedlist_datablock_internal* first_block;
+ linkedlist_datablock_internal* last_block;
+} linkedlist_data;
+
+
+typedef struct
+{
+ z_stream stream; /* zLib stream structure for inflate */
+ int stream_initialised; /* 1 is stream is initialised */
+ uInt pos_in_buffered_data; /* last written byte in buffered_data */
+
+ uLong pos_local_header; /* offset of the local header of the file
+ currenty writing */
+ char* central_header; /* central header data for the current file */
+ uLong size_centralheader; /* size of the central header for cur file */
+ uLong flag; /* flag of the file currently writing */
+
+ int method; /* compression method of file currenty wr.*/
+ int raw; /* 1 for directly writing raw data */
+ Byte buffered_data[Z_BUFSIZE];/* buffer contain compressed data to be writ*/
+ uLong dosDate;
+ uLong crc32;
+ int encrypt;
+#ifndef NOCRYPT
+ unsigned long keys[3]; /* keys defining the pseudo-random sequence */
+ const unsigned long* pcrc_32_tab;
+ int crypt_header_size;
+#endif
+} curfile_info;
+
+typedef struct
+{
+ zlib_filefunc_def z_filefunc;
+ voidpf filestream; /* io structore of the zipfile */
+ linkedlist_data central_dir;/* datablock with central dir in construction*/
+ int in_opened_file_inzip; /* 1 if a file in the zip is currently writ.*/
+ curfile_info ci; /* info on the file curretly writing */
+
+ uLong begin_pos; /* position of the beginning of the zipfile */
+ uLong add_position_when_writting_offset;
+ uLong number_entry;
+#ifndef NO_ADDFILEINEXISTINGZIP
+ char *globalcomment;
+#endif
+} zip_internal;
+
+
+
+#ifndef NOCRYPT
+#define INCLUDECRYPTINGCODE_IFCRYPTALLOWED
+#include "crypt.h"
+#endif
+
+local linkedlist_datablock_internal* allocate_new_datablock()
+{
+ linkedlist_datablock_internal* ldi;
+ ldi = (linkedlist_datablock_internal*)
+ ALLOC(sizeof(linkedlist_datablock_internal));
+ if (ldi!=NULL)
+ {
+ ldi->next_datablock = NULL ;
+ ldi->filled_in_this_block = 0 ;
+ ldi->avail_in_this_block = SIZEDATA_INDATABLOCK ;
+ }
+ return ldi;
+}
+
+local void free_datablock(ldi)
+ linkedlist_datablock_internal* ldi;
+{
+ while (ldi!=NULL)
+ {
+ linkedlist_datablock_internal* ldinext = ldi->next_datablock;
+ TRYFREE(ldi);
+ ldi = ldinext;
+ }
+}
+
+local void init_linkedlist(ll)
+ linkedlist_data* ll;
+{
+ ll->first_block = ll->last_block = NULL;
+}
+
+/*
+// Never used!
+local void free_linkedlist(ll)
+ linkedlist_data* ll;
+{
+ free_datablock(ll->first_block);
+ ll->first_block = ll->last_block = NULL;
+}
+*/
+
+local int add_data_in_datablock(ll,buf,len)
+ linkedlist_data* ll;
+ const void* buf;
+ uLong len;
+{
+ linkedlist_datablock_internal* ldi;
+ const unsigned char* from_copy;
+
+ if (ll==NULL)
+ return ZIP_INTERNALERROR;
+
+ if (ll->last_block == NULL)
+ {
+ ll->first_block = ll->last_block = allocate_new_datablock();
+ if (ll->first_block == NULL)
+ return ZIP_INTERNALERROR;
+ }
+
+ ldi = ll->last_block;
+ from_copy = (unsigned char*)buf;
+
+ while (len>0)
+ {
+ uInt copy_this;
+ uInt i;
+ unsigned char* to_copy;
+
+ if (ldi->avail_in_this_block==0)
+ {
+ ldi->next_datablock = allocate_new_datablock();
+ if (ldi->next_datablock == NULL)
+ return ZIP_INTERNALERROR;
+ ldi = ldi->next_datablock ;
+ ll->last_block = ldi;
+ }
+
+ if (ldi->avail_in_this_block < len)
+ copy_this = (uInt)ldi->avail_in_this_block;
+ else
+ copy_this = (uInt)len;
+
+ to_copy = &(ldi->data[ldi->filled_in_this_block]);
+
+ for (i=0;i<copy_this;i++)
+ *(to_copy+i)=*(from_copy+i);
+
+ ldi->filled_in_this_block += copy_this;
+ ldi->avail_in_this_block -= copy_this;
+ from_copy += copy_this ;
+ len -= copy_this;
+ }
+ return ZIP_OK;
+}
+
+
+
+/****************************************************************************/
+
+#ifndef NO_ADDFILEINEXISTINGZIP
+/* ===========================================================================
+ Inputs a long in LSB order to the given file
+ nbByte == 1, 2 or 4 (byte, short or long)
+*/
+
+local int ziplocal_putValue OF((const zlib_filefunc_def* pzlib_filefunc_def,
+ voidpf filestream, uLong x, int nbByte));
+local int ziplocal_putValue (pzlib_filefunc_def, filestream, x, nbByte)
+ const zlib_filefunc_def* pzlib_filefunc_def;
+ voidpf filestream;
+ uLong x;
+ int nbByte;
+{
+ unsigned char buf[4];
+ int n;
+ for (n = 0; n < nbByte; n++)
+ {
+ buf[n] = (unsigned char)(x & 0xff);
+ x >>= 8;
+ }
+ if (x != 0)
+ { /* data overflow - hack for ZIP64 (X Roche) */
+ for (n = 0; n < nbByte; n++)
+ {
+ buf[n] = 0xff;
+ }
+ }
+
+ if (ZWRITE(*pzlib_filefunc_def,filestream,buf,nbByte)!=(uLong)nbByte)
+ return ZIP_ERRNO;
+ else
+ return ZIP_OK;
+}
+
+local void ziplocal_putValue_inmemory OF((void* dest, uLong x, int nbByte));
+local void ziplocal_putValue_inmemory (dest, x, nbByte)
+ void* dest;
+ uLong x;
+ int nbByte;
+{
+ unsigned char* buf=(unsigned char*)dest;
+ int n;
+ for (n = 0; n < nbByte; n++) {
+ buf[n] = (unsigned char)(x & 0xff);
+ x >>= 8;
+ }
+
+ if (x != 0)
+ { /* data overflow - hack for ZIP64 */
+ for (n = 0; n < nbByte; n++)
+ {
+ buf[n] = 0xff;
+ }
+ }
+}
+
+/****************************************************************************/
+
+
+local uLong ziplocal_TmzDateToDosDate(ptm,dosDate)
+ const tm_zip* ptm;
+ uLong dosDate;
+{
+ uLong year = (uLong)ptm->tm_year;
+ if (year>1980)
+ year-=1980;
+ else if (year>80)
+ year-=80;
+ return
+ (uLong) (((ptm->tm_mday) + (32 * (ptm->tm_mon+1)) + (512 * year)) << 16) |
+ ((ptm->tm_sec/2) + (32* ptm->tm_min) + (2048 * (uLong)ptm->tm_hour));
+}
+
+
+/****************************************************************************/
+
+local int ziplocal_getByte OF((
+ const zlib_filefunc_def* pzlib_filefunc_def,
+ voidpf filestream,
+ int *pi));
+
+local int ziplocal_getByte(pzlib_filefunc_def,filestream,pi)
+ const zlib_filefunc_def* pzlib_filefunc_def;
+ voidpf filestream;
+ int *pi;
+{
+ unsigned char c;
+ int err = (int)ZREAD(*pzlib_filefunc_def,filestream,&c,1);
+ if (err==1)
+ {
+ *pi = (int)c;
+ return ZIP_OK;
+ }
+ else
+ {
+ if (ZERROR(*pzlib_filefunc_def,filestream))
+ return ZIP_ERRNO;
+ else
+ return ZIP_EOF;
+ }
+}
+
+
+/* ===========================================================================
+ Reads a long in LSB order from the given gz_stream. Sets
+*/
+local int ziplocal_getShort OF((
+ const zlib_filefunc_def* pzlib_filefunc_def,
+ voidpf filestream,
+ uLong *pX));
+
+local int ziplocal_getShort (pzlib_filefunc_def,filestream,pX)
+ const zlib_filefunc_def* pzlib_filefunc_def;
+ voidpf filestream;
+ uLong *pX;
+{
+ uLong x = 0;
+ int i = 0;
+ int err = 0;
+
+ err = ziplocal_getByte(pzlib_filefunc_def,filestream,&i);
+ x = (uLong)i;
+
+ if (err==ZIP_OK)
+ err = ziplocal_getByte(pzlib_filefunc_def,filestream,&i);
+ x += ((uLong)i)<<8;
+
+ if (err==ZIP_OK)
+ *pX = x;
+ else
+ *pX = 0;
+ return err;
+}
+
+local int ziplocal_getLong OF((
+ const zlib_filefunc_def* pzlib_filefunc_def,
+ voidpf filestream,
+ uLong *pX));
+
+local int ziplocal_getLong (pzlib_filefunc_def,filestream,pX)
+ const zlib_filefunc_def* pzlib_filefunc_def;
+ voidpf filestream;
+ uLong *pX;
+{
+ uLong x = 0;
+ int i = 0;
+ int err = 0;
+
+ err = ziplocal_getByte(pzlib_filefunc_def,filestream,&i);
+ x = (uLong)i;
+
+ if (err==ZIP_OK)
+ err = ziplocal_getByte(pzlib_filefunc_def,filestream,&i);
+ x += ((uLong)i)<<8;
+
+ if (err==ZIP_OK)
+ err = ziplocal_getByte(pzlib_filefunc_def,filestream,&i);
+ x += ((uLong)i)<<16;
+
+ if (err==ZIP_OK)
+ err = ziplocal_getByte(pzlib_filefunc_def,filestream,&i);
+ x += ((uLong)i)<<24;
+
+ if (err==ZIP_OK)
+ *pX = x;
+ else
+ *pX = 0;
+ return err;
+}
+
+#ifndef BUFREADCOMMENT
+#define BUFREADCOMMENT (0x400)
+#endif
+/*
+ Locate the Central directory of a zipfile (at the end, just before
+ the global comment)
+*/
+local uLong ziplocal_SearchCentralDir OF((
+ const zlib_filefunc_def* pzlib_filefunc_def,
+ voidpf filestream));
+
+local uLong ziplocal_SearchCentralDir(pzlib_filefunc_def,filestream)
+ const zlib_filefunc_def* pzlib_filefunc_def;
+ voidpf filestream;
+{
+ unsigned char* buf;
+ uLong uSizeFile;
+ uLong uBackRead;
+ uLong uMaxBack=0xffff; /* maximum size of global comment */
+ uLong uPosFound=0;
+
+ if (ZSEEK(*pzlib_filefunc_def,filestream,0,ZLIB_FILEFUNC_SEEK_END) != 0)
+ return 0;
+
+
+ uSizeFile = ZTELL(*pzlib_filefunc_def,filestream);
+
+ if (uMaxBack>uSizeFile)
+ uMaxBack = uSizeFile;
+
+ buf = (unsigned char*)ALLOC(BUFREADCOMMENT+4);
+ if (buf==NULL)
+ return 0;
+
+ uBackRead = 4;
+ while (uBackRead<uMaxBack)
+ {
+ uLong uReadSize,uReadPos ;
+ int i;
+ if (uBackRead+BUFREADCOMMENT>uMaxBack)
+ uBackRead = uMaxBack;
+ else
+ uBackRead+=BUFREADCOMMENT;
+ uReadPos = uSizeFile-uBackRead ;
+
+ uReadSize = ((BUFREADCOMMENT+4) < (uSizeFile-uReadPos)) ?
+ (BUFREADCOMMENT+4) : (uSizeFile-uReadPos);
+ if (ZSEEK(*pzlib_filefunc_def,filestream,uReadPos,ZLIB_FILEFUNC_SEEK_SET)!=0)
+ break;
+
+ if (ZREAD(*pzlib_filefunc_def,filestream,buf,uReadSize)!=uReadSize)
+ break;
+
+ for (i=(int)uReadSize-3; (i--)>0;)
+ if (((*(buf+i))==0x50) && ((*(buf+i+1))==0x4b) &&
+ ((*(buf+i+2))==0x05) && ((*(buf+i+3))==0x06))
+ {
+ uPosFound = uReadPos+i;
+ break;
+ }
+
+ if (uPosFound!=0)
+ break;
+ }
+ TRYFREE(buf);
+ return uPosFound;
+}
+#endif /* !NO_ADDFILEINEXISTINGZIP*/
+
+/************************************************************/
+extern zipFile ZEXPORT zipOpen2 (pathname, append, globalcomment, pzlib_filefunc_def)
+ const char *pathname;
+ int append;
+ zipcharpc* globalcomment;
+ zlib_filefunc_def* pzlib_filefunc_def;
+{
+ zip_internal ziinit;
+ zip_internal* zi;
+ int err=ZIP_OK;
+
+
+ if (pzlib_filefunc_def==NULL)
+ fill_fopen_filefunc(&ziinit.z_filefunc);
+ else
+ ziinit.z_filefunc = *pzlib_filefunc_def;
+
+ ziinit.filestream = (*(ziinit.z_filefunc.zopen_file))
+ (ziinit.z_filefunc.opaque,
+ pathname,
+ (append == APPEND_STATUS_CREATE) ?
+ (ZLIB_FILEFUNC_MODE_READ | ZLIB_FILEFUNC_MODE_WRITE | ZLIB_FILEFUNC_MODE_CREATE) :
+ (ZLIB_FILEFUNC_MODE_READ | ZLIB_FILEFUNC_MODE_WRITE | ZLIB_FILEFUNC_MODE_EXISTING));
+
+ if (ziinit.filestream == NULL)
+ return NULL;
+ ziinit.begin_pos = ZTELL(ziinit.z_filefunc,ziinit.filestream);
+ ziinit.in_opened_file_inzip = 0;
+ ziinit.ci.stream_initialised = 0;
+ ziinit.number_entry = 0;
+ ziinit.add_position_when_writting_offset = 0;
+ init_linkedlist(&(ziinit.central_dir));
+
+
+ zi = (zip_internal*)ALLOC(sizeof(zip_internal));
+ if (zi==NULL)
+ {
+ ZCLOSE(ziinit.z_filefunc,ziinit.filestream);
+ return NULL;
+ }
+
+ /* now we add file in a zipfile */
+# ifndef NO_ADDFILEINEXISTINGZIP
+ ziinit.globalcomment = NULL;
+ if (append == APPEND_STATUS_ADDINZIP)
+ {
+ uLong byte_before_the_zipfile;/* byte before the zipfile, (>0 for sfx)*/
+
+ uLong size_central_dir; /* size of the central directory */
+ uLong offset_central_dir; /* offset of start of central directory */
+ uLong central_pos,uL;
+
+ uLong number_disk; /* number of the current dist, used for
+ spaning ZIP, unsupported, always 0*/
+ uLong number_disk_with_CD; /* number the the disk with central dir, used
+ for spaning ZIP, unsupported, always 0*/
+ uLong number_entry;
+ uLong number_entry_CD; /* total number of entries in
+ the central dir
+ (same than number_entry on nospan) */
+ uLong size_comment;
+
+ central_pos = ziplocal_SearchCentralDir(&ziinit.z_filefunc,ziinit.filestream);
+ if (central_pos==0)
+ err=ZIP_ERRNO;
+
+ if (ZSEEK(ziinit.z_filefunc, ziinit.filestream,
+ central_pos,ZLIB_FILEFUNC_SEEK_SET)!=0)
+ err=ZIP_ERRNO;
+
+ /* the signature, already checked */
+ if (ziplocal_getLong(&ziinit.z_filefunc, ziinit.filestream,&uL)!=ZIP_OK)
+ err=ZIP_ERRNO;
+
+ /* number of this disk */
+ if (ziplocal_getShort(&ziinit.z_filefunc, ziinit.filestream,&number_disk)!=ZIP_OK)
+ err=ZIP_ERRNO;
+
+ /* number of the disk with the start of the central directory */
+ if (ziplocal_getShort(&ziinit.z_filefunc, ziinit.filestream,&number_disk_with_CD)!=ZIP_OK)
+ err=ZIP_ERRNO;
+
+ /* total number of entries in the central dir on this disk */
+ if (ziplocal_getShort(&ziinit.z_filefunc, ziinit.filestream,&number_entry)!=ZIP_OK)
+ err=ZIP_ERRNO;
+
+ /* total number of entries in the central dir */
+ if (ziplocal_getShort(&ziinit.z_filefunc, ziinit.filestream,&number_entry_CD)!=ZIP_OK)
+ err=ZIP_ERRNO;
+
+ if ((number_entry_CD!=number_entry) ||
+ (number_disk_with_CD!=0) ||
+ (number_disk!=0))
+ err=ZIP_BADZIPFILE;
+
+ /* size of the central directory */
+ if (ziplocal_getLong(&ziinit.z_filefunc, ziinit.filestream,&size_central_dir)!=ZIP_OK)
+ err=ZIP_ERRNO;
+
+ /* offset of start of central directory with respect to the
+ starting disk number */
+ if (ziplocal_getLong(&ziinit.z_filefunc, ziinit.filestream,&offset_central_dir)!=ZIP_OK)
+ err=ZIP_ERRNO;
+
+ /* zipfile global comment length */
+ if (ziplocal_getShort(&ziinit.z_filefunc, ziinit.filestream,&size_comment)!=ZIP_OK)
+ err=ZIP_ERRNO;
+
+ if ((central_pos<offset_central_dir+size_central_dir) &&
+ (err==ZIP_OK))
+ err=ZIP_BADZIPFILE;
+
+ if (err!=ZIP_OK)
+ {
+ ZCLOSE(ziinit.z_filefunc, ziinit.filestream);
+ return NULL;
+ }
+
+ if (size_comment>0)
+ {
+ ziinit.globalcomment = ALLOC(size_comment+1);
+ if (ziinit.globalcomment)
+ {
+ size_comment = ZREAD(ziinit.z_filefunc, ziinit.filestream,ziinit.globalcomment,size_comment);
+ ziinit.globalcomment[size_comment]=0;
+ }
+ }
+
+ byte_before_the_zipfile = central_pos -
+ (offset_central_dir+size_central_dir);
+ ziinit.add_position_when_writting_offset = byte_before_the_zipfile;
+
+ {
+ uLong size_central_dir_to_read = size_central_dir;
+ size_t buf_size = SIZEDATA_INDATABLOCK;
+ void* buf_read = (void*)ALLOC(buf_size);
+ if (ZSEEK(ziinit.z_filefunc, ziinit.filestream,
+ offset_central_dir + byte_before_the_zipfile,
+ ZLIB_FILEFUNC_SEEK_SET) != 0)
+ err=ZIP_ERRNO;
+
+ while ((size_central_dir_to_read>0) && (err==ZIP_OK))
+ {
+ uLong read_this = SIZEDATA_INDATABLOCK;
+ if (read_this > size_central_dir_to_read)
+ read_this = size_central_dir_to_read;
+ if (ZREAD(ziinit.z_filefunc, ziinit.filestream,buf_read,read_this) != read_this)
+ err=ZIP_ERRNO;
+
+ if (err==ZIP_OK)
+ err = add_data_in_datablock(&ziinit.central_dir,buf_read,
+ (uLong)read_this);
+ size_central_dir_to_read-=read_this;
+ }
+ TRYFREE(buf_read);
+ }
+ ziinit.begin_pos = byte_before_the_zipfile;
+ ziinit.number_entry = number_entry_CD;
+
+ if (ZSEEK(ziinit.z_filefunc, ziinit.filestream,
+ offset_central_dir+byte_before_the_zipfile,ZLIB_FILEFUNC_SEEK_SET)!=0)
+ err=ZIP_ERRNO;
+ }
+
+ if (globalcomment)
+ {
+ *globalcomment = ziinit.globalcomment;
+ }
+# endif /* !NO_ADDFILEINEXISTINGZIP*/
+
+ if (err != ZIP_OK)
+ {
+# ifndef NO_ADDFILEINEXISTINGZIP
+ TRYFREE(ziinit.globalcomment);
+# endif /* !NO_ADDFILEINEXISTINGZIP*/
+ TRYFREE(zi);
+ return NULL;
+ }
+ else
+ {
+ *zi = ziinit;
+ return (zipFile)zi;
+ }
+}
+
+extern zipFile ZEXPORT zipOpen (pathname, append)
+ const char *pathname;
+ int append;
+{
+ return zipOpen2(pathname,append,NULL,NULL);
+}
+
+extern int ZEXPORT zipOpenNewFileInZip3 (file, filename, zipfi,
+ extrafield_local, size_extrafield_local,
+ extrafield_global, size_extrafield_global,
+ comment, method, level, raw,
+ windowBits, memLevel, strategy,
+ password, crcForCrypting)
+ zipFile file;
+ const char* filename;
+ const zip_fileinfo* zipfi;
+ const void* extrafield_local;
+ uInt size_extrafield_local;
+ const void* extrafield_global;
+ uInt size_extrafield_global;
+ const char* comment;
+ int method;
+ int level;
+ int raw;
+ int windowBits;
+ int memLevel;
+ int strategy;
+ const char* password;
+ uLong crcForCrypting;
+{
+ zip_internal* zi;
+ uInt size_filename;
+ uInt size_comment;
+ uInt i;
+ int err = ZIP_OK;
+
+# ifdef NOCRYPT
+ if (password != NULL)
+ return ZIP_PARAMERROR;
+# endif
+
+ if (file == NULL)
+ return ZIP_PARAMERROR;
+ if ((method!=0) && (method!=Z_DEFLATED))
+ return ZIP_PARAMERROR;
+
+ zi = (zip_internal*)file;
+
+ if (zi->in_opened_file_inzip == 1)
+ {
+ err = zipCloseFileInZip (file);
+ if (err != ZIP_OK)
+ return err;
+ }
+
+
+ if (filename==NULL)
+ filename="-";
+
+ if (comment==NULL)
+ size_comment = 0;
+ else
+ size_comment = (uInt)strlen(comment);
+
+ size_filename = (uInt)strlen(filename);
+
+ if (zipfi == NULL)
+ zi->ci.dosDate = 0;
+ else
+ {
+ if (zipfi->dosDate != 0)
+ zi->ci.dosDate = zipfi->dosDate;
+ else zi->ci.dosDate = ziplocal_TmzDateToDosDate(&zipfi->tmz_date,zipfi->dosDate);
+ }
+
+ zi->ci.flag = 0;
+ if ((level==8) || (level==9))
+ zi->ci.flag |= 2;
+ if ((level==2))
+ zi->ci.flag |= 4;
+ if ((level==1))
+ zi->ci.flag |= 6;
+ if (password != NULL)
+ zi->ci.flag |= 1;
+
+ zi->ci.crc32 = 0;
+ zi->ci.method = method;
+ zi->ci.encrypt = 0;
+ zi->ci.stream_initialised = 0;
+ zi->ci.pos_in_buffered_data = 0;
+ zi->ci.raw = raw;
+ zi->ci.pos_local_header = ZTELL(zi->z_filefunc,zi->filestream) ;
+ zi->ci.size_centralheader = SIZECENTRALHEADER + size_filename +
+ size_extrafield_global + size_comment;
+ zi->ci.central_header = (char*)ALLOC((uInt)zi->ci.size_centralheader);
+
+ ziplocal_putValue_inmemory(zi->ci.central_header,(uLong)CENTRALHEADERMAGIC,4);
+ /* version info */
+ ziplocal_putValue_inmemory(zi->ci.central_header+4,(uLong)VERSIONMADEBY,2);
+ ziplocal_putValue_inmemory(zi->ci.central_header+6,(uLong)20,2);
+ ziplocal_putValue_inmemory(zi->ci.central_header+8,(uLong)zi->ci.flag,2);
+ ziplocal_putValue_inmemory(zi->ci.central_header+10,(uLong)zi->ci.method,2);
+ ziplocal_putValue_inmemory(zi->ci.central_header+12,(uLong)zi->ci.dosDate,4);
+ ziplocal_putValue_inmemory(zi->ci.central_header+16,(uLong)0,4); /*crc*/
+ ziplocal_putValue_inmemory(zi->ci.central_header+20,(uLong)0,4); /*compr size*/
+ ziplocal_putValue_inmemory(zi->ci.central_header+24,(uLong)0,4); /*uncompr size*/
+ ziplocal_putValue_inmemory(zi->ci.central_header+28,(uLong)size_filename,2);
+ ziplocal_putValue_inmemory(zi->ci.central_header+30,(uLong)size_extrafield_global,2);
+ ziplocal_putValue_inmemory(zi->ci.central_header+32,(uLong)size_comment,2);
+ ziplocal_putValue_inmemory(zi->ci.central_header+34,(uLong)0,2); /*disk nm start*/
+
+ if (zipfi==NULL)
+ ziplocal_putValue_inmemory(zi->ci.central_header+36,(uLong)0,2);
+ else
+ ziplocal_putValue_inmemory(zi->ci.central_header+36,(uLong)zipfi->internal_fa,2);
+
+ if (zipfi==NULL)
+ ziplocal_putValue_inmemory(zi->ci.central_header+38,(uLong)0,4);
+ else
+ ziplocal_putValue_inmemory(zi->ci.central_header+38,(uLong)zipfi->external_fa,4);
+
+ ziplocal_putValue_inmemory(zi->ci.central_header+42,(uLong)zi->ci.pos_local_header- zi->add_position_when_writting_offset,4);
+
+ for (i=0;i<size_filename;i++)
+ *(zi->ci.central_header+SIZECENTRALHEADER+i) = *(filename+i);
+
+ for (i=0;i<size_extrafield_global;i++)
+ *(zi->ci.central_header+SIZECENTRALHEADER+size_filename+i) =
+ *(((const char*)extrafield_global)+i);
+
+ for (i=0;i<size_comment;i++)
+ *(zi->ci.central_header+SIZECENTRALHEADER+size_filename+
+ size_extrafield_global+i) = *(comment+i);
+ if (zi->ci.central_header == NULL)
+ return ZIP_INTERNALERROR;
+
+ /* write the local header */
+ err = ziplocal_putValue(&zi->z_filefunc,zi->filestream,(uLong)LOCALHEADERMAGIC,4);
+
+ if (err==ZIP_OK)
+ err = ziplocal_putValue(&zi->z_filefunc,zi->filestream,(uLong)20,2);/* version needed to extract */
+ if (err==ZIP_OK)
+ err = ziplocal_putValue(&zi->z_filefunc,zi->filestream,(uLong)zi->ci.flag,2);
+
+ if (err==ZIP_OK)
+ err = ziplocal_putValue(&zi->z_filefunc,zi->filestream,(uLong)zi->ci.method,2);
+
+ if (err==ZIP_OK)
+ err = ziplocal_putValue(&zi->z_filefunc,zi->filestream,(uLong)zi->ci.dosDate,4);
+
+ if (err==ZIP_OK)
+ err = ziplocal_putValue(&zi->z_filefunc,zi->filestream,(uLong)0,4); /* crc 32, unknown */
+ if (err==ZIP_OK)
+ err = ziplocal_putValue(&zi->z_filefunc,zi->filestream,(uLong)0,4); /* compressed size, unknown */
+ if (err==ZIP_OK)
+ err = ziplocal_putValue(&zi->z_filefunc,zi->filestream,(uLong)0,4); /* uncompressed size, unknown */
+
+ if (err==ZIP_OK)
+ err = ziplocal_putValue(&zi->z_filefunc,zi->filestream,(uLong)size_filename,2);
+
+ if (err==ZIP_OK)
+ err = ziplocal_putValue(&zi->z_filefunc,zi->filestream,(uLong)size_extrafield_local,2);
+
+ if ((err==ZIP_OK) && (size_filename>0))
+ if (ZWRITE(zi->z_filefunc,zi->filestream,filename,size_filename)!=size_filename)
+ err = ZIP_ERRNO;
+
+ if ((err==ZIP_OK) && (size_extrafield_local>0))
+ if (ZWRITE(zi->z_filefunc,zi->filestream,extrafield_local,size_extrafield_local)
+ !=size_extrafield_local)
+ err = ZIP_ERRNO;
+
+ zi->ci.stream.avail_in = (uInt)0;
+ zi->ci.stream.avail_out = (uInt)Z_BUFSIZE;
+ zi->ci.stream.next_out = zi->ci.buffered_data;
+ zi->ci.stream.total_in = 0;
+ zi->ci.stream.total_out = 0;
+
+ if ((err==ZIP_OK) && (zi->ci.method == Z_DEFLATED) && (!zi->ci.raw))
+ {
+ zi->ci.stream.zalloc = (alloc_func)0;
+ zi->ci.stream.zfree = (free_func)0;
+ zi->ci.stream.opaque = (voidpf)0;
+
+ if (windowBits>0)
+ windowBits = -windowBits;
+
+ err = deflateInit2(&zi->ci.stream, level,
+ Z_DEFLATED, windowBits, memLevel, strategy);
+
+ if (err==Z_OK)
+ zi->ci.stream_initialised = 1;
+ }
+# ifndef NOCRYPT
+ zi->ci.crypt_header_size = 0;
+ if ((err==Z_OK) && (password != NULL))
+ {
+ unsigned char bufHead[RAND_HEAD_LEN];
+ unsigned int sizeHead;
+ zi->ci.encrypt = 1;
+ zi->ci.pcrc_32_tab = get_crc_table();
+ /*init_keys(password,zi->ci.keys,zi->ci.pcrc_32_tab);*/
+
+ sizeHead=crypthead(password,bufHead,RAND_HEAD_LEN,zi->ci.keys,zi->ci.pcrc_32_tab,crcForCrypting);
+ zi->ci.crypt_header_size = sizeHead;
+
+ if (ZWRITE(zi->z_filefunc,zi->filestream,bufHead,sizeHead) != sizeHead)
+ err = ZIP_ERRNO;
+ }
+# endif
+
+ if (err==Z_OK)
+ zi->in_opened_file_inzip = 1;
+ return err;
+}
+
+extern int ZEXPORT zipOpenNewFileInZip2(file, filename, zipfi,
+ extrafield_local, size_extrafield_local,
+ extrafield_global, size_extrafield_global,
+ comment, method, level, raw)
+ zipFile file;
+ const char* filename;
+ const zip_fileinfo* zipfi;
+ const void* extrafield_local;
+ uInt size_extrafield_local;
+ const void* extrafield_global;
+ uInt size_extrafield_global;
+ const char* comment;
+ int method;
+ int level;
+ int raw;
+{
+ return zipOpenNewFileInZip3 (file, filename, zipfi,
+ extrafield_local, size_extrafield_local,
+ extrafield_global, size_extrafield_global,
+ comment, method, level, raw,
+ -MAX_WBITS, DEF_MEM_LEVEL, Z_DEFAULT_STRATEGY,
+ NULL, 0);
+}
+
+extern int ZEXPORT zipOpenNewFileInZip (file, filename, zipfi,
+ extrafield_local, size_extrafield_local,
+ extrafield_global, size_extrafield_global,
+ comment, method, level)
+ zipFile file;
+ const char* filename;
+ const zip_fileinfo* zipfi;
+ const void* extrafield_local;
+ uInt size_extrafield_local;
+ const void* extrafield_global;
+ uInt size_extrafield_global;
+ const char* comment;
+ int method;
+ int level;
+{
+ return zipOpenNewFileInZip2 (file, filename, zipfi,
+ extrafield_local, size_extrafield_local,
+ extrafield_global, size_extrafield_global,
+ comment, method, level, 0);
+}
+
+local int zipFlushWriteBuffer(zi)
+ zip_internal* zi;
+{
+ int err=ZIP_OK;
+
+ if (zi->ci.encrypt != 0)
+ {
+#ifndef NOCRYPT
+ uInt i;
+ int t;
+ for (i=0;i<zi->ci.pos_in_buffered_data;i++)
+ zi->ci.buffered_data[i] = zencode(zi->ci.keys, zi->ci.pcrc_32_tab,
+ zi->ci.buffered_data[i],t);
+#endif
+ }
+ if (ZWRITE(zi->z_filefunc,zi->filestream,zi->ci.buffered_data,zi->ci.pos_in_buffered_data)
+ !=zi->ci.pos_in_buffered_data)
+ err = ZIP_ERRNO;
+ zi->ci.pos_in_buffered_data = 0;
+ return err;
+}
+
+extern int ZEXPORT zipWriteInFileInZip (file, buf, len)
+ zipFile file;
+ const void* buf;
+ unsigned len;
+{
+ zip_internal* zi;
+ int err=ZIP_OK;
+
+ if (file == NULL)
+ return ZIP_PARAMERROR;
+ zi = (zip_internal*)file;
+
+ if (zi->in_opened_file_inzip == 0)
+ return ZIP_PARAMERROR;
+
+ zi->ci.stream.next_in = (void*)buf;
+ zi->ci.stream.avail_in = len;
+ zi->ci.crc32 = crc32(zi->ci.crc32,buf,len);
+
+ while ((err==ZIP_OK) && (zi->ci.stream.avail_in>0))
+ {
+ if (zi->ci.stream.avail_out == 0)
+ {
+ if (zipFlushWriteBuffer(zi) == ZIP_ERRNO)
+ err = ZIP_ERRNO;
+ zi->ci.stream.avail_out = (uInt)Z_BUFSIZE;
+ zi->ci.stream.next_out = zi->ci.buffered_data;
+ }
+
+
+ if(err != ZIP_OK)
+ break;
+
+ if ((zi->ci.method == Z_DEFLATED) && (!zi->ci.raw))
+ {
+ uLong uTotalOutBefore = zi->ci.stream.total_out;
+ err=deflate(&zi->ci.stream, Z_NO_FLUSH);
+ zi->ci.pos_in_buffered_data += (uInt)(zi->ci.stream.total_out - uTotalOutBefore) ;
+
+ }
+ else
+ {
+ uInt copy_this,i;
+ if (zi->ci.stream.avail_in < zi->ci.stream.avail_out)
+ copy_this = zi->ci.stream.avail_in;
+ else
+ copy_this = zi->ci.stream.avail_out;
+ for (i=0;i<copy_this;i++)
+ *(((char*)zi->ci.stream.next_out)+i) =
+ *(((const char*)zi->ci.stream.next_in)+i);
+ {
+ zi->ci.stream.avail_in -= copy_this;
+ zi->ci.stream.avail_out-= copy_this;
+ zi->ci.stream.next_in+= copy_this;
+ zi->ci.stream.next_out+= copy_this;
+ zi->ci.stream.total_in+= copy_this;
+ zi->ci.stream.total_out+= copy_this;
+ zi->ci.pos_in_buffered_data += copy_this;
+ }
+ }
+ }
+
+ return err;
+}
+
+extern int ZEXPORT zipCloseFileInZipRaw (file, uncompressed_size, crc32)
+ zipFile file;
+ uLong uncompressed_size;
+ uLong crc32;
+{
+ zip_internal* zi;
+ uLong compressed_size;
+ int err=ZIP_OK;
+
+ if (file == NULL)
+ return ZIP_PARAMERROR;
+ zi = (zip_internal*)file;
+
+ if (zi->in_opened_file_inzip == 0)
+ return ZIP_PARAMERROR;
+ zi->ci.stream.avail_in = 0;
+
+ if ((zi->ci.method == Z_DEFLATED) && (!zi->ci.raw))
+ while (err==ZIP_OK)
+ {
+ uLong uTotalOutBefore;
+ if (zi->ci.stream.avail_out == 0)
+ {
+ if (zipFlushWriteBuffer(zi) == ZIP_ERRNO)
+ err = ZIP_ERRNO;
+ zi->ci.stream.avail_out = (uInt)Z_BUFSIZE;
+ zi->ci.stream.next_out = zi->ci.buffered_data;
+ }
+ uTotalOutBefore = zi->ci.stream.total_out;
+ err=deflate(&zi->ci.stream, Z_FINISH);
+ zi->ci.pos_in_buffered_data += (uInt)(zi->ci.stream.total_out - uTotalOutBefore) ;
+ }
+
+ if (err==Z_STREAM_END)
+ err=ZIP_OK; /* this is normal */
+
+ if ((zi->ci.pos_in_buffered_data>0) && (err==ZIP_OK))
+ if (zipFlushWriteBuffer(zi)==ZIP_ERRNO)
+ err = ZIP_ERRNO;
+
+ if ((zi->ci.method == Z_DEFLATED) && (!zi->ci.raw))
+ {
+ err=deflateEnd(&zi->ci.stream);
+ zi->ci.stream_initialised = 0;
+ }
+
+ if (!zi->ci.raw)
+ {
+ crc32 = (uLong)zi->ci.crc32;
+ uncompressed_size = (uLong)zi->ci.stream.total_in;
+ }
+ compressed_size = (uLong)zi->ci.stream.total_out;
+# ifndef NOCRYPT
+ compressed_size += zi->ci.crypt_header_size;
+# endif
+
+ ziplocal_putValue_inmemory(zi->ci.central_header+16,crc32,4); /*crc*/
+ ziplocal_putValue_inmemory(zi->ci.central_header+20,
+ compressed_size,4); /*compr size*/
+ if (zi->ci.stream.data_type == Z_ASCII)
+ ziplocal_putValue_inmemory(zi->ci.central_header+36,(uLong)Z_ASCII,2);
+ ziplocal_putValue_inmemory(zi->ci.central_header+24,
+ uncompressed_size,4); /*uncompr size*/
+
+ if (err==ZIP_OK)
+ err = add_data_in_datablock(&zi->central_dir,zi->ci.central_header,
+ (uLong)zi->ci.size_centralheader);
+ free(zi->ci.central_header);
+
+ if (err==ZIP_OK)
+ {
+ long cur_pos_inzip = ZTELL(zi->z_filefunc,zi->filestream);
+ if (ZSEEK(zi->z_filefunc,zi->filestream,
+ zi->ci.pos_local_header + 14,ZLIB_FILEFUNC_SEEK_SET)!=0)
+ err = ZIP_ERRNO;
+
+ if (err==ZIP_OK)
+ err = ziplocal_putValue(&zi->z_filefunc,zi->filestream,crc32,4); /* crc 32, unknown */
+
+ if (err==ZIP_OK) /* compressed size, unknown */
+ err = ziplocal_putValue(&zi->z_filefunc,zi->filestream,compressed_size,4);
+
+ if (err==ZIP_OK) /* uncompressed size, unknown */
+ err = ziplocal_putValue(&zi->z_filefunc,zi->filestream,uncompressed_size,4);
+
+ if (ZSEEK(zi->z_filefunc,zi->filestream,
+ cur_pos_inzip,ZLIB_FILEFUNC_SEEK_SET)!=0)
+ err = ZIP_ERRNO;
+ }
+
+ zi->number_entry ++;
+ zi->in_opened_file_inzip = 0;
+
+ return err;
+}
+
+extern int ZEXPORT zipCloseFileInZip (file)
+ zipFile file;
+{
+ return zipCloseFileInZipRaw (file,0,0);
+}
+
+extern int ZEXPORT zipClose (file, global_comment)
+ zipFile file;
+ const char* global_comment;
+{
+ zip_internal* zi;
+ int err = 0;
+ uLong size_centraldir = 0;
+ uLong centraldir_pos_inzip;
+ uInt size_global_comment;
+ if (file == NULL)
+ return ZIP_PARAMERROR;
+ zi = (zip_internal*)file;
+
+ if (zi->in_opened_file_inzip == 1)
+ {
+ err = zipCloseFileInZip (file);
+ }
+
+#ifndef NO_ADDFILEINEXISTINGZIP
+ if (global_comment==NULL)
+ global_comment = zi->globalcomment;
+#endif
+ if (global_comment==NULL)
+ size_global_comment = 0;
+ else
+ size_global_comment = (uInt)strlen(global_comment);
+
+ centraldir_pos_inzip = ZTELL(zi->z_filefunc,zi->filestream);
+ if (err==ZIP_OK)
+ {
+ linkedlist_datablock_internal* ldi = zi->central_dir.first_block ;
+ while (ldi!=NULL)
+ {
+ if ((err==ZIP_OK) && (ldi->filled_in_this_block>0))
+ if (ZWRITE(zi->z_filefunc,zi->filestream,
+ ldi->data,ldi->filled_in_this_block)
+ !=ldi->filled_in_this_block )
+ err = ZIP_ERRNO;
+
+ size_centraldir += ldi->filled_in_this_block;
+ ldi = ldi->next_datablock;
+ }
+ }
+ free_datablock(zi->central_dir.first_block);
+
+ if (err==ZIP_OK) /* Magic End */
+ err = ziplocal_putValue(&zi->z_filefunc,zi->filestream,(uLong)ENDHEADERMAGIC,4);
+
+ if (err==ZIP_OK) /* number of this disk */
+ err = ziplocal_putValue(&zi->z_filefunc,zi->filestream,(uLong)0,2);
+
+ if (err==ZIP_OK) /* number of the disk with the start of the central directory */
+ err = ziplocal_putValue(&zi->z_filefunc,zi->filestream,(uLong)0,2);
+
+ if (err==ZIP_OK) /* total number of entries in the central dir on this disk */
+ err = ziplocal_putValue(&zi->z_filefunc,zi->filestream,(uLong)zi->number_entry,2);
+
+ if (err==ZIP_OK) /* total number of entries in the central dir */
+ err = ziplocal_putValue(&zi->z_filefunc,zi->filestream,(uLong)zi->number_entry,2);
+
+ if (err==ZIP_OK) /* size of the central directory */
+ err = ziplocal_putValue(&zi->z_filefunc,zi->filestream,(uLong)size_centraldir,4);
+
+ if (err==ZIP_OK) /* offset of start of central directory with respect to the
+ starting disk number */
+ err = ziplocal_putValue(&zi->z_filefunc,zi->filestream,
+ (uLong)(centraldir_pos_inzip - zi->add_position_when_writting_offset),4);
+
+ if (err==ZIP_OK) /* zipfile comment length */
+ err = ziplocal_putValue(&zi->z_filefunc,zi->filestream,(uLong)size_global_comment,2);
+
+ if ((err==ZIP_OK) && (size_global_comment>0))
+ if (ZWRITE(zi->z_filefunc,zi->filestream,
+ global_comment,size_global_comment) != size_global_comment)
+ err = ZIP_ERRNO;
+
+ if (ZCLOSE(zi->z_filefunc,zi->filestream) != 0)
+ if (err == ZIP_OK)
+ err = ZIP_ERRNO;
+
+#ifndef NO_ADDFILEINEXISTINGZIP
+ TRYFREE(zi->globalcomment);
+#endif
+ TRYFREE(zi);
+
+ return err;
+}
diff --git a/externals/jemalloc/CMakeLists.txt b/externals/jemalloc/CMakeLists.txt
new file mode 100644
index 00000000000..c3e4e81782c
--- /dev/null
+++ b/externals/jemalloc/CMakeLists.txt
@@ -0,0 +1,27 @@
+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
+ )
+
+include_directories(
+ ${CMAKE_SOURCE_DIR}/dep/include
+ )
+
+add_definitions(-D_GNU_SOURCE -D_REENTRANT)
+
+add_library(jmalloc STATIC ${jmalloc_STAT_SRC}) \ No newline at end of file
diff --git a/externals/jemalloc/arena.c b/externals/jemalloc/arena.c
new file mode 100644
index 00000000000..e74b4701907
--- /dev/null
+++ b/externals/jemalloc/arena.c
@@ -0,0 +1,2446 @@
+#define JEMALLOC_ARENA_C_
+#include "jemalloc/internal/jemalloc_internal.h"
+
+/******************************************************************************/
+/* Data. */
+
+size_t opt_lg_qspace_max = LG_QSPACE_MAX_DEFAULT;
+size_t opt_lg_cspace_max = LG_CSPACE_MAX_DEFAULT;
+ssize_t opt_lg_dirty_mult = LG_DIRTY_MULT_DEFAULT;
+uint8_t const *small_size2bin;
+
+/* Various bin-related settings. */
+unsigned nqbins;
+unsigned ncbins;
+unsigned nsbins;
+unsigned nbins;
+size_t qspace_max;
+size_t cspace_min;
+size_t cspace_max;
+size_t sspace_min;
+size_t sspace_max;
+
+size_t lg_mspace;
+size_t mspace_mask;
+
+/*
+ * const_small_size2bin is a static constant lookup table that in the common
+ * case can be used as-is for small_size2bin. For dynamically linked programs,
+ * this avoids a page of memory overhead per process.
+ */
+#define S2B_1(i) i,
+#define S2B_2(i) S2B_1(i) S2B_1(i)
+#define S2B_4(i) S2B_2(i) S2B_2(i)
+#define S2B_8(i) S2B_4(i) S2B_4(i)
+#define S2B_16(i) S2B_8(i) S2B_8(i)
+#define S2B_32(i) S2B_16(i) S2B_16(i)
+#define S2B_64(i) S2B_32(i) S2B_32(i)
+#define S2B_128(i) S2B_64(i) S2B_64(i)
+#define S2B_256(i) S2B_128(i) S2B_128(i)
+/*
+ * The number of elements in const_small_size2bin is dependent on page size
+ * and on the definition for SUBPAGE. If SUBPAGE changes, the '- 255' must also
+ * change, along with the addition/removal of static lookup table element
+ * definitions.
+ */
+static const uint8_t const_small_size2bin[STATIC_PAGE_SIZE - 255] = {
+ S2B_1(0xffU) /* 0 */
+#if (LG_QUANTUM == 4)
+/* 16-byte quantum **********************/
+# ifdef JEMALLOC_TINY
+# if (LG_TINY_MIN == 2)
+ S2B_4(0) /* 4 */
+ S2B_4(1) /* 8 */
+ S2B_8(2) /* 16 */
+# define S2B_QMIN 2
+# elif (LG_TINY_MIN == 3)
+ S2B_8(0) /* 8 */
+ S2B_8(1) /* 16 */
+# define S2B_QMIN 1
+# else
+# error "Unsupported LG_TINY_MIN"
+# endif
+# else
+ S2B_16(0) /* 16 */
+# define S2B_QMIN 0
+# endif
+ S2B_16(S2B_QMIN + 1) /* 32 */
+ S2B_16(S2B_QMIN + 2) /* 48 */
+ S2B_16(S2B_QMIN + 3) /* 64 */
+ S2B_16(S2B_QMIN + 4) /* 80 */
+ S2B_16(S2B_QMIN + 5) /* 96 */
+ S2B_16(S2B_QMIN + 6) /* 112 */
+ S2B_16(S2B_QMIN + 7) /* 128 */
+# define S2B_CMIN (S2B_QMIN + 8)
+#else
+/* 8-byte quantum ***********************/
+# ifdef JEMALLOC_TINY
+# if (LG_TINY_MIN == 2)
+ S2B_4(0) /* 4 */
+ S2B_4(1) /* 8 */
+# define S2B_QMIN 1
+# else
+# error "Unsupported LG_TINY_MIN"
+# endif
+# else
+ S2B_8(0) /* 8 */
+# define S2B_QMIN 0
+# endif
+ S2B_8(S2B_QMIN + 1) /* 16 */
+ S2B_8(S2B_QMIN + 2) /* 24 */
+ S2B_8(S2B_QMIN + 3) /* 32 */
+ S2B_8(S2B_QMIN + 4) /* 40 */
+ S2B_8(S2B_QMIN + 5) /* 48 */
+ S2B_8(S2B_QMIN + 6) /* 56 */
+ S2B_8(S2B_QMIN + 7) /* 64 */
+ S2B_8(S2B_QMIN + 8) /* 72 */
+ S2B_8(S2B_QMIN + 9) /* 80 */
+ S2B_8(S2B_QMIN + 10) /* 88 */
+ S2B_8(S2B_QMIN + 11) /* 96 */
+ S2B_8(S2B_QMIN + 12) /* 104 */
+ S2B_8(S2B_QMIN + 13) /* 112 */
+ S2B_8(S2B_QMIN + 14) /* 120 */
+ S2B_8(S2B_QMIN + 15) /* 128 */
+# define S2B_CMIN (S2B_QMIN + 16)
+#endif
+/****************************************/
+ S2B_64(S2B_CMIN + 0) /* 192 */
+ S2B_64(S2B_CMIN + 1) /* 256 */
+ S2B_64(S2B_CMIN + 2) /* 320 */
+ S2B_64(S2B_CMIN + 3) /* 384 */
+ S2B_64(S2B_CMIN + 4) /* 448 */
+ S2B_64(S2B_CMIN + 5) /* 512 */
+# define S2B_SMIN (S2B_CMIN + 6)
+ S2B_256(S2B_SMIN + 0) /* 768 */
+ S2B_256(S2B_SMIN + 1) /* 1024 */
+ S2B_256(S2B_SMIN + 2) /* 1280 */
+ S2B_256(S2B_SMIN + 3) /* 1536 */
+ S2B_256(S2B_SMIN + 4) /* 1792 */
+ S2B_256(S2B_SMIN + 5) /* 2048 */
+ S2B_256(S2B_SMIN + 6) /* 2304 */
+ S2B_256(S2B_SMIN + 7) /* 2560 */
+ S2B_256(S2B_SMIN + 8) /* 2816 */
+ S2B_256(S2B_SMIN + 9) /* 3072 */
+ S2B_256(S2B_SMIN + 10) /* 3328 */
+ S2B_256(S2B_SMIN + 11) /* 3584 */
+ S2B_256(S2B_SMIN + 12) /* 3840 */
+#if (STATIC_PAGE_SHIFT == 13)
+ S2B_256(S2B_SMIN + 13) /* 4096 */
+ S2B_256(S2B_SMIN + 14) /* 4352 */
+ S2B_256(S2B_SMIN + 15) /* 4608 */
+ S2B_256(S2B_SMIN + 16) /* 4864 */
+ S2B_256(S2B_SMIN + 17) /* 5120 */
+ S2B_256(S2B_SMIN + 18) /* 5376 */
+ S2B_256(S2B_SMIN + 19) /* 5632 */
+ S2B_256(S2B_SMIN + 20) /* 5888 */
+ S2B_256(S2B_SMIN + 21) /* 6144 */
+ S2B_256(S2B_SMIN + 22) /* 6400 */
+ S2B_256(S2B_SMIN + 23) /* 6656 */
+ S2B_256(S2B_SMIN + 24) /* 6912 */
+ S2B_256(S2B_SMIN + 25) /* 7168 */
+ S2B_256(S2B_SMIN + 26) /* 7424 */
+ S2B_256(S2B_SMIN + 27) /* 7680 */
+ S2B_256(S2B_SMIN + 28) /* 7936 */
+#endif
+};
+#undef S2B_1
+#undef S2B_2
+#undef S2B_4
+#undef S2B_8
+#undef S2B_16
+#undef S2B_32
+#undef S2B_64
+#undef S2B_128
+#undef S2B_256
+#undef S2B_QMIN
+#undef S2B_CMIN
+#undef S2B_SMIN
+
+/******************************************************************************/
+/* Function prototypes for non-inline static functions. */
+
+static void arena_run_split(arena_t *arena, arena_run_t *run, size_t size,
+ bool large, bool zero);
+static arena_chunk_t *arena_chunk_alloc(arena_t *arena);
+static void arena_chunk_dealloc(arena_t *arena, arena_chunk_t *chunk);
+static arena_run_t *arena_run_alloc(arena_t *arena, size_t size, bool large,
+ bool zero);
+static void arena_purge(arena_t *arena);
+static void arena_run_dalloc(arena_t *arena, arena_run_t *run, bool dirty);
+static void arena_run_trim_head(arena_t *arena, arena_chunk_t *chunk,
+ arena_run_t *run, size_t oldsize, size_t newsize);
+static void arena_run_trim_tail(arena_t *arena, arena_chunk_t *chunk,
+ arena_run_t *run, size_t oldsize, size_t newsize, bool dirty);
+static arena_run_t *arena_bin_nonfull_run_get(arena_t *arena, arena_bin_t *bin);
+static void *arena_bin_malloc_hard(arena_t *arena, arena_bin_t *bin);
+static size_t arena_bin_run_size_calc(arena_bin_t *bin, size_t min_run_size);
+static void arena_dalloc_bin_run(arena_t *arena, arena_chunk_t *chunk,
+ arena_run_t *run, arena_bin_t *bin);
+static void arena_ralloc_large_shrink(arena_t *arena, arena_chunk_t *chunk,
+ void *ptr, size_t size, size_t oldsize);
+static bool arena_ralloc_large_grow(arena_t *arena, arena_chunk_t *chunk,
+ void *ptr, size_t size, size_t oldsize);
+static bool arena_ralloc_large(void *ptr, size_t size, size_t oldsize);
+#ifdef JEMALLOC_TINY
+static size_t pow2_ceil(size_t x);
+#endif
+static bool small_size2bin_init(void);
+#ifdef JEMALLOC_DEBUG
+static void small_size2bin_validate(void);
+#endif
+static bool small_size2bin_init_hard(void);
+
+/******************************************************************************/
+
+static inline int
+arena_run_comp(arena_chunk_map_t *a, arena_chunk_map_t *b)
+{
+ uintptr_t a_mapelm = (uintptr_t)a;
+ uintptr_t b_mapelm = (uintptr_t)b;
+
+ assert(a != NULL);
+ assert(b != NULL);
+
+ return ((a_mapelm > b_mapelm) - (a_mapelm < b_mapelm));
+}
+
+/* Generate red-black tree functions. */
+rb_gen(static JEMALLOC_ATTR(unused), arena_run_tree_, arena_run_tree_t,
+ arena_chunk_map_t, u.rb_link, arena_run_comp)
+
+static inline int
+arena_avail_comp(arena_chunk_map_t *a, arena_chunk_map_t *b)
+{
+ int ret;
+ size_t a_size = a->bits & ~PAGE_MASK;
+ size_t b_size = b->bits & ~PAGE_MASK;
+
+ assert((a->bits & CHUNK_MAP_KEY) == CHUNK_MAP_KEY || (a->bits &
+ CHUNK_MAP_DIRTY) == (b->bits & CHUNK_MAP_DIRTY));
+
+ ret = (a_size > b_size) - (a_size < b_size);
+ if (ret == 0) {
+ uintptr_t a_mapelm, b_mapelm;
+
+ if ((a->bits & CHUNK_MAP_KEY) != CHUNK_MAP_KEY)
+ a_mapelm = (uintptr_t)a;
+ else {
+ /*
+ * Treat keys as though they are lower than anything
+ * else.
+ */
+ a_mapelm = 0;
+ }
+ b_mapelm = (uintptr_t)b;
+
+ ret = (a_mapelm > b_mapelm) - (a_mapelm < b_mapelm);
+ }
+
+ return (ret);
+}
+
+/* Generate red-black tree functions. */
+rb_gen(static JEMALLOC_ATTR(unused), arena_avail_tree_, arena_avail_tree_t,
+ arena_chunk_map_t, u.rb_link, arena_avail_comp)
+
+static inline void *
+arena_run_reg_alloc(arena_run_t *run, arena_bin_t *bin)
+{
+ void *ret;
+
+ assert(run->magic == ARENA_RUN_MAGIC);
+ assert(run->nfree > 0);
+
+ run->nfree--;
+ ret = run->avail;
+ if (ret != NULL) {
+ run->avail = *(void **)ret;
+ /* Double free can cause assertion failure.*/
+ assert(ret != NULL);
+ /* Write-after free can cause assertion failure. */
+ assert((uintptr_t)ret >= (uintptr_t)run +
+ (uintptr_t)bin->reg0_offset);
+ assert((uintptr_t)ret < (uintptr_t)run->next);
+ assert(((uintptr_t)ret - ((uintptr_t)run +
+ (uintptr_t)bin->reg0_offset)) % (uintptr_t)bin->reg_size ==
+ 0);
+ return (ret);
+ }
+ ret = run->next;
+ run->next = (void *)((uintptr_t)ret + (uintptr_t)bin->reg_size);
+ assert(ret != NULL);
+ return (ret);
+}
+
+static inline void
+arena_run_reg_dalloc(arena_run_t *run, void *ptr)
+{
+
+ assert(run->nfree < run->bin->nregs);
+ /* Freeing an interior pointer can cause assertion failure. */
+ assert(((uintptr_t)ptr - ((uintptr_t)run +
+ (uintptr_t)run->bin->reg0_offset)) % (uintptr_t)run->bin->reg_size
+ == 0);
+
+ *(void **)ptr = run->avail;
+ run->avail = ptr;
+ run->nfree++;
+}
+
+static void
+arena_run_split(arena_t *arena, arena_run_t *run, size_t size, bool large,
+ bool zero)
+{
+ arena_chunk_t *chunk;
+ size_t old_ndirty, run_ind, total_pages, need_pages, rem_pages, i;
+ size_t flag_dirty;
+ arena_avail_tree_t *runs_avail;
+
+ chunk = (arena_chunk_t *)CHUNK_ADDR2BASE(run);
+ old_ndirty = chunk->ndirty;
+ run_ind = (unsigned)(((uintptr_t)run - (uintptr_t)chunk)
+ >> PAGE_SHIFT);
+ flag_dirty = chunk->map[run_ind].bits & CHUNK_MAP_DIRTY;
+ runs_avail = (flag_dirty != 0) ? &arena->runs_avail_dirty :
+ &arena->runs_avail_clean;
+ total_pages = (chunk->map[run_ind].bits & ~PAGE_MASK) >>
+ PAGE_SHIFT;
+ assert((chunk->map[run_ind+total_pages-1].bits & CHUNK_MAP_DIRTY) ==
+ flag_dirty);
+ need_pages = (size >> PAGE_SHIFT);
+ assert(need_pages > 0);
+ assert(need_pages <= total_pages);
+ rem_pages = total_pages - need_pages;
+
+ arena_avail_tree_remove(runs_avail, &chunk->map[run_ind]);
+ arena->nactive += need_pages;
+
+ /* Keep track of trailing unused pages for later use. */
+ if (rem_pages > 0) {
+ if (flag_dirty != 0) {
+ chunk->map[run_ind+need_pages].bits = (rem_pages <<
+ PAGE_SHIFT) | CHUNK_MAP_DIRTY;
+ chunk->map[run_ind+total_pages-1].bits = (rem_pages <<
+ PAGE_SHIFT) | CHUNK_MAP_DIRTY;
+ } else {
+ chunk->map[run_ind+need_pages].bits = (rem_pages <<
+ PAGE_SHIFT) | (chunk->map[run_ind+need_pages].bits &
+ CHUNK_MAP_ZEROED);
+ chunk->map[run_ind+total_pages-1].bits = (rem_pages <<
+ PAGE_SHIFT) |
+ (chunk->map[run_ind+total_pages-1].bits &
+ CHUNK_MAP_ZEROED);
+ }
+ arena_avail_tree_insert(runs_avail,
+ &chunk->map[run_ind+need_pages]);
+ }
+
+ /* Update dirty page accounting. */
+ if (flag_dirty != 0) {
+ chunk->ndirty -= need_pages;
+ arena->ndirty -= need_pages;
+ }
+
+ /*
+ * Update the page map separately for large vs. small runs, since it is
+ * possible to avoid iteration for large mallocs.
+ */
+ if (large) {
+ if (zero) {
+ if (flag_dirty == 0) {
+ /*
+ * The run is clean, so some pages may be
+ * zeroed (i.e. never before touched).
+ */
+ for (i = 0; i < need_pages; i++) {
+ if ((chunk->map[run_ind + i].bits &
+ CHUNK_MAP_ZEROED) == 0) {
+ memset((void *)((uintptr_t)
+ chunk + ((run_ind + i) <<
+ PAGE_SHIFT)), 0,
+ PAGE_SIZE);
+ }
+ }
+ } else {
+ /*
+ * The run is dirty, so all pages must be
+ * zeroed.
+ */
+ memset((void *)((uintptr_t)chunk + (run_ind <<
+ PAGE_SHIFT)), 0, (need_pages <<
+ PAGE_SHIFT));
+ }
+ }
+
+ /*
+ * Set the last element first, in case the run only contains one
+ * page (i.e. both statements set the same element).
+ */
+ chunk->map[run_ind+need_pages-1].bits = CHUNK_MAP_LARGE |
+ CHUNK_MAP_ALLOCATED | flag_dirty;
+ chunk->map[run_ind].bits = size | CHUNK_MAP_LARGE |
+#ifdef JEMALLOC_PROF
+ CHUNK_MAP_CLASS_MASK |
+#endif
+ CHUNK_MAP_ALLOCATED | flag_dirty;
+ } else {
+ assert(zero == false);
+ /*
+ * Propagate the dirty flag to the allocated small run, so that
+ * arena_dalloc_bin_run() has the ability to conditionally trim
+ * clean pages.
+ */
+ chunk->map[run_ind].bits = CHUNK_MAP_ALLOCATED | flag_dirty;
+ for (i = 1; i < need_pages - 1; i++) {
+ chunk->map[run_ind + i].bits = (i << PAGE_SHIFT)
+ | CHUNK_MAP_ALLOCATED;
+ }
+ chunk->map[run_ind + need_pages - 1].bits = ((need_pages - 1) <<
+ PAGE_SHIFT) | CHUNK_MAP_ALLOCATED | flag_dirty;
+ }
+}
+
+static arena_chunk_t *
+arena_chunk_alloc(arena_t *arena)
+{
+ arena_chunk_t *chunk;
+ size_t i;
+
+ if (arena->spare != NULL) {
+ arena_avail_tree_t *runs_avail;
+
+ chunk = arena->spare;
+ arena->spare = NULL;
+
+ /* Insert the run into the appropriate runs_avail_* tree. */
+ if ((chunk->map[arena_chunk_header_npages].bits &
+ CHUNK_MAP_DIRTY) == 0)
+ runs_avail = &arena->runs_avail_clean;
+ else
+ runs_avail = &arena->runs_avail_dirty;
+ arena_avail_tree_insert(runs_avail,
+ &chunk->map[arena_chunk_header_npages]);
+ } else {
+ bool zero;
+ size_t zeroed;
+
+ zero = false;
+ malloc_mutex_unlock(&arena->lock);
+ chunk = (arena_chunk_t *)chunk_alloc(chunksize, &zero);
+ malloc_mutex_lock(&arena->lock);
+ if (chunk == NULL)
+ return (NULL);
+#ifdef JEMALLOC_STATS
+ arena->stats.mapped += chunksize;
+#endif
+
+ chunk->arena = arena;
+ ql_elm_new(chunk, link_dirty);
+ chunk->dirtied = false;
+
+ /*
+ * Claim that no pages are in use, since the header is merely
+ * overhead.
+ */
+ chunk->ndirty = 0;
+
+ /*
+ * Initialize the map to contain one maximal free untouched run.
+ * Mark the pages as zeroed iff chunk_alloc() returned a zeroed
+ * chunk.
+ */
+ zeroed = zero ? CHUNK_MAP_ZEROED : 0;
+ for (i = 0; i < arena_chunk_header_npages; i++)
+ chunk->map[i].bits = 0;
+ chunk->map[i].bits = arena_maxclass | zeroed;
+ for (i++; i < chunk_npages-1; i++)
+ chunk->map[i].bits = zeroed;
+ chunk->map[chunk_npages-1].bits = arena_maxclass | zeroed;
+
+ /* Insert the run into the runs_avail_clean tree. */
+ arena_avail_tree_insert(&arena->runs_avail_clean,
+ &chunk->map[arena_chunk_header_npages]);
+ }
+
+ return (chunk);
+}
+
+static void
+arena_chunk_dealloc(arena_t *arena, arena_chunk_t *chunk)
+{
+ arena_avail_tree_t *runs_avail;
+
+ while (arena->spare != NULL) {
+ arena_chunk_t *spare = arena->spare;
+
+ arena->spare = NULL;
+ if (spare->dirtied) {
+ ql_remove(&chunk->arena->chunks_dirty, spare,
+ link_dirty);
+ arena->ndirty -= spare->ndirty;
+ }
+ malloc_mutex_unlock(&arena->lock);
+ chunk_dealloc((void *)spare, chunksize);
+ malloc_mutex_lock(&arena->lock);
+#ifdef JEMALLOC_STATS
+ arena->stats.mapped -= chunksize;
+#endif
+ }
+
+ /*
+ * Remove run from the appropriate runs_avail_* tree, so that the arena
+ * does not use it.
+ */
+ if ((chunk->map[arena_chunk_header_npages].bits &
+ CHUNK_MAP_DIRTY) == 0)
+ runs_avail = &arena->runs_avail_clean;
+ else
+ runs_avail = &arena->runs_avail_dirty;
+ arena_avail_tree_remove(runs_avail,
+ &chunk->map[arena_chunk_header_npages]);
+
+ arena->spare = chunk;
+}
+
+static arena_run_t *
+arena_run_alloc(arena_t *arena, size_t size, bool large, bool zero)
+{
+ arena_chunk_t *chunk;
+ arena_run_t *run;
+ arena_chunk_map_t *mapelm, key;
+
+ assert(size <= arena_maxclass);
+ assert((size & PAGE_MASK) == 0);
+
+ /* Search the arena's chunks for the lowest best fit. */
+ key.bits = size | CHUNK_MAP_KEY;
+ mapelm = arena_avail_tree_nsearch(&arena->runs_avail_dirty, &key);
+ if (mapelm != NULL) {
+ arena_chunk_t *run_chunk = CHUNK_ADDR2BASE(mapelm);
+ size_t pageind = ((uintptr_t)mapelm - (uintptr_t)run_chunk->map)
+ / sizeof(arena_chunk_map_t);
+
+ run = (arena_run_t *)((uintptr_t)run_chunk + (pageind <<
+ PAGE_SHIFT));
+ arena_run_split(arena, run, size, large, zero);
+ return (run);
+ }
+ mapelm = arena_avail_tree_nsearch(&arena->runs_avail_clean, &key);
+ if (mapelm != NULL) {
+ arena_chunk_t *run_chunk = CHUNK_ADDR2BASE(mapelm);
+ size_t pageind = ((uintptr_t)mapelm - (uintptr_t)run_chunk->map)
+ / sizeof(arena_chunk_map_t);
+
+ run = (arena_run_t *)((uintptr_t)run_chunk + (pageind <<
+ PAGE_SHIFT));
+ arena_run_split(arena, run, size, large, zero);
+ return (run);
+ }
+
+ /*
+ * No usable runs. Create a new chunk from which to allocate the run.
+ */
+ chunk = arena_chunk_alloc(arena);
+ if (chunk != NULL) {
+ run = (arena_run_t *)((uintptr_t)chunk +
+ (arena_chunk_header_npages << PAGE_SHIFT));
+ arena_run_split(arena, run, size, large, zero);
+ return (run);
+ }
+
+ /*
+ * arena_chunk_alloc() failed, but another thread may have made
+ * sufficient memory available while this one dropped arena->lock in
+ * arena_chunk_alloc(), so search one more time.
+ */
+ mapelm = arena_avail_tree_nsearch(&arena->runs_avail_dirty, &key);
+ if (mapelm != NULL) {
+ arena_chunk_t *run_chunk = CHUNK_ADDR2BASE(mapelm);
+ size_t pageind = ((uintptr_t)mapelm - (uintptr_t)run_chunk->map)
+ / sizeof(arena_chunk_map_t);
+
+ run = (arena_run_t *)((uintptr_t)run_chunk + (pageind <<
+ PAGE_SHIFT));
+ arena_run_split(arena, run, size, large, zero);
+ return (run);
+ }
+ mapelm = arena_avail_tree_nsearch(&arena->runs_avail_clean, &key);
+ if (mapelm != NULL) {
+ arena_chunk_t *run_chunk = CHUNK_ADDR2BASE(mapelm);
+ size_t pageind = ((uintptr_t)mapelm - (uintptr_t)run_chunk->map)
+ / sizeof(arena_chunk_map_t);
+
+ run = (arena_run_t *)((uintptr_t)run_chunk + (pageind <<
+ PAGE_SHIFT));
+ arena_run_split(arena, run, size, large, zero);
+ return (run);
+ }
+
+ return (NULL);
+}
+
+static inline void
+arena_maybe_purge(arena_t *arena)
+{
+
+ /* Enforce opt_lg_dirty_mult. */
+ if (opt_lg_dirty_mult >= 0 && arena->ndirty > arena->npurgatory &&
+ (arena->ndirty - arena->npurgatory) > chunk_npages &&
+ (arena->nactive >> opt_lg_dirty_mult) < (arena->ndirty -
+ arena->npurgatory))
+ arena_purge(arena);
+}
+
+static inline void
+arena_chunk_purge(arena_t *arena, arena_chunk_t *chunk)
+{
+ ql_head(arena_chunk_map_t) mapelms;
+ arena_chunk_map_t *mapelm;
+ size_t pageind, flag_zeroed;
+#ifdef JEMALLOC_DEBUG
+ size_t ndirty;
+#endif
+#ifdef JEMALLOC_STATS
+ size_t nmadvise;
+#endif
+
+ ql_new(&mapelms);
+
+ flag_zeroed =
+#ifdef JEMALLOC_SWAP
+ swap_enabled ? 0 :
+#endif
+ CHUNK_MAP_ZEROED;
+
+ /*
+ * If chunk is the spare, temporarily re-allocate it, 1) so that its
+ * run is reinserted into runs_avail_dirty, and 2) so that it cannot be
+ * completely discarded by another thread while arena->lock is dropped
+ * by this thread. Note that the arena_run_dalloc() call will
+ * implicitly deallocate the chunk, so no explicit action is required
+ * in this function to deallocate the chunk.
+ *
+ * Note that once a chunk contains dirty pages, it cannot again contain
+ * a single run unless 1) it is a dirty run, or 2) this function purges
+ * dirty pages and causes the transition to a single clean run. Thus
+ * (chunk == arena->spare) is possible, but it is not possible for
+ * this function to be called on the spare unless it contains a dirty
+ * run.
+ */
+ if (chunk == arena->spare) {
+ assert((chunk->map[arena_chunk_header_npages].bits &
+ CHUNK_MAP_DIRTY) != 0);
+ arena_chunk_alloc(arena);
+ }
+
+ /* Temporarily allocate all free dirty runs within chunk. */
+ for (pageind = arena_chunk_header_npages; pageind < chunk_npages;) {
+ mapelm = &chunk->map[pageind];
+ if ((mapelm->bits & CHUNK_MAP_ALLOCATED) == 0) {
+ size_t npages;
+
+ npages = mapelm->bits >> PAGE_SHIFT;
+ assert(pageind + npages <= chunk_npages);
+ if (mapelm->bits & CHUNK_MAP_DIRTY) {
+ size_t i;
+
+ arena_avail_tree_remove(
+ &arena->runs_avail_dirty, mapelm);
+
+ /*
+ * Update internal elements in the page map, so
+ * that CHUNK_MAP_ZEROED is properly set.
+ * madvise(..., MADV_DONTNEED) results in
+ * zero-filled pages for anonymous mappings,
+ * but not for file-backed mappings.
+ */
+ mapelm->bits = (npages << PAGE_SHIFT) |
+ CHUNK_MAP_LARGE | CHUNK_MAP_ALLOCATED |
+ flag_zeroed;
+ for (i = 1; i < npages - 1; i++) {
+ chunk->map[pageind + i].bits =
+ flag_zeroed;
+ }
+ if (npages > 1) {
+ chunk->map[pageind + npages - 1].bits =
+ (npages << PAGE_SHIFT) |
+ CHUNK_MAP_LARGE | CHUNK_MAP_ALLOCATED |
+ flag_zeroed;
+ }
+
+ arena->nactive += npages;
+ /* Append to list for later processing. */
+ ql_elm_new(mapelm, u.ql_link);
+ ql_tail_insert(&mapelms, mapelm, u.ql_link);
+ }
+
+ pageind += npages;
+ } else {
+ /* Skip allocated run. */
+ if (mapelm->bits & CHUNK_MAP_LARGE)
+ pageind += mapelm->bits >> PAGE_SHIFT;
+ else {
+ arena_run_t *run = (arena_run_t *)((uintptr_t)
+ chunk + (uintptr_t)(pageind << PAGE_SHIFT));
+
+ assert((mapelm->bits >> PAGE_SHIFT) == 0);
+ assert(run->magic == ARENA_RUN_MAGIC);
+ pageind += run->bin->run_size >> PAGE_SHIFT;
+ }
+ }
+ }
+ assert(pageind == chunk_npages);
+
+#ifdef JEMALLOC_DEBUG
+ ndirty = chunk->ndirty;
+#endif
+#ifdef JEMALLOC_STATS
+ arena->stats.purged += chunk->ndirty;
+#endif
+ arena->ndirty -= chunk->ndirty;
+ chunk->ndirty = 0;
+ ql_remove(&arena->chunks_dirty, chunk, link_dirty);
+ chunk->dirtied = false;
+
+ malloc_mutex_unlock(&arena->lock);
+#ifdef JEMALLOC_STATS
+ nmadvise = 0;
+#endif
+ ql_foreach(mapelm, &mapelms, u.ql_link) {
+ size_t pageind = ((uintptr_t)mapelm - (uintptr_t)chunk->map) /
+ sizeof(arena_chunk_map_t);
+ size_t npages = mapelm->bits >> PAGE_SHIFT;
+
+ assert(pageind + npages <= chunk_npages);
+#ifdef JEMALLOC_DEBUG
+ assert(ndirty >= npages);
+ ndirty -= npages;
+#endif
+ madvise((void *)((uintptr_t)chunk + (pageind << PAGE_SHIFT)),
+ (npages << PAGE_SHIFT), MADV_DONTNEED);
+#ifdef JEMALLOC_STATS
+ nmadvise++;
+#endif
+ }
+#ifdef JEMALLOC_DEBUG
+ assert(ndirty == 0);
+#endif
+ malloc_mutex_lock(&arena->lock);
+#ifdef JEMALLOC_STATS
+ arena->stats.nmadvise += nmadvise;
+#endif
+
+ /* Deallocate runs. */
+ for (mapelm = ql_first(&mapelms); mapelm != NULL;
+ mapelm = ql_first(&mapelms)) {
+ size_t pageind = ((uintptr_t)mapelm - (uintptr_t)chunk->map) /
+ sizeof(arena_chunk_map_t);
+ arena_run_t *run = (arena_run_t *)((uintptr_t)chunk +
+ (uintptr_t)(pageind << PAGE_SHIFT));
+
+ ql_remove(&mapelms, mapelm, u.ql_link);
+ arena_run_dalloc(arena, run, false);
+ }
+}
+
+static void
+arena_purge(arena_t *arena)
+{
+ arena_chunk_t *chunk;
+ size_t npurgatory;
+#ifdef JEMALLOC_DEBUG
+ size_t ndirty = 0;
+
+ ql_foreach(chunk, &arena->chunks_dirty, link_dirty) {
+ assert(chunk->dirtied);
+ ndirty += chunk->ndirty;
+ }
+ assert(ndirty == arena->ndirty);
+#endif
+ assert(arena->ndirty > arena->npurgatory);
+ assert(arena->ndirty > chunk_npages);
+ assert((arena->nactive >> opt_lg_dirty_mult) < arena->ndirty);
+
+#ifdef JEMALLOC_STATS
+ arena->stats.npurge++;
+#endif
+
+ /*
+ * Compute the minimum number of pages that this thread should try to
+ * purge, and add the result to arena->npurgatory. This will keep
+ * multiple threads from racing to reduce ndirty below the threshold.
+ */
+ npurgatory = (arena->ndirty - arena->npurgatory) - (arena->nactive >>
+ opt_lg_dirty_mult);
+ arena->npurgatory += npurgatory;
+
+ while (npurgatory > 0) {
+ /* Get next chunk with dirty pages. */
+ chunk = ql_first(&arena->chunks_dirty);
+ if (chunk == NULL) {
+ /*
+ * This thread was unable to purge as many pages as
+ * originally intended, due to races with other threads
+ * that either did some of the purging work, or re-used
+ * dirty pages.
+ */
+ arena->npurgatory -= npurgatory;
+ return;
+ }
+ while (chunk->ndirty == 0) {
+ ql_remove(&arena->chunks_dirty, chunk, link_dirty);
+ chunk->dirtied = false;
+ chunk = ql_first(&arena->chunks_dirty);
+ if (chunk == NULL) {
+ /* Same logic as for above. */
+ arena->npurgatory -= npurgatory;
+ return;
+ }
+ }
+
+ if (chunk->ndirty > npurgatory) {
+ /*
+ * This thread will, at a minimum, purge all the dirty
+ * pages in chunk, so set npurgatory to reflect this
+ * thread's commitment to purge the pages. This tends
+ * to reduce the chances of the following scenario:
+ *
+ * 1) This thread sets arena->npurgatory such that
+ * (arena->ndirty - arena->npurgatory) is at the
+ * threshold.
+ * 2) This thread drops arena->lock.
+ * 3) Another thread causes one or more pages to be
+ * dirtied, and immediately determines that it must
+ * purge dirty pages.
+ *
+ * If this scenario *does* play out, that's okay,
+ * because all of the purging work being done really
+ * needs to happen.
+ */
+ arena->npurgatory += chunk->ndirty - npurgatory;
+ npurgatory = chunk->ndirty;
+ }
+
+ arena->npurgatory -= chunk->ndirty;
+ npurgatory -= chunk->ndirty;
+ arena_chunk_purge(arena, chunk);
+ }
+}
+
+static void
+arena_run_dalloc(arena_t *arena, arena_run_t *run, bool dirty)
+{
+ arena_chunk_t *chunk;
+ size_t size, run_ind, run_pages, flag_dirty;
+ arena_avail_tree_t *runs_avail;
+
+ chunk = (arena_chunk_t *)CHUNK_ADDR2BASE(run);
+ run_ind = (size_t)(((uintptr_t)run - (uintptr_t)chunk)
+ >> PAGE_SHIFT);
+ assert(run_ind >= arena_chunk_header_npages);
+ assert(run_ind < chunk_npages);
+ if ((chunk->map[run_ind].bits & CHUNK_MAP_LARGE) != 0)
+ size = chunk->map[run_ind].bits & ~PAGE_MASK;
+ else
+ size = run->bin->run_size;
+ run_pages = (size >> PAGE_SHIFT);
+ arena->nactive -= run_pages;
+
+ /*
+ * The run is dirty if the caller claims to have dirtied it, as well as
+ * if it was already dirty before being allocated.
+ */
+ if ((chunk->map[run_ind].bits & CHUNK_MAP_DIRTY) != 0)
+ dirty = true;
+ flag_dirty = dirty ? CHUNK_MAP_DIRTY : 0;
+ runs_avail = dirty ? &arena->runs_avail_dirty :
+ &arena->runs_avail_clean;
+
+ /* Mark pages as unallocated in the chunk map. */
+ if (dirty) {
+ chunk->map[run_ind].bits = size | flag_dirty;
+ chunk->map[run_ind+run_pages-1].bits = size | flag_dirty;
+
+ chunk->ndirty += run_pages;
+ arena->ndirty += run_pages;
+ } else {
+ chunk->map[run_ind].bits = size | (chunk->map[run_ind].bits &
+ CHUNK_MAP_ZEROED);
+ chunk->map[run_ind+run_pages-1].bits = size |
+ (chunk->map[run_ind+run_pages-1].bits & CHUNK_MAP_ZEROED);
+ }
+
+ /* Try to coalesce forward. */
+ if (run_ind + run_pages < chunk_npages &&
+ (chunk->map[run_ind+run_pages].bits & CHUNK_MAP_ALLOCATED) == 0 &&
+ (chunk->map[run_ind+run_pages].bits & CHUNK_MAP_DIRTY) ==
+ flag_dirty) {
+ size_t nrun_size = chunk->map[run_ind+run_pages].bits &
+ ~PAGE_MASK;
+
+ /*
+ * Remove successor from runs_avail; the coalesced run is
+ * inserted later.
+ */
+ arena_avail_tree_remove(runs_avail,
+ &chunk->map[run_ind+run_pages]);
+
+ size += nrun_size;
+ run_pages = size >> PAGE_SHIFT;
+
+ assert((chunk->map[run_ind+run_pages-1].bits & ~PAGE_MASK)
+ == nrun_size);
+ chunk->map[run_ind].bits = size | (chunk->map[run_ind].bits &
+ CHUNK_MAP_FLAGS_MASK);
+ chunk->map[run_ind+run_pages-1].bits = size |
+ (chunk->map[run_ind+run_pages-1].bits &
+ CHUNK_MAP_FLAGS_MASK);
+ }
+
+ /* Try to coalesce backward. */
+ if (run_ind > arena_chunk_header_npages && (chunk->map[run_ind-1].bits &
+ CHUNK_MAP_ALLOCATED) == 0 && (chunk->map[run_ind-1].bits &
+ CHUNK_MAP_DIRTY) == flag_dirty) {
+ size_t prun_size = chunk->map[run_ind-1].bits & ~PAGE_MASK;
+
+ run_ind -= prun_size >> PAGE_SHIFT;
+
+ /*
+ * Remove predecessor from runs_avail; the coalesced run is
+ * inserted later.
+ */
+ arena_avail_tree_remove(runs_avail, &chunk->map[run_ind]);
+
+ size += prun_size;
+ run_pages = size >> PAGE_SHIFT;
+
+ assert((chunk->map[run_ind].bits & ~PAGE_MASK) == prun_size);
+ chunk->map[run_ind].bits = size | (chunk->map[run_ind].bits &
+ CHUNK_MAP_FLAGS_MASK);
+ chunk->map[run_ind+run_pages-1].bits = size |
+ (chunk->map[run_ind+run_pages-1].bits &
+ CHUNK_MAP_FLAGS_MASK);
+ }
+
+ /* Insert into runs_avail, now that coalescing is complete. */
+ arena_avail_tree_insert(runs_avail, &chunk->map[run_ind]);
+
+ /*
+ * Deallocate chunk if it is now completely unused. The bit
+ * manipulation checks whether the first run is unallocated and extends
+ * to the end of the chunk.
+ */
+ if ((chunk->map[arena_chunk_header_npages].bits & (~PAGE_MASK |
+ CHUNK_MAP_ALLOCATED)) == arena_maxclass)
+ arena_chunk_dealloc(arena, chunk);
+
+ /*
+ * It is okay to do dirty page processing even if the chunk was
+ * deallocated above, since in that case it is the spare. Waiting
+ * until after possible chunk deallocation to do dirty processing
+ * allows for an old spare to be fully deallocated, thus decreasing the
+ * chances of spuriously crossing the dirty page purging threshold.
+ */
+ if (dirty) {
+ if (chunk->dirtied == false) {
+ ql_tail_insert(&arena->chunks_dirty, chunk, link_dirty);
+ chunk->dirtied = true;
+ }
+ arena_maybe_purge(arena);
+ }
+}
+
+static void
+arena_run_trim_head(arena_t *arena, arena_chunk_t *chunk, arena_run_t *run,
+ size_t oldsize, size_t newsize)
+{
+ size_t pageind = ((uintptr_t)run - (uintptr_t)chunk) >> PAGE_SHIFT;
+ size_t head_npages = (oldsize - newsize) >> PAGE_SHIFT;
+ size_t flags = chunk->map[pageind].bits & CHUNK_MAP_FLAGS_MASK;
+
+ assert(oldsize > newsize);
+
+ /*
+ * Update the chunk map so that arena_run_dalloc() can treat the
+ * leading run as separately allocated.
+ */
+ assert(chunk->map[pageind].bits & CHUNK_MAP_LARGE);
+ assert(chunk->map[pageind].bits & CHUNK_MAP_ALLOCATED);
+ chunk->map[pageind].bits = (oldsize - newsize) | flags;
+ chunk->map[pageind+head_npages].bits = newsize | flags;
+
+ arena_run_dalloc(arena, run, false);
+}
+
+static void
+arena_run_trim_tail(arena_t *arena, arena_chunk_t *chunk, arena_run_t *run,
+ size_t oldsize, size_t newsize, bool dirty)
+{
+ size_t pageind = ((uintptr_t)run - (uintptr_t)chunk) >> PAGE_SHIFT;
+ size_t npages = newsize >> PAGE_SHIFT;
+ size_t flags = chunk->map[pageind].bits & CHUNK_MAP_FLAGS_MASK;
+
+ assert(oldsize > newsize);
+
+ /*
+ * Update the chunk map so that arena_run_dalloc() can treat the
+ * trailing run as separately allocated.
+ */
+ assert(chunk->map[pageind].bits & CHUNK_MAP_LARGE);
+ assert(chunk->map[pageind].bits & CHUNK_MAP_ALLOCATED);
+ chunk->map[pageind].bits = newsize | flags;
+ chunk->map[pageind+npages-1].bits = newsize | flags;
+ chunk->map[pageind+npages].bits = (oldsize - newsize) | flags;
+
+ arena_run_dalloc(arena, (arena_run_t *)((uintptr_t)run + newsize),
+ dirty);
+}
+
+static arena_run_t *
+arena_bin_nonfull_run_get(arena_t *arena, arena_bin_t *bin)
+{
+ arena_chunk_map_t *mapelm;
+ arena_run_t *run;
+
+ /* Look for a usable run. */
+ mapelm = arena_run_tree_first(&bin->runs);
+ if (mapelm != NULL) {
+ arena_chunk_t *chunk;
+ size_t pageind;
+
+ /* run is guaranteed to have available space. */
+ arena_run_tree_remove(&bin->runs, mapelm);
+
+ chunk = (arena_chunk_t *)CHUNK_ADDR2BASE(mapelm);
+ pageind = (((uintptr_t)mapelm - (uintptr_t)chunk->map) /
+ sizeof(arena_chunk_map_t));
+ run = (arena_run_t *)((uintptr_t)chunk + (uintptr_t)((pageind -
+ (mapelm->bits >> PAGE_SHIFT))
+ << PAGE_SHIFT));
+#ifdef JEMALLOC_STATS
+ bin->stats.reruns++;
+#endif
+ return (run);
+ }
+ /* No existing runs have any space available. */
+
+ /* Allocate a new run. */
+ malloc_mutex_unlock(&bin->lock);
+ /******************************/
+ malloc_mutex_lock(&arena->lock);
+ run = arena_run_alloc(arena, bin->run_size, false, false);
+ if (run != NULL) {
+ /* Initialize run internals. */
+ run->bin = bin;
+ run->avail = NULL;
+ run->next = (void *)(((uintptr_t)run) +
+ (uintptr_t)bin->reg0_offset);
+ run->nfree = bin->nregs;
+#ifdef JEMALLOC_DEBUG
+ run->magic = ARENA_RUN_MAGIC;
+#endif
+ }
+ malloc_mutex_unlock(&arena->lock);
+ /********************************/
+ malloc_mutex_lock(&bin->lock);
+ if (run != NULL) {
+#ifdef JEMALLOC_STATS
+ bin->stats.nruns++;
+ bin->stats.curruns++;
+ if (bin->stats.curruns > bin->stats.highruns)
+ bin->stats.highruns = bin->stats.curruns;
+#endif
+ return (run);
+ }
+
+ /*
+ * arena_run_alloc() failed, but another thread may have made
+ * sufficient memory available while this one dopped bin->lock above,
+ * so search one more time.
+ */
+ mapelm = arena_run_tree_first(&bin->runs);
+ if (mapelm != NULL) {
+ arena_chunk_t *chunk;
+ size_t pageind;
+
+ /* run is guaranteed to have available space. */
+ arena_run_tree_remove(&bin->runs, mapelm);
+
+ chunk = (arena_chunk_t *)CHUNK_ADDR2BASE(mapelm);
+ pageind = (((uintptr_t)mapelm - (uintptr_t)chunk->map) /
+ sizeof(arena_chunk_map_t));
+ run = (arena_run_t *)((uintptr_t)chunk + (uintptr_t)((pageind -
+ (mapelm->bits >> PAGE_SHIFT))
+ << PAGE_SHIFT));
+#ifdef JEMALLOC_STATS
+ bin->stats.reruns++;
+#endif
+ return (run);
+ }
+
+ return (NULL);
+}
+
+/* Re-fill bin->runcur, then call arena_run_reg_alloc(). */
+static void *
+arena_bin_malloc_hard(arena_t *arena, arena_bin_t *bin)
+{
+ void *ret;
+ arena_run_t *run;
+
+ bin->runcur = NULL;
+ run = arena_bin_nonfull_run_get(arena, bin);
+ if (bin->runcur != NULL && bin->runcur->nfree > 0) {
+ /*
+ * Another thread updated runcur while this one ran without the
+ * bin lock in arena_bin_nonfull_run_get().
+ */
+ assert(bin->runcur->magic == ARENA_RUN_MAGIC);
+ assert(bin->runcur->nfree > 0);
+ ret = arena_run_reg_alloc(bin->runcur, bin);
+ if (run != NULL) {
+ malloc_mutex_unlock(&bin->lock);
+ malloc_mutex_lock(&arena->lock);
+ arena_run_dalloc(arena, run, false);
+ malloc_mutex_unlock(&arena->lock);
+ malloc_mutex_lock(&bin->lock);
+ }
+ return (ret);
+ }
+
+ if (run == NULL)
+ return (NULL);
+
+ bin->runcur = run;
+
+ assert(bin->runcur->magic == ARENA_RUN_MAGIC);
+ assert(bin->runcur->nfree > 0);
+
+ return (arena_run_reg_alloc(bin->runcur, bin));
+}
+
+#ifdef JEMALLOC_PROF
+void
+arena_prof_accum(arena_t *arena, uint64_t accumbytes)
+{
+
+ if (prof_interval != 0) {
+ arena->prof_accumbytes += accumbytes;
+ if (arena->prof_accumbytes >= prof_interval) {
+ prof_idump();
+ arena->prof_accumbytes -= prof_interval;
+ }
+ }
+}
+#endif
+
+#ifdef JEMALLOC_TCACHE
+void
+arena_tcache_fill_small(arena_t *arena, tcache_bin_t *tbin, size_t binind
+# ifdef JEMALLOC_PROF
+ , uint64_t prof_accumbytes
+# endif
+ )
+{
+ unsigned i, nfill;
+ arena_bin_t *bin;
+ arena_run_t *run;
+ void *ptr;
+
+ assert(tbin->ncached == 0);
+
+#ifdef JEMALLOC_PROF
+ malloc_mutex_lock(&arena->lock);
+ arena_prof_accum(arena, prof_accumbytes);
+ malloc_mutex_unlock(&arena->lock);
+#endif
+ bin = &arena->bins[binind];
+ malloc_mutex_lock(&bin->lock);
+ for (i = 0, nfill = (tbin->ncached_max >> 1); i < nfill; i++) {
+ if ((run = bin->runcur) != NULL && run->nfree > 0)
+ ptr = arena_run_reg_alloc(run, bin);
+ else
+ ptr = arena_bin_malloc_hard(arena, bin);
+ if (ptr == NULL)
+ break;
+ *(void **)ptr = tbin->avail;
+ tbin->avail = ptr;
+ }
+#ifdef JEMALLOC_STATS
+ bin->stats.allocated += (i - tbin->ncached) * bin->reg_size;
+ bin->stats.nmalloc += i;
+ bin->stats.nrequests += tbin->tstats.nrequests;
+ bin->stats.nfills++;
+ tbin->tstats.nrequests = 0;
+#endif
+ malloc_mutex_unlock(&bin->lock);
+ tbin->ncached = i;
+ if (tbin->ncached > tbin->high_water)
+ tbin->high_water = tbin->ncached;
+}
+#endif
+
+/*
+ * Calculate bin->run_size such that it meets the following constraints:
+ *
+ * *) bin->run_size >= min_run_size
+ * *) bin->run_size <= arena_maxclass
+ * *) run header overhead <= RUN_MAX_OVRHD (or header overhead relaxed).
+ * *) run header size < PAGE_SIZE
+ *
+ * bin->nregs and bin->reg0_offset are also calculated here, since these
+ * settings are all interdependent.
+ */
+static size_t
+arena_bin_run_size_calc(arena_bin_t *bin, size_t min_run_size)
+{
+ size_t try_run_size, good_run_size;
+ uint32_t try_nregs, good_nregs;
+ uint32_t try_hdr_size, good_hdr_size;
+#ifdef JEMALLOC_PROF
+ uint32_t try_cnt0_offset, good_cnt0_offset;
+#endif
+ uint32_t try_reg0_offset, good_reg0_offset;
+
+ assert(min_run_size >= PAGE_SIZE);
+ assert(min_run_size <= arena_maxclass);
+
+ /*
+ * Calculate known-valid settings before entering the run_size
+ * expansion loop, so that the first part of the loop always copies
+ * valid settings.
+ *
+ * The do..while loop iteratively reduces the number of regions until
+ * the run header and the regions no longer overlap. A closed formula
+ * would be quite messy, since there is an interdependency between the
+ * header's mask length and the number of regions.
+ */
+ try_run_size = min_run_size;
+ try_nregs = ((try_run_size - sizeof(arena_run_t)) / bin->reg_size)
+ + 1; /* Counter-act try_nregs-- in loop. */
+ do {
+ try_nregs--;
+ try_hdr_size = sizeof(arena_run_t);
+#ifdef JEMALLOC_PROF
+ if (opt_prof && prof_promote == false) {
+ /* Pad to a quantum boundary. */
+ try_hdr_size = QUANTUM_CEILING(try_hdr_size);
+ try_cnt0_offset = try_hdr_size;
+ /* Add space for one (prof_thr_cnt_t *) per region. */
+ try_hdr_size += try_nregs * sizeof(prof_thr_cnt_t *);
+ } else
+ try_cnt0_offset = 0;
+#endif
+ try_reg0_offset = try_run_size - (try_nregs * bin->reg_size);
+ } while (try_hdr_size > try_reg0_offset);
+
+ /* run_size expansion loop. */
+ do {
+ /*
+ * Copy valid settings before trying more aggressive settings.
+ */
+ good_run_size = try_run_size;
+ good_nregs = try_nregs;
+ good_hdr_size = try_hdr_size;
+#ifdef JEMALLOC_PROF
+ good_cnt0_offset = try_cnt0_offset;
+#endif
+ good_reg0_offset = try_reg0_offset;
+
+ /* Try more aggressive settings. */
+ try_run_size += PAGE_SIZE;
+ try_nregs = ((try_run_size - sizeof(arena_run_t)) /
+ bin->reg_size) + 1; /* Counter-act try_nregs-- in loop. */
+ do {
+ try_nregs--;
+ try_hdr_size = sizeof(arena_run_t);
+#ifdef JEMALLOC_PROF
+ if (opt_prof && prof_promote == false) {
+ /* Pad to a quantum boundary. */
+ try_hdr_size = QUANTUM_CEILING(try_hdr_size);
+ try_cnt0_offset = try_hdr_size;
+ /*
+ * Add space for one (prof_thr_cnt_t *) per
+ * region.
+ */
+ try_hdr_size += try_nregs *
+ sizeof(prof_thr_cnt_t *);
+ }
+#endif
+ try_reg0_offset = try_run_size - (try_nregs *
+ bin->reg_size);
+ } while (try_hdr_size > try_reg0_offset);
+ } while (try_run_size <= arena_maxclass
+ && try_run_size <= arena_maxclass
+ && RUN_MAX_OVRHD * (bin->reg_size << 3) > RUN_MAX_OVRHD_RELAX
+ && (try_reg0_offset << RUN_BFP) > RUN_MAX_OVRHD * try_run_size
+ && try_hdr_size < PAGE_SIZE);
+
+ assert(good_hdr_size <= good_reg0_offset);
+
+ /* Copy final settings. */
+ bin->run_size = good_run_size;
+ bin->nregs = good_nregs;
+#ifdef JEMALLOC_PROF
+ bin->cnt0_offset = good_cnt0_offset;
+#endif
+ bin->reg0_offset = good_reg0_offset;
+
+ return (good_run_size);
+}
+
+void *
+arena_malloc_small(arena_t *arena, size_t size, bool zero)
+{
+ void *ret;
+ arena_bin_t *bin;
+ arena_run_t *run;
+ size_t binind;
+
+ binind = small_size2bin[size];
+ assert(binind < nbins);
+ bin = &arena->bins[binind];
+ size = bin->reg_size;
+
+ malloc_mutex_lock(&bin->lock);
+ if ((run = bin->runcur) != NULL && run->nfree > 0)
+ ret = arena_run_reg_alloc(run, bin);
+ else
+ ret = arena_bin_malloc_hard(arena, bin);
+
+ if (ret == NULL) {
+ malloc_mutex_unlock(&bin->lock);
+ return (NULL);
+ }
+
+#ifdef JEMALLOC_STATS
+ bin->stats.allocated += size;
+ bin->stats.nmalloc++;
+ bin->stats.nrequests++;
+#endif
+ malloc_mutex_unlock(&bin->lock);
+#ifdef JEMALLOC_PROF
+ if (isthreaded == false) {
+ malloc_mutex_lock(&arena->lock);
+ arena_prof_accum(arena, size);
+ malloc_mutex_unlock(&arena->lock);
+ }
+#endif
+
+ if (zero == false) {
+#ifdef JEMALLOC_FILL
+ if (opt_junk)
+ memset(ret, 0xa5, size);
+ else if (opt_zero)
+ memset(ret, 0, size);
+#endif
+ } else
+ memset(ret, 0, size);
+
+ return (ret);
+}
+
+void *
+arena_malloc_large(arena_t *arena, size_t size, bool zero)
+{
+ void *ret;
+
+ /* Large allocation. */
+ size = PAGE_CEILING(size);
+ malloc_mutex_lock(&arena->lock);
+ ret = (void *)arena_run_alloc(arena, size, true, zero);
+ if (ret == NULL) {
+ malloc_mutex_unlock(&arena->lock);
+ return (NULL);
+ }
+#ifdef JEMALLOC_STATS
+ arena->stats.nmalloc_large++;
+ arena->stats.nrequests_large++;
+ arena->stats.allocated_large += size;
+ arena->stats.lstats[(size >> PAGE_SHIFT) - 1].nmalloc++;
+ arena->stats.lstats[(size >> PAGE_SHIFT) - 1].nrequests++;
+ arena->stats.lstats[(size >> PAGE_SHIFT) - 1].curruns++;
+ if (arena->stats.lstats[(size >> PAGE_SHIFT) - 1].curruns >
+ arena->stats.lstats[(size >> PAGE_SHIFT) - 1].highruns) {
+ arena->stats.lstats[(size >> PAGE_SHIFT) - 1].highruns =
+ arena->stats.lstats[(size >> PAGE_SHIFT) - 1].curruns;
+ }
+#endif
+#ifdef JEMALLOC_PROF
+ arena_prof_accum(arena, size);
+#endif
+ malloc_mutex_unlock(&arena->lock);
+
+ if (zero == false) {
+#ifdef JEMALLOC_FILL
+ if (opt_junk)
+ memset(ret, 0xa5, size);
+ else if (opt_zero)
+ memset(ret, 0, size);
+#endif
+ }
+
+ return (ret);
+}
+
+void *
+arena_malloc(size_t size, bool zero)
+{
+
+ assert(size != 0);
+ assert(QUANTUM_CEILING(size) <= arena_maxclass);
+
+ if (size <= small_maxclass) {
+#ifdef JEMALLOC_TCACHE
+ tcache_t *tcache;
+
+ if ((tcache = tcache_get()) != NULL)
+ return (tcache_alloc_small(tcache, size, zero));
+ else
+
+#endif
+ return (arena_malloc_small(choose_arena(), size, zero));
+ } else {
+#ifdef JEMALLOC_TCACHE
+ if (size <= tcache_maxclass) {
+ tcache_t *tcache;
+
+ if ((tcache = tcache_get()) != NULL)
+ return (tcache_alloc_large(tcache, size, zero));
+ else {
+ return (arena_malloc_large(choose_arena(),
+ size, zero));
+ }
+ } else
+#endif
+ return (arena_malloc_large(choose_arena(), size, zero));
+ }
+}
+
+/* Only handles large allocations that require more than page alignment. */
+void *
+arena_palloc(arena_t *arena, size_t alignment, size_t size, size_t alloc_size)
+{
+ void *ret;
+ size_t offset;
+ arena_chunk_t *chunk;
+
+ assert((size & PAGE_MASK) == 0);
+ assert((alignment & PAGE_MASK) == 0);
+
+ malloc_mutex_lock(&arena->lock);
+ ret = (void *)arena_run_alloc(arena, alloc_size, true, false);
+ if (ret == NULL) {
+ malloc_mutex_unlock(&arena->lock);
+ return (NULL);
+ }
+
+ chunk = (arena_chunk_t *)CHUNK_ADDR2BASE(ret);
+
+ offset = (uintptr_t)ret & (alignment - 1);
+ assert((offset & PAGE_MASK) == 0);
+ assert(offset < alloc_size);
+ if (offset == 0)
+ arena_run_trim_tail(arena, chunk, ret, alloc_size, size, false);
+ else {
+ size_t leadsize, trailsize;
+
+ leadsize = alignment - offset;
+ if (leadsize > 0) {
+ arena_run_trim_head(arena, chunk, ret, alloc_size,
+ alloc_size - leadsize);
+ ret = (void *)((uintptr_t)ret + leadsize);
+ }
+
+ trailsize = alloc_size - leadsize - size;
+ if (trailsize != 0) {
+ /* Trim trailing space. */
+ assert(trailsize < alloc_size);
+ arena_run_trim_tail(arena, chunk, ret, size + trailsize,
+ size, false);
+ }
+ }
+
+#ifdef JEMALLOC_STATS
+ arena->stats.nmalloc_large++;
+ arena->stats.nrequests_large++;
+ arena->stats.allocated_large += size;
+ arena->stats.lstats[(size >> PAGE_SHIFT) - 1].nmalloc++;
+ arena->stats.lstats[(size >> PAGE_SHIFT) - 1].nrequests++;
+ arena->stats.lstats[(size >> PAGE_SHIFT) - 1].curruns++;
+ if (arena->stats.lstats[(size >> PAGE_SHIFT) - 1].curruns >
+ arena->stats.lstats[(size >> PAGE_SHIFT) - 1].highruns) {
+ arena->stats.lstats[(size >> PAGE_SHIFT) - 1].highruns =
+ arena->stats.lstats[(size >> PAGE_SHIFT) - 1].curruns;
+ }
+#endif
+ malloc_mutex_unlock(&arena->lock);
+
+#ifdef JEMALLOC_FILL
+ if (opt_junk)
+ memset(ret, 0xa5, size);
+ else if (opt_zero)
+ memset(ret, 0, size);
+#endif
+ return (ret);
+}
+
+/* Return the size of the allocation pointed to by ptr. */
+size_t
+arena_salloc(const void *ptr)
+{
+ size_t ret;
+ arena_chunk_t *chunk;
+ size_t pageind, mapbits;
+
+ assert(ptr != NULL);
+ assert(CHUNK_ADDR2BASE(ptr) != ptr);
+
+ chunk = (arena_chunk_t *)CHUNK_ADDR2BASE(ptr);
+ pageind = (((uintptr_t)ptr - (uintptr_t)chunk) >> PAGE_SHIFT);
+ mapbits = chunk->map[pageind].bits;
+ assert((mapbits & CHUNK_MAP_ALLOCATED) != 0);
+ if ((mapbits & CHUNK_MAP_LARGE) == 0) {
+ arena_run_t *run = (arena_run_t *)((uintptr_t)chunk +
+ (uintptr_t)((pageind - (mapbits >> PAGE_SHIFT)) <<
+ PAGE_SHIFT));
+ assert(run->magic == ARENA_RUN_MAGIC);
+ assert(((uintptr_t)ptr - ((uintptr_t)run +
+ (uintptr_t)run->bin->reg0_offset)) % run->bin->reg_size ==
+ 0);
+ ret = run->bin->reg_size;
+ } else {
+ assert(((uintptr_t)ptr & PAGE_MASK) == 0);
+ ret = mapbits & ~PAGE_MASK;
+ assert(ret != 0);
+ }
+
+ return (ret);
+}
+
+#ifdef JEMALLOC_PROF
+void
+arena_prof_promoted(const void *ptr, size_t size)
+{
+ arena_chunk_t *chunk;
+ size_t pageind, binind;
+
+ assert(ptr != NULL);
+ assert(CHUNK_ADDR2BASE(ptr) != ptr);
+ assert(isalloc(ptr) == PAGE_SIZE);
+
+ chunk = (arena_chunk_t *)CHUNK_ADDR2BASE(ptr);
+ pageind = (((uintptr_t)ptr - (uintptr_t)chunk) >> PAGE_SHIFT);
+ binind = small_size2bin[size];
+ assert(binind < nbins);
+ chunk->map[pageind].bits = (chunk->map[pageind].bits &
+ ~CHUNK_MAP_CLASS_MASK) | (binind << CHUNK_MAP_CLASS_SHIFT);
+}
+
+size_t
+arena_salloc_demote(const void *ptr)
+{
+ size_t ret;
+ arena_chunk_t *chunk;
+ size_t pageind, mapbits;
+
+ assert(ptr != NULL);
+ assert(CHUNK_ADDR2BASE(ptr) != ptr);
+
+ chunk = (arena_chunk_t *)CHUNK_ADDR2BASE(ptr);
+ pageind = (((uintptr_t)ptr - (uintptr_t)chunk) >> PAGE_SHIFT);
+ mapbits = chunk->map[pageind].bits;
+ assert((mapbits & CHUNK_MAP_ALLOCATED) != 0);
+ if ((mapbits & CHUNK_MAP_LARGE) == 0) {
+ arena_run_t *run = (arena_run_t *)((uintptr_t)chunk +
+ (uintptr_t)((pageind - (mapbits >> PAGE_SHIFT)) <<
+ PAGE_SHIFT));
+ assert(run->magic == ARENA_RUN_MAGIC);
+ assert(((uintptr_t)ptr - ((uintptr_t)run +
+ (uintptr_t)run->bin->reg0_offset)) % run->bin->reg_size ==
+ 0);
+ ret = run->bin->reg_size;
+ } else {
+ assert(((uintptr_t)ptr & PAGE_MASK) == 0);
+ ret = mapbits & ~PAGE_MASK;
+ if (prof_promote && ret == PAGE_SIZE && (mapbits &
+ CHUNK_MAP_CLASS_MASK) != CHUNK_MAP_CLASS_MASK) {
+ size_t binind = ((mapbits & CHUNK_MAP_CLASS_MASK) >>
+ CHUNK_MAP_CLASS_SHIFT);
+ assert(binind < nbins);
+ ret = chunk->arena->bins[binind].reg_size;
+ }
+ assert(ret != 0);
+ }
+
+ return (ret);
+}
+
+static inline unsigned
+arena_run_regind(arena_run_t *run, arena_bin_t *bin, const void *ptr,
+ size_t size)
+{
+ unsigned shift, diff, regind;
+
+ assert(run->magic == ARENA_RUN_MAGIC);
+
+ /*
+ * Avoid doing division with a variable divisor if possible. Using
+ * actual division here can reduce allocator throughput by over 20%!
+ */
+ diff = (unsigned)((uintptr_t)ptr - (uintptr_t)run - bin->reg0_offset);
+
+ /* Rescale (factor powers of 2 out of the numerator and denominator). */
+ shift = ffs(size) - 1;
+ diff >>= shift;
+ size >>= shift;
+
+ if (size == 1) {
+ /* The divisor was a power of 2. */
+ regind = diff;
+ } else {
+ /*
+ * To divide by a number D that is not a power of two we
+ * multiply by (2^21 / D) and then right shift by 21 positions.
+ *
+ * X / D
+ *
+ * becomes
+ *
+ * (X * size_invs[D - 3]) >> SIZE_INV_SHIFT
+ *
+ * We can omit the first three elements, because we never
+ * divide by 0, and 1 and 2 are both powers of two, which are
+ * handled above.
+ */
+#define SIZE_INV_SHIFT 21
+#define SIZE_INV(s) (((1U << SIZE_INV_SHIFT) / (s)) + 1)
+ static const unsigned size_invs[] = {
+ SIZE_INV(3),
+ SIZE_INV(4), SIZE_INV(5), SIZE_INV(6), SIZE_INV(7),
+ SIZE_INV(8), SIZE_INV(9), SIZE_INV(10), SIZE_INV(11),
+ SIZE_INV(12), SIZE_INV(13), SIZE_INV(14), SIZE_INV(15),
+ SIZE_INV(16), SIZE_INV(17), SIZE_INV(18), SIZE_INV(19),
+ SIZE_INV(20), SIZE_INV(21), SIZE_INV(22), SIZE_INV(23),
+ SIZE_INV(24), SIZE_INV(25), SIZE_INV(26), SIZE_INV(27),
+ SIZE_INV(28), SIZE_INV(29), SIZE_INV(30), SIZE_INV(31)
+ };
+
+ if (size <= ((sizeof(size_invs) / sizeof(unsigned)) + 2))
+ regind = (diff * size_invs[size - 3]) >> SIZE_INV_SHIFT;
+ else
+ regind = diff / size;
+#undef SIZE_INV
+#undef SIZE_INV_SHIFT
+ }
+ assert(diff == regind * size);
+ assert(regind < bin->nregs);
+
+ return (regind);
+}
+
+prof_thr_cnt_t *
+arena_prof_cnt_get(const void *ptr)
+{
+ prof_thr_cnt_t *ret;
+ arena_chunk_t *chunk;
+ size_t pageind, mapbits;
+
+ assert(ptr != NULL);
+ assert(CHUNK_ADDR2BASE(ptr) != ptr);
+
+ chunk = (arena_chunk_t *)CHUNK_ADDR2BASE(ptr);
+ pageind = (((uintptr_t)ptr - (uintptr_t)chunk) >> PAGE_SHIFT);
+ mapbits = chunk->map[pageind].bits;
+ assert((mapbits & CHUNK_MAP_ALLOCATED) != 0);
+ if ((mapbits & CHUNK_MAP_LARGE) == 0) {
+ if (prof_promote)
+ ret = (prof_thr_cnt_t *)(uintptr_t)1U;
+ else {
+ arena_run_t *run = (arena_run_t *)((uintptr_t)chunk +
+ (uintptr_t)((pageind - (mapbits >> PAGE_SHIFT)) <<
+ PAGE_SHIFT));
+ arena_bin_t *bin = run->bin;
+ unsigned regind;
+
+ assert(run->magic == ARENA_RUN_MAGIC);
+ regind = arena_run_regind(run, bin, ptr, bin->reg_size);
+ ret = *(prof_thr_cnt_t **)((uintptr_t)run +
+ bin->cnt0_offset + (regind *
+ sizeof(prof_thr_cnt_t *)));
+ }
+ } else
+ ret = chunk->map[pageind].prof_cnt;
+
+ return (ret);
+}
+
+void
+arena_prof_cnt_set(const void *ptr, prof_thr_cnt_t *cnt)
+{
+ arena_chunk_t *chunk;
+ size_t pageind, mapbits;
+
+ assert(ptr != NULL);
+ assert(CHUNK_ADDR2BASE(ptr) != ptr);
+
+ chunk = (arena_chunk_t *)CHUNK_ADDR2BASE(ptr);
+ pageind = (((uintptr_t)ptr - (uintptr_t)chunk) >> PAGE_SHIFT);
+ mapbits = chunk->map[pageind].bits;
+ assert((mapbits & CHUNK_MAP_ALLOCATED) != 0);
+ if ((mapbits & CHUNK_MAP_LARGE) == 0) {
+ if (prof_promote == false) {
+ arena_run_t *run = (arena_run_t *)((uintptr_t)chunk +
+ (uintptr_t)((pageind - (mapbits >> PAGE_SHIFT)) <<
+ PAGE_SHIFT));
+ arena_bin_t *bin = run->bin;
+ unsigned regind;
+
+ assert(run->magic == ARENA_RUN_MAGIC);
+ regind = arena_run_regind(run, bin, ptr, bin->reg_size);
+
+ *((prof_thr_cnt_t **)((uintptr_t)run + bin->cnt0_offset
+ + (regind * sizeof(prof_thr_cnt_t *)))) = cnt;
+ } else
+ assert((uintptr_t)cnt == (uintptr_t)1U);
+ } else
+ chunk->map[pageind].prof_cnt = cnt;
+}
+#endif
+
+static void
+arena_dalloc_bin_run(arena_t *arena, arena_chunk_t *chunk, arena_run_t *run,
+ arena_bin_t *bin)
+{
+ size_t npages, run_ind, past;
+
+ /* Dissociate run from bin. */
+ if (run == bin->runcur)
+ bin->runcur = NULL;
+ else if (bin->nregs != 1) {
+ size_t run_pageind = (((uintptr_t)run - (uintptr_t)chunk)) >>
+ PAGE_SHIFT;
+ arena_chunk_map_t *run_mapelm = &chunk->map[run_pageind];
+ /*
+ * This block's conditional is necessary because if the run
+ * only contains one region, then it never gets inserted into
+ * the non-full runs tree.
+ */
+ arena_run_tree_remove(&bin->runs, run_mapelm);
+ }
+
+ malloc_mutex_unlock(&bin->lock);
+ /******************************/
+ npages = bin->run_size >> PAGE_SHIFT;
+ run_ind = (size_t)(((uintptr_t)run - (uintptr_t)chunk) >> PAGE_SHIFT);
+ past = (size_t)(((uintptr_t)run->next - (uintptr_t)1U -
+ (uintptr_t)chunk) >> PAGE_SHIFT) + 1;
+ malloc_mutex_lock(&arena->lock);
+
+ /*
+ * If the run was originally clean, and some pages were never touched,
+ * trim the clean pages before deallocating the dirty portion of the
+ * run.
+ */
+ if ((chunk->map[run_ind].bits & CHUNK_MAP_DIRTY) == 0 && past - run_ind
+ < npages) {
+ /*
+ * Trim clean pages. Convert to large run beforehand. Set the
+ * last map element first, in case this is a one-page run.
+ */
+ chunk->map[run_ind+npages-1].bits = CHUNK_MAP_LARGE |
+ (chunk->map[run_ind].bits & CHUNK_MAP_FLAGS_MASK);
+ chunk->map[run_ind].bits = bin->run_size | CHUNK_MAP_LARGE |
+ (chunk->map[run_ind].bits & CHUNK_MAP_FLAGS_MASK);
+ arena_run_trim_tail(arena, chunk, run, (npages << PAGE_SHIFT),
+ ((npages - (past - run_ind)) << PAGE_SHIFT), false);
+ npages = past - run_ind;
+ }
+#ifdef JEMALLOC_DEBUG
+ run->magic = 0;
+#endif
+ arena_run_dalloc(arena, run, true);
+ malloc_mutex_unlock(&arena->lock);
+ /****************************/
+ malloc_mutex_lock(&bin->lock);
+#ifdef JEMALLOC_STATS
+ bin->stats.curruns--;
+#endif
+}
+
+void
+arena_dalloc_bin(arena_t *arena, arena_chunk_t *chunk, void *ptr,
+ arena_chunk_map_t *mapelm)
+{
+ size_t pageind;
+ arena_run_t *run;
+ arena_bin_t *bin;
+#if (defined(JEMALLOC_FILL) || defined(JEMALLOC_STATS))
+ size_t size;
+#endif
+
+ pageind = (((uintptr_t)ptr - (uintptr_t)chunk) >> PAGE_SHIFT);
+ run = (arena_run_t *)((uintptr_t)chunk + (uintptr_t)((pageind -
+ (mapelm->bits >> PAGE_SHIFT)) << PAGE_SHIFT));
+ assert(run->magic == ARENA_RUN_MAGIC);
+ bin = run->bin;
+#if (defined(JEMALLOC_FILL) || defined(JEMALLOC_STATS))
+ size = bin->reg_size;
+#endif
+
+#ifdef JEMALLOC_FILL
+ if (opt_junk)
+ memset(ptr, 0x5a, size);
+#endif
+
+ arena_run_reg_dalloc(run, ptr);
+
+ if (run->nfree == bin->nregs)
+ arena_dalloc_bin_run(arena, chunk, run, bin);
+ else if (run->nfree == 1 && run != bin->runcur) {
+ /*
+ * Make sure that bin->runcur always refers to the lowest
+ * non-full run, if one exists.
+ */
+ if (bin->runcur == NULL)
+ bin->runcur = run;
+ else if ((uintptr_t)run < (uintptr_t)bin->runcur) {
+ /* Switch runcur. */
+ if (bin->runcur->nfree > 0) {
+ arena_chunk_t *runcur_chunk =
+ CHUNK_ADDR2BASE(bin->runcur);
+ size_t runcur_pageind =
+ (((uintptr_t)bin->runcur -
+ (uintptr_t)runcur_chunk)) >> PAGE_SHIFT;
+ arena_chunk_map_t *runcur_mapelm =
+ &runcur_chunk->map[runcur_pageind];
+
+ /* Insert runcur. */
+ arena_run_tree_insert(&bin->runs,
+ runcur_mapelm);
+ }
+ bin->runcur = run;
+ } else {
+ size_t run_pageind = (((uintptr_t)run -
+ (uintptr_t)chunk)) >> PAGE_SHIFT;
+ arena_chunk_map_t *run_mapelm =
+ &chunk->map[run_pageind];
+
+ assert(arena_run_tree_search(&bin->runs, run_mapelm) ==
+ NULL);
+ arena_run_tree_insert(&bin->runs, run_mapelm);
+ }
+ }
+
+#ifdef JEMALLOC_STATS
+ bin->stats.allocated -= size;
+ bin->stats.ndalloc++;
+#endif
+}
+
+#ifdef JEMALLOC_STATS
+void
+arena_stats_merge(arena_t *arena, size_t *nactive, size_t *ndirty,
+ arena_stats_t *astats, malloc_bin_stats_t *bstats,
+ malloc_large_stats_t *lstats)
+{
+ unsigned i;
+
+ malloc_mutex_lock(&arena->lock);
+ *nactive += arena->nactive;
+ *ndirty += arena->ndirty;
+
+ astats->mapped += arena->stats.mapped;
+ astats->npurge += arena->stats.npurge;
+ astats->nmadvise += arena->stats.nmadvise;
+ astats->purged += arena->stats.purged;
+ astats->allocated_large += arena->stats.allocated_large;
+ astats->nmalloc_large += arena->stats.nmalloc_large;
+ astats->ndalloc_large += arena->stats.ndalloc_large;
+ astats->nrequests_large += arena->stats.nrequests_large;
+
+ for (i = 0; i < nlclasses; i++) {
+ lstats[i].nmalloc += arena->stats.lstats[i].nmalloc;
+ lstats[i].ndalloc += arena->stats.lstats[i].ndalloc;
+ lstats[i].nrequests += arena->stats.lstats[i].nrequests;
+ lstats[i].highruns += arena->stats.lstats[i].highruns;
+ lstats[i].curruns += arena->stats.lstats[i].curruns;
+ }
+ malloc_mutex_unlock(&arena->lock);
+
+ for (i = 0; i < nbins; i++) {
+ arena_bin_t *bin = &arena->bins[i];
+
+ malloc_mutex_lock(&bin->lock);
+ bstats[i].allocated += bin->stats.allocated;
+ bstats[i].nmalloc += bin->stats.nmalloc;
+ bstats[i].ndalloc += bin->stats.ndalloc;
+ bstats[i].nrequests += bin->stats.nrequests;
+#ifdef JEMALLOC_TCACHE
+ bstats[i].nfills += bin->stats.nfills;
+ bstats[i].nflushes += bin->stats.nflushes;
+#endif
+ bstats[i].nruns += bin->stats.nruns;
+ bstats[i].reruns += bin->stats.reruns;
+ bstats[i].highruns += bin->stats.highruns;
+ bstats[i].curruns += bin->stats.curruns;
+ malloc_mutex_unlock(&bin->lock);
+ }
+}
+#endif
+
+void
+arena_dalloc_large(arena_t *arena, arena_chunk_t *chunk, void *ptr)
+{
+
+ /* Large allocation. */
+#ifdef JEMALLOC_FILL
+# ifndef JEMALLOC_STATS
+ if (opt_junk)
+# endif
+#endif
+ {
+#if (defined(JEMALLOC_FILL) || defined(JEMALLOC_STATS))
+ size_t pageind = ((uintptr_t)ptr - (uintptr_t)chunk) >>
+ PAGE_SHIFT;
+ size_t size = chunk->map[pageind].bits & ~PAGE_MASK;
+#endif
+
+#ifdef JEMALLOC_FILL
+# ifdef JEMALLOC_STATS
+ if (opt_junk)
+# endif
+ memset(ptr, 0x5a, size);
+#endif
+#ifdef JEMALLOC_STATS
+ arena->stats.ndalloc_large++;
+ arena->stats.allocated_large -= size;
+ arena->stats.lstats[(size >> PAGE_SHIFT) - 1].ndalloc++;
+ arena->stats.lstats[(size >> PAGE_SHIFT) - 1].curruns--;
+#endif
+ }
+
+ arena_run_dalloc(arena, (arena_run_t *)ptr, true);
+}
+
+static void
+arena_ralloc_large_shrink(arena_t *arena, arena_chunk_t *chunk, void *ptr,
+ size_t size, size_t oldsize)
+{
+
+ assert(size < oldsize);
+
+ /*
+ * Shrink the run, and make trailing pages available for other
+ * allocations.
+ */
+ malloc_mutex_lock(&arena->lock);
+ arena_run_trim_tail(arena, chunk, (arena_run_t *)ptr, oldsize, size,
+ true);
+#ifdef JEMALLOC_STATS
+ arena->stats.ndalloc_large++;
+ arena->stats.allocated_large -= oldsize;
+ arena->stats.lstats[(oldsize >> PAGE_SHIFT) - 1].ndalloc++;
+ arena->stats.lstats[(oldsize >> PAGE_SHIFT) - 1].curruns--;
+
+ arena->stats.nmalloc_large++;
+ arena->stats.nrequests_large++;
+ arena->stats.allocated_large += size;
+ arena->stats.lstats[(size >> PAGE_SHIFT) - 1].nmalloc++;
+ arena->stats.lstats[(size >> PAGE_SHIFT) - 1].nrequests++;
+ arena->stats.lstats[(size >> PAGE_SHIFT) - 1].curruns++;
+ if (arena->stats.lstats[(size >> PAGE_SHIFT) - 1].curruns >
+ arena->stats.lstats[(size >> PAGE_SHIFT) - 1].highruns) {
+ arena->stats.lstats[(size >> PAGE_SHIFT) - 1].highruns =
+ arena->stats.lstats[(size >> PAGE_SHIFT) - 1].curruns;
+ }
+#endif
+ malloc_mutex_unlock(&arena->lock);
+}
+
+static bool
+arena_ralloc_large_grow(arena_t *arena, arena_chunk_t *chunk, void *ptr,
+ size_t size, size_t oldsize)
+{
+ size_t pageind = ((uintptr_t)ptr - (uintptr_t)chunk) >> PAGE_SHIFT;
+ size_t npages = oldsize >> PAGE_SHIFT;
+
+ assert(oldsize == (chunk->map[pageind].bits & ~PAGE_MASK));
+
+ /* Try to extend the run. */
+ assert(size > oldsize);
+ malloc_mutex_lock(&arena->lock);
+ if (pageind + npages < chunk_npages && (chunk->map[pageind+npages].bits
+ & CHUNK_MAP_ALLOCATED) == 0 && (chunk->map[pageind+npages].bits &
+ ~PAGE_MASK) >= size - oldsize) {
+ /*
+ * The next run is available and sufficiently large. Split the
+ * following run, then merge the first part with the existing
+ * allocation.
+ */
+ arena_run_split(arena, (arena_run_t *)((uintptr_t)chunk +
+ ((pageind+npages) << PAGE_SHIFT)), size - oldsize, true,
+ false);
+
+ chunk->map[pageind].bits = size | CHUNK_MAP_LARGE |
+ CHUNK_MAP_ALLOCATED;
+ chunk->map[pageind+npages].bits = CHUNK_MAP_LARGE |
+ CHUNK_MAP_ALLOCATED;
+
+#ifdef JEMALLOC_STATS
+ arena->stats.ndalloc_large++;
+ arena->stats.allocated_large -= oldsize;
+ arena->stats.lstats[(oldsize >> PAGE_SHIFT) - 1].ndalloc++;
+ arena->stats.lstats[(oldsize >> PAGE_SHIFT) - 1].curruns--;
+
+ arena->stats.nmalloc_large++;
+ arena->stats.nrequests_large++;
+ arena->stats.allocated_large += size;
+ arena->stats.lstats[(size >> PAGE_SHIFT) - 1].nmalloc++;
+ arena->stats.lstats[(size >> PAGE_SHIFT) - 1].nrequests++;
+ arena->stats.lstats[(size >> PAGE_SHIFT) - 1].curruns++;
+ if (arena->stats.lstats[(size >> PAGE_SHIFT) - 1].curruns >
+ arena->stats.lstats[(size >> PAGE_SHIFT) - 1].highruns) {
+ arena->stats.lstats[(size >> PAGE_SHIFT) - 1].highruns =
+ arena->stats.lstats[(size >> PAGE_SHIFT) - 1].curruns;
+ }
+#endif
+ malloc_mutex_unlock(&arena->lock);
+ return (false);
+ }
+ malloc_mutex_unlock(&arena->lock);
+
+ return (true);
+}
+
+/*
+ * Try to resize a large allocation, in order to avoid copying. This will
+ * always fail if growing an object, and the following run is already in use.
+ */
+static bool
+arena_ralloc_large(void *ptr, size_t size, size_t oldsize)
+{
+ size_t psize;
+
+ psize = PAGE_CEILING(size);
+ if (psize == oldsize) {
+ /* Same size class. */
+#ifdef JEMALLOC_FILL
+ if (opt_junk && size < oldsize) {
+ memset((void *)((uintptr_t)ptr + size), 0x5a, oldsize -
+ size);
+ }
+#endif
+ return (false);
+ } else {
+ arena_chunk_t *chunk;
+ arena_t *arena;
+
+ chunk = (arena_chunk_t *)CHUNK_ADDR2BASE(ptr);
+ arena = chunk->arena;
+ assert(arena->magic == ARENA_MAGIC);
+
+ if (psize < oldsize) {
+#ifdef JEMALLOC_FILL
+ /* Fill before shrinking in order avoid a race. */
+ if (opt_junk) {
+ memset((void *)((uintptr_t)ptr + size), 0x5a,
+ oldsize - size);
+ }
+#endif
+ arena_ralloc_large_shrink(arena, chunk, ptr, psize,
+ oldsize);
+ return (false);
+ } else {
+ bool ret = arena_ralloc_large_grow(arena, chunk, ptr,
+ psize, oldsize);
+#ifdef JEMALLOC_FILL
+ if (ret == false && opt_zero) {
+ memset((void *)((uintptr_t)ptr + oldsize), 0,
+ size - oldsize);
+ }
+#endif
+ return (ret);
+ }
+ }
+}
+
+void *
+arena_ralloc(void *ptr, size_t size, size_t oldsize)
+{
+ void *ret;
+ size_t copysize;
+
+ /* Try to avoid moving the allocation. */
+ if (oldsize <= arena_maxclass) {
+ if (oldsize <= small_maxclass) {
+ if (size <= small_maxclass && small_size2bin[size] ==
+ small_size2bin[oldsize])
+ goto IN_PLACE;
+ } else {
+ assert(size <= arena_maxclass);
+ if (size > small_maxclass) {
+ if (arena_ralloc_large(ptr, size, oldsize) ==
+ false)
+ return (ptr);
+ }
+ }
+ }
+
+ /*
+ * If we get here, then size and oldsize are different enough that we
+ * need to move the object. In that case, fall back to allocating new
+ * space and copying.
+ */
+ ret = arena_malloc(size, false);
+ if (ret == NULL)
+ return (NULL);
+
+ /* Junk/zero-filling were already done by arena_malloc(). */
+ copysize = (size < oldsize) ? size : oldsize;
+ memcpy(ret, ptr, copysize);
+ idalloc(ptr);
+ return (ret);
+IN_PLACE:
+#ifdef JEMALLOC_FILL
+ if (opt_junk && size < oldsize)
+ memset((void *)((uintptr_t)ptr + size), 0x5a, oldsize - size);
+ else if (opt_zero && size > oldsize)
+ memset((void *)((uintptr_t)ptr + oldsize), 0, size - oldsize);
+#endif
+ return (ptr);
+}
+
+bool
+arena_new(arena_t *arena, unsigned ind)
+{
+ unsigned i;
+ arena_bin_t *bin;
+ size_t prev_run_size;
+
+ arena->ind = ind;
+
+ if (malloc_mutex_init(&arena->lock))
+ return (true);
+
+#ifdef JEMALLOC_STATS
+ memset(&arena->stats, 0, sizeof(arena_stats_t));
+ arena->stats.lstats = (malloc_large_stats_t *)base_alloc(nlclasses *
+ sizeof(malloc_large_stats_t));
+ if (arena->stats.lstats == NULL)
+ return (true);
+ memset(arena->stats.lstats, 0, nlclasses *
+ sizeof(malloc_large_stats_t));
+# ifdef JEMALLOC_TCACHE
+ ql_new(&arena->tcache_ql);
+# endif
+#endif
+
+#ifdef JEMALLOC_PROF
+ arena->prof_accumbytes = 0;
+#endif
+
+ /* Initialize chunks. */
+ ql_new(&arena->chunks_dirty);
+ arena->spare = NULL;
+
+ arena->nactive = 0;
+ arena->ndirty = 0;
+ arena->npurgatory = 0;
+
+ arena_avail_tree_new(&arena->runs_avail_clean);
+ arena_avail_tree_new(&arena->runs_avail_dirty);
+
+ /* Initialize bins. */
+ prev_run_size = PAGE_SIZE;
+
+ i = 0;
+#ifdef JEMALLOC_TINY
+ /* (2^n)-spaced tiny bins. */
+ for (; i < ntbins; i++) {
+ bin = &arena->bins[i];
+ if (malloc_mutex_init(&bin->lock))
+ return (true);
+ bin->runcur = NULL;
+ arena_run_tree_new(&bin->runs);
+
+ bin->reg_size = (1U << (LG_TINY_MIN + i));
+
+ prev_run_size = arena_bin_run_size_calc(bin, prev_run_size);
+
+#ifdef JEMALLOC_STATS
+ memset(&bin->stats, 0, sizeof(malloc_bin_stats_t));
+#endif
+ }
+#endif
+
+ /* Quantum-spaced bins. */
+ for (; i < ntbins + nqbins; i++) {
+ bin = &arena->bins[i];
+ if (malloc_mutex_init(&bin->lock))
+ return (true);
+ bin->runcur = NULL;
+ arena_run_tree_new(&bin->runs);
+
+ bin->reg_size = (i - ntbins + 1) << LG_QUANTUM;
+
+ prev_run_size = arena_bin_run_size_calc(bin, prev_run_size);
+
+#ifdef JEMALLOC_STATS
+ memset(&bin->stats, 0, sizeof(malloc_bin_stats_t));
+#endif
+ }
+
+ /* Cacheline-spaced bins. */
+ for (; i < ntbins + nqbins + ncbins; i++) {
+ bin = &arena->bins[i];
+ if (malloc_mutex_init(&bin->lock))
+ return (true);
+ bin->runcur = NULL;
+ arena_run_tree_new(&bin->runs);
+
+ bin->reg_size = cspace_min + ((i - (ntbins + nqbins)) <<
+ LG_CACHELINE);
+
+ prev_run_size = arena_bin_run_size_calc(bin, prev_run_size);
+
+#ifdef JEMALLOC_STATS
+ memset(&bin->stats, 0, sizeof(malloc_bin_stats_t));
+#endif
+ }
+
+ /* Subpage-spaced bins. */
+ for (; i < nbins; i++) {
+ bin = &arena->bins[i];
+ if (malloc_mutex_init(&bin->lock))
+ return (true);
+ bin->runcur = NULL;
+ arena_run_tree_new(&bin->runs);
+
+ bin->reg_size = sspace_min + ((i - (ntbins + nqbins + ncbins))
+ << LG_SUBPAGE);
+
+ prev_run_size = arena_bin_run_size_calc(bin, prev_run_size);
+
+#ifdef JEMALLOC_STATS
+ memset(&bin->stats, 0, sizeof(malloc_bin_stats_t));
+#endif
+ }
+
+#ifdef JEMALLOC_DEBUG
+ arena->magic = ARENA_MAGIC;
+#endif
+
+ return (false);
+}
+
+#ifdef JEMALLOC_TINY
+/* Compute the smallest power of 2 that is >= x. */
+static size_t
+pow2_ceil(size_t x)
+{
+
+ x--;
+ x |= x >> 1;
+ x |= x >> 2;
+ x |= x >> 4;
+ x |= x >> 8;
+ x |= x >> 16;
+#if (SIZEOF_PTR == 8)
+ x |= x >> 32;
+#endif
+ x++;
+ return (x);
+}
+#endif
+
+#ifdef JEMALLOC_DEBUG
+static void
+small_size2bin_validate(void)
+{
+ size_t i, size, binind;
+
+ assert(small_size2bin[0] == 0xffU);
+ i = 1;
+# ifdef JEMALLOC_TINY
+ /* Tiny. */
+ for (; i < (1U << LG_TINY_MIN); i++) {
+ size = pow2_ceil(1U << LG_TINY_MIN);
+ binind = ffs((int)(size >> (LG_TINY_MIN + 1)));
+ assert(small_size2bin[i] == binind);
+ }
+ for (; i < qspace_min; i++) {
+ size = pow2_ceil(i);
+ binind = ffs((int)(size >> (LG_TINY_MIN + 1)));
+ assert(small_size2bin[i] == binind);
+ }
+# endif
+ /* Quantum-spaced. */
+ for (; i <= qspace_max; i++) {
+ size = QUANTUM_CEILING(i);
+ binind = ntbins + (size >> LG_QUANTUM) - 1;
+ assert(small_size2bin[i] == binind);
+ }
+ /* Cacheline-spaced. */
+ for (; i <= cspace_max; i++) {
+ size = CACHELINE_CEILING(i);
+ binind = ntbins + nqbins + ((size - cspace_min) >>
+ LG_CACHELINE);
+ assert(small_size2bin[i] == binind);
+ }
+ /* Sub-page. */
+ for (; i <= sspace_max; i++) {
+ size = SUBPAGE_CEILING(i);
+ binind = ntbins + nqbins + ncbins + ((size - sspace_min)
+ >> LG_SUBPAGE);
+ assert(small_size2bin[i] == binind);
+ }
+}
+#endif
+
+static bool
+small_size2bin_init(void)
+{
+
+ if (opt_lg_qspace_max != LG_QSPACE_MAX_DEFAULT
+ || opt_lg_cspace_max != LG_CSPACE_MAX_DEFAULT
+ || sizeof(const_small_size2bin) != small_maxclass + 1)
+ return (small_size2bin_init_hard());
+
+ small_size2bin = const_small_size2bin;
+#ifdef JEMALLOC_DEBUG
+ assert(sizeof(const_small_size2bin) == small_maxclass + 1);
+ small_size2bin_validate();
+#endif
+ return (false);
+}
+
+static bool
+small_size2bin_init_hard(void)
+{
+ size_t i, size, binind;
+ uint8_t *custom_small_size2bin;
+
+ assert(opt_lg_qspace_max != LG_QSPACE_MAX_DEFAULT
+ || opt_lg_cspace_max != LG_CSPACE_MAX_DEFAULT
+ || sizeof(const_small_size2bin) != small_maxclass + 1);
+
+ custom_small_size2bin = (uint8_t *)base_alloc(small_maxclass + 1);
+ if (custom_small_size2bin == NULL)
+ return (true);
+
+ custom_small_size2bin[0] = 0xffU;
+ i = 1;
+#ifdef JEMALLOC_TINY
+ /* Tiny. */
+ for (; i < (1U << LG_TINY_MIN); i++) {
+ size = pow2_ceil(1U << LG_TINY_MIN);
+ binind = ffs((int)(size >> (LG_TINY_MIN + 1)));
+ custom_small_size2bin[i] = binind;
+ }
+ for (; i < qspace_min; i++) {
+ size = pow2_ceil(i);
+ binind = ffs((int)(size >> (LG_TINY_MIN + 1)));
+ custom_small_size2bin[i] = binind;
+ }
+#endif
+ /* Quantum-spaced. */
+ for (; i <= qspace_max; i++) {
+ size = QUANTUM_CEILING(i);
+ binind = ntbins + (size >> LG_QUANTUM) - 1;
+ custom_small_size2bin[i] = binind;
+ }
+ /* Cacheline-spaced. */
+ for (; i <= cspace_max; i++) {
+ size = CACHELINE_CEILING(i);
+ binind = ntbins + nqbins + ((size - cspace_min) >>
+ LG_CACHELINE);
+ custom_small_size2bin[i] = binind;
+ }
+ /* Sub-page. */
+ for (; i <= sspace_max; i++) {
+ size = SUBPAGE_CEILING(i);
+ binind = ntbins + nqbins + ncbins + ((size - sspace_min) >>
+ LG_SUBPAGE);
+ custom_small_size2bin[i] = binind;
+ }
+
+ small_size2bin = custom_small_size2bin;
+#ifdef JEMALLOC_DEBUG
+ small_size2bin_validate();
+#endif
+ return (false);
+}
+
+bool
+arena_boot(void)
+{
+ size_t header_size;
+
+ /* Set variables according to the value of opt_lg_[qc]space_max. */
+ qspace_max = (1U << opt_lg_qspace_max);
+ cspace_min = CACHELINE_CEILING(qspace_max);
+ if (cspace_min == qspace_max)
+ cspace_min += CACHELINE;
+ cspace_max = (1U << opt_lg_cspace_max);
+ sspace_min = SUBPAGE_CEILING(cspace_max);
+ if (sspace_min == cspace_max)
+ sspace_min += SUBPAGE;
+ assert(sspace_min < PAGE_SIZE);
+ sspace_max = PAGE_SIZE - SUBPAGE;
+
+#ifdef JEMALLOC_TINY
+ assert(LG_QUANTUM >= LG_TINY_MIN);
+#endif
+ assert(ntbins <= LG_QUANTUM);
+ nqbins = qspace_max >> LG_QUANTUM;
+ ncbins = ((cspace_max - cspace_min) >> LG_CACHELINE) + 1;
+ nsbins = ((sspace_max - sspace_min) >> LG_SUBPAGE) + 1;
+ nbins = ntbins + nqbins + ncbins + nsbins;
+
+ /*
+ * The small_size2bin lookup table uses uint8_t to encode each bin
+ * index, so we cannot support more than 256 small size classes. This
+ * limit is difficult to exceed (not even possible with 16B quantum and
+ * 4KiB pages), and such configurations are impractical, but
+ * nonetheless we need to protect against this case in order to avoid
+ * undefined behavior.
+ *
+ * Further constrain nbins to 255 if prof_promote is true, since all
+ * small size classes, plus a "not small" size class must be stored in
+ * 8 bits of arena_chunk_map_t's bits field.
+ */
+#ifdef JEMALLOC_PROF
+ if (opt_prof && prof_promote) {
+ if (nbins > 255) {
+ char line_buf[UMAX2S_BUFSIZE];
+ malloc_write("<jemalloc>: Too many small size classes (");
+ malloc_write(umax2s(nbins, 10, line_buf));
+ malloc_write(" > max 255)\n");
+ abort();
+ }
+ } else
+#endif
+ if (nbins > 256) {
+ char line_buf[UMAX2S_BUFSIZE];
+ malloc_write("<jemalloc>: Too many small size classes (");
+ malloc_write(umax2s(nbins, 10, line_buf));
+ malloc_write(" > max 256)\n");
+ abort();
+ }
+
+ if (small_size2bin_init())
+ return (true);
+
+ /*
+ * Compute the header size such that it is large enough to contain the
+ * page map.
+ */
+ header_size = sizeof(arena_chunk_t) +
+ (sizeof(arena_chunk_map_t) * (chunk_npages - 1));
+ arena_chunk_header_npages = (header_size >> PAGE_SHIFT) +
+ ((header_size & PAGE_MASK) != 0);
+ arena_maxclass = chunksize - (arena_chunk_header_npages << PAGE_SHIFT);
+
+ return (false);
+}
diff --git a/externals/jemalloc/base.c b/externals/jemalloc/base.c
new file mode 100644
index 00000000000..605197eaced
--- /dev/null
+++ b/externals/jemalloc/base.c
@@ -0,0 +1,106 @@
+#define JEMALLOC_BASE_C_
+#include "jemalloc/internal/jemalloc_internal.h"
+
+/******************************************************************************/
+/* Data. */
+
+malloc_mutex_t base_mtx;
+
+/*
+ * Current pages that are being used for internal memory allocations. These
+ * pages are carved up in cacheline-size quanta, so that there is no chance of
+ * false cache line sharing.
+ */
+static void *base_pages;
+static void *base_next_addr;
+static void *base_past_addr; /* Addr immediately past base_pages. */
+static extent_node_t *base_nodes;
+
+/******************************************************************************/
+/* Function prototypes for non-inline static functions. */
+
+static bool base_pages_alloc(size_t minsize);
+
+/******************************************************************************/
+
+static bool
+base_pages_alloc(size_t minsize)
+{
+ size_t csize;
+ bool zero;
+
+ assert(minsize != 0);
+ csize = CHUNK_CEILING(minsize);
+ zero = false;
+ base_pages = chunk_alloc(csize, &zero);
+ if (base_pages == NULL)
+ return (true);
+ base_next_addr = base_pages;
+ base_past_addr = (void *)((uintptr_t)base_pages + csize);
+
+ return (false);
+}
+
+void *
+base_alloc(size_t size)
+{
+ void *ret;
+ size_t csize;
+
+ /* Round size up to nearest multiple of the cacheline size. */
+ csize = CACHELINE_CEILING(size);
+
+ malloc_mutex_lock(&base_mtx);
+ /* Make sure there's enough space for the allocation. */
+ if ((uintptr_t)base_next_addr + csize > (uintptr_t)base_past_addr) {
+ if (base_pages_alloc(csize)) {
+ malloc_mutex_unlock(&base_mtx);
+ return (NULL);
+ }
+ }
+ /* Allocate. */
+ ret = base_next_addr;
+ base_next_addr = (void *)((uintptr_t)base_next_addr + csize);
+ malloc_mutex_unlock(&base_mtx);
+
+ return (ret);
+}
+
+extent_node_t *
+base_node_alloc(void)
+{
+ extent_node_t *ret;
+
+ malloc_mutex_lock(&base_mtx);
+ if (base_nodes != NULL) {
+ ret = base_nodes;
+ base_nodes = *(extent_node_t **)ret;
+ malloc_mutex_unlock(&base_mtx);
+ } else {
+ malloc_mutex_unlock(&base_mtx);
+ ret = (extent_node_t *)base_alloc(sizeof(extent_node_t));
+ }
+
+ return (ret);
+}
+
+void
+base_node_dealloc(extent_node_t *node)
+{
+
+ malloc_mutex_lock(&base_mtx);
+ *(extent_node_t **)node = base_nodes;
+ base_nodes = node;
+ malloc_mutex_unlock(&base_mtx);
+}
+
+bool
+base_boot(void)
+{
+
+ base_nodes = NULL;
+ if (malloc_mutex_init(&base_mtx))
+ return (true);
+
+ return (false);
+}
diff --git a/externals/jemalloc/chunk.c b/externals/jemalloc/chunk.c
new file mode 100644
index 00000000000..e6e3bcd195a
--- /dev/null
+++ b/externals/jemalloc/chunk.c
@@ -0,0 +1,150 @@
+#define JEMALLOC_CHUNK_C_
+#include "jemalloc/internal/jemalloc_internal.h"
+
+/******************************************************************************/
+/* Data. */
+
+size_t opt_lg_chunk = LG_CHUNK_DEFAULT;
+#ifdef JEMALLOC_SWAP
+bool opt_overcommit = true;
+#endif
+
+#if (defined(JEMALLOC_STATS) || defined(JEMALLOC_PROF))
+malloc_mutex_t chunks_mtx;
+chunk_stats_t stats_chunks;
+#endif
+
+/* Various chunk-related settings. */
+size_t chunksize;
+size_t chunksize_mask; /* (chunksize - 1). */
+size_t chunk_npages;
+size_t arena_chunk_header_npages;
+size_t arena_maxclass; /* Max size class for arenas. */
+
+/******************************************************************************/
+
+/*
+ * If the caller specifies (*zero == false), it is still possible to receive
+ * zeroed memory, in which case *zero is toggled to true. arena_chunk_alloc()
+ * takes advantage of this to avoid demanding zeroed chunks, but taking
+ * advantage of them if they are returned.
+ */
+void *
+chunk_alloc(size_t size, bool *zero)
+{
+ void *ret;
+
+ assert(size != 0);
+ assert((size & chunksize_mask) == 0);
+
+#ifdef JEMALLOC_SWAP
+ if (swap_enabled) {
+ ret = chunk_alloc_swap(size, zero);
+ if (ret != NULL)
+ goto RETURN;
+ }
+
+ if (swap_enabled == false || opt_overcommit) {
+#endif
+#ifdef JEMALLOC_DSS
+ ret = chunk_alloc_dss(size, zero);
+ if (ret != NULL)
+ goto RETURN;
+#endif
+ ret = chunk_alloc_mmap(size);
+ if (ret != NULL) {
+ *zero = true;
+ goto RETURN;
+ }
+#ifdef JEMALLOC_SWAP
+ }
+#endif
+
+ /* All strategies for allocation failed. */
+ ret = NULL;
+RETURN:
+#if (defined(JEMALLOC_STATS) || defined(JEMALLOC_PROF))
+ if (ret != NULL) {
+# ifdef JEMALLOC_PROF
+ bool udump;
+# endif
+ malloc_mutex_lock(&chunks_mtx);
+# ifdef JEMALLOC_STATS
+ stats_chunks.nchunks += (size / chunksize);
+# endif
+ stats_chunks.curchunks += (size / chunksize);
+ if (stats_chunks.curchunks > stats_chunks.highchunks) {
+ stats_chunks.highchunks = stats_chunks.curchunks;
+# ifdef JEMALLOC_PROF
+ udump = true;
+# endif
+ }
+# ifdef JEMALLOC_PROF
+ else
+ udump = false;
+# endif
+ malloc_mutex_unlock(&chunks_mtx);
+# ifdef JEMALLOC_PROF
+ if (opt_prof && opt_prof_udump && udump)
+ prof_udump();
+# endif
+ }
+#endif
+
+ assert(CHUNK_ADDR2BASE(ret) == ret);
+ return (ret);
+}
+
+void
+chunk_dealloc(void *chunk, size_t size)
+{
+
+ assert(chunk != NULL);
+ assert(CHUNK_ADDR2BASE(chunk) == chunk);
+ assert(size != 0);
+ assert((size & chunksize_mask) == 0);
+
+#if (defined(JEMALLOC_STATS) || defined(JEMALLOC_PROF))
+ malloc_mutex_lock(&chunks_mtx);
+ stats_chunks.curchunks -= (size / chunksize);
+ malloc_mutex_unlock(&chunks_mtx);
+#endif
+
+#ifdef JEMALLOC_SWAP
+ if (swap_enabled && chunk_dealloc_swap(chunk, size) == false)
+ return;
+#endif
+#ifdef JEMALLOC_DSS
+ if (chunk_dealloc_dss(chunk, size) == false)
+ return;
+#endif
+ chunk_dealloc_mmap(chunk, size);
+}
+
+bool
+chunk_boot(void)
+{
+
+ /* Set variables according to the value of opt_lg_chunk. */
+ chunksize = (1LU << opt_lg_chunk);
+ assert(chunksize >= PAGE_SIZE);
+ chunksize_mask = chunksize - 1;
+ chunk_npages = (chunksize >> PAGE_SHIFT);
+
+#if (defined(JEMALLOC_STATS) || defined(JEMALLOC_PROF))
+ if (malloc_mutex_init(&chunks_mtx))
+ return (true);
+ memset(&stats_chunks, 0, sizeof(chunk_stats_t));
+#endif
+
+#ifdef JEMALLOC_SWAP
+ if (chunk_swap_boot())
+ return (true);
+#endif
+#ifdef JEMALLOC_DSS
+ if (chunk_dss_boot())
+ return (true);
+#endif
+
+ return (false);
+}
diff --git a/externals/jemalloc/chunk_dss.c b/externals/jemalloc/chunk_dss.c
new file mode 100644
index 00000000000..d9bd63c3ac4
--- /dev/null
+++ b/externals/jemalloc/chunk_dss.c
@@ -0,0 +1,268 @@
+#define JEMALLOC_CHUNK_DSS_C_
+#include "jemalloc/internal/jemalloc_internal.h"
+#ifdef JEMALLOC_DSS
+/******************************************************************************/
+/* Data. */
+
+malloc_mutex_t dss_mtx;
+
+/* Base address of the DSS. */
+static void *dss_base;
+/* Current end of the DSS, or ((void *)-1) if the DSS is exhausted. */
+static void *dss_prev;
+/* Current upper limit on DSS addresses. */
+static void *dss_max;
+
+/*
+ * Trees of chunks that were previously allocated (trees differ only in node
+ * ordering). These are used when allocating chunks, in an attempt to re-use
+ * address space. Depending on function, different tree orderings are needed,
+ * which is why there are two trees with the same contents.
+ */
+static extent_tree_t dss_chunks_szad;
+static extent_tree_t dss_chunks_ad;
+
+/******************************************************************************/
+/* Function prototypes for non-inline static functions. */
+
+static void *chunk_recycle_dss(size_t size, bool *zero);
+static extent_node_t *chunk_dealloc_dss_record(void *chunk, size_t size);
+
+/******************************************************************************/
+
+static void *
+chunk_recycle_dss(size_t size, bool *zero)
+{
+ extent_node_t *node, key;
+
+ key.addr = NULL;
+ key.size = size;
+ malloc_mutex_lock(&dss_mtx);
+ node = extent_tree_szad_nsearch(&dss_chunks_szad, &key);
+ if (node != NULL) {
+ void *ret = node->addr;
+
+ /* Remove node from the tree. */
+ extent_tree_szad_remove(&dss_chunks_szad, node);
+ if (node->size == size) {
+ extent_tree_ad_remove(&dss_chunks_ad, node);
+ base_node_dealloc(node);
+ } else {
+ /*
+ * Insert the remainder of node's address range as a
+ * smaller chunk. Its position within dss_chunks_ad
+ * does not change.
+ */
+ assert(node->size > size);
+ node->addr = (void *)((uintptr_t)node->addr + size);
+ node->size -= size;
+ extent_tree_szad_insert(&dss_chunks_szad, node);
+ }
+ malloc_mutex_unlock(&dss_mtx);
+
+ if (*zero)
+ memset(ret, 0, size);
+ return (ret);
+ }
+ malloc_mutex_unlock(&dss_mtx);
+
+ return (NULL);
+}
+
+void *
+chunk_alloc_dss(size_t size, bool *zero)
+{
+ void *ret;
+
+ ret = chunk_recycle_dss(size, zero);
+ if (ret != NULL)
+ return (ret);
+
+ /*
+ * sbrk() uses a signed increment argument, so take care not to
+ * interpret a huge allocation request as a negative increment.
+ */
+ if ((intptr_t)size < 0)
+ return (NULL);
+
+ malloc_mutex_lock(&dss_mtx);
+ if (dss_prev != (void *)-1) {
+ intptr_t incr;
+
+ /*
+ * The loop is necessary to recover from races with other
+ * threads that are using the DSS for something other than
+ * malloc.
+ */
+ do {
+ /* Get the current end of the DSS. */
+ dss_max = sbrk(0);
+
+ /*
+ * Calculate how much padding is necessary to
+ * chunk-align the end of the DSS.
+ */
+ incr = (intptr_t)size
+ - (intptr_t)CHUNK_ADDR2OFFSET(dss_max);
+ if (incr == (intptr_t)size)
+ ret = dss_max;
+ else {
+ ret = (void *)((intptr_t)dss_max + incr);
+ incr += size;
+ }
+
+ dss_prev = sbrk(incr);
+ if (dss_prev == dss_max) {
+ /* Success. */
+ dss_max = (void *)((intptr_t)dss_prev + incr);
+ malloc_mutex_unlock(&dss_mtx);
+ *zero = true;
+ return (ret);
+ }
+ } while (dss_prev != (void *)-1);
+ }
+ malloc_mutex_unlock(&dss_mtx);
+
+ return (NULL);
+}
+
+static extent_node_t *
+chunk_dealloc_dss_record(void *chunk, size_t size)
+{
+ extent_node_t *xnode, *node, *prev, key;
+
+ xnode = NULL;
+ while (true) {
+ key.addr = (void *)((uintptr_t)chunk + size);
+ node = extent_tree_ad_nsearch(&dss_chunks_ad, &key);
+ /* Try to coalesce forward. */
+ if (node != NULL && node->addr == key.addr) {
+ /*
+ * Coalesce chunk with the following address range.
+ * This does not change the position within
+ * dss_chunks_ad, so only remove/insert from/into
+ * dss_chunks_szad.
+ */
+ extent_tree_szad_remove(&dss_chunks_szad, node);
+ node->addr = chunk;
+ node->size += size;
+ extent_tree_szad_insert(&dss_chunks_szad, node);
+ break;
+ } else if (xnode == NULL) {
+ /*
+ * It is possible that base_node_alloc() will cause a
+ * new base chunk to be allocated, so take care not to
+ * deadlock on dss_mtx, and recover if another thread
+ * deallocates an adjacent chunk while this one is busy
+ * allocating xnode.
+ */
+ malloc_mutex_unlock(&dss_mtx);
+ xnode = base_node_alloc();
+ malloc_mutex_lock(&dss_mtx);
+ if (xnode == NULL)
+ return (NULL);
+ } else {
+ /* Coalescing forward failed, so insert a new node. */
+ node = xnode;
+ xnode = NULL;
+ node->addr = chunk;
+ node->size = size;
+ extent_tree_ad_insert(&dss_chunks_ad, node);
+ extent_tree_szad_insert(&dss_chunks_szad, node);
+ break;
+ }
+ }
+ /* Discard xnode if it ended up unused do to a race. */
+ if (xnode != NULL)
+ base_node_dealloc(xnode);
+
+ /* Try to coalesce backward. */
+ prev = extent_tree_ad_prev(&dss_chunks_ad, node);
+ if (prev != NULL && (void *)((uintptr_t)prev->addr + prev->size) ==
+ chunk) {
+ /*
+ * Coalesce chunk with the previous address range. This does
+ * not change the position within dss_chunks_ad, so only
+ * remove/insert node from/into dss_chunks_szad.
+ */
+ extent_tree_szad_remove(&dss_chunks_szad, prev);
+ extent_tree_ad_remove(&dss_chunks_ad, prev);
+
+ extent_tree_szad_remove(&dss_chunks_szad, node);
+ node->addr = prev->addr;
+ node->size += prev->size;
+ extent_tree_szad_insert(&dss_chunks_szad, node);
+
+ base_node_dealloc(prev);
+ }
+
+ return (node);
+}
+
+bool
+chunk_dealloc_dss(void *chunk, size_t size)
+{
+ bool ret;
+
+ malloc_mutex_lock(&dss_mtx);
+ if ((uintptr_t)chunk >= (uintptr_t)dss_base
+ && (uintptr_t)chunk < (uintptr_t)dss_max) {
+ extent_node_t *node;
+
+ /* Try to coalesce with other unused chunks. */
+ node = chunk_dealloc_dss_record(chunk, size);
+ if (node != NULL) {
+ chunk = node->addr;
+ size = node->size;
+ }
+
+ /* Get the current end of the DSS. */
+ dss_max = sbrk(0);
+
+ /*
+ * Try to shrink the DSS if this chunk is at the end of the
+ * DSS. The sbrk() call here is subject to a race condition
+ * with threads that use brk(2) or sbrk(2) directly, but the
+ * alternative would be to leak memory for the sake of poorly
+ * designed multi-threaded programs.
+ */
+ if ((void *)((uintptr_t)chunk + size) == dss_max
+ && (dss_prev = sbrk(-(intptr_t)size)) == dss_max) {
+ /* Success. */
+ dss_max = (void *)((intptr_t)dss_prev - (intptr_t)size);
+
+ if (node != NULL) {
+ extent_tree_szad_remove(&dss_chunks_szad, node);
+ extent_tree_ad_remove(&dss_chunks_ad, node);
+ base_node_dealloc(node);
+ }
+ } else
+ madvise(chunk, size, MADV_DONTNEED);
+
+ ret = false;
+ goto RETURN;
+ }
+
+ ret = true;
+RETURN:
+ malloc_mutex_unlock(&dss_mtx);
+ return (ret);
+}
+
+bool
+chunk_dss_boot(void)
+{
+
+ if (malloc_mutex_init(&dss_mtx))
+ return (true);
+ dss_base = sbrk(0);
+ dss_prev = dss_base;
+ dss_max = dss_base;
+ extent_tree_szad_new(&dss_chunks_szad);
+ extent_tree_ad_new(&dss_chunks_ad);
+
+ return (false);
+}
+
+/******************************************************************************/
+#endif /* JEMALLOC_DSS */
diff --git a/externals/jemalloc/chunk_mmap.c b/externals/jemalloc/chunk_mmap.c
new file mode 100644
index 00000000000..8f0711384e3
--- /dev/null
+++ b/externals/jemalloc/chunk_mmap.c
@@ -0,0 +1,201 @@
+#define JEMALLOC_CHUNK_MMAP_C_
+#include "jemalloc/internal/jemalloc_internal.h"
+
+/******************************************************************************/
+/* Data. */
+
+/*
+ * Used by chunk_alloc_mmap() to decide whether to attempt the fast path and
+ * potentially avoid some system calls. We can get away without TLS here,
+ * since the state of mmap_unaligned only affects performance, rather than
+ * correct function.
+ */
+static
+#ifndef NO_TLS
+ __thread
+#endif
+ bool mmap_unaligned
+#ifndef NO_TLS
+ JEMALLOC_ATTR(tls_model("initial-exec"))
+#endif
+ ;
+
+/******************************************************************************/
+/* Function prototypes for non-inline static functions. */
+
+static void *pages_map(void *addr, size_t size);
+static void pages_unmap(void *addr, size_t size);
+static void *chunk_alloc_mmap_slow(size_t size, bool unaligned);
+
+/******************************************************************************/
+
+static void *
+pages_map(void *addr, size_t size)
+{
+ void *ret;
+
+ /*
+ * We don't use MAP_FIXED here, because it can cause the *replacement*
+ * of existing mappings, and we only want to create new mappings.
+ */
+ ret = mmap(addr, size, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANON,
+ -1, 0);
+ assert(ret != NULL);
+
+ if (ret == MAP_FAILED)
+ ret = NULL;
+ else if (addr != NULL && ret != addr) {
+ /*
+ * We succeeded in mapping memory, but not in the right place.
+ */
+ if (munmap(ret, size) == -1) {
+ char buf[STRERROR_BUF];
+
+ strerror_r(errno, buf, sizeof(buf));
+ malloc_write("<jemalloc>: Error in munmap(): ");
+ malloc_write(buf);
+ malloc_write("\n");
+ if (opt_abort)
+ abort();
+ }
+ ret = NULL;
+ }
+
+ assert(ret == NULL || (addr == NULL && ret != addr)
+ || (addr != NULL && ret == addr));
+ return (ret);
+}
+
+static void
+pages_unmap(void *addr, size_t size)
+{
+
+ if (munmap(addr, size) == -1) {
+ char buf[STRERROR_BUF];
+
+ strerror_r(errno, buf, sizeof(buf));
+ malloc_write("<jemalloc>: Error in munmap(): ");
+ malloc_write(buf);
+ malloc_write("\n");
+ if (opt_abort)
+ abort();
+ }
+}
+
+static void *
+chunk_alloc_mmap_slow(size_t size, bool unaligned)
+{
+ void *ret;
+ size_t offset;
+
+ /* Beware size_t wrap-around. */
+ if (size + chunksize <= size)
+ return (NULL);
+
+ ret = pages_map(NULL, size + chunksize);
+ if (ret == NULL)
+ return (NULL);
+
+ /* Clean up unneeded leading/trailing space. */
+ offset = CHUNK_ADDR2OFFSET(ret);
+ if (offset != 0) {
+ /* Note that mmap() returned an unaligned mapping. */
+ unaligned = true;
+
+ /* Leading space. */
+ pages_unmap(ret, chunksize - offset);
+
+ ret = (void *)((uintptr_t)ret +
+ (chunksize - offset));
+
+ /* Trailing space. */
+ pages_unmap((void *)((uintptr_t)ret + size),
+ offset);
+ } else {
+ /* Trailing space only. */
+ pages_unmap((void *)((uintptr_t)ret + size),
+ chunksize);
+ }
+
+ /*
+ * If mmap() returned an aligned mapping, reset mmap_unaligned so that
+ * the next chunk_alloc_mmap() execution tries the fast allocation
+ * method.
+ */
+ if (unaligned == false)
+ mmap_unaligned = false;
+
+ return (ret);
+}
+
+void *
+chunk_alloc_mmap(size_t size)
+{
+ void *ret;
+
+ /*
+ * Ideally, there would be a way to specify alignment to mmap() (like
+ * NetBSD has), but in the absence of such a feature, we have to work
+ * hard to efficiently create aligned mappings. The reliable, but
+ * slow method is to create a mapping that is over-sized, then trim the
+ * excess. However, that always results in at least one call to
+ * pages_unmap().
+ *
+ * A more optimistic approach is to try mapping precisely the right
+ * amount, then try to append another mapping if alignment is off. In
+ * practice, this works out well as long as the application is not
+ * interleaving mappings via direct mmap() calls. If we do run into a
+ * situation where there is an interleaved mapping and we are unable to
+ * extend an unaligned mapping, our best option is to switch to the
+ * slow method until mmap() returns another aligned mapping. This will
+ * tend to leave a gap in the memory map that is too small to cause
+ * later problems for the optimistic method.
+ *
+ * Another possible confounding factor is address space layout
+ * randomization (ASLR), which causes mmap(2) to disregard the
+ * requested address. mmap_unaligned tracks whether the previous
+ * chunk_alloc_mmap() execution received any unaligned or relocated
+ * mappings, and if so, the current execution will immediately fall
+ * back to the slow method. However, we keep track of whether the fast
+ * method would have succeeded, and if so, we make a note to try the
+ * fast method next time.
+ */
+
+ if (mmap_unaligned == false) {
+ size_t offset;
+
+ ret = pages_map(NULL, size);
+ if (ret == NULL)
+ return (NULL);
+
+ offset = CHUNK_ADDR2OFFSET(ret);
+ if (offset != 0) {
+ mmap_unaligned = true;
+ /* Try to extend chunk boundary. */
+ if (pages_map((void *)((uintptr_t)ret + size),
+ chunksize - offset) == NULL) {
+ /*
+ * Extension failed. Clean up, then revert to
+ * the reliable-but-expensive method.
+ */
+ pages_unmap(ret, size);
+ ret = chunk_alloc_mmap_slow(size, true);
+ } else {
+ /* Clean up unneeded leading space. */
+ pages_unmap(ret, chunksize - offset);
+ ret = (void *)((uintptr_t)ret + (chunksize -
+ offset));
+ }
+ }
+ } else
+ ret = chunk_alloc_mmap_slow(size, false);
+
+ return (ret);
+}
+
+void
+chunk_dealloc_mmap(void *chunk, size_t size)
+{
+
+ pages_unmap(chunk, size);
+}
diff --git a/externals/jemalloc/chunk_swap.c b/externals/jemalloc/chunk_swap.c
new file mode 100644
index 00000000000..b8c880f0a17
--- /dev/null
+++ b/externals/jemalloc/chunk_swap.c
@@ -0,0 +1,383 @@
+#define JEMALLOC_CHUNK_SWAP_C_
+#include "jemalloc/internal/jemalloc_internal.h"
+#ifdef JEMALLOC_SWAP
+/******************************************************************************/
+/* Data. */
+
+malloc_mutex_t swap_mtx;
+bool swap_enabled;
+bool swap_prezeroed;
+size_t swap_nfds;
+int *swap_fds;
+#ifdef JEMALLOC_STATS
+size_t swap_avail;
+#endif
+
+/* Base address of the mmap()ed file(s). */
+static void *swap_base;
+/* Current end of the space in use (<= swap_max). */
+static void *swap_end;
+/* Absolute upper limit on file-backed addresses. */
+static void *swap_max;
+
+/*
+ * Trees of chunks that were previously allocated (trees differ only in node
+ * ordering). These are used when allocating chunks, in an attempt to re-use
+ * address space. Depending on function, different tree orderings are needed,
+ * which is why there are two trees with the same contents.
+ */
+static extent_tree_t swap_chunks_szad;
+static extent_tree_t swap_chunks_ad;
+
+/******************************************************************************/
+/* Function prototypes for non-inline static functions. */
+
+static void *chunk_recycle_swap(size_t size, bool *zero);
+static extent_node_t *chunk_dealloc_swap_record(void *chunk, size_t size);
+
+/******************************************************************************/
+
+static void *
+chunk_recycle_swap(size_t size, bool *zero)
+{
+ extent_node_t *node, key;
+
+ key.addr = NULL;
+ key.size = size;
+ malloc_mutex_lock(&swap_mtx);
+ node = extent_tree_szad_nsearch(&swap_chunks_szad, &key);
+ if (node != NULL) {
+ void *ret = node->addr;
+
+ /* Remove node from the tree. */
+ extent_tree_szad_remove(&swap_chunks_szad, node);
+ if (node->size == size) {
+ extent_tree_ad_remove(&swap_chunks_ad, node);
+ base_node_dealloc(node);
+ } else {
+ /*
+ * Insert the remainder of node's address range as a
+ * smaller chunk. Its position within swap_chunks_ad
+ * does not change.
+ */
+ assert(node->size > size);
+ node->addr = (void *)((uintptr_t)node->addr + size);
+ node->size -= size;
+ extent_tree_szad_insert(&swap_chunks_szad, node);
+ }
+#ifdef JEMALLOC_STATS
+ swap_avail -= size;
+#endif
+ malloc_mutex_unlock(&swap_mtx);
+
+ if (*zero)
+ memset(ret, 0, size);
+ return (ret);
+ }
+ malloc_mutex_unlock(&swap_mtx);
+
+ return (NULL);
+}
+
+void *
+chunk_alloc_swap(size_t size, bool *zero)
+{
+ void *ret;
+
+ assert(swap_enabled);
+
+ ret = chunk_recycle_swap(size, zero);
+ if (ret != NULL)
+ return (ret);
+
+ malloc_mutex_lock(&swap_mtx);
+ if ((uintptr_t)swap_end + size <= (uintptr_t)swap_max) {
+ ret = swap_end;
+ swap_end = (void *)((uintptr_t)swap_end + size);
+#ifdef JEMALLOC_STATS
+ swap_avail -= size;
+#endif
+ malloc_mutex_unlock(&swap_mtx);
+
+ if (swap_prezeroed)
+ *zero = true;
+ else if (*zero)
+ memset(ret, 0, size);
+ } else {
+ malloc_mutex_unlock(&swap_mtx);
+ return (NULL);
+ }
+
+ return (ret);
+}
+
+static extent_node_t *
+chunk_dealloc_swap_record(void *chunk, size_t size)
+{
+ extent_node_t *xnode, *node, *prev, key;
+
+ xnode = NULL;
+ while (true) {
+ key.addr = (void *)((uintptr_t)chunk + size);
+ node = extent_tree_ad_nsearch(&swap_chunks_ad, &key);
+ /* Try to coalesce forward. */
+ if (node != NULL && node->addr == key.addr) {
+ /*
+ * Coalesce chunk with the following address range.
+ * This does not change the position within
+ * swap_chunks_ad, so only remove/insert from/into
+ * swap_chunks_szad.
+ */
+ extent_tree_szad_remove(&swap_chunks_szad, node);
+ node->addr = chunk;
+ node->size += size;
+ extent_tree_szad_insert(&swap_chunks_szad, node);
+ break;
+ } else if (xnode == NULL) {
+ /*
+ * It is possible that base_node_alloc() will cause a
+ * new base chunk to be allocated, so take care not to
+ * deadlock on swap_mtx, and recover if another thread
+ * deallocates an adjacent chunk while this one is busy
+ * allocating xnode.
+ */
+ malloc_mutex_unlock(&swap_mtx);
+ xnode = base_node_alloc();
+ malloc_mutex_lock(&swap_mtx);
+ if (xnode == NULL)
+ return (NULL);
+ } else {
+ /* Coalescing forward failed, so insert a new node. */
+ node = xnode;
+ xnode = NULL;
+ node->addr = chunk;
+ node->size = size;
+ extent_tree_ad_insert(&swap_chunks_ad, node);
+ extent_tree_szad_insert(&swap_chunks_szad, node);
+ break;
+ }
+ }
+ /* Discard xnode if it ended up unused do to a race. */
+ if (xnode != NULL)
+ base_node_dealloc(xnode);
+
+ /* Try to coalesce backward. */
+ prev = extent_tree_ad_prev(&swap_chunks_ad, node);
+ if (prev != NULL && (void *)((uintptr_t)prev->addr + prev->size) ==
+ chunk) {
+ /*
+ * Coalesce chunk with the previous address range. This does
+ * not change the position within swap_chunks_ad, so only
+ * remove/insert node from/into swap_chunks_szad.
+ */
+ extent_tree_szad_remove(&swap_chunks_szad, prev);
+ extent_tree_ad_remove(&swap_chunks_ad, prev);
+
+ extent_tree_szad_remove(&swap_chunks_szad, node);
+ node->addr = prev->addr;
+ node->size += prev->size;
+ extent_tree_szad_insert(&swap_chunks_szad, node);
+
+ base_node_dealloc(prev);
+ }
+
+ return (node);
+}
+
+bool
+chunk_dealloc_swap(void *chunk, size_t size)
+{
+ bool ret;
+
+ assert(swap_enabled);
+
+ malloc_mutex_lock(&swap_mtx);
+ if ((uintptr_t)chunk >= (uintptr_t)swap_base
+ && (uintptr_t)chunk < (uintptr_t)swap_max) {
+ extent_node_t *node;
+
+ /* Try to coalesce with other unused chunks. */
+ node = chunk_dealloc_swap_record(chunk, size);
+ if (node != NULL) {
+ chunk = node->addr;
+ size = node->size;
+ }
+
+ /*
+ * Try to shrink the in-use memory if this chunk is at the end
+ * of the in-use memory.
+ */
+ if ((void *)((uintptr_t)chunk + size) == swap_end) {
+ swap_end = (void *)((uintptr_t)swap_end - size);
+
+ if (node != NULL) {
+ extent_tree_szad_remove(&swap_chunks_szad,
+ node);
+ extent_tree_ad_remove(&swap_chunks_ad, node);
+ base_node_dealloc(node);
+ }
+ } else
+ madvise(chunk, size, MADV_DONTNEED);
+
+ ret = false;
+ goto RETURN;
+ }
+
+ ret = true;
+RETURN:
+#ifdef JEMALLOC_STATS
+ swap_avail += size;
+#endif
+ malloc_mutex_unlock(&swap_mtx);
+ return (ret);
+}
+
+bool
+chunk_swap_enable(const int *fds, unsigned nfds, bool prezeroed)
+{
+ bool ret;
+ unsigned i;
+ off_t off;
+ void *vaddr;
+ size_t cumsize, voff;
+ size_t sizes[nfds];
+
+ malloc_mutex_lock(&swap_mtx);
+
+ /* Get file sizes. */
+ for (i = 0, cumsize = 0; i < nfds; i++) {
+ off = lseek(fds[i], 0, SEEK_END);
+ if (off == ((off_t)-1)) {
+ ret = true;
+ goto RETURN;
+ }
+ if (PAGE_CEILING(off) != off) {
+ /* Truncate to a multiple of the page size. */
+ off &= ~PAGE_MASK;
+ if (ftruncate(fds[i], off) != 0) {
+ ret = true;
+ goto RETURN;
+ }
+ }
+ sizes[i] = off;
+ if (cumsize + off < cumsize) {
+ /*
+ * Cumulative file size is greater than the total
+ * address space. Bail out while it's still obvious
+ * what the problem is.
+ */
+ ret = true;
+ goto RETURN;
+ }
+ cumsize += off;
+ }
+
+ /* Round down to a multiple of the chunk size. */
+ cumsize &= ~chunksize_mask;
+ if (cumsize == 0) {
+ ret = true;
+ goto RETURN;
+ }
+
+ /*
+ * Allocate a chunk-aligned region of anonymous memory, which will
+ * be the final location for the memory-mapped files.
+ */
+ vaddr = chunk_alloc_mmap(cumsize);
+ if (vaddr == NULL) {
+ ret = true;
+ goto RETURN;
+ }
+
+ /* Overlay the files onto the anonymous mapping. */
+ for (i = 0, voff = 0; i < nfds; i++) {
+ void *addr = mmap((void *)((uintptr_t)vaddr + voff), sizes[i],
+ PROT_READ | PROT_WRITE, MAP_SHARED | MAP_FIXED, fds[i], 0);
+ if (addr == MAP_FAILED) {
+ char buf[STRERROR_BUF];
+
+ strerror_r(errno, buf, sizeof(buf));
+ malloc_write(
+ "<jemalloc>: Error in mmap(..., MAP_FIXED, ...): ");
+ malloc_write(buf);
+ malloc_write("\n");
+ if (opt_abort)
+ abort();
+ if (munmap(vaddr, voff) == -1) {
+ strerror_r(errno, buf, sizeof(buf));
+ malloc_write("<jemalloc>: Error in munmap(): ");
+ malloc_write(buf);
+ malloc_write("\n");
+ }
+ ret = true;
+ goto RETURN;
+ }
+ assert(addr == (void *)((uintptr_t)vaddr + voff));
+
+ /*
+ * Tell the kernel that the mapping will be accessed randomly,
+ * and that it should not gratuitously sync pages to the
+ * filesystem.
+ */
+#ifdef MADV_RANDOM
+ madvise(addr, sizes[i], MADV_RANDOM);
+#endif
+#ifdef MADV_NOSYNC
+ madvise(addr, sizes[i], MADV_NOSYNC);
+#endif
+
+ voff += sizes[i];
+ }
+
+ swap_prezeroed = prezeroed;
+ swap_base = vaddr;
+ swap_end = swap_base;
+ swap_max = (void *)((uintptr_t)vaddr + cumsize);
+
+ /* Copy the fds array for mallctl purposes. */
+ swap_fds = (int *)base_alloc(nfds * sizeof(int));
+ if (swap_fds == NULL) {
+ ret = true;
+ goto RETURN;
+ }
+ memcpy(swap_fds, fds, nfds * sizeof(int));
+ swap_nfds = nfds;
+
+#ifdef JEMALLOC_STATS
+ swap_avail = cumsize;
+#endif
+
+ swap_enabled = true;
+
+ ret = false;
+RETURN:
+ malloc_mutex_unlock(&swap_mtx);
+ return (ret);
+}
+
+bool
+chunk_swap_boot(void)
+{
+
+ if (malloc_mutex_init(&swap_mtx))
+ return (true);
+
+ swap_enabled = false;
+ swap_prezeroed = false; /* swap.* mallctl's depend on this. */
+ swap_nfds = 0;
+ swap_fds = NULL;
+#ifdef JEMALLOC_STATS
+ swap_avail = 0;
+#endif
+ swap_base = NULL;
+ swap_end = NULL;
+ swap_max = NULL;
+
+ extent_tree_szad_new(&swap_chunks_szad);
+ extent_tree_ad_new(&swap_chunks_ad);
+
+ return (false);
+}
+
+/******************************************************************************/
+#endif /* JEMALLOC_SWAP */
diff --git a/externals/jemalloc/ckh.c b/externals/jemalloc/ckh.c
new file mode 100644
index 00000000000..a0c4162aa19
--- /dev/null
+++ b/externals/jemalloc/ckh.c
@@ -0,0 +1,601 @@
+/*
+ *******************************************************************************
+ * Implementation of (2^1+,2) cuckoo hashing, where 2^1+ indicates that each
+ * hash bucket contains 2^n cells, for n >= 1, and 2 indicates that two hash
+ * functions are employed. The original cuckoo hashing algorithm was described
+ * in:
+ *
+ * Pagh, R., F.F. Rodler (2004) Cuckoo Hashing. Journal of Algorithms
+ * 51(2):122-144.
+ *
+ * Generalization of cuckoo hashing was discussed in:
+ *
+ * Erlingsson, U., M. Manasse, F. McSherry (2006) A cool and practical
+ * alternative to traditional hash tables. In Proceedings of the 7th
+ * Workshop on Distributed Data and Structures (WDAS'06), Santa Clara, CA,
+ * January 2006.
+ *
+ * This implementation uses precisely two hash functions because that is the
+ * fewest that can work, and supporting multiple hashes is an implementation
+ * burden. Here is a reproduction of Figure 1 from Erlingsson et al. (2006)
+ * that shows approximate expected maximum load factors for various
+ * configurations:
+ *
+ * | #cells/bucket |
+ * #hashes | 1 | 2 | 4 | 8 |
+ * --------+-------+-------+-------+-------+
+ * 1 | 0.006 | 0.006 | 0.03 | 0.12 |
+ * 2 | 0.49 | 0.86 |>0.93< |>0.96< |
+ * 3 | 0.91 | 0.97 | 0.98 | 0.999 |
+ * 4 | 0.97 | 0.99 | 0.999 | |
+ *
+ * The number of cells per bucket is chosen such that a bucket fits in one cache
+ * line. So, on 32- and 64-bit systems, we use (8,2) and (4,2) cuckoo hashing,
+ * respectively.
+ *
+ ******************************************************************************/
+#define CKH_C_
+#include "jemalloc/internal/jemalloc_internal.h"
+
+/******************************************************************************/
+/* Function prototypes for non-inline static functions. */
+
+static bool ckh_grow(ckh_t *ckh);
+static void ckh_shrink(ckh_t *ckh);
+
+/******************************************************************************/
+
+/*
+ * Search bucket for key and return the cell number if found; SIZE_T_MAX
+ * otherwise.
+ */
+JEMALLOC_INLINE size_t
+ckh_bucket_search(ckh_t *ckh, size_t bucket, const void *key)
+{
+ ckhc_t *cell;
+ unsigned i;
+
+ for (i = 0; i < (ZU(1) << LG_CKH_BUCKET_CELLS); i++) {
+ cell = &ckh->tab[(bucket << LG_CKH_BUCKET_CELLS) + i];
+ if (cell->key != NULL && ckh->keycomp(key, cell->key))
+ return ((bucket << LG_CKH_BUCKET_CELLS) + i);
+ }
+
+ return (SIZE_T_MAX);
+}
+
+/*
+ * Search table for key and return cell number if found; SIZE_T_MAX otherwise.
+ */
+JEMALLOC_INLINE size_t
+ckh_isearch(ckh_t *ckh, const void *key)
+{
+ size_t hash1, hash2, bucket, cell;
+
+ assert(ckh != NULL);
+ assert(ckh->magic = CKH_MAGIG);
+
+ ckh->hash(key, ckh->lg_curbuckets, &hash1, &hash2);
+
+ /* Search primary bucket. */
+ bucket = hash1 & ((ZU(1) << ckh->lg_curbuckets) - 1);
+ cell = ckh_bucket_search(ckh, bucket, key);
+ if (cell != SIZE_T_MAX)
+ return (cell);
+
+ /* Search secondary bucket. */
+ bucket = hash2 & ((ZU(1) << ckh->lg_curbuckets) - 1);
+ cell = ckh_bucket_search(ckh, bucket, key);
+ return (cell);
+}
+
+JEMALLOC_INLINE bool
+ckh_try_bucket_insert(ckh_t *ckh, size_t bucket, const void *key,
+ const void *data)
+{
+ ckhc_t *cell;
+ unsigned offset, i;
+
+ /*
+ * Cycle through the cells in the bucket, starting at a random position.
+ * The randomness avoids worst-case search overhead as buckets fill up.
+ */
+ prn32(offset, LG_CKH_BUCKET_CELLS, ckh->prn_state, CKH_A, CKH_C);
+ for (i = 0; i < (ZU(1) << LG_CKH_BUCKET_CELLS); i++) {
+ cell = &ckh->tab[(bucket << LG_CKH_BUCKET_CELLS) +
+ ((i + offset) & ((ZU(1) << LG_CKH_BUCKET_CELLS) - 1))];
+ if (cell->key == NULL) {
+ cell->key = key;
+ cell->data = data;
+ ckh->count++;
+ return (false);
+ }
+ }
+
+ return (true);
+}
+
+/*
+ * No space is available in bucket. Randomly evict an item, then try to find an
+ * alternate location for that item. Iteratively repeat this
+ * eviction/relocation procedure until either success or detection of an
+ * eviction/relocation bucket cycle.
+ */
+JEMALLOC_INLINE bool
+ckh_evict_reloc_insert(ckh_t *ckh, size_t argbucket, void const **argkey,
+ void const **argdata)
+{
+ const void *key, *data, *tkey, *tdata;
+ ckhc_t *cell;
+ size_t hash1, hash2, bucket, tbucket;
+ unsigned i;
+
+ bucket = argbucket;
+ key = *argkey;
+ data = *argdata;
+ while (true) {
+ /*
+ * Choose a random item within the bucket to evict. This is
+ * critical to correct function, because without (eventually)
+ * evicting all items within a bucket during iteration, it
+ * would be possible to get stuck in an infinite loop if there
+ * were an item for which both hashes indicated the same
+ * bucket.
+ */
+ prn32(i, LG_CKH_BUCKET_CELLS, ckh->prn_state, CKH_A, CKH_C);
+ cell = &ckh->tab[(bucket << LG_CKH_BUCKET_CELLS) + i];
+ assert(cell->key != NULL);
+
+ /* Swap cell->{key,data} and {key,data} (evict). */
+ tkey = cell->key; tdata = cell->data;
+ cell->key = key; cell->data = data;
+ key = tkey; data = tdata;
+
+#ifdef CKH_COUNT
+ ckh->nrelocs++;
+#endif
+
+ /* Find the alternate bucket for the evicted item. */
+ ckh->hash(key, ckh->lg_curbuckets, &hash1, &hash2);
+ tbucket = hash2 & ((ZU(1) << ckh->lg_curbuckets) - 1);
+ if (tbucket == bucket) {
+ tbucket = hash1 & ((ZU(1) << ckh->lg_curbuckets) - 1);
+ /*
+ * It may be that (tbucket == bucket) still, if the
+ * item's hashes both indicate this bucket. However,
+ * we are guaranteed to eventually escape this bucket
+ * during iteration, assuming pseudo-random item
+ * selection (true randomness would make infinite
+ * looping a remote possibility). The reason we can
+ * never get trapped forever is that there are two
+ * cases:
+ *
+ * 1) This bucket == argbucket, so we will quickly
+ * detect an eviction cycle and terminate.
+ * 2) An item was evicted to this bucket from another,
+ * which means that at least one item in this bucket
+ * has hashes that indicate distinct buckets.
+ */
+ }
+ /* Check for a cycle. */
+ if (tbucket == argbucket) {
+ *argkey = key;
+ *argdata = data;
+ return (true);
+ }
+
+ bucket = tbucket;
+ if (ckh_try_bucket_insert(ckh, bucket, key, data) == false)
+ return (false);
+ }
+}
+
+JEMALLOC_INLINE bool
+ckh_try_insert(ckh_t *ckh, void const**argkey, void const**argdata)
+{
+ size_t hash1, hash2, bucket;
+ const void *key = *argkey;
+ const void *data = *argdata;
+
+ ckh->hash(key, ckh->lg_curbuckets, &hash1, &hash2);
+
+ /* Try to insert in primary bucket. */
+ bucket = hash1 & ((ZU(1) << ckh->lg_curbuckets) - 1);
+ if (ckh_try_bucket_insert(ckh, bucket, key, data) == false)
+ return (false);
+
+ /* Try to insert in secondary bucket. */
+ bucket = hash2 & ((ZU(1) << ckh->lg_curbuckets) - 1);
+ if (ckh_try_bucket_insert(ckh, bucket, key, data) == false)
+ return (false);
+
+ /*
+ * Try to find a place for this item via iterative eviction/relocation.
+ */
+ return (ckh_evict_reloc_insert(ckh, bucket, argkey, argdata));
+}
+
+/*
+ * Try to rebuild the hash table from scratch by inserting all items from the
+ * old table into the new.
+ */
+JEMALLOC_INLINE bool
+ckh_rebuild(ckh_t *ckh, ckhc_t *aTab)
+{
+ size_t count, i, nins;
+ const void *key, *data;
+
+ count = ckh->count;
+ ckh->count = 0;
+ for (i = nins = 0; nins < count; i++) {
+ if (aTab[i].key != NULL) {
+ key = aTab[i].key;
+ data = aTab[i].data;
+ if (ckh_try_insert(ckh, &key, &data)) {
+ ckh->count = count;
+ return (true);
+ }
+ nins++;
+ }
+ }
+
+ return (false);
+}
+
+static bool
+ckh_grow(ckh_t *ckh)
+{
+ bool ret;
+ ckhc_t *tab, *ttab;
+ size_t lg_curcells;
+ unsigned lg_prevbuckets;
+
+#ifdef CKH_COUNT
+ ckh->ngrows++;
+#endif
+
+ /*
+ * It is possible (though unlikely, given well behaved hashes) that the
+ * table will have to be doubled more than once in order to create a
+ * usable table.
+ */
+ lg_prevbuckets = ckh->lg_curbuckets;
+ lg_curcells = ckh->lg_curbuckets + LG_CKH_BUCKET_CELLS;
+ while (true) {
+ lg_curcells++;
+ tab = (ckhc_t *) ipalloc((ZU(1) << LG_CACHELINE),
+ sizeof(ckhc_t) << lg_curcells);
+ if (tab == NULL) {
+ ret = true;
+ goto RETURN;
+ }
+ memset(tab, 0, sizeof(ckhc_t) << lg_curcells);
+ /* Swap in new table. */
+ ttab = ckh->tab;
+ ckh->tab = tab;
+ tab = ttab;
+ ckh->lg_curbuckets = lg_curcells - LG_CKH_BUCKET_CELLS;
+
+ if (ckh_rebuild(ckh, tab) == false) {
+ idalloc(tab);
+ break;
+ }
+
+ /* Rebuilding failed, so back out partially rebuilt table. */
+ idalloc(ckh->tab);
+ ckh->tab = tab;
+ ckh->lg_curbuckets = lg_prevbuckets;
+ }
+
+ ret = false;
+RETURN:
+ return (ret);
+}
+
+static void
+ckh_shrink(ckh_t *ckh)
+{
+ ckhc_t *tab, *ttab;
+ size_t lg_curcells;
+ unsigned lg_prevbuckets;
+
+ /*
+ * It is possible (though unlikely, given well behaved hashes) that the
+ * table rebuild will fail.
+ */
+ lg_prevbuckets = ckh->lg_curbuckets;
+ lg_curcells = ckh->lg_curbuckets + LG_CKH_BUCKET_CELLS - 1;
+ tab = (ckhc_t *)ipalloc((ZU(1) << LG_CACHELINE),
+ sizeof(ckhc_t) << lg_curcells);
+ if (tab == NULL) {
+ /*
+ * An OOM error isn't worth propagating, since it doesn't
+ * prevent this or future operations from proceeding.
+ */
+ return;
+ }
+ memset(tab, 0, sizeof(ckhc_t) << lg_curcells);
+ /* Swap in new table. */
+ ttab = ckh->tab;
+ ckh->tab = tab;
+ tab = ttab;
+ ckh->lg_curbuckets = lg_curcells - LG_CKH_BUCKET_CELLS;
+
+ if (ckh_rebuild(ckh, tab) == false) {
+ idalloc(tab);
+#ifdef CKH_COUNT
+ ckh->nshrinks++;
+#endif
+ return;
+ }
+
+ /* Rebuilding failed, so back out partially rebuilt table. */
+ idalloc(ckh->tab);
+ ckh->tab = tab;
+ ckh->lg_curbuckets = lg_prevbuckets;
+#ifdef CKH_COUNT
+ ckh->nshrinkfails++;
+#endif
+}
+
+bool
+ckh_new(ckh_t *ckh, size_t minitems, ckh_hash_t *hash, ckh_keycomp_t *keycomp)
+{
+ bool ret;
+ size_t mincells;
+ unsigned lg_mincells;
+
+ assert(minitems > 0);
+ assert(hash != NULL);
+ assert(keycomp != NULL);
+
+#ifdef CKH_COUNT
+ ckh->ngrows = 0;
+ ckh->nshrinks = 0;
+ ckh->nshrinkfails = 0;
+ ckh->ninserts = 0;
+ ckh->nrelocs = 0;
+#endif
+ ckh->prn_state = 42; /* Value doesn't really matter. */
+ ckh->count = 0;
+
+ /*
+ * Find the minimum power of 2 that is large enough to fit aBaseCount
+ * entries. We are using (2+,2) cuckoo hashing, which has an expected
+ * maximum load factor of at least ~0.86, so 0.75 is a conservative load
+ * factor that will typically allow 2^aLgMinItems to fit without ever
+ * growing the table.
+ */
+ assert(LG_CKH_BUCKET_CELLS > 0);
+ mincells = ((minitems + (3 - (minitems % 3))) / 3) << 2;
+ for (lg_mincells = LG_CKH_BUCKET_CELLS;
+ (ZU(1) << lg_mincells) < mincells;
+ lg_mincells++)
+ ; /* Do nothing. */
+ ckh->lg_minbuckets = lg_mincells - LG_CKH_BUCKET_CELLS;
+ ckh->lg_curbuckets = lg_mincells - LG_CKH_BUCKET_CELLS;
+ ckh->hash = hash;
+ ckh->keycomp = keycomp;
+
+ ckh->tab = (ckhc_t *)ipalloc((ZU(1) << LG_CACHELINE),
+ sizeof(ckhc_t) << lg_mincells);
+ if (ckh->tab == NULL) {
+ ret = true;
+ goto RETURN;
+ }
+ memset(ckh->tab, 0, sizeof(ckhc_t) << lg_mincells);
+
+#ifdef JEMALLOC_DEBUG
+ ckh->magic = CKH_MAGIG;
+#endif
+
+ ret = false;
+RETURN:
+ return (ret);
+}
+
+void
+ckh_delete(ckh_t *ckh)
+{
+
+ assert(ckh != NULL);
+ assert(ckh->magic = CKH_MAGIG);
+
+#ifdef CKH_VERBOSE
+ malloc_printf(
+ "%s(%p): ngrows: %"PRIu64", nshrinks: %"PRIu64","
+ " nshrinkfails: %"PRIu64", ninserts: %"PRIu64","
+ " nrelocs: %"PRIu64"\n", __func__, ckh,
+ (unsigned long long)ckh->ngrows,
+ (unsigned long long)ckh->nshrinks,
+ (unsigned long long)ckh->nshrinkfails,
+ (unsigned long long)ckh->ninserts,
+ (unsigned long long)ckh->nrelocs);
+#endif
+
+ idalloc(ckh->tab);
+#ifdef JEMALLOC_DEBUG
+ memset(ckh, 0x5a, sizeof(ckh_t));
+#endif
+}
+
+size_t
+ckh_count(ckh_t *ckh)
+{
+
+ assert(ckh != NULL);
+ assert(ckh->magic = CKH_MAGIG);
+
+ return (ckh->count);
+}
+
+bool
+ckh_iter(ckh_t *ckh, size_t *tabind, void **key, void **data)
+{
+ size_t i, ncells;
+
+ for (i = *tabind, ncells = (ZU(1) << (ckh->lg_curbuckets +
+ LG_CKH_BUCKET_CELLS)); i < ncells; i++) {
+ if (ckh->tab[i].key != NULL) {
+ if (key != NULL)
+ *key = (void *)ckh->tab[i].key;
+ if (data != NULL)
+ *data = (void *)ckh->tab[i].data;
+ *tabind = i + 1;
+ return (false);
+ }
+ }
+
+ return (true);
+}
+
+bool
+ckh_insert(ckh_t *ckh, const void *key, const void *data)
+{
+ bool ret;
+
+ assert(ckh != NULL);
+ assert(ckh->magic = CKH_MAGIG);
+ assert(ckh_search(ckh, key, NULL, NULL));
+
+#ifdef CKH_COUNT
+ ckh->ninserts++;
+#endif
+
+ while (ckh_try_insert(ckh, &key, &data)) {
+ if (ckh_grow(ckh)) {
+ ret = true;
+ goto RETURN;
+ }
+ }
+
+ ret = false;
+RETURN:
+ return (ret);
+}
+
+bool
+ckh_remove(ckh_t *ckh, const void *searchkey, void **key, void **data)
+{
+ size_t cell;
+
+ assert(ckh != NULL);
+ assert(ckh->magic = CKH_MAGIG);
+
+ cell = ckh_isearch(ckh, searchkey);
+ if (cell != SIZE_T_MAX) {
+ if (key != NULL)
+ *key = (void *)ckh->tab[cell].key;
+ if (data != NULL)
+ *data = (void *)ckh->tab[cell].data;
+ ckh->tab[cell].key = NULL;
+ ckh->tab[cell].data = NULL; /* Not necessary. */
+
+ ckh->count--;
+ /* Try to halve the table if it is less than 1/4 full. */
+ if (ckh->count < (ZU(1) << (ckh->lg_curbuckets
+ + LG_CKH_BUCKET_CELLS - 2)) && ckh->lg_curbuckets
+ > ckh->lg_minbuckets) {
+ /* Ignore error due to OOM. */
+ ckh_shrink(ckh);
+ }
+
+ return (false);
+ }
+
+ return (true);
+}
+
+bool
+ckh_search(ckh_t *ckh, const void *searchkey, void **key, void **data)
+{
+ size_t cell;
+
+ assert(ckh != NULL);
+ assert(ckh->magic = CKH_MAGIG);
+
+ cell = ckh_isearch(ckh, searchkey);
+ if (cell != SIZE_T_MAX) {
+ if (key != NULL)
+ *key = (void *)ckh->tab[cell].key;
+ if (data != NULL)
+ *data = (void *)ckh->tab[cell].data;
+ return (false);
+ }
+
+ return (true);
+}
+
+void
+ckh_string_hash(const void *key, unsigned minbits, size_t *hash1, size_t *hash2)
+{
+ size_t ret1, ret2;
+ uint64_t h;
+
+ assert(minbits <= 32 || (SIZEOF_PTR == 8 && minbits <= 64));
+ assert(hash1 != NULL);
+ assert(hash2 != NULL);
+
+ h = hash(key, strlen((const char *)key), 0x94122f335b332aeaLLU);
+ if (minbits <= 32) {
+ /*
+ * Avoid doing multiple hashes, since a single hash provides
+ * enough bits.
+ */
+ ret1 = h & ZU(0xffffffffU);
+ ret2 = h >> 32;
+ } else {
+ ret1 = h;
+ ret2 = hash(key, strlen((const char *)key),
+ 0x8432a476666bbc13U);
+ }
+
+ *hash1 = ret1;
+ *hash2 = ret2;
+}
+
+bool
+ckh_string_keycomp(const void *k1, const void *k2)
+{
+
+ assert(k1 != NULL);
+ assert(k2 != NULL);
+
+ return (strcmp((char *)k1, (char *)k2) ? false : true);
+}
+
+void
+ckh_pointer_hash(const void *key, unsigned minbits, size_t *hash1,
+ size_t *hash2)
+{
+ size_t ret1, ret2;
+ uint64_t h;
+
+ assert(minbits <= 32 || (SIZEOF_PTR == 8 && minbits <= 64));
+ assert(hash1 != NULL);
+ assert(hash2 != NULL);
+
+ h = hash(&key, sizeof(void *), 0xd983396e68886082LLU);
+ if (minbits <= 32) {
+ /*
+ * Avoid doing multiple hashes, since a single hash provides
+ * enough bits.
+ */
+ ret1 = h & ZU(0xffffffffU);
+ ret2 = h >> 32;
+ } else {
+ assert(SIZEOF_PTR == 8);
+ ret1 = h;
+ ret2 = hash(&key, sizeof(void *), 0x5e2be9aff8709a5dLLU);
+ }
+
+ *hash1 = ret1;
+ *hash2 = ret2;
+}
+
+bool
+ckh_pointer_keycomp(const void *k1, const void *k2)
+{
+
+ return ((k1 == k2) ? true : false);
+}
diff --git a/externals/jemalloc/ctl.c b/externals/jemalloc/ctl.c
new file mode 100644
index 00000000000..ffb732d5bef
--- /dev/null
+++ b/externals/jemalloc/ctl.c
@@ -0,0 +1,1482 @@
+#define JEMALLOC_CTL_C_
+#include "jemalloc/internal/jemalloc_internal.h"
+
+/******************************************************************************/
+/* Data. */
+
+static malloc_mutex_t ctl_mtx;
+static bool ctl_initialized;
+static uint64_t ctl_epoch;
+static ctl_stats_t ctl_stats;
+
+/******************************************************************************/
+/* Function prototypes for non-inline static functions. */
+
+#define CTL_PROTO(n) \
+static int n##_ctl(const size_t *mib, size_t miblen, void *oldp, \
+ size_t *oldlenp, void *newp, size_t newlen);
+
+#define INDEX_PROTO(n) \
+const ctl_node_t *n##_index(const size_t *mib, size_t miblen, \
+ size_t i);
+
+#ifdef JEMALLOC_STATS
+static bool ctl_arena_init(ctl_arena_stats_t *astats);
+#endif
+static void ctl_arena_clear(ctl_arena_stats_t *astats);
+#ifdef JEMALLOC_STATS
+static void ctl_arena_stats_amerge(ctl_arena_stats_t *cstats,
+ arena_t *arena);
+static void ctl_arena_stats_smerge(ctl_arena_stats_t *sstats,
+ ctl_arena_stats_t *astats);
+#endif
+static void ctl_arena_refresh(arena_t *arena, unsigned i);
+static void ctl_refresh(void);
+static bool ctl_init(void);
+static int ctl_lookup(const char *name, ctl_node_t const **nodesp,
+ size_t *mibp, size_t *depthp);
+
+CTL_PROTO(version)
+CTL_PROTO(epoch)
+#ifdef JEMALLOC_TCACHE
+CTL_PROTO(tcache_flush)
+#endif
+CTL_PROTO(config_debug)
+CTL_PROTO(config_dss)
+CTL_PROTO(config_dynamic_page_shift)
+CTL_PROTO(config_fill)
+CTL_PROTO(config_lazy_lock)
+CTL_PROTO(config_prof)
+CTL_PROTO(config_prof_libgcc)
+CTL_PROTO(config_prof_libunwind)
+CTL_PROTO(config_stats)
+CTL_PROTO(config_swap)
+CTL_PROTO(config_sysv)
+CTL_PROTO(config_tcache)
+CTL_PROTO(config_tiny)
+CTL_PROTO(config_tls)
+CTL_PROTO(config_xmalloc)
+CTL_PROTO(opt_abort)
+#ifdef JEMALLOC_FILL
+CTL_PROTO(opt_junk)
+#endif
+#ifdef JEMALLOC_SYSV
+CTL_PROTO(opt_sysv)
+#endif
+#ifdef JEMALLOC_XMALLOC
+CTL_PROTO(opt_xmalloc)
+#endif
+#ifdef JEMALLOC_ZERO
+CTL_PROTO(opt_zero)
+#endif
+#ifdef JEMALLOC_TCACHE
+CTL_PROTO(opt_tcache)
+CTL_PROTO(opt_lg_tcache_gc_sweep)
+#endif
+#ifdef JEMALLOC_PROF
+CTL_PROTO(opt_prof)
+CTL_PROTO(opt_prof_active)
+CTL_PROTO(opt_lg_prof_bt_max)
+CTL_PROTO(opt_lg_prof_sample)
+CTL_PROTO(opt_lg_prof_interval)
+CTL_PROTO(opt_prof_udump)
+CTL_PROTO(opt_prof_leak)
+#endif
+CTL_PROTO(opt_stats_print)
+CTL_PROTO(opt_lg_qspace_max)
+CTL_PROTO(opt_lg_cspace_max)
+CTL_PROTO(opt_lg_dirty_mult)
+CTL_PROTO(opt_lg_chunk)
+#ifdef JEMALLOC_SWAP
+CTL_PROTO(opt_overcommit)
+#endif
+CTL_PROTO(arenas_bin_i_size)
+CTL_PROTO(arenas_bin_i_nregs)
+CTL_PROTO(arenas_bin_i_run_size)
+INDEX_PROTO(arenas_bin_i)
+CTL_PROTO(arenas_lrun_i_size)
+INDEX_PROTO(arenas_lrun_i)
+CTL_PROTO(arenas_narenas)
+CTL_PROTO(arenas_initialized)
+CTL_PROTO(arenas_quantum)
+CTL_PROTO(arenas_cacheline)
+CTL_PROTO(arenas_subpage)
+CTL_PROTO(arenas_pagesize)
+CTL_PROTO(arenas_chunksize)
+#ifdef JEMALLOC_TINY
+CTL_PROTO(arenas_tspace_min)
+CTL_PROTO(arenas_tspace_max)
+#endif
+CTL_PROTO(arenas_qspace_min)
+CTL_PROTO(arenas_qspace_max)
+CTL_PROTO(arenas_cspace_min)
+CTL_PROTO(arenas_cspace_max)
+CTL_PROTO(arenas_sspace_min)
+CTL_PROTO(arenas_sspace_max)
+#ifdef JEMALLOC_TCACHE
+CTL_PROTO(arenas_tcache_max)
+#endif
+CTL_PROTO(arenas_ntbins)
+CTL_PROTO(arenas_nqbins)
+CTL_PROTO(arenas_ncbins)
+CTL_PROTO(arenas_nsbins)
+CTL_PROTO(arenas_nbins)
+#ifdef JEMALLOC_TCACHE
+CTL_PROTO(arenas_nhbins)
+#endif
+CTL_PROTO(arenas_nlruns)
+#ifdef JEMALLOC_PROF
+CTL_PROTO(prof_active)
+CTL_PROTO(prof_dump)
+CTL_PROTO(prof_interval)
+#endif
+#ifdef JEMALLOC_STATS
+CTL_PROTO(stats_chunks_current)
+CTL_PROTO(stats_chunks_total)
+CTL_PROTO(stats_chunks_high)
+CTL_PROTO(stats_huge_allocated)
+CTL_PROTO(stats_huge_nmalloc)
+CTL_PROTO(stats_huge_ndalloc)
+CTL_PROTO(stats_arenas_i_small_allocated)
+CTL_PROTO(stats_arenas_i_small_nmalloc)
+CTL_PROTO(stats_arenas_i_small_ndalloc)
+CTL_PROTO(stats_arenas_i_small_nrequests)
+CTL_PROTO(stats_arenas_i_large_allocated)
+CTL_PROTO(stats_arenas_i_large_nmalloc)
+CTL_PROTO(stats_arenas_i_large_ndalloc)
+CTL_PROTO(stats_arenas_i_large_nrequests)
+CTL_PROTO(stats_arenas_i_bins_j_allocated)
+CTL_PROTO(stats_arenas_i_bins_j_nmalloc)
+CTL_PROTO(stats_arenas_i_bins_j_ndalloc)
+CTL_PROTO(stats_arenas_i_bins_j_nrequests)
+#ifdef JEMALLOC_TCACHE
+CTL_PROTO(stats_arenas_i_bins_j_nfills)
+CTL_PROTO(stats_arenas_i_bins_j_nflushes)
+#endif
+CTL_PROTO(stats_arenas_i_bins_j_nruns)
+CTL_PROTO(stats_arenas_i_bins_j_nreruns)
+CTL_PROTO(stats_arenas_i_bins_j_highruns)
+CTL_PROTO(stats_arenas_i_bins_j_curruns)
+INDEX_PROTO(stats_arenas_i_bins_j)
+CTL_PROTO(stats_arenas_i_lruns_j_nmalloc)
+CTL_PROTO(stats_arenas_i_lruns_j_ndalloc)
+CTL_PROTO(stats_arenas_i_lruns_j_nrequests)
+CTL_PROTO(stats_arenas_i_lruns_j_highruns)
+CTL_PROTO(stats_arenas_i_lruns_j_curruns)
+INDEX_PROTO(stats_arenas_i_lruns_j)
+#endif
+CTL_PROTO(stats_arenas_i_pactive)
+CTL_PROTO(stats_arenas_i_pdirty)
+#ifdef JEMALLOC_STATS
+CTL_PROTO(stats_arenas_i_mapped)
+CTL_PROTO(stats_arenas_i_npurge)
+CTL_PROTO(stats_arenas_i_nmadvise)
+CTL_PROTO(stats_arenas_i_purged)
+#endif
+INDEX_PROTO(stats_arenas_i)
+#ifdef JEMALLOC_STATS
+CTL_PROTO(stats_allocated)
+CTL_PROTO(stats_active)
+CTL_PROTO(stats_mapped)
+#endif
+#ifdef JEMALLOC_SWAP
+# ifdef JEMALLOC_STATS
+CTL_PROTO(swap_avail)
+# endif
+CTL_PROTO(swap_prezeroed)
+CTL_PROTO(swap_nfds)
+CTL_PROTO(swap_fds)
+#endif
+
+/******************************************************************************/
+/* mallctl tree. */
+
+/* Maximum tree depth. */
+#define CTL_MAX_DEPTH 6
+
+#define NAME(n) true, {.named = {n
+#define CHILD(c) sizeof(c##_node) / sizeof(ctl_node_t), c##_node}}, NULL
+#define CTL(c) 0, NULL}}, c##_ctl
+
+/*
+ * Only handles internal indexed nodes, since there are currently no external
+ * ones.
+ */
+#define INDEX(i) false, {.indexed = {i##_index}}, NULL
+
+#ifdef JEMALLOC_TCACHE
+static const ctl_node_t tcache_node[] = {
+ {NAME("flush"), CTL(tcache_flush)}
+};
+#endif
+
+static const ctl_node_t config_node[] = {
+ {NAME("debug"), CTL(config_debug)},
+ {NAME("dss"), CTL(config_dss)},
+ {NAME("dynamic_page_shift"), CTL(config_dynamic_page_shift)},
+ {NAME("fill"), CTL(config_fill)},
+ {NAME("lazy_lock"), CTL(config_lazy_lock)},
+ {NAME("prof"), CTL(config_prof)},
+ {NAME("prof_libgcc"), CTL(config_prof_libgcc)},
+ {NAME("prof_libunwind"), CTL(config_prof_libunwind)},
+ {NAME("stats"), CTL(config_stats)},
+ {NAME("swap"), CTL(config_swap)},
+ {NAME("sysv"), CTL(config_sysv)},
+ {NAME("tcache"), CTL(config_tcache)},
+ {NAME("tiny"), CTL(config_tiny)},
+ {NAME("tls"), CTL(config_tls)},
+ {NAME("xmalloc"), CTL(config_xmalloc)}
+};
+
+static const ctl_node_t opt_node[] = {
+ {NAME("abort"), CTL(opt_abort)},
+#ifdef JEMALLOC_FILL
+ {NAME("junk"), CTL(opt_junk)},
+#endif
+#ifdef JEMALLOC_SYSV
+ {NAME("sysv"), CTL(opt_sysv)},
+#endif
+#ifdef JEMALLOC_XMALLOC
+ {NAME("xmalloc"), CTL(opt_xmalloc)},
+#endif
+#ifdef JEMALLOC_ZERO
+ {NAME("zero"), CTL(opt_zero)},
+#endif
+#ifdef JEMALLOC_TCACHE
+ {NAME("tcache"), CTL(opt_tcache)},
+ {NAME("lg_tcache_gc_sweep"), CTL(opt_lg_tcache_gc_sweep)},
+#endif
+#ifdef JEMALLOC_PROF
+ {NAME("prof"), CTL(opt_prof)},
+ {NAME("prof_active"), CTL(opt_prof_active)},
+ {NAME("lg_prof_bt_max"), CTL(opt_lg_prof_bt_max)},
+ {NAME("lg_prof_sample"), CTL(opt_lg_prof_sample)},
+ {NAME("lg_prof_interval"), CTL(opt_lg_prof_interval)},
+ {NAME("prof_udump"), CTL(opt_prof_udump)},
+ {NAME("prof_leak"), CTL(opt_prof_leak)},
+#endif
+ {NAME("stats_print"), CTL(opt_stats_print)},
+ {NAME("lg_qspace_max"), CTL(opt_lg_qspace_max)},
+ {NAME("lg_cspace_max"), CTL(opt_lg_cspace_max)},
+ {NAME("lg_dirty_mult"), CTL(opt_lg_dirty_mult)},
+ {NAME("lg_chunk"), CTL(opt_lg_chunk)}
+#ifdef JEMALLOC_SWAP
+ ,
+ {NAME("overcommit"), CTL(opt_overcommit)}
+#endif
+};
+
+static const ctl_node_t arenas_bin_i_node[] = {
+ {NAME("size"), CTL(arenas_bin_i_size)},
+ {NAME("nregs"), CTL(arenas_bin_i_nregs)},
+ {NAME("run_size"), CTL(arenas_bin_i_run_size)}
+};
+static const ctl_node_t super_arenas_bin_i_node[] = {
+ {NAME(""), CHILD(arenas_bin_i)}
+};
+
+static const ctl_node_t arenas_bin_node[] = {
+ {INDEX(arenas_bin_i)}
+};
+
+static const ctl_node_t arenas_lrun_i_node[] = {
+ {NAME("size"), CTL(arenas_lrun_i_size)}
+};
+static const ctl_node_t super_arenas_lrun_i_node[] = {
+ {NAME(""), CHILD(arenas_lrun_i)}
+};
+
+static const ctl_node_t arenas_lrun_node[] = {
+ {INDEX(arenas_lrun_i)}
+};
+
+static const ctl_node_t arenas_node[] = {
+ {NAME("narenas"), CTL(arenas_narenas)},
+ {NAME("initialized"), CTL(arenas_initialized)},
+ {NAME("quantum"), CTL(arenas_quantum)},
+ {NAME("cacheline"), CTL(arenas_cacheline)},
+ {NAME("subpage"), CTL(arenas_subpage)},
+ {NAME("pagesize"), CTL(arenas_pagesize)},
+ {NAME("chunksize"), CTL(arenas_chunksize)},
+#ifdef JEMALLOC_TINY
+ {NAME("tspace_min"), CTL(arenas_tspace_min)},
+ {NAME("tspace_max"), CTL(arenas_tspace_max)},
+#endif
+ {NAME("qspace_min"), CTL(arenas_qspace_min)},
+ {NAME("qspace_max"), CTL(arenas_qspace_max)},
+ {NAME("cspace_min"), CTL(arenas_cspace_min)},
+ {NAME("cspace_max"), CTL(arenas_cspace_max)},
+ {NAME("sspace_min"), CTL(arenas_sspace_min)},
+ {NAME("sspace_max"), CTL(arenas_sspace_max)},
+#ifdef JEMALLOC_TCACHE
+ {NAME("tcache_max"), CTL(arenas_tcache_max)},
+#endif
+ {NAME("ntbins"), CTL(arenas_ntbins)},
+ {NAME("nqbins"), CTL(arenas_nqbins)},
+ {NAME("ncbins"), CTL(arenas_ncbins)},
+ {NAME("nsbins"), CTL(arenas_nsbins)},
+ {NAME("nbins"), CTL(arenas_nbins)},
+#ifdef JEMALLOC_TCACHE
+ {NAME("nhbins"), CTL(arenas_nhbins)},
+#endif
+ {NAME("bin"), CHILD(arenas_bin)},
+ {NAME("nlruns"), CTL(arenas_nlruns)},
+ {NAME("lrun"), CHILD(arenas_lrun)}
+};
+
+#ifdef JEMALLOC_PROF
+static const ctl_node_t prof_node[] = {
+ {NAME("active"), CTL(prof_active)},
+ {NAME("dump"), CTL(prof_dump)},
+ {NAME("interval"), CTL(prof_interval)}
+};
+#endif
+
+#ifdef JEMALLOC_STATS
+static const ctl_node_t stats_chunks_node[] = {
+ {NAME("current"), CTL(stats_chunks_current)},
+ {NAME("total"), CTL(stats_chunks_total)},
+ {NAME("high"), CTL(stats_chunks_high)}
+};
+
+static const ctl_node_t stats_huge_node[] = {
+ {NAME("allocated"), CTL(stats_huge_allocated)},
+ {NAME("nmalloc"), CTL(stats_huge_nmalloc)},
+ {NAME("ndalloc"), CTL(stats_huge_ndalloc)}
+};
+
+static const ctl_node_t stats_arenas_i_small_node[] = {
+ {NAME("allocated"), CTL(stats_arenas_i_small_allocated)},
+ {NAME("nmalloc"), CTL(stats_arenas_i_small_nmalloc)},
+ {NAME("ndalloc"), CTL(stats_arenas_i_small_ndalloc)},
+ {NAME("nrequests"), CTL(stats_arenas_i_small_nrequests)}
+};
+
+static const ctl_node_t stats_arenas_i_large_node[] = {
+ {NAME("allocated"), CTL(stats_arenas_i_large_allocated)},
+ {NAME("nmalloc"), CTL(stats_arenas_i_large_nmalloc)},
+ {NAME("ndalloc"), CTL(stats_arenas_i_large_ndalloc)},
+ {NAME("nrequests"), CTL(stats_arenas_i_large_nrequests)}
+};
+
+static const ctl_node_t stats_arenas_i_bins_j_node[] = {
+ {NAME("allocated"), CTL(stats_arenas_i_bins_j_allocated)},
+ {NAME("nmalloc"), CTL(stats_arenas_i_bins_j_nmalloc)},
+ {NAME("ndalloc"), CTL(stats_arenas_i_bins_j_ndalloc)},
+ {NAME("nrequests"), CTL(stats_arenas_i_bins_j_nrequests)},
+#ifdef JEMALLOC_TCACHE
+ {NAME("nfills"), CTL(stats_arenas_i_bins_j_nfills)},
+ {NAME("nflushes"), CTL(stats_arenas_i_bins_j_nflushes)},
+#endif
+ {NAME("nruns"), CTL(stats_arenas_i_bins_j_nruns)},
+ {NAME("nreruns"), CTL(stats_arenas_i_bins_j_nreruns)},
+ {NAME("highruns"), CTL(stats_arenas_i_bins_j_highruns)},
+ {NAME("curruns"), CTL(stats_arenas_i_bins_j_curruns)}
+};
+static const ctl_node_t super_stats_arenas_i_bins_j_node[] = {
+ {NAME(""), CHILD(stats_arenas_i_bins_j)}
+};
+
+static const ctl_node_t stats_arenas_i_bins_node[] = {
+ {INDEX(stats_arenas_i_bins_j)}
+};
+
+static const ctl_node_t stats_arenas_i_lruns_j_node[] = {
+ {NAME("nmalloc"), CTL(stats_arenas_i_lruns_j_nmalloc)},
+ {NAME("ndalloc"), CTL(stats_arenas_i_lruns_j_ndalloc)},
+ {NAME("nrequests"), CTL(stats_arenas_i_lruns_j_nrequests)},
+ {NAME("highruns"), CTL(stats_arenas_i_lruns_j_highruns)},
+ {NAME("curruns"), CTL(stats_arenas_i_lruns_j_curruns)}
+};
+static const ctl_node_t super_stats_arenas_i_lruns_j_node[] = {
+ {NAME(""), CHILD(stats_arenas_i_lruns_j)}
+};
+
+static const ctl_node_t stats_arenas_i_lruns_node[] = {
+ {INDEX(stats_arenas_i_lruns_j)}
+};
+#endif
+
+static const ctl_node_t stats_arenas_i_node[] = {
+ {NAME("pactive"), CTL(stats_arenas_i_pactive)},
+ {NAME("pdirty"), CTL(stats_arenas_i_pdirty)}
+#ifdef JEMALLOC_STATS
+ ,
+ {NAME("mapped"), CTL(stats_arenas_i_mapped)},
+ {NAME("npurge"), CTL(stats_arenas_i_npurge)},
+ {NAME("nmadvise"), CTL(stats_arenas_i_nmadvise)},
+ {NAME("purged"), CTL(stats_arenas_i_purged)},
+ {NAME("small"), CHILD(stats_arenas_i_small)},
+ {NAME("large"), CHILD(stats_arenas_i_large)},
+ {NAME("bins"), CHILD(stats_arenas_i_bins)},
+ {NAME("lruns"), CHILD(stats_arenas_i_lruns)}
+#endif
+};
+static const ctl_node_t super_stats_arenas_i_node[] = {
+ {NAME(""), CHILD(stats_arenas_i)}
+};
+
+static const ctl_node_t stats_arenas_node[] = {
+ {INDEX(stats_arenas_i)}
+};
+
+static const ctl_node_t stats_node[] = {
+#ifdef JEMALLOC_STATS
+ {NAME("allocated"), CTL(stats_allocated)},
+ {NAME("active"), CTL(stats_active)},
+ {NAME("mapped"), CTL(stats_mapped)},
+ {NAME("chunks"), CHILD(stats_chunks)},
+ {NAME("huge"), CHILD(stats_huge)},
+#endif
+ {NAME("arenas"), CHILD(stats_arenas)}
+};
+
+#ifdef JEMALLOC_SWAP
+static const ctl_node_t swap_node[] = {
+# ifdef JEMALLOC_STATS
+ {NAME("avail"), CTL(swap_avail)},
+# endif
+ {NAME("prezeroed"), CTL(swap_prezeroed)},
+ {NAME("nfds"), CTL(swap_nfds)},
+ {NAME("fds"), CTL(swap_fds)}
+};
+#endif
+
+static const ctl_node_t root_node[] = {
+ {NAME("version"), CTL(version)},
+ {NAME("epoch"), CTL(epoch)},
+#ifdef JEMALLOC_TCACHE
+ {NAME("tcache"), CHILD(tcache)},
+#endif
+ {NAME("config"), CHILD(config)},
+ {NAME("opt"), CHILD(opt)},
+ {NAME("arenas"), CHILD(arenas)},
+#ifdef JEMALLOC_PROF
+ {NAME("prof"), CHILD(prof)},
+#endif
+ {NAME("stats"), CHILD(stats)}
+#ifdef JEMALLOC_SWAP
+ ,
+ {NAME("swap"), CHILD(swap)}
+#endif
+};
+static const ctl_node_t super_root_node[] = {
+ {NAME(""), CHILD(root)}
+};
+
+#undef NAME
+#undef CHILD
+#undef CTL
+#undef INDEX
+
+/******************************************************************************/
+
+#ifdef JEMALLOC_STATS
+static bool
+ctl_arena_init(ctl_arena_stats_t *astats)
+{
+
+ if (astats->bstats == NULL) {
+ astats->bstats = (malloc_bin_stats_t *)base_alloc(nbins *
+ sizeof(malloc_bin_stats_t));
+ if (astats->bstats == NULL)
+ return (true);
+ }
+ if (astats->lstats == NULL) {
+ astats->lstats = (malloc_large_stats_t *)base_alloc(nlclasses *
+ sizeof(malloc_large_stats_t));
+ if (astats->lstats == NULL)
+ return (true);
+ }
+
+ return (false);
+}
+#endif
+
+static void
+ctl_arena_clear(ctl_arena_stats_t *astats)
+{
+
+ astats->pactive = 0;
+ astats->pdirty = 0;
+#ifdef JEMALLOC_STATS
+ memset(&astats->astats, 0, sizeof(arena_stats_t));
+ astats->allocated_small = 0;
+ astats->nmalloc_small = 0;
+ astats->ndalloc_small = 0;
+ astats->nrequests_small = 0;
+ memset(astats->bstats, 0, nbins * sizeof(malloc_bin_stats_t));
+ memset(astats->lstats, 0, nlclasses * sizeof(malloc_large_stats_t));
+#endif
+}
+
+#ifdef JEMALLOC_STATS
+static void
+ctl_arena_stats_amerge(ctl_arena_stats_t *cstats, arena_t *arena)
+{
+ unsigned i;
+
+ arena_stats_merge(arena, &cstats->pactive, &cstats->pdirty,
+ &cstats->astats, cstats->bstats, cstats->lstats);
+
+ for (i = 0; i < nbins; i++) {
+ cstats->allocated_small += cstats->bstats[i].allocated;
+ cstats->nmalloc_small += cstats->bstats[i].nmalloc;
+ cstats->ndalloc_small += cstats->bstats[i].ndalloc;
+ cstats->nrequests_small += cstats->bstats[i].nrequests;
+ }
+}
+
+static void
+ctl_arena_stats_smerge(ctl_arena_stats_t *sstats, ctl_arena_stats_t *astats)
+{
+ unsigned i;
+
+ sstats->pactive += astats->pactive;
+ sstats->pdirty += astats->pdirty;
+
+ sstats->astats.mapped += astats->astats.mapped;
+ sstats->astats.npurge += astats->astats.npurge;
+ sstats->astats.nmadvise += astats->astats.nmadvise;
+ sstats->astats.purged += astats->astats.purged;
+
+ sstats->allocated_small += astats->allocated_small;
+ sstats->nmalloc_small += astats->nmalloc_small;
+ sstats->ndalloc_small += astats->ndalloc_small;
+ sstats->nrequests_small += astats->nrequests_small;
+
+ sstats->astats.allocated_large += astats->astats.allocated_large;
+ sstats->astats.nmalloc_large += astats->astats.nmalloc_large;
+ sstats->astats.ndalloc_large += astats->astats.ndalloc_large;
+ sstats->astats.nrequests_large += astats->astats.nrequests_large;
+
+ for (i = 0; i < nlclasses; i++) {
+ sstats->lstats[i].nmalloc += astats->lstats[i].nmalloc;
+ sstats->lstats[i].ndalloc += astats->lstats[i].ndalloc;
+ sstats->lstats[i].nrequests += astats->lstats[i].nrequests;
+ sstats->lstats[i].highruns += astats->lstats[i].highruns;
+ sstats->lstats[i].curruns += astats->lstats[i].curruns;
+ }
+
+ for (i = 0; i < nbins; i++) {
+ sstats->bstats[i].allocated += astats->bstats[i].allocated;
+ sstats->bstats[i].nmalloc += astats->bstats[i].nmalloc;
+ sstats->bstats[i].ndalloc += astats->bstats[i].ndalloc;
+ sstats->bstats[i].nrequests += astats->bstats[i].nrequests;
+#ifdef JEMALLOC_TCACHE
+ sstats->bstats[i].nfills += astats->bstats[i].nfills;
+ sstats->bstats[i].nflushes += astats->bstats[i].nflushes;
+#endif
+ sstats->bstats[i].nruns += astats->bstats[i].nruns;
+ sstats->bstats[i].reruns += astats->bstats[i].reruns;
+ sstats->bstats[i].highruns += astats->bstats[i].highruns;
+ sstats->bstats[i].curruns += astats->bstats[i].curruns;
+ }
+}
+#endif
+
+static void
+ctl_arena_refresh(arena_t *arena, unsigned i)
+{
+ ctl_arena_stats_t *astats = &ctl_stats.arenas[i];
+ ctl_arena_stats_t *sstats = &ctl_stats.arenas[narenas];
+
+ ctl_arena_clear(astats);
+
+#ifdef JEMALLOC_STATS
+ ctl_arena_stats_amerge(astats, arena);
+ /* Merge into sum stats as well. */
+ ctl_arena_stats_smerge(sstats, astats);
+#else
+ astats->pactive += arena->nactive;
+ astats->pdirty += arena->ndirty;
+ /* Merge into sum stats as well. */
+ sstats->pactive += arena->nactive;
+ sstats->pdirty += arena->ndirty;
+#endif
+}
+
+static void
+ctl_refresh(void)
+{
+ unsigned i;
+ arena_t *tarenas[narenas];
+
+#ifdef JEMALLOC_STATS
+ malloc_mutex_lock(&chunks_mtx);
+ ctl_stats.chunks.current = stats_chunks.curchunks;
+ ctl_stats.chunks.total = stats_chunks.nchunks;
+ ctl_stats.chunks.high = stats_chunks.highchunks;
+ malloc_mutex_unlock(&chunks_mtx);
+
+ malloc_mutex_lock(&huge_mtx);
+ ctl_stats.huge.allocated = huge_allocated;
+ ctl_stats.huge.nmalloc = huge_nmalloc;
+ ctl_stats.huge.ndalloc = huge_ndalloc;
+ malloc_mutex_unlock(&huge_mtx);
+#endif
+
+ /*
+ * Clear sum stats, since they will be merged into by
+ * ctl_arena_refresh().
+ */
+ ctl_arena_clear(&ctl_stats.arenas[narenas]);
+
+ malloc_mutex_lock(&arenas_lock);
+ memcpy(tarenas, arenas, sizeof(arena_t *) * narenas);
+ malloc_mutex_unlock(&arenas_lock);
+ for (i = 0; i < narenas; i++) {
+ bool initialized = (tarenas[i] != NULL);
+
+ ctl_stats.arenas[i].initialized = initialized;
+ if (initialized)
+ ctl_arena_refresh(tarenas[i], i);
+ }
+
+#ifdef JEMALLOC_STATS
+ ctl_stats.allocated = ctl_stats.arenas[narenas].allocated_small
+ + ctl_stats.arenas[narenas].astats.allocated_large
+ + ctl_stats.huge.allocated;
+ ctl_stats.active = (ctl_stats.arenas[narenas].pactive << PAGE_SHIFT)
+ + ctl_stats.huge.allocated;
+ ctl_stats.mapped = (ctl_stats.chunks.current << opt_lg_chunk);
+
+# ifdef JEMALLOC_SWAP
+ malloc_mutex_lock(&swap_mtx);
+ ctl_stats.swap_avail = swap_avail;
+ malloc_mutex_unlock(&swap_mtx);
+# endif
+#endif
+
+ ctl_epoch++;
+}
+
+static bool
+ctl_init(void)
+{
+
+ if (ctl_initialized == false) {
+#ifdef JEMALLOC_STATS
+ unsigned i;
+#endif
+
+ /*
+ * Allocate space for one extra arena stats element, which
+ * contains summed stats across all arenas.
+ */
+ ctl_stats.arenas = (ctl_arena_stats_t *)base_alloc(
+ (narenas + 1) * sizeof(ctl_arena_stats_t));
+ if (ctl_stats.arenas == NULL)
+ return (true);
+ memset(ctl_stats.arenas, 0, (narenas + 1) *
+ sizeof(ctl_arena_stats_t));
+
+ /*
+ * Initialize all stats structures, regardless of whether they
+ * ever get used. Lazy initialization would allow errors to
+ * cause inconsistent state to be viewable by the application.
+ */
+#ifdef JEMALLOC_STATS
+ for (i = 0; i <= narenas; i++) {
+ if (ctl_arena_init(&ctl_stats.arenas[i]))
+ return (true);
+ }
+#endif
+ ctl_stats.arenas[narenas].initialized = true;
+
+ ctl_epoch = 0;
+ ctl_refresh();
+ ctl_initialized = true;
+ }
+
+ return (false);
+}
+
+static int
+ctl_lookup(const char *name, ctl_node_t const **nodesp, size_t *mibp,
+ size_t *depthp)
+{
+ int ret;
+ const char *elm, *tdot, *dot;
+ size_t elen, i, j;
+ const ctl_node_t *node;
+
+ elm = name;
+ /* Equivalent to strchrnul(). */
+ dot = ((tdot = strchr(elm, '.')) != NULL) ? tdot : strchr(elm, '\0');
+ elen = (size_t)((uintptr_t)dot - (uintptr_t)elm);
+ if (elen == 0) {
+ ret = ENOENT;
+ goto RETURN;
+ }
+ node = super_root_node;
+ for (i = 0; i < *depthp; i++) {
+ assert(node->named);
+ assert(node->u.named.nchildren > 0);
+ if (node->u.named.children[0].named) {
+ const ctl_node_t *pnode = node;
+
+ /* Children are named. */
+ for (j = 0; j < node->u.named.nchildren; j++) {
+ const ctl_node_t *child =
+ &node->u.named.children[j];
+ if (strlen(child->u.named.name) == elen
+ && strncmp(elm, child->u.named.name,
+ elen) == 0) {
+ node = child;
+ if (nodesp != NULL)
+ nodesp[i] = node;
+ mibp[i] = j;
+ break;
+ }
+ }
+ if (node == pnode) {
+ ret = ENOENT;
+ goto RETURN;
+ }
+ } else {
+ unsigned long index;
+ const ctl_node_t *inode;
+
+ /* Children are indexed. */
+ index = strtoul(elm, NULL, 10);
+ if (index == ULONG_MAX) {
+ ret = ENOENT;
+ goto RETURN;
+ }
+
+ inode = &node->u.named.children[0];
+ node = inode->u.indexed.index(mibp, *depthp,
+ index);
+ if (node == NULL) {
+ ret = ENOENT;
+ goto RETURN;
+ }
+
+ if (nodesp != NULL)
+ nodesp[i] = node;
+ mibp[i] = (size_t)index;
+ }
+
+ if (node->ctl != NULL) {
+ /* Terminal node. */
+ if (*dot != '\0') {
+ /*
+ * The name contains more elements than are
+ * in this path through the tree.
+ */
+ ret = ENOENT;
+ goto RETURN;
+ }
+ /* Complete lookup successful. */
+ *depthp = i + 1;
+ break;
+ }
+
+ /* Update elm. */
+ if (*dot == '\0') {
+ /* No more elements. */
+ ret = ENOENT;
+ goto RETURN;
+ }
+ elm = &dot[1];
+ dot = ((tdot = strchr(elm, '.')) != NULL) ? tdot :
+ strchr(elm, '\0');
+ elen = (size_t)((uintptr_t)dot - (uintptr_t)elm);
+ }
+
+ ret = 0;
+RETURN:
+ return (ret);
+}
+
+int
+ctl_byname(const char *name, void *oldp, size_t *oldlenp, void *newp,
+ size_t newlen)
+{
+ int ret;
+ size_t depth;
+ ctl_node_t const *nodes[CTL_MAX_DEPTH];
+ size_t mib[CTL_MAX_DEPTH];
+
+ malloc_mutex_lock(&ctl_mtx);
+ if (ctl_init()) {
+ ret = EAGAIN;
+ goto RETURN;
+ }
+
+ depth = CTL_MAX_DEPTH;
+ ret = ctl_lookup(name, nodes, mib, &depth);
+ if (ret != 0)
+ goto RETURN;
+
+ if (nodes[depth-1]->ctl == NULL) {
+ /* The name refers to a partial path through the ctl tree. */
+ ret = ENOENT;
+ goto RETURN;
+ }
+ ret = nodes[depth-1]->ctl(mib, depth, oldp, oldlenp, newp, newlen);
+
+RETURN:
+ malloc_mutex_unlock(&ctl_mtx);
+ return(ret);
+}
+
+int
+ctl_nametomib(const char *name, size_t *mibp, size_t *miblenp)
+{
+ int ret;
+
+ malloc_mutex_lock(&ctl_mtx);
+ if (ctl_init()) {
+ ret = EAGAIN;
+ goto RETURN;
+ }
+
+ ret = ctl_lookup(name, NULL, mibp, miblenp);
+
+RETURN:
+ malloc_mutex_unlock(&ctl_mtx);
+ return(ret);
+}
+
+int
+ctl_bymib(const size_t *mib, size_t miblen, void *oldp, size_t *oldlenp,
+ void *newp, size_t newlen)
+{
+ int ret;
+ const ctl_node_t *node;
+ size_t i;
+
+ malloc_mutex_lock(&ctl_mtx);
+ if (ctl_init()) {
+ ret = EAGAIN;
+ goto RETURN;
+ }
+
+ /* Iterate down the tree. */
+ node = super_root_node;
+ for (i = 0; i < miblen; i++) {
+ if (node->u.named.children[0].named) {
+ /* Children are named. */
+ if (node->u.named.nchildren <= mib[i]) {
+ ret = ENOENT;
+ goto RETURN;
+ }
+ node = &node->u.named.children[mib[i]];
+ } else {
+ const ctl_node_t *inode;
+
+ /* Indexed element. */
+ inode = &node->u.named.children[0];
+ node = inode->u.indexed.index(mib, miblen, mib[i]);
+ if (node == NULL) {
+ ret = ENOENT;
+ goto RETURN;
+ }
+ }
+ }
+
+ /* Call the ctl function. */
+ if (node->ctl == NULL) {
+ /* Partial MIB. */
+ ret = ENOENT;
+ goto RETURN;
+ }
+ ret = node->ctl(mib, miblen, oldp, oldlenp, newp, newlen);
+
+RETURN:
+ malloc_mutex_unlock(&ctl_mtx);
+ return(ret);
+}
+
+bool
+ctl_boot(void)
+{
+
+ if (malloc_mutex_init(&ctl_mtx))
+ return (true);
+
+ ctl_initialized = false;
+
+ return (false);
+}
+
+/******************************************************************************/
+/* *_ctl() functions. */
+
+#define READONLY() do { \
+ if (newp != NULL || newlen != 0) { \
+ ret = EPERM; \
+ goto RETURN; \
+ } \
+} while (0)
+
+#define WRITEONLY() do { \
+ if (oldp != NULL || oldlenp != NULL) { \
+ ret = EPERM; \
+ goto RETURN; \
+ } \
+} while (0)
+
+#define VOID() do { \
+ READONLY(); \
+ WRITEONLY(); \
+} while (0)
+
+#define READ(v, t) do { \
+ if (oldp != NULL && oldlenp != NULL) { \
+ if (*oldlenp != sizeof(t)) { \
+ size_t copylen = (sizeof(t) <= *oldlenp) \
+ ? sizeof(t) : *oldlenp; \
+ memcpy(oldp, (void *)&v, copylen); \
+ ret = EINVAL; \
+ goto RETURN; \
+ } else \
+ *(t *)oldp = v; \
+ } \
+} while (0)
+
+#define WRITE(v, t) do { \
+ if (newp != NULL) { \
+ if (newlen != sizeof(t)) { \
+ ret = EINVAL; \
+ goto RETURN; \
+ } \
+ v = *(t *)newp; \
+ } \
+} while (0)
+
+#define CTL_RO_GEN(n, v, t) \
+static int \
+n##_ctl(const size_t *mib, size_t miblen, void *oldp, size_t *oldlenp, \
+ void *newp, size_t newlen) \
+{ \
+ int ret; \
+ t oldval; \
+ \
+ READONLY(); \
+ oldval = v; \
+ READ(oldval, t); \
+ \
+ ret = 0; \
+RETURN: \
+ return (ret); \
+}
+
+#define CTL_RO_TRUE_GEN(n) \
+static int \
+n##_ctl(const size_t *mib, size_t miblen, void *oldp, size_t *oldlenp, \
+ void *newp, size_t newlen) \
+{ \
+ int ret; \
+ bool oldval; \
+ \
+ READONLY(); \
+ oldval = true; \
+ READ(oldval, bool); \
+ \
+ ret = 0; \
+RETURN: \
+ return (ret); \
+}
+
+#define CTL_RO_FALSE_GEN(n) \
+static int \
+n##_ctl(const size_t *mib, size_t miblen, void *oldp, size_t *oldlenp, \
+ void *newp, size_t newlen) \
+{ \
+ int ret; \
+ bool oldval; \
+ \
+ READONLY(); \
+ oldval = false; \
+ READ(oldval, bool); \
+ \
+ ret = 0; \
+RETURN: \
+ return (ret); \
+}
+
+CTL_RO_GEN(version, JEMALLOC_VERSION, const char *)
+
+static int
+epoch_ctl(const size_t *mib, size_t miblen, void *oldp, size_t *oldlenp,
+ void *newp, size_t newlen)
+{
+ int ret;
+ uint64_t newval;
+
+ newval = 0;
+ WRITE(newval, uint64_t);
+ if (newval != 0)
+ ctl_refresh();
+ READ(ctl_epoch, uint64_t);
+
+ ret = 0;
+RETURN:
+ return (ret);
+}
+
+#ifdef JEMALLOC_TCACHE
+static int
+tcache_flush_ctl(const size_t *mib, size_t miblen, void *oldp, size_t *oldlenp,
+ void *newp, size_t newlen)
+{
+ int ret;
+ tcache_t *tcache;
+
+ VOID();
+
+ tcache = tcache_tls;
+ if (tcache == NULL) {
+ ret = 0;
+ goto RETURN;
+ }
+ tcache_destroy(tcache);
+ tcache_tls = NULL;
+
+ ret = 0;
+RETURN:
+ return (ret);
+}
+#endif
+
+/******************************************************************************/
+
+#ifdef JEMALLOC_DEBUG
+CTL_RO_TRUE_GEN(config_debug)
+#else
+CTL_RO_FALSE_GEN(config_debug)
+#endif
+
+#ifdef JEMALLOC_DSS
+CTL_RO_TRUE_GEN(config_dss)
+#else
+CTL_RO_FALSE_GEN(config_dss)
+#endif
+
+#ifdef JEMALLOC_DYNAMIC_PAGE_SHIFT
+CTL_RO_TRUE_GEN(config_dynamic_page_shift)
+#else
+CTL_RO_FALSE_GEN(config_dynamic_page_shift)
+#endif
+
+#ifdef JEMALLOC_FILL
+CTL_RO_TRUE_GEN(config_fill)
+#else
+CTL_RO_FALSE_GEN(config_fill)
+#endif
+
+#ifdef JEMALLOC_LAZY_LOCK
+CTL_RO_TRUE_GEN(config_lazy_lock)
+#else
+CTL_RO_FALSE_GEN(config_lazy_lock)
+#endif
+
+#ifdef JEMALLOC_PROF
+CTL_RO_TRUE_GEN(config_prof)
+#else
+CTL_RO_FALSE_GEN(config_prof)
+#endif
+
+#ifdef JEMALLOC_PROF_LIBGCC
+CTL_RO_TRUE_GEN(config_prof_libgcc)
+#else
+CTL_RO_FALSE_GEN(config_prof_libgcc)
+#endif
+
+#ifdef JEMALLOC_PROF_LIBUNWIND
+CTL_RO_TRUE_GEN(config_prof_libunwind)
+#else
+CTL_RO_FALSE_GEN(config_prof_libunwind)
+#endif
+
+#ifdef JEMALLOC_STATS
+CTL_RO_TRUE_GEN(config_stats)
+#else
+CTL_RO_FALSE_GEN(config_stats)
+#endif
+
+#ifdef JEMALLOC_SWAP
+CTL_RO_TRUE_GEN(config_swap)
+#else
+CTL_RO_FALSE_GEN(config_swap)
+#endif
+
+#ifdef JEMALLOC_SYSV
+CTL_RO_TRUE_GEN(config_sysv)
+#else
+CTL_RO_FALSE_GEN(config_sysv)
+#endif
+
+#ifdef JEMALLOC_TCACHE
+CTL_RO_TRUE_GEN(config_tcache)
+#else
+CTL_RO_FALSE_GEN(config_tcache)
+#endif
+
+#ifdef JEMALLOC_TINY
+CTL_RO_TRUE_GEN(config_tiny)
+#else
+CTL_RO_FALSE_GEN(config_tiny)
+#endif
+
+#ifdef JEMALLOC_TLS
+CTL_RO_TRUE_GEN(config_tls)
+#else
+CTL_RO_FALSE_GEN(config_tls)
+#endif
+
+#ifdef JEMALLOC_XMALLOC
+CTL_RO_TRUE_GEN(config_xmalloc)
+#else
+CTL_RO_FALSE_GEN(config_xmalloc)
+#endif
+
+/******************************************************************************/
+
+CTL_RO_GEN(opt_abort, opt_abort, bool)
+#ifdef JEMALLOC_FILL
+CTL_RO_GEN(opt_junk, opt_junk, bool)
+#endif
+#ifdef JEMALLOC_SYSV
+CTL_RO_GEN(opt_sysv, opt_sysv, bool)
+#endif
+#ifdef JEMALLOC_XMALLOC
+CTL_RO_GEN(opt_xmalloc, opt_xmalloc, bool)
+#endif
+#ifdef JEMALLOC_ZERO
+CTL_RO_GEN(opt_zero, opt_zero, bool)
+#endif
+#ifdef JEMALLOC_TCACHE
+CTL_RO_GEN(opt_tcache, opt_tcache, bool)
+CTL_RO_GEN(opt_lg_tcache_gc_sweep, opt_lg_tcache_gc_sweep, ssize_t)
+#endif
+#ifdef JEMALLOC_PROF
+CTL_RO_GEN(opt_prof, opt_prof, bool)
+CTL_RO_GEN(opt_prof_active, opt_prof_active, bool)
+CTL_RO_GEN(opt_lg_prof_bt_max, opt_lg_prof_bt_max, size_t)
+CTL_RO_GEN(opt_lg_prof_sample, opt_lg_prof_sample, size_t)
+CTL_RO_GEN(opt_lg_prof_interval, opt_lg_prof_interval, ssize_t)
+CTL_RO_GEN(opt_prof_udump, opt_prof_udump, bool)
+CTL_RO_GEN(opt_prof_leak, opt_prof_leak, bool)
+#endif
+CTL_RO_GEN(opt_stats_print, opt_stats_print, bool)
+CTL_RO_GEN(opt_lg_qspace_max, opt_lg_qspace_max, size_t)
+CTL_RO_GEN(opt_lg_cspace_max, opt_lg_cspace_max, size_t)
+CTL_RO_GEN(opt_lg_dirty_mult, opt_lg_dirty_mult, ssize_t)
+CTL_RO_GEN(opt_lg_chunk, opt_lg_chunk, size_t)
+#ifdef JEMALLOC_SWAP
+CTL_RO_GEN(opt_overcommit, opt_overcommit, bool)
+#endif
+
+/******************************************************************************/
+
+CTL_RO_GEN(arenas_bin_i_size, arenas[0]->bins[mib[2]].reg_size, size_t)
+CTL_RO_GEN(arenas_bin_i_nregs, arenas[0]->bins[mib[2]].nregs, uint32_t)
+CTL_RO_GEN(arenas_bin_i_run_size, arenas[0]->bins[mib[2]].run_size, size_t)
+const ctl_node_t *
+arenas_bin_i_index(const size_t *mib, size_t miblen, size_t i)
+{
+
+ if (i > nbins)
+ return (NULL);
+ return (super_arenas_bin_i_node);
+}
+
+CTL_RO_GEN(arenas_lrun_i_size, ((mib[2]+1) << PAGE_SHIFT), size_t)
+const ctl_node_t *
+arenas_lrun_i_index(const size_t *mib, size_t miblen, size_t i)
+{
+
+ if (i > nlclasses)
+ return (NULL);
+ return (super_arenas_lrun_i_node);
+}
+
+CTL_RO_GEN(arenas_narenas, narenas, unsigned)
+
+static int
+arenas_initialized_ctl(const size_t *mib, size_t miblen, void *oldp,
+ size_t *oldlenp, void *newp, size_t newlen)
+{
+ int ret;
+ unsigned nread, i;
+
+ READONLY();
+ if (*oldlenp != narenas * sizeof(bool)) {
+ ret = EINVAL;
+ nread = (*oldlenp < narenas * sizeof(bool))
+ ? (*oldlenp / sizeof(bool)) : narenas;
+ } else {
+ ret = 0;
+ nread = narenas;
+ }
+
+ for (i = 0; i < nread; i++)
+ ((bool *)oldp)[i] = ctl_stats.arenas[i].initialized;
+
+RETURN:
+ return (ret);
+}
+
+CTL_RO_GEN(arenas_quantum, QUANTUM, size_t)
+CTL_RO_GEN(arenas_cacheline, CACHELINE, size_t)
+CTL_RO_GEN(arenas_subpage, SUBPAGE, size_t)
+CTL_RO_GEN(arenas_pagesize, PAGE_SIZE, size_t)
+CTL_RO_GEN(arenas_chunksize, chunksize, size_t)
+#ifdef JEMALLOC_TINY
+CTL_RO_GEN(arenas_tspace_min, (1U << LG_TINY_MIN), size_t)
+CTL_RO_GEN(arenas_tspace_max, (qspace_min >> 1), size_t)
+#endif
+CTL_RO_GEN(arenas_qspace_min, qspace_min, size_t)
+CTL_RO_GEN(arenas_qspace_max, qspace_max, size_t)
+CTL_RO_GEN(arenas_cspace_min, cspace_min, size_t)
+CTL_RO_GEN(arenas_cspace_max, cspace_max, size_t)
+CTL_RO_GEN(arenas_sspace_min, sspace_min, size_t)
+CTL_RO_GEN(arenas_sspace_max, sspace_max, size_t)
+#ifdef JEMALLOC_TCACHE
+CTL_RO_GEN(arenas_tcache_max, tcache_maxclass, size_t)
+#endif
+CTL_RO_GEN(arenas_ntbins, ntbins, unsigned)
+CTL_RO_GEN(arenas_nqbins, nqbins, unsigned)
+CTL_RO_GEN(arenas_ncbins, ncbins, unsigned)
+CTL_RO_GEN(arenas_nsbins, nsbins, unsigned)
+CTL_RO_GEN(arenas_nbins, nbins, unsigned)
+#ifdef JEMALLOC_TCACHE
+CTL_RO_GEN(arenas_nhbins, nhbins, unsigned)
+#endif
+CTL_RO_GEN(arenas_nlruns, nlclasses, size_t)
+
+/******************************************************************************/
+
+#ifdef JEMALLOC_PROF
+static int
+prof_active_ctl(const size_t *mib, size_t miblen, void *oldp, size_t *oldlenp,
+ void *newp, size_t newlen)
+{
+ int ret;
+ bool oldval;
+
+ oldval = opt_prof_active;
+ if (newp != NULL) {
+ /*
+ * The memory barriers will tend to make opt_prof_active
+ * propagate faster on systems with weak memory ordering.
+ */
+ mb_write();
+ WRITE(opt_prof_active, bool);
+ mb_write();
+ }
+ READ(oldval, bool);
+
+ ret = 0;
+RETURN:
+ return (ret);
+}
+
+static int
+prof_dump_ctl(const size_t *mib, size_t miblen, void *oldp, size_t *oldlenp,
+ void *newp, size_t newlen)
+{
+ int ret;
+ const char *filename = NULL;
+
+ WRITEONLY();
+ WRITE(filename, const char *);
+
+ if (prof_mdump(filename)) {
+ ret = EFAULT;
+ goto RETURN;
+ }
+
+ ret = 0;
+RETURN:
+ return (ret);
+}
+
+CTL_RO_GEN(prof_interval, prof_interval, uint64_t)
+#endif
+
+/******************************************************************************/
+
+#ifdef JEMALLOC_STATS
+CTL_RO_GEN(stats_chunks_current, ctl_stats.chunks.current, size_t)
+CTL_RO_GEN(stats_chunks_total, ctl_stats.chunks.total, uint64_t)
+CTL_RO_GEN(stats_chunks_high, ctl_stats.chunks.high, size_t)
+CTL_RO_GEN(stats_huge_allocated, huge_allocated, size_t)
+CTL_RO_GEN(stats_huge_nmalloc, huge_nmalloc, uint64_t)
+CTL_RO_GEN(stats_huge_ndalloc, huge_ndalloc, uint64_t)
+CTL_RO_GEN(stats_arenas_i_small_allocated,
+ ctl_stats.arenas[mib[2]].allocated_small, size_t)
+CTL_RO_GEN(stats_arenas_i_small_nmalloc,
+ ctl_stats.arenas[mib[2]].nmalloc_small, uint64_t)
+CTL_RO_GEN(stats_arenas_i_small_ndalloc,
+ ctl_stats.arenas[mib[2]].ndalloc_small, uint64_t)
+CTL_RO_GEN(stats_arenas_i_small_nrequests,
+ ctl_stats.arenas[mib[2]].nrequests_small, uint64_t)
+CTL_RO_GEN(stats_arenas_i_large_allocated,
+ ctl_stats.arenas[mib[2]].astats.allocated_large, size_t)
+CTL_RO_GEN(stats_arenas_i_large_nmalloc,
+ ctl_stats.arenas[mib[2]].astats.nmalloc_large, uint64_t)
+CTL_RO_GEN(stats_arenas_i_large_ndalloc,
+ ctl_stats.arenas[mib[2]].astats.ndalloc_large, uint64_t)
+CTL_RO_GEN(stats_arenas_i_large_nrequests,
+ ctl_stats.arenas[mib[2]].astats.nrequests_large, uint64_t)
+
+CTL_RO_GEN(stats_arenas_i_bins_j_allocated,
+ ctl_stats.arenas[mib[2]].bstats[mib[4]].allocated, size_t)
+CTL_RO_GEN(stats_arenas_i_bins_j_nmalloc,
+ ctl_stats.arenas[mib[2]].bstats[mib[4]].nmalloc, uint64_t)
+CTL_RO_GEN(stats_arenas_i_bins_j_ndalloc,
+ ctl_stats.arenas[mib[2]].bstats[mib[4]].ndalloc, uint64_t)
+CTL_RO_GEN(stats_arenas_i_bins_j_nrequests,
+ ctl_stats.arenas[mib[2]].bstats[mib[4]].nrequests, uint64_t)
+#ifdef JEMALLOC_TCACHE
+CTL_RO_GEN(stats_arenas_i_bins_j_nfills,
+ ctl_stats.arenas[mib[2]].bstats[mib[4]].nfills, uint64_t)
+CTL_RO_GEN(stats_arenas_i_bins_j_nflushes,
+ ctl_stats.arenas[mib[2]].bstats[mib[4]].nflushes, uint64_t)
+#endif
+CTL_RO_GEN(stats_arenas_i_bins_j_nruns,
+ ctl_stats.arenas[mib[2]].bstats[mib[4]].nruns, uint64_t)
+CTL_RO_GEN(stats_arenas_i_bins_j_nreruns,
+ ctl_stats.arenas[mib[2]].bstats[mib[4]].reruns, uint64_t)
+CTL_RO_GEN(stats_arenas_i_bins_j_highruns,
+ ctl_stats.arenas[mib[2]].bstats[mib[4]].highruns, size_t)
+CTL_RO_GEN(stats_arenas_i_bins_j_curruns,
+ ctl_stats.arenas[mib[2]].bstats[mib[4]].curruns, size_t)
+
+const ctl_node_t *
+stats_arenas_i_bins_j_index(const size_t *mib, size_t miblen, size_t j)
+{
+
+ if (j > nbins)
+ return (NULL);
+ return (super_stats_arenas_i_bins_j_node);
+}
+
+CTL_RO_GEN(stats_arenas_i_lruns_j_nmalloc,
+ ctl_stats.arenas[mib[2]].lstats[mib[4]].nmalloc, uint64_t)
+CTL_RO_GEN(stats_arenas_i_lruns_j_ndalloc,
+ ctl_stats.arenas[mib[2]].lstats[mib[4]].ndalloc, uint64_t)
+CTL_RO_GEN(stats_arenas_i_lruns_j_nrequests,
+ ctl_stats.arenas[mib[2]].lstats[mib[4]].nrequests, uint64_t)
+CTL_RO_GEN(stats_arenas_i_lruns_j_curruns,
+ ctl_stats.arenas[mib[2]].lstats[mib[4]].curruns, size_t)
+CTL_RO_GEN(stats_arenas_i_lruns_j_highruns,
+ ctl_stats.arenas[mib[2]].lstats[mib[4]].highruns, size_t)
+
+const ctl_node_t *
+stats_arenas_i_lruns_j_index(const size_t *mib, size_t miblen, size_t j)
+{
+
+ if (j > nlclasses)
+ return (NULL);
+ return (super_stats_arenas_i_lruns_j_node);
+}
+
+#endif
+CTL_RO_GEN(stats_arenas_i_pactive, ctl_stats.arenas[mib[2]].pactive, size_t)
+CTL_RO_GEN(stats_arenas_i_pdirty, ctl_stats.arenas[mib[2]].pdirty, size_t)
+#ifdef JEMALLOC_STATS
+CTL_RO_GEN(stats_arenas_i_mapped, ctl_stats.arenas[mib[2]].astats.mapped,
+ size_t)
+CTL_RO_GEN(stats_arenas_i_npurge, ctl_stats.arenas[mib[2]].astats.npurge,
+ uint64_t)
+CTL_RO_GEN(stats_arenas_i_nmadvise, ctl_stats.arenas[mib[2]].astats.nmadvise,
+ uint64_t)
+CTL_RO_GEN(stats_arenas_i_purged, ctl_stats.arenas[mib[2]].astats.purged,
+ uint64_t)
+#endif
+
+const ctl_node_t *
+stats_arenas_i_index(const size_t *mib, size_t miblen, size_t i)
+{
+
+ if (ctl_stats.arenas[i].initialized == false)
+ return (NULL);
+ return (super_stats_arenas_i_node);
+}
+
+#ifdef JEMALLOC_STATS
+CTL_RO_GEN(stats_allocated, ctl_stats.allocated, size_t)
+CTL_RO_GEN(stats_active, ctl_stats.active, size_t)
+CTL_RO_GEN(stats_mapped, ctl_stats.mapped, size_t)
+#endif
+
+/******************************************************************************/
+
+#ifdef JEMALLOC_SWAP
+# ifdef JEMALLOC_STATS
+CTL_RO_GEN(swap_avail, ctl_stats.swap_avail, size_t)
+# endif
+
+static int
+swap_prezeroed_ctl(const size_t *mib, size_t miblen, void *oldp,
+ size_t *oldlenp, void *newp, size_t newlen)
+{
+ int ret;
+
+ if (swap_enabled) {
+ READONLY();
+ } else {
+ /*
+ * swap_prezeroed isn't actually used by the swap code until it
+ * is set during a successful chunk_swap_enabled() call. We
+ * use it here to store the value that we'll pass to
+ * chunk_swap_enable() in a swap.fds mallctl(). This is not
+ * very clean, but the obvious alternatives are even worse.
+ */
+ WRITE(swap_prezeroed, bool);
+ }
+
+ READ(swap_prezeroed, bool);
+
+ ret = 0;
+RETURN:
+ return (ret);
+}
+
+CTL_RO_GEN(swap_nfds, swap_nfds, size_t)
+
+static int
+swap_fds_ctl(const size_t *mib, size_t miblen, void *oldp, size_t *oldlenp,
+ void *newp, size_t newlen)
+{
+ int ret;
+
+ if (swap_enabled) {
+ READONLY();
+ } else if (newp != NULL) {
+ size_t nfds = newlen / sizeof(int);
+
+ {
+ int fds[nfds];
+
+ memcpy(fds, newp, nfds * sizeof(int));
+ if (chunk_swap_enable(fds, nfds, swap_prezeroed)) {
+ ret = EFAULT;
+ goto RETURN;
+ }
+ }
+ }
+
+ if (oldp != NULL && oldlenp != NULL) {
+ if (*oldlenp != swap_nfds * sizeof(int)) {
+ size_t copylen = (swap_nfds * sizeof(int) <= *oldlenp)
+ ? swap_nfds * sizeof(int) : *oldlenp;
+
+ memcpy(oldp, swap_fds, copylen);
+ ret = EINVAL;
+ goto RETURN;
+ } else
+ memcpy(oldp, swap_fds, *oldlenp);
+ }
+
+ ret = 0;
+RETURN:
+ return (ret);
+}
+#endif
diff --git a/externals/jemalloc/delme b/externals/jemalloc/delme
deleted file mode 100644
index e69de29bb2d..00000000000
--- a/externals/jemalloc/delme
+++ /dev/null
diff --git a/externals/jemalloc/extent.c b/externals/jemalloc/extent.c
new file mode 100644
index 00000000000..3c04d3aa5d1
--- /dev/null
+++ b/externals/jemalloc/extent.c
@@ -0,0 +1,41 @@
+#define JEMALLOC_EXTENT_C_
+#include "jemalloc/internal/jemalloc_internal.h"
+
+/******************************************************************************/
+
+#if (defined(JEMALLOC_SWAP) || defined(JEMALLOC_DSS))
+static inline int
+extent_szad_comp(extent_node_t *a, extent_node_t *b)
+{
+ int ret;
+ size_t a_size = a->size;
+ size_t b_size = b->size;
+
+ ret = (a_size > b_size) - (a_size < b_size);
+ if (ret == 0) {
+ uintptr_t a_addr = (uintptr_t)a->addr;
+ uintptr_t b_addr = (uintptr_t)b->addr;
+
+ ret = (a_addr > b_addr) - (a_addr < b_addr);
+ }
+
+ return (ret);
+}
+
+/* Generate red-black tree functions. */
+rb_gen(, extent_tree_szad_, extent_tree_t, extent_node_t, link_szad,
+ extent_szad_comp)
+#endif
+
+static inline int
+extent_ad_comp(extent_node_t *a, extent_node_t *b)
+{
+ uintptr_t a_addr = (uintptr_t)a->addr;
+ uintptr_t b_addr = (uintptr_t)b->addr;
+
+ return ((a_addr > b_addr) - (a_addr < b_addr));
+}
+
+/* Generate red-black tree functions. */
+rb_gen(, extent_tree_ad_, extent_tree_t, extent_node_t, link_ad,
+ extent_ad_comp)
diff --git a/externals/jemalloc/hash.c b/externals/jemalloc/hash.c
new file mode 100644
index 00000000000..6a13d7a03c0
--- /dev/null
+++ b/externals/jemalloc/hash.c
@@ -0,0 +1,2 @@
+#define HASH_C_
+#include "jemalloc/internal/jemalloc_internal.h"
diff --git a/externals/jemalloc/huge.c b/externals/jemalloc/huge.c
new file mode 100644
index 00000000000..d35aa5cdd00
--- /dev/null
+++ b/externals/jemalloc/huge.c
@@ -0,0 +1,298 @@
+#define JEMALLOC_HUGE_C_
+#include "jemalloc/internal/jemalloc_internal.h"
+
+/******************************************************************************/
+/* Data. */
+
+#ifdef JEMALLOC_STATS
+uint64_t huge_nmalloc;
+uint64_t huge_ndalloc;
+size_t huge_allocated;
+#endif
+
+malloc_mutex_t huge_mtx;
+
+/******************************************************************************/
+
+/* Tree of chunks that are stand-alone huge allocations. */
+static extent_tree_t huge;
+
+void *
+huge_malloc(size_t size, bool zero)
+{
+ void *ret;
+ size_t csize;
+ extent_node_t *node;
+
+ /* Allocate one or more contiguous chunks for this request. */
+
+ csize = CHUNK_CEILING(size);
+ if (csize == 0) {
+ /* size is large enough to cause size_t wrap-around. */
+ return (NULL);
+ }
+
+ /* Allocate an extent node with which to track the chunk. */
+ node = base_node_alloc();
+ if (node == NULL)
+ return (NULL);
+
+ ret = chunk_alloc(csize, &zero);
+ if (ret == NULL) {
+ base_node_dealloc(node);
+ return (NULL);
+ }
+
+ /* Insert node into huge. */
+ node->addr = ret;
+ node->size = csize;
+
+ malloc_mutex_lock(&huge_mtx);
+ extent_tree_ad_insert(&huge, node);
+#ifdef JEMALLOC_STATS
+ huge_nmalloc++;
+ huge_allocated += csize;
+#endif
+ malloc_mutex_unlock(&huge_mtx);
+
+#ifdef JEMALLOC_FILL
+ if (zero == false) {
+ if (opt_junk)
+ memset(ret, 0xa5, csize);
+ else if (opt_zero)
+ memset(ret, 0, csize);
+ }
+#endif
+
+ return (ret);
+}
+
+/* Only handles large allocations that require more than chunk alignment. */
+void *
+huge_palloc(size_t alignment, size_t size)
+{
+ void *ret;
+ size_t alloc_size, chunk_size, offset;
+ extent_node_t *node;
+ bool zero;
+
+ /*
+ * This allocation requires alignment that is even larger than chunk
+ * alignment. This means that huge_malloc() isn't good enough.
+ *
+ * Allocate almost twice as many chunks as are demanded by the size or
+ * alignment, in order to assure the alignment can be achieved, then
+ * unmap leading and trailing chunks.
+ */
+ assert(alignment >= chunksize);
+
+ chunk_size = CHUNK_CEILING(size);
+
+ if (size >= alignment)
+ alloc_size = chunk_size + alignment - chunksize;
+ else
+ alloc_size = (alignment << 1) - chunksize;
+
+ /* Allocate an extent node with which to track the chunk. */
+ node = base_node_alloc();
+ if (node == NULL)
+ return (NULL);
+
+ zero = false;
+ ret = chunk_alloc(alloc_size, &zero);
+ if (ret == NULL) {
+ base_node_dealloc(node);
+ return (NULL);
+ }
+
+ offset = (uintptr_t)ret & (alignment - 1);
+ assert((offset & chunksize_mask) == 0);
+ assert(offset < alloc_size);
+ if (offset == 0) {
+ /* Trim trailing space. */
+ chunk_dealloc((void *)((uintptr_t)ret + chunk_size), alloc_size
+ - chunk_size);
+ } else {
+ size_t trailsize;
+
+ /* Trim leading space. */
+ chunk_dealloc(ret, alignment - offset);
+
+ ret = (void *)((uintptr_t)ret + (alignment - offset));
+
+ trailsize = alloc_size - (alignment - offset) - chunk_size;
+ if (trailsize != 0) {
+ /* Trim trailing space. */
+ assert(trailsize < alloc_size);
+ chunk_dealloc((void *)((uintptr_t)ret + chunk_size),
+ trailsize);
+ }
+ }
+
+ /* Insert node into huge. */
+ node->addr = ret;
+ node->size = chunk_size;
+
+ malloc_mutex_lock(&huge_mtx);
+ extent_tree_ad_insert(&huge, node);
+#ifdef JEMALLOC_STATS
+ huge_nmalloc++;
+ huge_allocated += chunk_size;
+#endif
+ malloc_mutex_unlock(&huge_mtx);
+
+#ifdef JEMALLOC_FILL
+ if (opt_junk)
+ memset(ret, 0xa5, chunk_size);
+ else if (opt_zero)
+ memset(ret, 0, chunk_size);
+#endif
+
+ return (ret);
+}
+
+void *
+huge_ralloc(void *ptr, size_t size, size_t oldsize)
+{
+ void *ret;
+ size_t copysize;
+
+ /* Avoid moving the allocation if the size class would not change. */
+ if (oldsize > arena_maxclass &&
+ CHUNK_CEILING(size) == CHUNK_CEILING(oldsize)) {
+#ifdef JEMALLOC_FILL
+ if (opt_junk && size < oldsize) {
+ memset((void *)((uintptr_t)ptr + size), 0x5a, oldsize
+ - size);
+ } else if (opt_zero && size > oldsize) {
+ memset((void *)((uintptr_t)ptr + oldsize), 0, size
+ - oldsize);
+ }
+#endif
+ return (ptr);
+ }
+
+ /*
+ * If we get here, then size and oldsize are different enough that we
+ * need to use a different size class. In that case, fall back to
+ * allocating new space and copying.
+ */
+ ret = huge_malloc(size, false);
+ if (ret == NULL)
+ return (NULL);
+
+ copysize = (size < oldsize) ? size : oldsize;
+ memcpy(ret, ptr, copysize);
+ idalloc(ptr);
+ return (ret);
+}
+
+void
+huge_dalloc(void *ptr)
+{
+ extent_node_t *node, key;
+
+ malloc_mutex_lock(&huge_mtx);
+
+ /* Extract from tree of huge allocations. */
+ key.addr = ptr;
+ node = extent_tree_ad_search(&huge, &key);
+ assert(node != NULL);
+ assert(node->addr == ptr);
+ extent_tree_ad_remove(&huge, node);
+
+#ifdef JEMALLOC_STATS
+ huge_ndalloc++;
+ huge_allocated -= node->size;
+#endif
+
+ malloc_mutex_unlock(&huge_mtx);
+
+ /* Unmap chunk. */
+#ifdef JEMALLOC_FILL
+#if (defined(JEMALLOC_SWAP) || defined(JEMALLOC_DSS))
+ if (opt_junk)
+ memset(node->addr, 0x5a, node->size);
+#endif
+#endif
+ chunk_dealloc(node->addr, node->size);
+
+ base_node_dealloc(node);
+}
+
+size_t
+huge_salloc(const void *ptr)
+{
+ size_t ret;
+ extent_node_t *node, key;
+
+ malloc_mutex_lock(&huge_mtx);
+
+ /* Extract from tree of huge allocations. */
+ key.addr = __DECONST(void *, ptr);
+ node = extent_tree_ad_search(&huge, &key);
+ assert(node != NULL);
+
+ ret = node->size;
+
+ malloc_mutex_unlock(&huge_mtx);
+
+ return (ret);
+}
+
+#ifdef JEMALLOC_PROF
+prof_thr_cnt_t *
+huge_prof_cnt_get(const void *ptr)
+{
+ prof_thr_cnt_t *ret;
+ extent_node_t *node, key;
+
+ malloc_mutex_lock(&huge_mtx);
+
+ /* Extract from tree of huge allocations. */
+ key.addr = __DECONST(void *, ptr);
+ node = extent_tree_ad_search(&huge, &key);
+ assert(node != NULL);
+
+ ret = node->prof_cnt;
+
+ malloc_mutex_unlock(&huge_mtx);
+
+ return (ret);
+}
+
+void
+huge_prof_cnt_set(const void *ptr, prof_thr_cnt_t *cnt)
+{
+ extent_node_t *node, key;
+
+ malloc_mutex_lock(&huge_mtx);
+
+ /* Extract from tree of huge allocations. */
+ key.addr = __DECONST(void *, ptr);
+ node = extent_tree_ad_search(&huge, &key);
+ assert(node != NULL);
+
+ node->prof_cnt = cnt;
+
+ malloc_mutex_unlock(&huge_mtx);
+}
+#endif
+
+bool
+huge_boot(void)
+{
+
+ /* Initialize chunks data. */
+ if (malloc_mutex_init(&huge_mtx))
+ return (true);
+ extent_tree_ad_new(&huge);
+
+#ifdef JEMALLOC_STATS
+ huge_nmalloc = 0;
+ huge_ndalloc = 0;
+ huge_allocated = 0;
+#endif
+
+ return (false);
+}
diff --git a/externals/jemalloc/include/internal/arena.h b/externals/jemalloc/include/internal/arena.h
new file mode 100644
index 00000000000..bb4ce2a54f7
--- /dev/null
+++ b/externals/jemalloc/include/internal/arena.h
@@ -0,0 +1,537 @@
+/******************************************************************************/
+#ifdef JEMALLOC_H_TYPES
+
+/*
+ * Subpages are an artificially designated partitioning of pages. Their only
+ * purpose is to support subpage-spaced size classes.
+ *
+ * There must be at least 4 subpages per page, due to the way size classes are
+ * handled.
+ */
+#define LG_SUBPAGE 8
+#define SUBPAGE ((size_t)(1U << LG_SUBPAGE))
+#define SUBPAGE_MASK (SUBPAGE - 1)
+
+/* Return the smallest subpage multiple that is >= s. */
+#define SUBPAGE_CEILING(s) \
+ (((s) + SUBPAGE_MASK) & ~SUBPAGE_MASK)
+
+#ifdef JEMALLOC_TINY
+ /* Smallest size class to support. */
+# define LG_TINY_MIN LG_SIZEOF_PTR
+#endif
+
+/*
+ * Maximum size class that is a multiple of the quantum, but not (necessarily)
+ * a power of 2. Above this size, allocations are rounded up to the nearest
+ * power of 2.
+ */
+#define LG_QSPACE_MAX_DEFAULT 7
+
+/*
+ * Maximum size class that is a multiple of the cacheline, but not (necessarily)
+ * a power of 2. Above this size, allocations are rounded up to the nearest
+ * power of 2.
+ */
+#define LG_CSPACE_MAX_DEFAULT 9
+
+/*
+ * RUN_MAX_OVRHD indicates maximum desired run header overhead. Runs are sized
+ * as small as possible such that this setting is still honored, without
+ * violating other constraints. The goal is to make runs as small as possible
+ * without exceeding a per run external fragmentation threshold.
+ *
+ * We use binary fixed point math for overhead computations, where the binary
+ * point is implicitly RUN_BFP bits to the left.
+ *
+ * Note that it is possible to set RUN_MAX_OVRHD low enough that it cannot be
+ * honored for some/all object sizes, since there is one bit of header overhead
+ * per object (plus a constant). This constraint is relaxed (ignored) for runs
+ * that are so small that the per-region overhead is greater than:
+ *
+ * (RUN_MAX_OVRHD / (reg_size << (3+RUN_BFP))
+ */
+#define RUN_BFP 12
+/* \/ Implicit binary fixed point. */
+#define RUN_MAX_OVRHD 0x0000003dU
+#define RUN_MAX_OVRHD_RELAX 0x00001800U
+
+/*
+ * The minimum ratio of active:dirty pages per arena is computed as:
+ *
+ * (nactive >> opt_lg_dirty_mult) >= ndirty
+ *
+ * So, supposing that opt_lg_dirty_mult is 5, there can be no less than 32
+ * times as many active pages as dirty pages.
+ */
+#define LG_DIRTY_MULT_DEFAULT 5
+
+typedef struct arena_chunk_map_s arena_chunk_map_t;
+typedef struct arena_chunk_s arena_chunk_t;
+typedef struct arena_run_s arena_run_t;
+typedef struct arena_bin_s arena_bin_t;
+typedef struct arena_s arena_t;
+
+#endif /* JEMALLOC_H_TYPES */
+/******************************************************************************/
+#ifdef JEMALLOC_H_STRUCTS
+
+/* Each element of the chunk map corresponds to one page within the chunk. */
+struct arena_chunk_map_s {
+ union {
+ /*
+ * Linkage for run trees. There are two disjoint uses:
+ *
+ * 1) arena_t's runs_avail_{clean,dirty} trees.
+ * 2) arena_run_t conceptually uses this linkage for in-use
+ * non-full runs, rather than directly embedding linkage.
+ */
+ rb_node(arena_chunk_map_t) rb_link;
+ /*
+ * List of runs currently in purgatory. arena_chunk_purge()
+ * temporarily allocates runs that contain dirty pages while
+ * purging, so that other threads cannot use the runs while the
+ * purging thread is operating without the arena lock held.
+ */
+ ql_elm(arena_chunk_map_t) ql_link;
+ } u;
+
+#ifdef JEMALLOC_PROF
+ /* Profile counters, used for large object runs. */
+ prof_thr_cnt_t *prof_cnt;
+#endif
+
+ /*
+ * Run address (or size) and various flags are stored together. The bit
+ * layout looks like (assuming 32-bit system):
+ *
+ * ???????? ???????? ????---- ----dzla
+ *
+ * ? : Unallocated: Run address for first/last pages, unset for internal
+ * pages.
+ * Small: Run page offset.
+ * Large: Run size for first page, unset for trailing pages.
+ * - : Unused.
+ * d : dirty?
+ * z : zeroed?
+ * l : large?
+ * a : allocated?
+ *
+ * Following are example bit patterns for the three types of runs.
+ *
+ * p : run page offset
+ * s : run size
+ * c : size class (used only if prof_promote is true)
+ * x : don't care
+ * - : 0
+ * + : 1
+ * [DZLA] : bit set
+ * [dzla] : bit unset
+ *
+ * Unallocated (clean):
+ * ssssssss ssssssss ssss---- ----dz--
+ * xxxxxxxx xxxxxxxx xxxx---- -----Zxx
+ * ssssssss ssssssss ssss---- ----dZ--
+ *
+ * Unallocated (dirty):
+ * ssssssss ssssssss ssss---- ----D---
+ * xxxxxxxx xxxxxxxx xxxx---- ----xxxx
+ * ssssssss ssssssss ssss---- ----D---
+ *
+ * Small:
+ * pppppppp pppppppp pppp---- ----d--a
+ * pppppppp pppppppp pppp---- -------a
+ * pppppppp pppppppp pppp---- ----d--a
+ *
+ * Large:
+ * ssssssss ssssssss ssss++++ ++++D-la
+ * xxxxxxxx xxxxxxxx xxxx---- ----xxxx
+ * -------- -------- -------- ----D-la
+ *
+ * Large (sampled, size <= PAGE_SIZE):
+ * ssssssss ssssssss sssscccc ccccD-la
+ *
+ * Large (not sampled, size == PAGE_SIZE):
+ * ssssssss ssssssss ssss++++ ++++D-la
+ */
+ size_t bits;
+#ifdef JEMALLOC_PROF
+#define CHUNK_MAP_CLASS_SHIFT 4
+#define CHUNK_MAP_CLASS_MASK ((size_t)0xff0U)
+#endif
+#define CHUNK_MAP_FLAGS_MASK ((size_t)0xfU)
+#define CHUNK_MAP_DIRTY ((size_t)0x8U)
+#define CHUNK_MAP_ZEROED ((size_t)0x4U)
+#define CHUNK_MAP_LARGE ((size_t)0x2U)
+#define CHUNK_MAP_ALLOCATED ((size_t)0x1U)
+#define CHUNK_MAP_KEY CHUNK_MAP_ALLOCATED
+};
+typedef rb_tree(arena_chunk_map_t) arena_avail_tree_t;
+typedef rb_tree(arena_chunk_map_t) arena_run_tree_t;
+
+/* Arena chunk header. */
+struct arena_chunk_s {
+ /* Arena that owns the chunk. */
+ arena_t *arena;
+
+ /* Linkage for the arena's chunks_dirty list. */
+ ql_elm(arena_chunk_t) link_dirty;
+
+ /*
+ * True if the chunk is currently in the chunks_dirty list, due to
+ * having at some point contained one or more dirty pages. Removal
+ * from chunks_dirty is lazy, so (dirtied && ndirty == 0) is possible.
+ */
+ bool dirtied;
+
+ /* Number of dirty pages. */
+ size_t ndirty;
+
+ /* Map of pages within chunk that keeps track of free/large/small. */
+ arena_chunk_map_t map[1]; /* Dynamically sized. */
+};
+typedef rb_tree(arena_chunk_t) arena_chunk_tree_t;
+
+struct arena_run_s {
+#ifdef JEMALLOC_DEBUG
+ uint32_t magic;
+# define ARENA_RUN_MAGIC 0x384adf93
+#endif
+
+ /* Bin this run is associated with. */
+ arena_bin_t *bin;
+
+ /* Stack of available freed regions, or NULL. */
+ void *avail;
+
+ /* Next region that has never been allocated, or run boundary. */
+ void *next;
+
+ /* Number of free regions in run. */
+ unsigned nfree;
+};
+
+struct arena_bin_s {
+ /*
+ * All operations on runcur, runs, and stats require that lock be
+ * locked. Run allocation/deallocation are protected by the arena lock,
+ * which may be acquired while holding one or more bin locks, but not
+ * vise versa.
+ */
+ malloc_mutex_t lock;
+
+ /*
+ * Current run being used to service allocations of this bin's size
+ * class.
+ */
+ arena_run_t *runcur;
+
+ /*
+ * Tree of non-full runs. This tree is used when looking for an
+ * existing run when runcur is no longer usable. We choose the
+ * non-full run that is lowest in memory; this policy tends to keep
+ * objects packed well, and it can also help reduce the number of
+ * almost-empty chunks.
+ */
+ arena_run_tree_t runs;
+
+ /* Size of regions in a run for this bin's size class. */
+ size_t reg_size;
+
+ /* Total size of a run for this bin's size class. */
+ size_t run_size;
+
+ /* Total number of regions in a run for this bin's size class. */
+ uint32_t nregs;
+
+#ifdef JEMALLOC_PROF
+ /*
+ * Offset of first (prof_cnt_t *) in a run header for this bin's size
+ * class, or 0 if (opt_prof == false).
+ */
+ uint32_t cnt0_offset;
+#endif
+
+ /* Offset of first region in a run for this bin's size class. */
+ uint32_t reg0_offset;
+
+#ifdef JEMALLOC_STATS
+ /* Bin statistics. */
+ malloc_bin_stats_t stats;
+#endif
+};
+
+struct arena_s {
+#ifdef JEMALLOC_DEBUG
+ uint32_t magic;
+# define ARENA_MAGIC 0x947d3d24
+#endif
+
+ /* This arena's index within the arenas array. */
+ unsigned ind;
+
+ /*
+ * All non-bin-related operations on this arena require that lock be
+ * locked.
+ */
+ malloc_mutex_t lock;
+
+#ifdef JEMALLOC_STATS
+ arena_stats_t stats;
+# ifdef JEMALLOC_TCACHE
+ /*
+ * List of tcaches for extant threads associated with this arena.
+ * Stats from these are merged incrementally, and at exit.
+ */
+ ql_head(tcache_t) tcache_ql;
+# endif
+#endif
+
+#ifdef JEMALLOC_PROF
+ uint64_t prof_accumbytes;
+#endif
+
+ /* List of dirty-page-containing chunks this arena manages. */
+ ql_head(arena_chunk_t) chunks_dirty;
+
+ /*
+ * In order to avoid rapid chunk allocation/deallocation when an arena
+ * oscillates right on the cusp of needing a new chunk, cache the most
+ * recently freed chunk. The spare is left in the arena's chunk trees
+ * until it is deleted.
+ *
+ * There is one spare chunk per arena, rather than one spare total, in
+ * order to avoid interactions between multiple threads that could make
+ * a single spare inadequate.
+ */
+ arena_chunk_t *spare;
+
+ /* Number of pages in active runs. */
+ size_t nactive;
+
+ /*
+ * Current count of pages within unused runs that are potentially
+ * dirty, and for which madvise(... MADV_DONTNEED) has not been called.
+ * By tracking this, we can institute a limit on how much dirty unused
+ * memory is mapped for each arena.
+ */
+ size_t ndirty;
+
+ /*
+ * Approximate number of pages being purged. It is possible for
+ * multiple threads to purge dirty pages concurrently, and they use
+ * npurgatory to indicate the total number of pages all threads are
+ * attempting to purge.
+ */
+ size_t npurgatory;
+
+ /*
+ * Size/address-ordered trees of this arena's available runs. The trees
+ * are used for first-best-fit run allocation. The dirty tree contains
+ * runs with dirty pages (i.e. very likely to have been touched and
+ * therefore have associated physical pages), whereas the clean tree
+ * contains runs with pages that either have no associated physical
+ * pages, or have pages that the kernel may recycle at any time due to
+ * previous madvise(2) calls. The dirty tree is used in preference to
+ * the clean tree for allocations, because using dirty pages reduces
+ * the amount of dirty purging necessary to keep the active:dirty page
+ * ratio below the purge threshold.
+ */
+ arena_avail_tree_t runs_avail_clean;
+ arena_avail_tree_t runs_avail_dirty;
+
+ /*
+ * bins is used to store trees of free regions of the following sizes,
+ * assuming a 16-byte quantum, 4 KiB page size, and default
+ * JEMALLOC_OPTIONS.
+ *
+ * bins[i] | size |
+ * --------+--------+
+ * 0 | 2 |
+ * 1 | 4 |
+ * 2 | 8 |
+ * --------+--------+
+ * 3 | 16 |
+ * 4 | 32 |
+ * 5 | 48 |
+ * : :
+ * 8 | 96 |
+ * 9 | 112 |
+ * 10 | 128 |
+ * --------+--------+
+ * 11 | 192 |
+ * 12 | 256 |
+ * 13 | 320 |
+ * 14 | 384 |
+ * 15 | 448 |
+ * 16 | 512 |
+ * --------+--------+
+ * 17 | 768 |
+ * 18 | 1024 |
+ * 19 | 1280 |
+ * : :
+ * 27 | 3328 |
+ * 28 | 3584 |
+ * 29 | 3840 |
+ * --------+--------+
+ * 30 | 4 KiB |
+ * 31 | 6 KiB |
+ * 33 | 8 KiB |
+ * : :
+ * 43 | 28 KiB |
+ * 44 | 30 KiB |
+ * 45 | 32 KiB |
+ * --------+--------+
+ */
+ arena_bin_t bins[1]; /* Dynamically sized. */
+};
+
+#endif /* JEMALLOC_H_STRUCTS */
+/******************************************************************************/
+#ifdef JEMALLOC_H_EXTERNS
+
+extern size_t opt_lg_qspace_max;
+extern size_t opt_lg_cspace_max;
+extern ssize_t opt_lg_dirty_mult;
+extern uint8_t const *small_size2bin;
+
+/* Various bin-related settings. */
+#ifdef JEMALLOC_TINY /* Number of (2^n)-spaced tiny bins. */
+# define ntbins ((unsigned)(LG_QUANTUM - LG_TINY_MIN))
+#else
+# define ntbins 0
+#endif
+extern unsigned nqbins; /* Number of quantum-spaced bins. */
+extern unsigned ncbins; /* Number of cacheline-spaced bins. */
+extern unsigned nsbins; /* Number of subpage-spaced bins. */
+extern unsigned nbins;
+#ifdef JEMALLOC_TINY
+# define tspace_max ((size_t)(QUANTUM >> 1))
+#endif
+#define qspace_min QUANTUM
+extern size_t qspace_max;
+extern size_t cspace_min;
+extern size_t cspace_max;
+extern size_t sspace_min;
+extern size_t sspace_max;
+#define small_maxclass sspace_max
+
+#define nlclasses (chunk_npages - arena_chunk_header_npages)
+
+#ifdef JEMALLOC_TCACHE
+void arena_tcache_fill_small(arena_t *arena, tcache_bin_t *tbin,
+ size_t binind
+# ifdef JEMALLOC_PROF
+ , uint64_t prof_accumbytes
+# endif
+ );
+#endif
+#ifdef JEMALLOC_PROF
+void arena_prof_accum(arena_t *arena, uint64_t accumbytes);
+#endif
+void *arena_malloc_small(arena_t *arena, size_t size, bool zero);
+void *arena_malloc_large(arena_t *arena, size_t size, bool zero);
+void *arena_malloc(size_t size, bool zero);
+void *arena_palloc(arena_t *arena, size_t alignment, size_t size,
+ size_t alloc_size);
+size_t arena_salloc(const void *ptr);
+#ifdef JEMALLOC_PROF
+void arena_prof_promoted(const void *ptr, size_t size);
+size_t arena_salloc_demote(const void *ptr);
+prof_thr_cnt_t *arena_prof_cnt_get(const void *ptr);
+void arena_prof_cnt_set(const void *ptr, prof_thr_cnt_t *cnt);
+#endif
+void arena_dalloc_bin(arena_t *arena, arena_chunk_t *chunk, void *ptr,
+ arena_chunk_map_t *mapelm);
+void arena_dalloc_large(arena_t *arena, arena_chunk_t *chunk, void *ptr);
+#ifdef JEMALLOC_STATS
+void arena_stats_merge(arena_t *arena, size_t *nactive, size_t *ndirty,
+ arena_stats_t *astats, malloc_bin_stats_t *bstats,
+ malloc_large_stats_t *lstats);
+#endif
+void *arena_ralloc(void *ptr, size_t size, size_t oldsize);
+bool arena_new(arena_t *arena, unsigned ind);
+bool arena_boot(void);
+
+#endif /* JEMALLOC_H_EXTERNS */
+/******************************************************************************/
+#ifdef JEMALLOC_H_INLINES
+
+#ifndef JEMALLOC_ENABLE_INLINE
+void arena_dalloc(arena_t *arena, arena_chunk_t *chunk, void *ptr);
+#endif
+
+#if (defined(JEMALLOC_ENABLE_INLINE) || defined(JEMALLOC_ARENA_C_))
+JEMALLOC_INLINE void
+arena_dalloc(arena_t *arena, arena_chunk_t *chunk, void *ptr)
+{
+ size_t pageind;
+ arena_chunk_map_t *mapelm;
+
+ assert(arena != NULL);
+ assert(arena->magic == ARENA_MAGIC);
+ assert(chunk->arena == arena);
+ assert(ptr != NULL);
+ assert(CHUNK_ADDR2BASE(ptr) != ptr);
+
+ pageind = (((uintptr_t)ptr - (uintptr_t)chunk) >> PAGE_SHIFT);
+ mapelm = &chunk->map[pageind];
+ assert((mapelm->bits & CHUNK_MAP_ALLOCATED) != 0);
+ if ((mapelm->bits & CHUNK_MAP_LARGE) == 0) {
+ /* Small allocation. */
+#ifdef JEMALLOC_TCACHE
+ tcache_t *tcache;
+
+ if ((tcache = tcache_get()) != NULL)
+ tcache_dalloc_small(tcache, ptr);
+ else {
+#endif
+ arena_run_t *run;
+ arena_bin_t *bin;
+
+ run = (arena_run_t *)((uintptr_t)chunk +
+ (uintptr_t)((pageind - (mapelm->bits >>
+ PAGE_SHIFT)) << PAGE_SHIFT));
+ assert(run->magic == ARENA_RUN_MAGIC);
+ assert(((uintptr_t)ptr - ((uintptr_t)run +
+ (uintptr_t)run->bin->reg0_offset)) %
+ run->bin->reg_size == 0);
+ bin = run->bin;
+ malloc_mutex_lock(&bin->lock);
+ arena_dalloc_bin(arena, chunk, ptr, mapelm);
+ malloc_mutex_unlock(&bin->lock);
+#ifdef JEMALLOC_TCACHE
+ }
+#endif
+ } else {
+#ifdef JEMALLOC_TCACHE
+ size_t size = mapelm->bits & ~PAGE_MASK;
+
+ assert(((uintptr_t)ptr & PAGE_MASK) == 0);
+ if (size <= tcache_maxclass) {
+ tcache_t *tcache;
+
+ if ((tcache = tcache_get()) != NULL)
+ tcache_dalloc_large(tcache, ptr, size);
+ else {
+ malloc_mutex_lock(&arena->lock);
+ arena_dalloc_large(arena, chunk, ptr);
+ malloc_mutex_unlock(&arena->lock);
+ }
+ } else {
+ malloc_mutex_lock(&arena->lock);
+ arena_dalloc_large(arena, chunk, ptr);
+ malloc_mutex_unlock(&arena->lock);
+ }
+#else
+ assert(((uintptr_t)ptr & PAGE_MASK) == 0);
+ malloc_mutex_lock(&arena->lock);
+ arena_dalloc_large(arena, chunk, ptr);
+ malloc_mutex_unlock(&arena->lock);
+#endif
+ }
+}
+#endif
+
+#endif /* JEMALLOC_H_INLINES */
+/******************************************************************************/
diff --git a/externals/jemalloc/include/internal/base.h b/externals/jemalloc/include/internal/base.h
new file mode 100644
index 00000000000..e353f309bd2
--- /dev/null
+++ b/externals/jemalloc/include/internal/base.h
@@ -0,0 +1,24 @@
+/******************************************************************************/
+#ifdef JEMALLOC_H_TYPES
+
+#endif /* JEMALLOC_H_TYPES */
+/******************************************************************************/
+#ifdef JEMALLOC_H_STRUCTS
+
+#endif /* JEMALLOC_H_STRUCTS */
+/******************************************************************************/
+#ifdef JEMALLOC_H_EXTERNS
+
+extern malloc_mutex_t base_mtx;
+
+void *base_alloc(size_t size);
+extent_node_t *base_node_alloc(void);
+void base_node_dealloc(extent_node_t *node);
+bool base_boot(void);
+
+#endif /* JEMALLOC_H_EXTERNS */
+/******************************************************************************/
+#ifdef JEMALLOC_H_INLINES
+
+#endif /* JEMALLOC_H_INLINES */
+/******************************************************************************/
diff --git a/externals/jemalloc/include/internal/chunk.h b/externals/jemalloc/include/internal/chunk.h
new file mode 100644
index 00000000000..1f6abf782f1
--- /dev/null
+++ b/externals/jemalloc/include/internal/chunk.h
@@ -0,0 +1,61 @@
+/******************************************************************************/
+#ifdef JEMALLOC_H_TYPES
+
+/*
+ * Size and alignment of memory chunks that are allocated by the OS's virtual
+ * memory system.
+ */
+#define LG_CHUNK_DEFAULT 22
+
+/* Return the chunk address for allocation address a. */
+#define CHUNK_ADDR2BASE(a) \
+ ((void *)((uintptr_t)(a) & ~chunksize_mask))
+
+/* Return the chunk offset of address a. */
+#define CHUNK_ADDR2OFFSET(a) \
+ ((size_t)((uintptr_t)(a) & chunksize_mask))
+
+/* Return the smallest chunk multiple that is >= s. */
+#define CHUNK_CEILING(s) \
+ (((s) + chunksize_mask) & ~chunksize_mask)
+
+#endif /* JEMALLOC_H_TYPES */
+/******************************************************************************/
+#ifdef JEMALLOC_H_STRUCTS
+
+#endif /* JEMALLOC_H_STRUCTS */
+/******************************************************************************/
+#ifdef JEMALLOC_H_EXTERNS
+
+extern size_t opt_lg_chunk;
+#ifdef JEMALLOC_SWAP
+extern bool opt_overcommit;
+#endif
+
+#if (defined(JEMALLOC_STATS) || defined(JEMALLOC_PROF))
+/* Protects stats_chunks; currently not used for any other purpose. */
+extern malloc_mutex_t chunks_mtx;
+/* Chunk statistics. */
+extern chunk_stats_t stats_chunks;
+#endif
+
+extern size_t chunksize;
+extern size_t chunksize_mask; /* (chunksize - 1). */
+extern size_t chunk_npages;
+extern size_t arena_chunk_header_npages;
+extern size_t arena_maxclass; /* Max size class for arenas. */
+
+void *chunk_alloc(size_t size, bool *zero);
+void chunk_dealloc(void *chunk, size_t size);
+bool chunk_boot(void);
+
+#endif /* JEMALLOC_H_EXTERNS */
+/******************************************************************************/
+#ifdef JEMALLOC_H_INLINES
+
+#endif /* JEMALLOC_H_INLINES */
+/******************************************************************************/
+
+#include "jemalloc/internal/chunk_swap.h"
+#include "jemalloc/internal/chunk_dss.h"
+#include "jemalloc/internal/chunk_mmap.h"
diff --git a/externals/jemalloc/include/internal/chunk_dss.h b/externals/jemalloc/include/internal/chunk_dss.h
new file mode 100644
index 00000000000..6be4ad1f212
--- /dev/null
+++ b/externals/jemalloc/include/internal/chunk_dss.h
@@ -0,0 +1,29 @@
+#ifdef JEMALLOC_DSS
+/******************************************************************************/
+#ifdef JEMALLOC_H_TYPES
+
+#endif /* JEMALLOC_H_TYPES */
+/******************************************************************************/
+#ifdef JEMALLOC_H_STRUCTS
+
+#endif /* JEMALLOC_H_STRUCTS */
+/******************************************************************************/
+#ifdef JEMALLOC_H_EXTERNS
+
+/*
+ * Protects sbrk() calls. This avoids malloc races among threads, though it
+ * does not protect against races with threads that call sbrk() directly.
+ */
+extern malloc_mutex_t dss_mtx;
+
+void *chunk_alloc_dss(size_t size, bool *zero);
+bool chunk_dealloc_dss(void *chunk, size_t size);
+bool chunk_dss_boot(void);
+
+#endif /* JEMALLOC_H_EXTERNS */
+/******************************************************************************/
+#ifdef JEMALLOC_H_INLINES
+
+#endif /* JEMALLOC_H_INLINES */
+/******************************************************************************/
+#endif /* JEMALLOC_DSS */
diff --git a/externals/jemalloc/include/internal/chunk_mmap.h b/externals/jemalloc/include/internal/chunk_mmap.h
new file mode 100644
index 00000000000..8fb90b77c9b
--- /dev/null
+++ b/externals/jemalloc/include/internal/chunk_mmap.h
@@ -0,0 +1,20 @@
+/******************************************************************************/
+#ifdef JEMALLOC_H_TYPES
+
+#endif /* JEMALLOC_H_TYPES */
+/******************************************************************************/
+#ifdef JEMALLOC_H_STRUCTS
+
+#endif /* JEMALLOC_H_STRUCTS */
+/******************************************************************************/
+#ifdef JEMALLOC_H_EXTERNS
+
+void *chunk_alloc_mmap(size_t size);
+void chunk_dealloc_mmap(void *chunk, size_t size);
+
+#endif /* JEMALLOC_H_EXTERNS */
+/******************************************************************************/
+#ifdef JEMALLOC_H_INLINES
+
+#endif /* JEMALLOC_H_INLINES */
+/******************************************************************************/
diff --git a/externals/jemalloc/include/internal/chunk_swap.h b/externals/jemalloc/include/internal/chunk_swap.h
new file mode 100644
index 00000000000..d50cb197449
--- /dev/null
+++ b/externals/jemalloc/include/internal/chunk_swap.h
@@ -0,0 +1,33 @@
+#ifdef JEMALLOC_SWAP
+/******************************************************************************/
+#ifdef JEMALLOC_H_TYPES
+
+#endif /* JEMALLOC_H_TYPES */
+/******************************************************************************/
+#ifdef JEMALLOC_H_STRUCTS
+
+#endif /* JEMALLOC_H_STRUCTS */
+/******************************************************************************/
+#ifdef JEMALLOC_H_EXTERNS
+
+extern malloc_mutex_t swap_mtx;
+extern bool swap_enabled;
+extern bool swap_prezeroed;
+extern size_t swap_nfds;
+extern int *swap_fds;
+#ifdef JEMALLOC_STATS
+extern size_t swap_avail;
+#endif
+
+void *chunk_alloc_swap(size_t size, bool *zero);
+bool chunk_dealloc_swap(void *chunk, size_t size);
+bool chunk_swap_enable(const int *fds, unsigned nfds, bool prezeroed);
+bool chunk_swap_boot(void);
+
+#endif /* JEMALLOC_H_EXTERNS */
+/******************************************************************************/
+#ifdef JEMALLOC_H_INLINES
+
+#endif /* JEMALLOC_H_INLINES */
+/******************************************************************************/
+#endif /* JEMALLOC_SWAP */
diff --git a/externals/jemalloc/include/internal/ckh.h b/externals/jemalloc/include/internal/ckh.h
new file mode 100644
index 00000000000..c39ea5c75ef
--- /dev/null
+++ b/externals/jemalloc/include/internal/ckh.h
@@ -0,0 +1,95 @@
+/******************************************************************************/
+#ifdef JEMALLOC_H_TYPES
+
+typedef struct ckh_s ckh_t;
+typedef struct ckhc_s ckhc_t;
+
+/* Typedefs to allow easy function pointer passing. */
+typedef void ckh_hash_t (const void *, unsigned, size_t *, size_t *);
+typedef bool ckh_keycomp_t (const void *, const void *);
+
+/* Maintain counters used to get an idea of performance. */
+/* #define CKH_COUNT */
+/* Print counter values in ckh_delete() (requires CKH_COUNT). */
+/* #define CKH_VERBOSE */
+
+/*
+ * There are 2^LG_CKH_BUCKET_CELLS cells in each hash table bucket. Try to fit
+ * one bucket per L1 cache line.
+ */
+#define LG_CKH_BUCKET_CELLS (LG_CACHELINE - LG_SIZEOF_PTR - 1)
+
+#endif /* JEMALLOC_H_TYPES */
+/******************************************************************************/
+#ifdef JEMALLOC_H_STRUCTS
+
+/* Hash table cell. */
+struct ckhc_s {
+ const void *key;
+ const void *data;
+};
+
+struct ckh_s {
+#ifdef JEMALLOC_DEBUG
+#define CKH_MAGIG 0x3af2489d
+ uint32_t magic;
+#endif
+
+#ifdef CKH_COUNT
+ /* Counters used to get an idea of performance. */
+ uint64_t ngrows;
+ uint64_t nshrinks;
+ uint64_t nshrinkfails;
+ uint64_t ninserts;
+ uint64_t nrelocs;
+#endif
+
+ /* Used for pseudo-random number generation. */
+#define CKH_A 12345
+#define CKH_C 12347
+ uint32_t prn_state;
+
+ /* Total number of items. */
+ size_t count;
+
+ /*
+ * Minimum and current number of hash table buckets. There are
+ * 2^LG_CKH_BUCKET_CELLS cells per bucket.
+ */
+ unsigned lg_minbuckets;
+ unsigned lg_curbuckets;
+
+ /* Hash and comparison functions. */
+ ckh_hash_t *hash;
+ ckh_keycomp_t *keycomp;
+
+ /* Hash table with 2^lg_curbuckets buckets. */
+ ckhc_t *tab;
+};
+
+#endif /* JEMALLOC_H_STRUCTS */
+/******************************************************************************/
+#ifdef JEMALLOC_H_EXTERNS
+
+bool ckh_new(ckh_t *ckh, size_t minitems, ckh_hash_t *hash,
+ ckh_keycomp_t *keycomp);
+void ckh_delete(ckh_t *ckh);
+size_t ckh_count(ckh_t *ckh);
+bool ckh_iter(ckh_t *ckh, size_t *tabind, void **key, void **data);
+bool ckh_insert(ckh_t *ckh, const void *key, const void *data);
+bool ckh_remove(ckh_t *ckh, const void *searchkey, void **key,
+ void **data);
+bool ckh_search(ckh_t *ckh, const void *seachkey, void **key, void **data);
+void ckh_string_hash(const void *key, unsigned minbits, size_t *hash1,
+ size_t *hash2);
+bool ckh_string_keycomp(const void *k1, const void *k2);
+void ckh_pointer_hash(const void *key, unsigned minbits, size_t *hash1,
+ size_t *hash2);
+bool ckh_pointer_keycomp(const void *k1, const void *k2);
+
+#endif /* JEMALLOC_H_EXTERNS */
+/******************************************************************************/
+#ifdef JEMALLOC_H_INLINES
+
+#endif /* JEMALLOC_H_INLINES */
+/******************************************************************************/
diff --git a/externals/jemalloc/include/internal/ctl.h b/externals/jemalloc/include/internal/ctl.h
new file mode 100644
index 00000000000..7bbf21e0e85
--- /dev/null
+++ b/externals/jemalloc/include/internal/ctl.h
@@ -0,0 +1,117 @@
+/******************************************************************************/
+#ifdef JEMALLOC_H_TYPES
+
+typedef struct ctl_node_s ctl_node_t;
+typedef struct ctl_arena_stats_s ctl_arena_stats_t;
+typedef struct ctl_stats_s ctl_stats_t;
+
+#endif /* JEMALLOC_H_TYPES */
+/******************************************************************************/
+#ifdef JEMALLOC_H_STRUCTS
+
+struct ctl_node_s {
+ bool named;
+ union {
+ struct {
+ const char *name;
+ /* If (nchildren == 0), this is a terminal node. */
+ unsigned nchildren;
+ const ctl_node_t *children;
+ } named;
+ struct {
+ const ctl_node_t *(*index)(const size_t *, size_t,
+ size_t);
+ } indexed;
+ } u;
+ int (*ctl)(const size_t *, size_t, void *, size_t *, void *,
+ size_t);
+};
+
+struct ctl_arena_stats_s {
+ bool initialized;
+ size_t pactive;
+ size_t pdirty;
+#ifdef JEMALLOC_STATS
+ arena_stats_t astats;
+
+ /* Aggregate stats for small size classes, based on bin stats. */
+ size_t allocated_small;
+ uint64_t nmalloc_small;
+ uint64_t ndalloc_small;
+ uint64_t nrequests_small;
+
+ malloc_bin_stats_t *bstats; /* nbins elements. */
+ malloc_large_stats_t *lstats; /* nlclasses elements. */
+#endif
+};
+
+struct ctl_stats_s {
+#ifdef JEMALLOC_STATS
+ size_t allocated;
+ size_t active;
+ size_t mapped;
+ struct {
+ size_t current; /* stats_chunks.curchunks */
+ uint64_t total; /* stats_chunks.nchunks */
+ size_t high; /* stats_chunks.highchunks */
+ } chunks;
+ struct {
+ size_t allocated; /* huge_allocated */
+ uint64_t nmalloc; /* huge_nmalloc */
+ uint64_t ndalloc; /* huge_ndalloc */
+ } huge;
+#endif
+ ctl_arena_stats_t *arenas; /* (narenas + 1) elements. */
+#ifdef JEMALLOC_SWAP
+ size_t swap_avail;
+#endif
+};
+
+#endif /* JEMALLOC_H_STRUCTS */
+/******************************************************************************/
+#ifdef JEMALLOC_H_EXTERNS
+
+int ctl_byname(const char *name, void *oldp, size_t *oldlenp, void *newp,
+ size_t newlen);
+int ctl_nametomib(const char *name, size_t *mibp, size_t *miblenp);
+
+int ctl_bymib(const size_t *mib, size_t miblen, void *oldp, size_t *oldlenp,
+ void *newp, size_t newlen);
+bool ctl_boot(void);
+
+#define xmallctl(name, oldp, oldlenp, newp, newlen) do { \
+ if (JEMALLOC_P(mallctl)(name, oldp, oldlenp, newp, newlen) \
+ != 0) { \
+ malloc_write("<jemalloc>: Invalid xmallctl(\""); \
+ malloc_write(name); \
+ malloc_write("\", ...) call\n"); \
+ abort(); \
+ } \
+} while (0)
+
+#define xmallctlnametomib(name, mibp, miblenp) do { \
+ if (JEMALLOC_P(mallctlnametomib)(name, mibp, miblenp) != 0) { \
+ malloc_write( \
+ "<jemalloc>: Invalid xmallctlnametomib(\""); \
+ malloc_write(name); \
+ malloc_write("\", ...) call\n"); \
+ abort(); \
+ } \
+} while (0)
+
+#define xmallctlbymib(mib, miblen, oldp, oldlenp, newp, newlen) do { \
+ if (JEMALLOC_P(mallctlbymib)(mib, miblen, oldp, oldlenp, newp, \
+ newlen) != 0) { \
+ malloc_write( \
+ "<jemalloc>: Invalid xmallctlbymib() call\n"); \
+ abort(); \
+ } \
+} while (0)
+
+#endif /* JEMALLOC_H_EXTERNS */
+/******************************************************************************/
+#ifdef JEMALLOC_H_INLINES
+
+#endif /* JEMALLOC_H_INLINES */
+/******************************************************************************/
+
diff --git a/externals/jemalloc/include/internal/extent.h b/externals/jemalloc/include/internal/extent.h
new file mode 100644
index 00000000000..33a4e9a3852
--- /dev/null
+++ b/externals/jemalloc/include/internal/extent.h
@@ -0,0 +1,49 @@
+/******************************************************************************/
+#ifdef JEMALLOC_H_TYPES
+
+typedef struct extent_node_s extent_node_t;
+
+#endif /* JEMALLOC_H_TYPES */
+/******************************************************************************/
+#ifdef JEMALLOC_H_STRUCTS
+
+/* Tree of extents. */
+struct extent_node_s {
+#if (defined(JEMALLOC_SWAP) || defined(JEMALLOC_DSS))
+ /* Linkage for the size/address-ordered tree. */
+ rb_node(extent_node_t) link_szad;
+#endif
+
+ /* Linkage for the address-ordered tree. */
+ rb_node(extent_node_t) link_ad;
+
+#ifdef JEMALLOC_PROF
+ /* Profile counters, used for huge objects. */
+ prof_thr_cnt_t *prof_cnt;
+#endif
+
+ /* Pointer to the extent that this tree node is responsible for. */
+ void *addr;
+
+ /* Total region size. */
+ size_t size;
+};
+typedef rb_tree(extent_node_t) extent_tree_t;
+
+#endif /* JEMALLOC_H_STRUCTS */
+/******************************************************************************/
+#ifdef JEMALLOC_H_EXTERNS
+
+#if (defined(JEMALLOC_SWAP) || defined(JEMALLOC_DSS))
+rb_proto(, extent_tree_szad_, extent_tree_t, extent_node_t)
+#endif
+
+rb_proto(, extent_tree_ad_, extent_tree_t, extent_node_t)
+
+#endif /* JEMALLOC_H_EXTERNS */
+/******************************************************************************/
+#ifdef JEMALLOC_H_INLINES
+
+#endif /* JEMALLOC_H_INLINES */
+/******************************************************************************/
+
diff --git a/externals/jemalloc/include/internal/hash.h b/externals/jemalloc/include/internal/hash.h
new file mode 100644
index 00000000000..d12cdb8359f
--- /dev/null
+++ b/externals/jemalloc/include/internal/hash.h
@@ -0,0 +1,70 @@
+/******************************************************************************/
+#ifdef JEMALLOC_H_TYPES
+
+#endif /* JEMALLOC_H_TYPES */
+/******************************************************************************/
+#ifdef JEMALLOC_H_STRUCTS
+
+#endif /* JEMALLOC_H_STRUCTS */
+/******************************************************************************/
+#ifdef JEMALLOC_H_EXTERNS
+
+#endif /* JEMALLOC_H_EXTERNS */
+/******************************************************************************/
+#ifdef JEMALLOC_H_INLINES
+
+#ifndef JEMALLOC_ENABLE_INLINE
+uint64_t hash(const void *key, size_t len, uint64_t seed);
+#endif
+
+#if (defined(JEMALLOC_ENABLE_INLINE) || defined(HASH_C_))
+/*
+ * The following hash function is based on MurmurHash64A(), placed into the
+ * public domain by Austin Appleby. See http://murmurhash.googlepages.com/ for
+ * details.
+ */
+JEMALLOC_INLINE uint64_t
+hash(const void *key, size_t len, uint64_t seed)
+{
+ const uint64_t m = 0xc6a4a7935bd1e995;
+ const int r = 47;
+ uint64_t h = seed ^ (len * m);
+ const uint64_t *data = (const uint64_t *)key;
+ const uint64_t *end = data + (len/8);
+ const unsigned char *data2;
+
+ assert(((uintptr_t)key & 0x7) == 0);
+
+ while(data != end) {
+ uint64_t k = *data++;
+
+ k *= m;
+ k ^= k >> r;
+ k *= m;
+
+ h ^= k;
+ h *= m;
+ }
+
+ data2 = (const unsigned char *)data;
+ switch(len & 7) {
+ case 7: h ^= ((uint64_t)(data2[6])) << 48;
+ case 6: h ^= ((uint64_t)(data2[5])) << 40;
+ case 5: h ^= ((uint64_t)(data2[4])) << 32;
+ case 4: h ^= ((uint64_t)(data2[3])) << 24;
+ case 3: h ^= ((uint64_t)(data2[2])) << 16;
+ case 2: h ^= ((uint64_t)(data2[1])) << 8;
+ case 1: h ^= ((uint64_t)(data2[0]));
+ h *= m;
+ }
+
+ h ^= h >> r;
+ h *= m;
+ h ^= h >> r;
+
+ return h;
+}
+#endif
+
+#endif /* JEMALLOC_H_INLINES */
+/******************************************************************************/
diff --git a/externals/jemalloc/include/internal/huge.h b/externals/jemalloc/include/internal/huge.h
new file mode 100644
index 00000000000..3cf32f7506d
--- /dev/null
+++ b/externals/jemalloc/include/internal/huge.h
@@ -0,0 +1,38 @@
+/******************************************************************************/
+#ifdef JEMALLOC_H_TYPES
+
+#endif /* JEMALLOC_H_TYPES */
+/******************************************************************************/
+#ifdef JEMALLOC_H_STRUCTS
+
+#endif /* JEMALLOC_H_STRUCTS */
+/******************************************************************************/
+#ifdef JEMALLOC_H_EXTERNS
+
+#ifdef JEMALLOC_STATS
+/* Huge allocation statistics. */
+extern uint64_t huge_nmalloc;
+extern uint64_t huge_ndalloc;
+extern size_t huge_allocated;
+#endif
+
+/* Protects chunk-related data structures. */
+extern malloc_mutex_t huge_mtx;
+
+void *huge_malloc(size_t size, bool zero);
+void *huge_palloc(size_t alignment, size_t size);
+void *huge_ralloc(void *ptr, size_t size, size_t oldsize);
+void huge_dalloc(void *ptr);
+size_t huge_salloc(const void *ptr);
+#ifdef JEMALLOC_PROF
+prof_thr_cnt_t *huge_prof_cnt_get(const void *ptr);
+void huge_prof_cnt_set(const void *ptr, prof_thr_cnt_t *cnt);
+#endif
+bool huge_boot(void);
+
+#endif /* JEMALLOC_H_EXTERNS */
+/******************************************************************************/
+#ifdef JEMALLOC_H_INLINES
+
+#endif /* JEMALLOC_H_INLINES */
+/******************************************************************************/
diff --git a/externals/jemalloc/include/internal/jemalloc_internal.h b/externals/jemalloc/include/internal/jemalloc_internal.h
new file mode 100644
index 00000000000..03782dd6690
--- /dev/null
+++ b/externals/jemalloc/include/internal/jemalloc_internal.h
@@ -0,0 +1,561 @@
+#include <sys/mman.h>
+#include <sys/param.h>
+#include <sys/time.h>
+#include <sys/types.h>
+#include <sys/sysctl.h>
+#include <sys/uio.h>
+
+#include <errno.h>
+#include <limits.h>
+#ifndef SIZE_T_MAX
+# define SIZE_T_MAX SIZE_MAX
+#endif
+#include <pthread.h>
+#include <sched.h>
+#include <stdarg.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include <inttypes.h>
+#include <string.h>
+#include <strings.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <pthread.h>
+
+#define JEMALLOC_MANGLE
+#include "../jemalloc.h"
+
+#ifdef JEMALLOC_LAZY_LOCK
+#include <dlfcn.h>
+#endif
+
+#define RB_COMPACT
+#include "jemalloc/internal/rb.h"
+#include "jemalloc/internal/qr.h"
+#include "jemalloc/internal/ql.h"
+
+extern void (*JEMALLOC_P(malloc_message))(void *wcbopaque, const char *s);
+
+/*
+ * Define a custom assert() in order to reduce the chances of deadlock during
+ * assertion failure.
+ */
+#ifdef JEMALLOC_DEBUG
+# define assert(e) do { \
+ if (!(e)) { \
+ char line_buf[UMAX2S_BUFSIZE]; \
+ malloc_write("<jemalloc>: "); \
+ malloc_write(__FILE__); \
+ malloc_write(":"); \
+ malloc_write(umax2s(__LINE__, 10, line_buf)); \
+ malloc_write(": Failed assertion: "); \
+ malloc_write("\""); \
+ malloc_write(#e); \
+ malloc_write("\"\n"); \
+ abort(); \
+ } \
+} while (0)
+#else
+#define assert(e)
+#endif
+
+/*
+ * jemalloc can conceptually be broken into components (arena, tcache, etc.),
+ * but there are circular dependencies that cannot be broken without
+ * substantial performance degradation. In order to reduce the effect on
+ * visual code flow, read the header files in multiple passes, with one of the
+ * following cpp variables defined during each pass:
+ *
+ * JEMALLOC_H_TYPES : Preprocessor-defined constants and psuedo-opaque data
+ * types.
+ * JEMALLOC_H_STRUCTS : Data structures.
+ * JEMALLOC_H_EXTERNS : Extern data declarations and function prototypes.
+ * JEMALLOC_H_INLINES : Inline functions.
+ */
+/******************************************************************************/
+#define JEMALLOC_H_TYPES
+
+#define ZU(z) ((size_t)z)
+
+#ifndef __DECONST
+# define __DECONST(type, var) ((type)(uintptr_t)(const void *)(var))
+#endif
+
+#ifdef JEMALLOC_DEBUG
+ /* Disable inlining to make debugging easier. */
+# define JEMALLOC_INLINE
+# define inline
+#else
+# define JEMALLOC_ENABLE_INLINE
+# define JEMALLOC_INLINE static inline
+#endif
+
+/* Size of stack-allocated buffer passed to strerror_r(). */
+#define STRERROR_BUF 64
+
+/* Minimum alignment of allocations is 2^LG_QUANTUM bytes. */
+#ifdef __i386__
+# define LG_QUANTUM 4
+#endif
+#ifdef __ia64__
+# define LG_QUANTUM 4
+#endif
+#ifdef __alpha__
+# define LG_QUANTUM 4
+#endif
+#ifdef __sparc64__
+# define LG_QUANTUM 4
+#endif
+#if (defined(__amd64__) || defined(__x86_64__))
+# define LG_QUANTUM 4
+#endif
+#ifdef __arm__
+# define LG_QUANTUM 3
+#endif
+#ifdef __mips__
+# define LG_QUANTUM 3
+#endif
+#ifdef __powerpc__
+# define LG_QUANTUM 4
+#endif
+#ifdef __s390x__
+# define LG_QUANTUM 4
+#endif
+
+#define QUANTUM ((size_t)(1U << LG_QUANTUM))
+#define QUANTUM_MASK (QUANTUM - 1)
+
+/* Return the smallest quantum multiple that is >= a. */
+#define QUANTUM_CEILING(a) \
+ (((a) + QUANTUM_MASK) & ~QUANTUM_MASK)
+
+#define SIZEOF_PTR (1U << LG_SIZEOF_PTR)
+
+/* We can't use TLS in non-PIC programs, since TLS relies on loader magic. */
+#if (!defined(PIC) && !defined(NO_TLS))
+# define NO_TLS
+#endif
+
+/*
+ * Maximum size of L1 cache line. This is used to avoid cache line aliasing.
+ * In addition, this controls the spacing of cacheline-spaced size classes.
+ */
+#define LG_CACHELINE 6
+#define CACHELINE ((size_t)(1U << LG_CACHELINE))
+#define CACHELINE_MASK (CACHELINE - 1)
+
+/* Return the smallest cacheline multiple that is >= s. */
+#define CACHELINE_CEILING(s) \
+ (((s) + CACHELINE_MASK) & ~CACHELINE_MASK)
+
+/*
+ * Page size. STATIC_PAGE_SHIFT is determined by the configure script. If
+ * DYNAMIC_PAGE_SHIFT is enabled, only use the STATIC_PAGE_* macros where
+ * compile-time values are required for the purposes of defining data
+ * structures.
+ */
+#define STATIC_PAGE_SIZE ((size_t)(1U << STATIC_PAGE_SHIFT))
+#define STATIC_PAGE_MASK ((size_t)(STATIC_PAGE_SIZE - 1))
+
+#ifdef DYNAMIC_PAGE_SHIFT
+# define PAGE_SHIFT lg_pagesize
+# define PAGE_SIZE pagesize
+# define PAGE_MASK pagesize_mask
+#else
+# define PAGE_SHIFT STATIC_PAGE_SHIFT
+# define PAGE_SIZE STATIC_PAGE_SIZE
+# define PAGE_MASK STATIC_PAGE_MASK
+#endif
+
+/* Return the smallest pagesize multiple that is >= s. */
+#define PAGE_CEILING(s) \
+ (((s) + PAGE_MASK) & ~PAGE_MASK)
+
+#include "jemalloc/internal/totally_not_p_r_n.h"
+#include "jemalloc/internal/ckh.h"
+#include "jemalloc/internal/stats.h"
+#include "jemalloc/internal/ctl.h"
+#include "jemalloc/internal/mutex.h"
+#include "jemalloc/internal/mb.h"
+#include "jemalloc/internal/extent.h"
+#include "jemalloc/internal/arena.h"
+#include "jemalloc/internal/base.h"
+#include "jemalloc/internal/chunk.h"
+#include "jemalloc/internal/huge.h"
+#include "jemalloc/internal/tcache.h"
+#include "jemalloc/internal/hash.h"
+#include "jemalloc/internal/prof.h"
+
+#undef JEMALLOC_H_TYPES
+/******************************************************************************/
+#define JEMALLOC_H_STRUCTS
+
+#include "jemalloc/internal/totally_not_p_r_n.h"
+#include "jemalloc/internal/ckh.h"
+#include "jemalloc/internal/stats.h"
+#include "jemalloc/internal/ctl.h"
+#include "jemalloc/internal/mutex.h"
+#include "jemalloc/internal/mb.h"
+#include "jemalloc/internal/extent.h"
+#include "jemalloc/internal/arena.h"
+#include "jemalloc/internal/base.h"
+#include "jemalloc/internal/chunk.h"
+#include "jemalloc/internal/huge.h"
+#include "jemalloc/internal/tcache.h"
+#include "jemalloc/internal/hash.h"
+#include "jemalloc/internal/prof.h"
+
+#undef JEMALLOC_H_STRUCTS
+/******************************************************************************/
+#define JEMALLOC_H_EXTERNS
+
+extern bool opt_abort;
+#ifdef JEMALLOC_FILL
+extern bool opt_junk;
+#endif
+#ifdef JEMALLOC_SYSV
+extern bool opt_sysv;
+#endif
+#ifdef JEMALLOC_XMALLOC
+extern bool opt_xmalloc;
+#endif
+#ifdef JEMALLOC_FILL
+extern bool opt_zero;
+#endif
+
+#ifdef DYNAMIC_PAGE_SHIFT
+extern size_t pagesize;
+extern size_t pagesize_mask;
+extern size_t lg_pagesize;
+#endif
+
+/* Number of CPUs. */
+extern unsigned ncpus;
+
+extern malloc_mutex_t arenas_lock; /* Protects arenas initialization. */
+#ifndef NO_TLS
+/*
+ * Map of pthread_self() --> arenas[???], used for selecting an arena to use
+ * for allocations.
+ */
+extern __thread arena_t *arenas_map JEMALLOC_ATTR(tls_model("initial-exec"));
+#endif
+/*
+ * Arenas that are used to service external requests. Not all elements of the
+ * arenas array are necessarily used; arenas are created lazily as needed.
+ */
+extern arena_t **arenas;
+extern unsigned narenas;
+
+arena_t *arenas_extend(unsigned ind);
+#ifndef NO_TLS
+arena_t *choose_arena_hard(void);
+#endif
+
+#include "jemalloc/internal/totally_not_p_r_n.h"
+#include "jemalloc/internal/ckh.h"
+#include "jemalloc/internal/stats.h"
+#include "jemalloc/internal/ctl.h"
+#include "jemalloc/internal/mutex.h"
+#include "jemalloc/internal/mb.h"
+#include "jemalloc/internal/extent.h"
+#include "jemalloc/internal/arena.h"
+#include "jemalloc/internal/base.h"
+#include "jemalloc/internal/chunk.h"
+#include "jemalloc/internal/huge.h"
+#include "jemalloc/internal/tcache.h"
+#include "jemalloc/internal/hash.h"
+#include "jemalloc/internal/prof.h"
+
+#undef JEMALLOC_H_EXTERNS
+/******************************************************************************/
+#define JEMALLOC_H_INLINES
+
+#include "jemalloc/internal/totally_not_p_r_n.h"
+#include "jemalloc/internal/ckh.h"
+#include "jemalloc/internal/stats.h"
+#include "jemalloc/internal/ctl.h"
+#include "jemalloc/internal/mutex.h"
+#include "jemalloc/internal/mb.h"
+#include "jemalloc/internal/extent.h"
+#include "jemalloc/internal/base.h"
+#include "jemalloc/internal/chunk.h"
+#include "jemalloc/internal/huge.h"
+
+#ifndef JEMALLOC_ENABLE_INLINE
+void malloc_write(const char *s);
+arena_t *choose_arena(void);
+#endif
+
+#if (defined(JEMALLOC_ENABLE_INLINE) || defined(JEMALLOC_C_))
+/*
+ * Wrapper around malloc_message() that avoids the need for
+ * JEMALLOC_P(malloc_message)(...) throughout the code.
+ */
+JEMALLOC_INLINE void
+malloc_write(const char *s)
+{
+
+ JEMALLOC_P(malloc_message)(NULL, s);
+}
+
+/*
+ * Choose an arena based on a per-thread value (fast-path code, calls slow-path
+ * code if necessary).
+ */
+JEMALLOC_INLINE arena_t *
+choose_arena(void)
+{
+ arena_t *ret;
+
+ /*
+ * We can only use TLS if this is a PIC library, since for the static
+ * library version, libc's malloc is used by TLS allocation, which
+ * introduces a bootstrapping issue.
+ */
+#ifndef NO_TLS
+ ret = arenas_map;
+ if (ret == NULL) {
+ ret = choose_arena_hard();
+ assert(ret != NULL);
+ }
+#else
+ if (isthreaded && narenas > 1) {
+ unsigned long ind;
+
+ /*
+ * Hash pthread_self() to one of the arenas. There is a prime
+ * number of arenas, so this has a reasonable chance of
+ * working. Even so, the hashing can be easily thwarted by
+ * inconvenient pthread_self() values. Without specific
+ * knowledge of how pthread_self() calculates values, we can't
+ * easily do much better than this.
+ */
+ ind = (unsigned long) pthread_self() % narenas;
+
+ /*
+ * Optimistially assume that arenas[ind] has been initialized.
+ * At worst, we find out that some other thread has already
+ * done so, after acquiring the lock in preparation. Note that
+ * this lazy locking also has the effect of lazily forcing
+ * cache coherency; without the lock acquisition, there's no
+ * guarantee that modification of arenas[ind] by another thread
+ * would be seen on this CPU for an arbitrary amount of time.
+ *
+ * In general, this approach to modifying a synchronized value
+ * isn't a good idea, but in this case we only ever modify the
+ * value once, so things work out well.
+ */
+ ret = arenas[ind];
+ if (ret == NULL) {
+ /*
+ * Avoid races with another thread that may have already
+ * initialized arenas[ind].
+ */
+ malloc_mutex_lock(&arenas_lock);
+ if (arenas[ind] == NULL)
+ ret = arenas_extend((unsigned)ind);
+ else
+ ret = arenas[ind];
+ malloc_mutex_unlock(&arenas_lock);
+ }
+ } else
+ ret = arenas[0];
+#endif
+
+ assert(ret != NULL);
+ return (ret);
+}
+#endif
+
+#include "jemalloc/internal/tcache.h"
+#include "jemalloc/internal/arena.h"
+#include "jemalloc/internal/hash.h"
+#include "jemalloc/internal/prof.h"
+
+#ifndef JEMALLOC_ENABLE_INLINE
+void *imalloc(size_t size);
+void *icalloc(size_t size);
+void *ipalloc(size_t alignment, size_t size);
+size_t isalloc(const void *ptr);
+void *iralloc(void *ptr, size_t size);
+void idalloc(void *ptr);
+#endif
+
+#if (defined(JEMALLOC_ENABLE_INLINE) || defined(JEMALLOC_C_))
+JEMALLOC_INLINE void *
+imalloc(size_t size)
+{
+
+ assert(size != 0);
+
+ if (size <= arena_maxclass)
+ return (arena_malloc(size, false));
+ else
+ return (huge_malloc(size, false));
+}
+
+JEMALLOC_INLINE void *
+icalloc(size_t size)
+{
+
+ if (size <= arena_maxclass)
+ return (arena_malloc(size, true));
+ else
+ return (huge_malloc(size, true));
+}
+
+JEMALLOC_INLINE void *
+ipalloc(size_t alignment, size_t size)
+{
+ void *ret;
+ size_t ceil_size;
+
+ /*
+ * Round size up to the nearest multiple of alignment.
+ *
+ * This done, we can take advantage of the fact that for each small
+ * size class, every object is aligned at the smallest power of two
+ * that is non-zero in the base two representation of the size. For
+ * example:
+ *
+ * Size | Base 2 | Minimum alignment
+ * -----+----------+------------------
+ * 96 | 1100000 | 32
+ * 144 | 10100000 | 32
+ * 192 | 11000000 | 64
+ *
+ * Depending on runtime settings, it is possible that arena_malloc()
+ * will further round up to a power of two, but that never causes
+ * correctness issues.
+ */
+ ceil_size = (size + (alignment - 1)) & (-alignment);
+ /*
+ * (ceil_size < size) protects against the combination of maximal
+ * alignment and size greater than maximal alignment.
+ */
+ if (ceil_size < size) {
+ /* size_t overflow. */
+ return (NULL);
+ }
+
+ if (ceil_size <= PAGE_SIZE || (alignment <= PAGE_SIZE
+ && ceil_size <= arena_maxclass))
+ ret = arena_malloc(ceil_size, false);
+ else {
+ size_t run_size;
+
+ /*
+ * We can't achieve subpage alignment, so round up alignment
+ * permanently; it makes later calculations simpler.
+ */
+ alignment = PAGE_CEILING(alignment);
+ ceil_size = PAGE_CEILING(size);
+ /*
+ * (ceil_size < size) protects against very large sizes within
+ * PAGE_SIZE of SIZE_T_MAX.
+ *
+ * (ceil_size + alignment < ceil_size) protects against the
+ * combination of maximal alignment and ceil_size large enough
+ * to cause overflow. This is similar to the first overflow
+ * check above, but it needs to be repeated due to the new
+ * ceil_size value, which may now be *equal* to maximal
+ * alignment, whereas before we only detected overflow if the
+ * original size was *greater* than maximal alignment.
+ */
+ if (ceil_size < size || ceil_size + alignment < ceil_size) {
+ /* size_t overflow. */
+ return (NULL);
+ }
+
+ /*
+ * Calculate the size of the over-size run that arena_palloc()
+ * would need to allocate in order to guarantee the alignment.
+ */
+ if (ceil_size >= alignment)
+ run_size = ceil_size + alignment - PAGE_SIZE;
+ else {
+ /*
+ * It is possible that (alignment << 1) will cause
+ * overflow, but it doesn't matter because we also
+ * subtract PAGE_SIZE, which in the case of overflow
+ * leaves us with a very large run_size. That causes
+ * the first conditional below to fail, which means
+ * that the bogus run_size value never gets used for
+ * anything important.
+ */
+ run_size = (alignment << 1) - PAGE_SIZE;
+ }
+
+ if (run_size <= arena_maxclass) {
+ ret = arena_palloc(choose_arena(), alignment, ceil_size,
+ run_size);
+ } else if (alignment <= chunksize)
+ ret = huge_malloc(ceil_size, false);
+ else
+ ret = huge_palloc(alignment, ceil_size);
+ }
+
+ assert(((uintptr_t)ret & (alignment - 1)) == 0);
+ return (ret);
+}
+
+JEMALLOC_INLINE size_t
+isalloc(const void *ptr)
+{
+ size_t ret;
+ arena_chunk_t *chunk;
+
+ assert(ptr != NULL);
+
+ chunk = (arena_chunk_t *)CHUNK_ADDR2BASE(ptr);
+ if (chunk != ptr) {
+ /* Region. */
+ assert(chunk->arena->magic == ARENA_MAGIC);
+
+#ifdef JEMALLOC_PROF
+ ret = arena_salloc_demote(ptr);
+#else
+ ret = arena_salloc(ptr);
+#endif
+ } else
+ ret = huge_salloc(ptr);
+
+ return (ret);
+}
+
+JEMALLOC_INLINE void *
+iralloc(void *ptr, size_t size)
+{
+ size_t oldsize;
+
+ assert(ptr != NULL);
+ assert(size != 0);
+
+ oldsize = isalloc(ptr);
+
+ if (size <= arena_maxclass)
+ return (arena_ralloc(ptr, size, oldsize));
+ else
+ return (huge_ralloc(ptr, size, oldsize));
+}
+
+JEMALLOC_INLINE void
+idalloc(void *ptr)
+{
+ arena_chunk_t *chunk;
+
+ assert(ptr != NULL);
+
+ chunk = (arena_chunk_t *)CHUNK_ADDR2BASE(ptr);
+ if (chunk != ptr)
+ arena_dalloc(chunk->arena, chunk, ptr);
+ else
+ huge_dalloc(ptr);
+}
+#endif
+
+#undef JEMALLOC_H_INLINES
+/******************************************************************************/
diff --git a/externals/jemalloc/include/internal/jemalloc_internal.h.in b/externals/jemalloc/include/internal/jemalloc_internal.h.in
new file mode 100644
index 00000000000..2c3f32f126d
--- /dev/null
+++ b/externals/jemalloc/include/internal/jemalloc_internal.h.in
@@ -0,0 +1,561 @@
+#include <sys/mman.h>
+#include <sys/param.h>
+#include <sys/time.h>
+#include <sys/types.h>
+#include <sys/sysctl.h>
+#include <sys/uio.h>
+
+#include <errno.h>
+#include <limits.h>
+#ifndef SIZE_T_MAX
+# define SIZE_T_MAX SIZE_MAX
+#endif
+#include <pthread.h>
+#include <sched.h>
+#include <stdarg.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include <inttypes.h>
+#include <string.h>
+#include <strings.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <pthread.h>
+
+#define JEMALLOC_MANGLE
+#include "../jemalloc@install_suffix@.h"
+
+#ifdef JEMALLOC_LAZY_LOCK
+#include <dlfcn.h>
+#endif
+
+#define RB_COMPACT
+#include "jemalloc/internal/rb.h"
+#include "jemalloc/internal/qr.h"
+#include "jemalloc/internal/ql.h"
+
+extern void (*JEMALLOC_P(malloc_message))(void *wcbopaque, const char *s);
+
+/*
+ * Define a custom assert() in order to reduce the chances of deadlock during
+ * assertion failure.
+ */
+#ifdef JEMALLOC_DEBUG
+# define assert(e) do { \
+ if (!(e)) { \
+ char line_buf[UMAX2S_BUFSIZE]; \
+ malloc_write("<jemalloc>: "); \
+ malloc_write(__FILE__); \
+ malloc_write(":"); \
+ malloc_write(umax2s(__LINE__, 10, line_buf)); \
+ malloc_write(": Failed assertion: "); \
+ malloc_write("\""); \
+ malloc_write(#e); \
+ malloc_write("\"\n"); \
+ abort(); \
+ } \
+} while (0)
+#else
+#define assert(e)
+#endif
+
+/*
+ * jemalloc can conceptually be broken into components (arena, tcache, etc.),
+ * but there are circular dependencies that cannot be broken without
+ * substantial performance degradation. In order to reduce the effect on
+ * visual code flow, read the header files in multiple passes, with one of the
+ * following cpp variables defined during each pass:
+ *
+ * JEMALLOC_H_TYPES : Preprocessor-defined constants and psuedo-opaque data
+ * types.
+ * JEMALLOC_H_STRUCTS : Data structures.
+ * JEMALLOC_H_EXTERNS : Extern data declarations and function prototypes.
+ * JEMALLOC_H_INLINES : Inline functions.
+ */
+/******************************************************************************/
+#define JEMALLOC_H_TYPES
+
+#define ZU(z) ((size_t)z)
+
+#ifndef __DECONST
+# define __DECONST(type, var) ((type)(uintptr_t)(const void *)(var))
+#endif
+
+#ifdef JEMALLOC_DEBUG
+ /* Disable inlining to make debugging easier. */
+# define JEMALLOC_INLINE
+# define inline
+#else
+# define JEMALLOC_ENABLE_INLINE
+# define JEMALLOC_INLINE static inline
+#endif
+
+/* Size of stack-allocated buffer passed to strerror_r(). */
+#define STRERROR_BUF 64
+
+/* Minimum alignment of allocations is 2^LG_QUANTUM bytes. */
+#ifdef __i386__
+# define LG_QUANTUM 4
+#endif
+#ifdef __ia64__
+# define LG_QUANTUM 4
+#endif
+#ifdef __alpha__
+# define LG_QUANTUM 4
+#endif
+#ifdef __sparc64__
+# define LG_QUANTUM 4
+#endif
+#if (defined(__amd64__) || defined(__x86_64__))
+# define LG_QUANTUM 4
+#endif
+#ifdef __arm__
+# define LG_QUANTUM 3
+#endif
+#ifdef __mips__
+# define LG_QUANTUM 3
+#endif
+#ifdef __powerpc__
+# define LG_QUANTUM 4
+#endif
+#ifdef __s390x__
+# define LG_QUANTUM 4
+#endif
+
+#define QUANTUM ((size_t)(1U << LG_QUANTUM))
+#define QUANTUM_MASK (QUANTUM - 1)
+
+/* Return the smallest quantum multiple that is >= a. */
+#define QUANTUM_CEILING(a) \
+ (((a) + QUANTUM_MASK) & ~QUANTUM_MASK)
+
+#define SIZEOF_PTR (1U << LG_SIZEOF_PTR)
+
+/* We can't use TLS in non-PIC programs, since TLS relies on loader magic. */
+#if (!defined(PIC) && !defined(NO_TLS))
+# define NO_TLS
+#endif
+
+/*
+ * Maximum size of L1 cache line. This is used to avoid cache line aliasing.
+ * In addition, this controls the spacing of cacheline-spaced size classes.
+ */
+#define LG_CACHELINE 6
+#define CACHELINE ((size_t)(1U << LG_CACHELINE))
+#define CACHELINE_MASK (CACHELINE - 1)
+
+/* Return the smallest cacheline multiple that is >= s. */
+#define CACHELINE_CEILING(s) \
+ (((s) + CACHELINE_MASK) & ~CACHELINE_MASK)
+
+/*
+ * Page size. STATIC_PAGE_SHIFT is determined by the configure script. If
+ * DYNAMIC_PAGE_SHIFT is enabled, only use the STATIC_PAGE_* macros where
+ * compile-time values are required for the purposes of defining data
+ * structures.
+ */
+#define STATIC_PAGE_SIZE ((size_t)(1U << STATIC_PAGE_SHIFT))
+#define STATIC_PAGE_MASK ((size_t)(STATIC_PAGE_SIZE - 1))
+
+#ifdef DYNAMIC_PAGE_SHIFT
+# define PAGE_SHIFT lg_pagesize
+# define PAGE_SIZE pagesize
+# define PAGE_MASK pagesize_mask
+#else
+# define PAGE_SHIFT STATIC_PAGE_SHIFT
+# define PAGE_SIZE STATIC_PAGE_SIZE
+# define PAGE_MASK STATIC_PAGE_MASK
+#endif
+
+/* Return the smallest pagesize multiple that is >= s. */
+#define PAGE_CEILING(s) \
+ (((s) + PAGE_MASK) & ~PAGE_MASK)
+
+#include "jemalloc/internal/prn.h"
+#include "jemalloc/internal/ckh.h"
+#include "jemalloc/internal/stats.h"
+#include "jemalloc/internal/ctl.h"
+#include "jemalloc/internal/mutex.h"
+#include "jemalloc/internal/mb.h"
+#include "jemalloc/internal/extent.h"
+#include "jemalloc/internal/arena.h"
+#include "jemalloc/internal/base.h"
+#include "jemalloc/internal/chunk.h"
+#include "jemalloc/internal/huge.h"
+#include "jemalloc/internal/tcache.h"
+#include "jemalloc/internal/hash.h"
+#include "jemalloc/internal/prof.h"
+
+#undef JEMALLOC_H_TYPES
+/******************************************************************************/
+#define JEMALLOC_H_STRUCTS
+
+#include "jemalloc/internal/prn.h"
+#include "jemalloc/internal/ckh.h"
+#include "jemalloc/internal/stats.h"
+#include "jemalloc/internal/ctl.h"
+#include "jemalloc/internal/mutex.h"
+#include "jemalloc/internal/mb.h"
+#include "jemalloc/internal/extent.h"
+#include "jemalloc/internal/arena.h"
+#include "jemalloc/internal/base.h"
+#include "jemalloc/internal/chunk.h"
+#include "jemalloc/internal/huge.h"
+#include "jemalloc/internal/tcache.h"
+#include "jemalloc/internal/hash.h"
+#include "jemalloc/internal/prof.h"
+
+#undef JEMALLOC_H_STRUCTS
+/******************************************************************************/
+#define JEMALLOC_H_EXTERNS
+
+extern bool opt_abort;
+#ifdef JEMALLOC_FILL
+extern bool opt_junk;
+#endif
+#ifdef JEMALLOC_SYSV
+extern bool opt_sysv;
+#endif
+#ifdef JEMALLOC_XMALLOC
+extern bool opt_xmalloc;
+#endif
+#ifdef JEMALLOC_FILL
+extern bool opt_zero;
+#endif
+
+#ifdef DYNAMIC_PAGE_SHIFT
+extern size_t pagesize;
+extern size_t pagesize_mask;
+extern size_t lg_pagesize;
+#endif
+
+/* Number of CPUs. */
+extern unsigned ncpus;
+
+extern malloc_mutex_t arenas_lock; /* Protects arenas initialization. */
+#ifndef NO_TLS
+/*
+ * Map of pthread_self() --> arenas[???], used for selecting an arena to use
+ * for allocations.
+ */
+extern __thread arena_t *arenas_map JEMALLOC_ATTR(tls_model("initial-exec"));
+#endif
+/*
+ * Arenas that are used to service external requests. Not all elements of the
+ * arenas array are necessarily used; arenas are created lazily as needed.
+ */
+extern arena_t **arenas;
+extern unsigned narenas;
+
+arena_t *arenas_extend(unsigned ind);
+#ifndef NO_TLS
+arena_t *choose_arena_hard(void);
+#endif
+
+#include "jemalloc/internal/prn.h"
+#include "jemalloc/internal/ckh.h"
+#include "jemalloc/internal/stats.h"
+#include "jemalloc/internal/ctl.h"
+#include "jemalloc/internal/mutex.h"
+#include "jemalloc/internal/mb.h"
+#include "jemalloc/internal/extent.h"
+#include "jemalloc/internal/arena.h"
+#include "jemalloc/internal/base.h"
+#include "jemalloc/internal/chunk.h"
+#include "jemalloc/internal/huge.h"
+#include "jemalloc/internal/tcache.h"
+#include "jemalloc/internal/hash.h"
+#include "jemalloc/internal/prof.h"
+
+#undef JEMALLOC_H_EXTERNS
+/******************************************************************************/
+#define JEMALLOC_H_INLINES
+
+#include "jemalloc/internal/prn.h"
+#include "jemalloc/internal/ckh.h"
+#include "jemalloc/internal/stats.h"
+#include "jemalloc/internal/ctl.h"
+#include "jemalloc/internal/mutex.h"
+#include "jemalloc/internal/mb.h"
+#include "jemalloc/internal/extent.h"
+#include "jemalloc/internal/base.h"
+#include "jemalloc/internal/chunk.h"
+#include "jemalloc/internal/huge.h"
+
+#ifndef JEMALLOC_ENABLE_INLINE
+void malloc_write(const char *s);
+arena_t *choose_arena(void);
+#endif
+
+#if (defined(JEMALLOC_ENABLE_INLINE) || defined(JEMALLOC_C_))
+/*
+ * Wrapper around malloc_message() that avoids the need for
+ * JEMALLOC_P(malloc_message)(...) throughout the code.
+ */
+JEMALLOC_INLINE void
+malloc_write(const char *s)
+{
+
+ JEMALLOC_P(malloc_message)(NULL, s);
+}
+
+/*
+ * Choose an arena based on a per-thread value (fast-path code, calls slow-path
+ * code if necessary).
+ */
+JEMALLOC_INLINE arena_t *
+choose_arena(void)
+{
+ arena_t *ret;
+
+ /*
+ * We can only use TLS if this is a PIC library, since for the static
+ * library version, libc's malloc is used by TLS allocation, which
+ * introduces a bootstrapping issue.
+ */
+#ifndef NO_TLS
+ ret = arenas_map;
+ if (ret == NULL) {
+ ret = choose_arena_hard();
+ assert(ret != NULL);
+ }
+#else
+ if (isthreaded && narenas > 1) {
+ unsigned long ind;
+
+ /*
+ * Hash pthread_self() to one of the arenas. There is a prime
+ * number of arenas, so this has a reasonable chance of
+ * working. Even so, the hashing can be easily thwarted by
+ * inconvenient pthread_self() values. Without specific
+ * knowledge of how pthread_self() calculates values, we can't
+ * easily do much better than this.
+ */
+ ind = (unsigned long) pthread_self() % narenas;
+
+ /*
+ * Optimistially assume that arenas[ind] has been initialized.
+ * At worst, we find out that some other thread has already
+ * done so, after acquiring the lock in preparation. Note that
+ * this lazy locking also has the effect of lazily forcing
+ * cache coherency; without the lock acquisition, there's no
+ * guarantee that modification of arenas[ind] by another thread
+ * would be seen on this CPU for an arbitrary amount of time.
+ *
+ * In general, this approach to modifying a synchronized value
+ * isn't a good idea, but in this case we only ever modify the
+ * value once, so things work out well.
+ */
+ ret = arenas[ind];
+ if (ret == NULL) {
+ /*
+ * Avoid races with another thread that may have already
+ * initialized arenas[ind].
+ */
+ malloc_mutex_lock(&arenas_lock);
+ if (arenas[ind] == NULL)
+ ret = arenas_extend((unsigned)ind);
+ else
+ ret = arenas[ind];
+ malloc_mutex_unlock(&arenas_lock);
+ }
+ } else
+ ret = arenas[0];
+#endif
+
+ assert(ret != NULL);
+ return (ret);
+}
+#endif
+
+#include "jemalloc/internal/tcache.h"
+#include "jemalloc/internal/arena.h"
+#include "jemalloc/internal/hash.h"
+#include "jemalloc/internal/prof.h"
+
+#ifndef JEMALLOC_ENABLE_INLINE
+void *imalloc(size_t size);
+void *icalloc(size_t size);
+void *ipalloc(size_t alignment, size_t size);
+size_t isalloc(const void *ptr);
+void *iralloc(void *ptr, size_t size);
+void idalloc(void *ptr);
+#endif
+
+#if (defined(JEMALLOC_ENABLE_INLINE) || defined(JEMALLOC_C_))
+JEMALLOC_INLINE void *
+imalloc(size_t size)
+{
+
+ assert(size != 0);
+
+ if (size <= arena_maxclass)
+ return (arena_malloc(size, false));
+ else
+ return (huge_malloc(size, false));
+}
+
+JEMALLOC_INLINE void *
+icalloc(size_t size)
+{
+
+ if (size <= arena_maxclass)
+ return (arena_malloc(size, true));
+ else
+ return (huge_malloc(size, true));
+}
+
+JEMALLOC_INLINE void *
+ipalloc(size_t alignment, size_t size)
+{
+ void *ret;
+ size_t ceil_size;
+
+ /*
+ * Round size up to the nearest multiple of alignment.
+ *
+ * This done, we can take advantage of the fact that for each small
+ * size class, every object is aligned at the smallest power of two
+ * that is non-zero in the base two representation of the size. For
+ * example:
+ *
+ * Size | Base 2 | Minimum alignment
+ * -----+----------+------------------
+ * 96 | 1100000 | 32
+ * 144 | 10100000 | 32
+ * 192 | 11000000 | 64
+ *
+ * Depending on runtime settings, it is possible that arena_malloc()
+ * will further round up to a power of two, but that never causes
+ * correctness issues.
+ */
+ ceil_size = (size + (alignment - 1)) & (-alignment);
+ /*
+ * (ceil_size < size) protects against the combination of maximal
+ * alignment and size greater than maximal alignment.
+ */
+ if (ceil_size < size) {
+ /* size_t overflow. */
+ return (NULL);
+ }
+
+ if (ceil_size <= PAGE_SIZE || (alignment <= PAGE_SIZE
+ && ceil_size <= arena_maxclass))
+ ret = arena_malloc(ceil_size, false);
+ else {
+ size_t run_size;
+
+ /*
+ * We can't achieve subpage alignment, so round up alignment
+ * permanently; it makes later calculations simpler.
+ */
+ alignment = PAGE_CEILING(alignment);
+ ceil_size = PAGE_CEILING(size);
+ /*
+ * (ceil_size < size) protects against very large sizes within
+ * PAGE_SIZE of SIZE_T_MAX.
+ *
+ * (ceil_size + alignment < ceil_size) protects against the
+ * combination of maximal alignment and ceil_size large enough
+ * to cause overflow. This is similar to the first overflow
+ * check above, but it needs to be repeated due to the new
+ * ceil_size value, which may now be *equal* to maximal
+ * alignment, whereas before we only detected overflow if the
+ * original size was *greater* than maximal alignment.
+ */
+ if (ceil_size < size || ceil_size + alignment < ceil_size) {
+ /* size_t overflow. */
+ return (NULL);
+ }
+
+ /*
+ * Calculate the size of the over-size run that arena_palloc()
+ * would need to allocate in order to guarantee the alignment.
+ */
+ if (ceil_size >= alignment)
+ run_size = ceil_size + alignment - PAGE_SIZE;
+ else {
+ /*
+ * It is possible that (alignment << 1) will cause
+ * overflow, but it doesn't matter because we also
+ * subtract PAGE_SIZE, which in the case of overflow
+ * leaves us with a very large run_size. That causes
+ * the first conditional below to fail, which means
+ * that the bogus run_size value never gets used for
+ * anything important.
+ */
+ run_size = (alignment << 1) - PAGE_SIZE;
+ }
+
+ if (run_size <= arena_maxclass) {
+ ret = arena_palloc(choose_arena(), alignment, ceil_size,
+ run_size);
+ } else if (alignment <= chunksize)
+ ret = huge_malloc(ceil_size, false);
+ else
+ ret = huge_palloc(alignment, ceil_size);
+ }
+
+ assert(((uintptr_t)ret & (alignment - 1)) == 0);
+ return (ret);
+}
+
+JEMALLOC_INLINE size_t
+isalloc(const void *ptr)
+{
+ size_t ret;
+ arena_chunk_t *chunk;
+
+ assert(ptr != NULL);
+
+ chunk = (arena_chunk_t *)CHUNK_ADDR2BASE(ptr);
+ if (chunk != ptr) {
+ /* Region. */
+ assert(chunk->arena->magic == ARENA_MAGIC);
+
+#ifdef JEMALLOC_PROF
+ ret = arena_salloc_demote(ptr);
+#else
+ ret = arena_salloc(ptr);
+#endif
+ } else
+ ret = huge_salloc(ptr);
+
+ return (ret);
+}
+
+JEMALLOC_INLINE void *
+iralloc(void *ptr, size_t size)
+{
+ size_t oldsize;
+
+ assert(ptr != NULL);
+ assert(size != 0);
+
+ oldsize = isalloc(ptr);
+
+ if (size <= arena_maxclass)
+ return (arena_ralloc(ptr, size, oldsize));
+ else
+ return (huge_ralloc(ptr, size, oldsize));
+}
+
+JEMALLOC_INLINE void
+idalloc(void *ptr)
+{
+ arena_chunk_t *chunk;
+
+ assert(ptr != NULL);
+
+ chunk = (arena_chunk_t *)CHUNK_ADDR2BASE(ptr);
+ if (chunk != ptr)
+ arena_dalloc(chunk->arena, chunk, ptr);
+ else
+ huge_dalloc(ptr);
+}
+#endif
+
+#undef JEMALLOC_H_INLINES
+/******************************************************************************/
diff --git a/externals/jemalloc/include/internal/mb.h b/externals/jemalloc/include/internal/mb.h
new file mode 100644
index 00000000000..1707aa91d68
--- /dev/null
+++ b/externals/jemalloc/include/internal/mb.h
@@ -0,0 +1,108 @@
+/******************************************************************************/
+#ifdef JEMALLOC_H_TYPES
+
+#endif /* JEMALLOC_H_TYPES */
+/******************************************************************************/
+#ifdef JEMALLOC_H_STRUCTS
+
+#endif /* JEMALLOC_H_STRUCTS */
+/******************************************************************************/
+#ifdef JEMALLOC_H_EXTERNS
+
+#endif /* JEMALLOC_H_EXTERNS */
+/******************************************************************************/
+#ifdef JEMALLOC_H_INLINES
+
+#ifndef JEMALLOC_ENABLE_INLINE
+void mb_write(void);
+#endif
+
+#if (defined(JEMALLOC_ENABLE_INLINE) || defined(MB_C_))
+#ifdef __i386__
+/*
+ * According to the Intel Architecture Software Developer's Manual, current
+ * processors execute instructions in order from the perspective of other
+ * processors in a multiprocessor system, but 1) Intel reserves the right to
+ * change that, and 2) the compiler's optimizer could re-order instructions if
+ * there weren't some form of barrier. Therefore, even if running on an
+ * architecture that does not need memory barriers (everything through at least
+ * i686), an "optimizer barrier" is necessary.
+ */
+JEMALLOC_INLINE void
+mb_write(void)
+{
+
+# if 0
+ /* This is a true memory barrier. */
+ asm volatile ("pusha;"
+ "xor %%eax,%%eax;"
+ "cpuid;"
+ "popa;"
+ : /* Outputs. */
+ : /* Inputs. */
+ : "memory" /* Clobbers. */
+ );
+#else
+ /*
+ * This is hopefully enough to keep the compiler from reordering
+ * instructions around this one.
+ */
+ asm volatile ("nop;"
+ : /* Outputs. */
+ : /* Inputs. */
+ : "memory" /* Clobbers. */
+ );
+#endif
+}
+#elif (defined(__amd64_) || defined(__x86_64__))
+JEMALLOC_INLINE void
+mb_write(void)
+{
+
+ asm volatile ("sfence"
+ : /* Outputs. */
+ : /* Inputs. */
+ : "memory" /* Clobbers. */
+ );
+}
+#elif defined(__powerpc__)
+JEMALLOC_INLINE void
+mb_write(void)
+{
+
+ asm volatile ("eieio"
+ : /* Outputs. */
+ : /* Inputs. */
+ : "memory" /* Clobbers. */
+ );
+}
+#elif defined(__sparc64__)
+JEMALLOC_INLINE void
+mb_write(void)
+{
+
+ asm volatile ("membar #StoreStore"
+ : /* Outputs. */
+ : /* Inputs. */
+ : "memory" /* Clobbers. */
+ );
+}
+#else
+/*
+ * This is much slower than a simple memory barrier, but the semantics of mutex
+ * unlock make this work.
+ */
+JEMALLOC_INLINE void
+mb_write(void)
+{
+ malloc_mutex_t mtx;
+
+ malloc_mutex_init(&mtx);
+ malloc_mutex_lock(&mtx);
+ malloc_mutex_unlock(&mtx);
+}
+#endif
+#endif
+
+#endif /* JEMALLOC_H_INLINES */
+/******************************************************************************/
diff --git a/externals/jemalloc/include/internal/mutex.h b/externals/jemalloc/include/internal/mutex.h
new file mode 100644
index 00000000000..108bfa8abfd
--- /dev/null
+++ b/externals/jemalloc/include/internal/mutex.h
@@ -0,0 +1,61 @@
+/******************************************************************************/
+#ifdef JEMALLOC_H_TYPES
+
+typedef pthread_mutex_t malloc_mutex_t;
+
+#endif /* JEMALLOC_H_TYPES */
+/******************************************************************************/
+#ifdef JEMALLOC_H_STRUCTS
+
+#endif /* JEMALLOC_H_STRUCTS */
+/******************************************************************************/
+#ifdef JEMALLOC_H_EXTERNS
+
+#ifdef JEMALLOC_LAZY_LOCK
+extern bool isthreaded;
+#else
+# define isthreaded true
+#endif
+
+bool malloc_mutex_init(malloc_mutex_t *mutex);
+
+#endif /* JEMALLOC_H_EXTERNS */
+/******************************************************************************/
+#ifdef JEMALLOC_H_INLINES
+
+#ifndef JEMALLOC_ENABLE_INLINE
+void malloc_mutex_lock(malloc_mutex_t *mutex);
+bool malloc_mutex_trylock(malloc_mutex_t *mutex);
+void malloc_mutex_unlock(malloc_mutex_t *mutex);
+#endif
+
+#if (defined(JEMALLOC_ENABLE_INLINE) || defined(JEMALLOC_MUTEX_C_))
+JEMALLOC_INLINE void
+malloc_mutex_lock(malloc_mutex_t *mutex)
+{
+
+ if (isthreaded)
+ pthread_mutex_lock(mutex);
+}
+
+JEMALLOC_INLINE bool
+malloc_mutex_trylock(malloc_mutex_t *mutex)
+{
+
+ if (isthreaded)
+ return (pthread_mutex_trylock(mutex) != 0);
+ else
+ return (false);
+}
+
+JEMALLOC_INLINE void
+malloc_mutex_unlock(malloc_mutex_t *mutex)
+{
+
+ if (isthreaded)
+ pthread_mutex_unlock(mutex);
+}
+#endif
+
+#endif /* JEMALLOC_H_INLINES */
+/******************************************************************************/
diff --git a/externals/jemalloc/include/internal/prof.h b/externals/jemalloc/include/internal/prof.h
new file mode 100644
index 00000000000..6e71552d85e
--- /dev/null
+++ b/externals/jemalloc/include/internal/prof.h
@@ -0,0 +1,171 @@
+#ifdef JEMALLOC_PROF
+/******************************************************************************/
+#ifdef JEMALLOC_H_TYPES
+
+typedef struct prof_bt_s prof_bt_t;
+typedef struct prof_cnt_s prof_cnt_t;
+typedef struct prof_thr_cnt_s prof_thr_cnt_t;
+typedef struct prof_ctx_s prof_ctx_t;
+typedef struct prof_s prof_t;
+
+/* Option defaults. */
+#define LG_PROF_BT_MAX_DEFAULT 2
+#define LG_PROF_SAMPLE_DEFAULT 0
+#define LG_PROF_INTERVAL_DEFAULT 30
+
+/*
+ * Hard limit on stack backtrace depth. Note that the version of
+ * prof_backtrace() that is based on __builtin_return_address() necessarily has
+ * a hard-coded number of backtrace frame handlers, so increasing
+ * LG_PROF_BT_MAX requires changing prof_backtrace().
+ */
+#define LG_PROF_BT_MAX 7 /* >= LG_PROF_BT_MAX_DEFAULT */
+#define PROF_BT_MAX (1U << LG_PROF_BT_MAX)
+
+/* Initial hash table size. */
+#define PROF_CKH_MINITEMS 64
+
+/* Size of memory buffer to use when writing dump files. */
+#define PROF_DUMP_BUF_SIZE 65536
+
+#endif /* JEMALLOC_H_TYPES */
+/******************************************************************************/
+#ifdef JEMALLOC_H_STRUCTS
+
+struct prof_bt_s {
+ /* Backtrace, stored as len program counters. */
+ void **vec;
+ unsigned len;
+};
+
+#ifdef JEMALLOC_PROF_LIBGCC
+/* Data structure passed to libgcc _Unwind_Backtrace() callback functions. */
+typedef struct {
+ prof_bt_t *bt;
+ unsigned nignore;
+ unsigned max;
+} prof_unwind_data_t;
+#endif
+
+struct prof_cnt_s {
+ /*
+ * Profiling counters. An allocation/deallocation pair can operate on
+ * different prof_thr_cnt_t objects that are linked into the same
+ * prof_ctx_t sets_ql, so it is possible for the cur* counters to go
+ * negative. In principle it is possible for the *bytes counters to
+ * overflow/underflow, but a general solution would require some form
+ * of 128-bit counter solution; this implementation doesn't bother to
+ * solve that problem.
+ */
+ int64_t curobjs;
+ int64_t curbytes;
+ uint64_t accumobjs;
+ uint64_t accumbytes;
+};
+
+struct prof_thr_cnt_s {
+ /* Linkage into prof_ctx_t's sets_ql. */
+ ql_elm(prof_thr_cnt_t) link;
+
+ /*
+ * Associated context. If a thread frees an object that it did not
+ * allocate, it is possible that the context is not cached in the
+ * thread's hash table, in which case it must be able to look up the
+ * context, insert a new prof_thr_cnt_t into the thread's hash table,
+ * and link it into the prof_ctx_t's sets_ql.
+ */
+ prof_ctx_t *ctx;
+
+ /*
+ * Threads use memory barriers to update the counters. Since there is
+ * only ever one writer, the only challenge is for the reader to get a
+ * consistent read of the counters.
+ *
+ * The writer uses this series of operations:
+ *
+ * 1) Increment epoch to an odd number.
+ * 2) Update counters.
+ * 3) Increment epoch to an even number.
+ *
+ * The reader must assure 1) that the epoch is even while it reads the
+ * counters, and 2) that the epoch doesn't change between the time it
+ * starts and finishes reading the counters.
+ */
+ unsigned epoch;
+
+ /* Profiling counters. */
+ prof_cnt_t cnts;
+};
+
+struct prof_ctx_s {
+ /* Protects cnt_merged and sets_ql. */
+ malloc_mutex_t lock;
+
+ /* Temporary storage for aggregation during dump. */
+ prof_cnt_t cnt_dump;
+
+ /* When threads exit, they merge their stats into cnt_merged. */
+ prof_cnt_t cnt_merged;
+
+ /*
+ * List of profile counters, one for each thread that has allocated in
+ * this context.
+ */
+ ql_head(prof_thr_cnt_t) cnts_ql;
+};
+
+#endif /* JEMALLOC_H_STRUCTS */
+/******************************************************************************/
+#ifdef JEMALLOC_H_EXTERNS
+
+extern bool opt_prof;
+/*
+ * Even if opt_prof is true, sampling can be temporarily disabled by setting
+ * opt_prof_active to false. No locking is used when updating opt_prof_active,
+ * so there are no guarantees regarding how long it will take for all threads
+ * to notice state changes.
+ */
+extern bool opt_prof_active;
+extern size_t opt_lg_prof_bt_max; /* Maximum backtrace depth. */
+extern size_t opt_lg_prof_sample; /* Mean bytes between samples. */
+extern ssize_t opt_lg_prof_interval; /* lg(prof_interval). */
+extern bool opt_prof_udump; /* High-water memory dumping. */
+extern bool opt_prof_leak; /* Dump leak summary at exit. */
+
+/*
+ * Profile dump interval, measured in bytes allocated. Each arena triggers a
+ * profile dump when it reaches this threshold. The effect is that the
+ * interval between profile dumps averages prof_interval, though the actual
+ * interval between dumps will tend to be sporadic, and the interval will be a
+ * maximum of approximately (prof_interval * narenas).
+ */
+extern uint64_t prof_interval;
+
+/*
+ * If true, promote small sampled objects to large objects, since small run
+ * headers do not have embedded profile context pointers.
+ */
+extern bool prof_promote;
+
+bool prof_init(prof_t *prof, bool master);
+void prof_destroy(prof_t *prof);
+
+prof_thr_cnt_t *prof_alloc_prep(size_t size);
+prof_thr_cnt_t *prof_cnt_get(const void *ptr);
+void prof_malloc(const void *ptr, prof_thr_cnt_t *cnt);
+void prof_realloc(const void *ptr, prof_thr_cnt_t *cnt, const void *old_ptr,
+ size_t old_size, prof_thr_cnt_t *old_cnt);
+void prof_free(const void *ptr);
+void prof_idump(void);
+bool prof_mdump(const char *filename);
+void prof_udump(void);
+void prof_boot0(void);
+bool prof_boot1(void);
+
+#endif /* JEMALLOC_H_EXTERNS */
+/******************************************************************************/
+#ifdef JEMALLOC_H_INLINES
+
+#endif /* JEMALLOC_H_INLINES */
+/******************************************************************************/
+#endif /* JEMALLOC_PROF */
diff --git a/externals/jemalloc/include/internal/ql.h b/externals/jemalloc/include/internal/ql.h
new file mode 100644
index 00000000000..a9ed2393f0c
--- /dev/null
+++ b/externals/jemalloc/include/internal/ql.h
@@ -0,0 +1,83 @@
+/*
+ * List definitions.
+ */
+#define ql_head(a_type) \
+struct { \
+ a_type *qlh_first; \
+}
+
+#define ql_head_initializer(a_head) {NULL}
+
+#define ql_elm(a_type) qr(a_type)
+
+/* List functions. */
+#define ql_new(a_head) do { \
+ (a_head)->qlh_first = NULL; \
+} while (0)
+
+#define ql_elm_new(a_elm, a_field) qr_new((a_elm), a_field)
+
+#define ql_first(a_head) ((a_head)->qlh_first)
+
+#define ql_last(a_head, a_field) \
+ ((ql_first(a_head) != NULL) \
+ ? qr_prev(ql_first(a_head), a_field) : NULL)
+
+#define ql_next(a_head, a_elm, a_field) \
+ ((ql_last(a_head, a_field) != (a_elm)) \
+ ? qr_next((a_elm), a_field) : NULL)
+
+#define ql_prev(a_head, a_elm, a_field) \
+ ((ql_first(a_head) != (a_elm)) ? qr_prev((a_elm), a_field) \
+ : NULL)
+
+#define ql_before_insert(a_head, a_qlelm, a_elm, a_field) do { \
+ qr_before_insert((a_qlelm), (a_elm), a_field); \
+ if (ql_first(a_head) == (a_qlelm)) { \
+ ql_first(a_head) = (a_elm); \
+ } \
+} while (0)
+
+#define ql_after_insert(a_qlelm, a_elm, a_field) \
+ qr_after_insert((a_qlelm), (a_elm), a_field)
+
+#define ql_head_insert(a_head, a_elm, a_field) do { \
+ if (ql_first(a_head) != NULL) { \
+ qr_before_insert(ql_first(a_head), (a_elm), a_field); \
+ } \
+ ql_first(a_head) = (a_elm); \
+} while (0)
+
+#define ql_tail_insert(a_head, a_elm, a_field) do { \
+ if (ql_first(a_head) != NULL) { \
+ qr_before_insert(ql_first(a_head), (a_elm), a_field); \
+ } \
+ ql_first(a_head) = qr_next((a_elm), a_field); \
+} while (0)
+
+#define ql_remove(a_head, a_elm, a_field) do { \
+ if (ql_first(a_head) == (a_elm)) { \
+ ql_first(a_head) = qr_next(ql_first(a_head), a_field); \
+ } \
+ if (ql_first(a_head) != (a_elm)) { \
+ qr_remove((a_elm), a_field); \
+ } else { \
+ ql_first(a_head) = NULL; \
+ } \
+} while (0)
+
+#define ql_head_remove(a_head, a_type, a_field) do { \
+ a_type *t = ql_first(a_head); \
+ ql_remove((a_head), t, a_field); \
+} while (0)
+
+#define ql_tail_remove(a_head, a_type, a_field) do { \
+ a_type *t = ql_last(a_head, a_field); \
+ ql_remove((a_head), t, a_field); \
+} while (0)
+
+#define ql_foreach(a_var, a_head, a_field) \
+ qr_foreach((a_var), ql_first(a_head), a_field)
+
+#define ql_reverse_foreach(a_var, a_head, a_field) \
+ qr_reverse_foreach((a_var), ql_first(a_head), a_field)
diff --git a/externals/jemalloc/include/internal/qr.h b/externals/jemalloc/include/internal/qr.h
new file mode 100644
index 00000000000..fe22352fedd
--- /dev/null
+++ b/externals/jemalloc/include/internal/qr.h
@@ -0,0 +1,67 @@
+/* Ring definitions. */
+#define qr(a_type) \
+struct { \
+ a_type *qre_next; \
+ a_type *qre_prev; \
+}
+
+/* Ring functions. */
+#define qr_new(a_qr, a_field) do { \
+ (a_qr)->a_field.qre_next = (a_qr); \
+ (a_qr)->a_field.qre_prev = (a_qr); \
+} while (0)
+
+#define qr_next(a_qr, a_field) ((a_qr)->a_field.qre_next)
+
+#define qr_prev(a_qr, a_field) ((a_qr)->a_field.qre_prev)
+
+#define qr_before_insert(a_qrelm, a_qr, a_field) do { \
+ (a_qr)->a_field.qre_prev = (a_qrelm)->a_field.qre_prev; \
+ (a_qr)->a_field.qre_next = (a_qrelm); \
+ (a_qr)->a_field.qre_prev->a_field.qre_next = (a_qr); \
+ (a_qrelm)->a_field.qre_prev = (a_qr); \
+} while (0)
+
+#define qr_after_insert(a_qrelm, a_qr, a_field) \
+ do \
+ { \
+ (a_qr)->a_field.qre_next = (a_qrelm)->a_field.qre_next; \
+ (a_qr)->a_field.qre_prev = (a_qrelm); \
+ (a_qr)->a_field.qre_next->a_field.qre_prev = (a_qr); \
+ (a_qrelm)->a_field.qre_next = (a_qr); \
+ } while (0)
+
+#define qr_meld(a_qr_a, a_qr_b, a_field) do { \
+ void *t; \
+ (a_qr_a)->a_field.qre_prev->a_field.qre_next = (a_qr_b); \
+ (a_qr_b)->a_field.qre_prev->a_field.qre_next = (a_qr_a); \
+ t = (a_qr_a)->a_field.qre_prev; \
+ (a_qr_a)->a_field.qre_prev = (a_qr_b)->a_field.qre_prev; \
+ (a_qr_b)->a_field.qre_prev = t; \
+} while (0)
+
+/* qr_meld() and qr_split() are functionally equivalent, so there's no need to
+ * have two copies of the code. */
+#define qr_split(a_qr_a, a_qr_b, a_field) \
+ qr_meld((a_qr_a), (a_qr_b), a_field)
+
+#define qr_remove(a_qr, a_field) do { \
+ (a_qr)->a_field.qre_prev->a_field.qre_next \
+ = (a_qr)->a_field.qre_next; \
+ (a_qr)->a_field.qre_next->a_field.qre_prev \
+ = (a_qr)->a_field.qre_prev; \
+ (a_qr)->a_field.qre_next = (a_qr); \
+ (a_qr)->a_field.qre_prev = (a_qr); \
+} while (0)
+
+#define qr_foreach(var, a_qr, a_field) \
+ for ((var) = (a_qr); \
+ (var) != NULL; \
+ (var) = (((var)->a_field.qre_next != (a_qr)) \
+ ? (var)->a_field.qre_next : NULL))
+
+#define qr_reverse_foreach(var, a_qr, a_field) \
+ for ((var) = ((a_qr) != NULL) ? qr_prev(a_qr, a_field) : NULL; \
+ (var) != NULL; \
+ (var) = (((var) != (a_qr)) \
+ ? (var)->a_field.qre_prev : NULL))
diff --git a/externals/jemalloc/include/internal/rb.h b/externals/jemalloc/include/internal/rb.h
new file mode 100644
index 00000000000..ee9b009d235
--- /dev/null
+++ b/externals/jemalloc/include/internal/rb.h
@@ -0,0 +1,973 @@
+/*-
+ *******************************************************************************
+ *
+ * cpp macro implementation of left-leaning 2-3 red-black trees. Parent
+ * pointers are not used, and color bits are stored in the least significant
+ * bit of right-child pointers (if RB_COMPACT is defined), thus making node
+ * linkage as compact as is possible for red-black trees.
+ *
+ * Usage:
+ *
+ * #include <stdint.h>
+ * #include <stdbool.h>
+ * #define NDEBUG // (Optional, see assert(3).)
+ * #include <assert.h>
+ * #define RB_COMPACT // (Optional, embed color bits in right-child pointers.)
+ * #include <rb.h>
+ * ...
+ *
+ *******************************************************************************
+ */
+
+#ifndef RB_H_
+#define RB_H_
+
+#if 0
+__FBSDID("$FreeBSD: head/lib/libc/stdlib/rb.h 204493 2010-02-28 22:57:13Z jasone $");
+#endif
+
+#ifdef RB_COMPACT
+/* Node structure. */
+#define rb_node(a_type) \
+struct { \
+ a_type *rbn_left; \
+ a_type *rbn_right_red; \
+}
+#else
+#define rb_node(a_type) \
+struct { \
+ a_type *rbn_left; \
+ a_type *rbn_right; \
+ bool rbn_red; \
+}
+#endif
+
+/* Root structure. */
+#define rb_tree(a_type) \
+struct { \
+ a_type *rbt_root; \
+ a_type rbt_nil; \
+}
+
+/* Left accessors. */
+#define rbtn_left_get(a_type, a_field, a_node) \
+ ((a_node)->a_field.rbn_left)
+#define rbtn_left_set(a_type, a_field, a_node, a_left) do { \
+ (a_node)->a_field.rbn_left = a_left; \
+} while (0)
+
+#ifdef RB_COMPACT
+/* Right accessors. */
+#define rbtn_right_get(a_type, a_field, a_node) \
+ ((a_type *) (((intptr_t) (a_node)->a_field.rbn_right_red) \
+ & ((ssize_t)-2)))
+#define rbtn_right_set(a_type, a_field, a_node, a_right) do { \
+ (a_node)->a_field.rbn_right_red = (a_type *) (((uintptr_t) a_right) \
+ | (((uintptr_t) (a_node)->a_field.rbn_right_red) & ((size_t)1))); \
+} while (0)
+
+/* Color accessors. */
+#define rbtn_red_get(a_type, a_field, a_node) \
+ ((bool) (((uintptr_t) (a_node)->a_field.rbn_right_red) \
+ & ((size_t)1)))
+#define rbtn_color_set(a_type, a_field, a_node, a_red) do { \
+ (a_node)->a_field.rbn_right_red = (a_type *) ((((intptr_t) \
+ (a_node)->a_field.rbn_right_red) & ((ssize_t)-2)) \
+ | ((ssize_t)a_red)); \
+} while (0)
+#define rbtn_red_set(a_type, a_field, a_node) do { \
+ (a_node)->a_field.rbn_right_red = (a_type *) (((uintptr_t) \
+ (a_node)->a_field.rbn_right_red) | ((size_t)1)); \
+} while (0)
+#define rbtn_black_set(a_type, a_field, a_node) do { \
+ (a_node)->a_field.rbn_right_red = (a_type *) (((intptr_t) \
+ (a_node)->a_field.rbn_right_red) & ((ssize_t)-2)); \
+} while (0)
+#else
+/* Right accessors. */
+#define rbtn_right_get(a_type, a_field, a_node) \
+ ((a_node)->a_field.rbn_right)
+#define rbtn_right_set(a_type, a_field, a_node, a_right) do { \
+ (a_node)->a_field.rbn_right = a_right; \
+} while (0)
+
+/* Color accessors. */
+#define rbtn_red_get(a_type, a_field, a_node) \
+ ((a_node)->a_field.rbn_red)
+#define rbtn_color_set(a_type, a_field, a_node, a_red) do { \
+ (a_node)->a_field.rbn_red = (a_red); \
+} while (0)
+#define rbtn_red_set(a_type, a_field, a_node) do { \
+ (a_node)->a_field.rbn_red = true; \
+} while (0)
+#define rbtn_black_set(a_type, a_field, a_node) do { \
+ (a_node)->a_field.rbn_red = false; \
+} while (0)
+#endif
+
+/* Node initializer. */
+#define rbt_node_new(a_type, a_field, a_rbt, a_node) do { \
+ rbtn_left_set(a_type, a_field, (a_node), &(a_rbt)->rbt_nil); \
+ rbtn_right_set(a_type, a_field, (a_node), &(a_rbt)->rbt_nil); \
+ rbtn_red_set(a_type, a_field, (a_node)); \
+} while (0)
+
+/* Tree initializer. */
+#define rb_new(a_type, a_field, a_rbt) do { \
+ (a_rbt)->rbt_root = &(a_rbt)->rbt_nil; \
+ rbt_node_new(a_type, a_field, a_rbt, &(a_rbt)->rbt_nil); \
+ rbtn_black_set(a_type, a_field, &(a_rbt)->rbt_nil); \
+} while (0)
+
+/* Internal utility macros. */
+#define rbtn_first(a_type, a_field, a_rbt, a_root, r_node) do { \
+ (r_node) = (a_root); \
+ if ((r_node) != &(a_rbt)->rbt_nil) { \
+ for (; \
+ rbtn_left_get(a_type, a_field, (r_node)) != &(a_rbt)->rbt_nil;\
+ (r_node) = rbtn_left_get(a_type, a_field, (r_node))) { \
+ } \
+ } \
+} while (0)
+
+#define rbtn_last(a_type, a_field, a_rbt, a_root, r_node) do { \
+ (r_node) = (a_root); \
+ if ((r_node) != &(a_rbt)->rbt_nil) { \
+ for (; rbtn_right_get(a_type, a_field, (r_node)) != \
+ &(a_rbt)->rbt_nil; (r_node) = rbtn_right_get(a_type, a_field, \
+ (r_node))) { \
+ } \
+ } \
+} while (0)
+
+#define rbtn_rotate_left(a_type, a_field, a_node, r_node) do { \
+ (r_node) = rbtn_right_get(a_type, a_field, (a_node)); \
+ rbtn_right_set(a_type, a_field, (a_node), \
+ rbtn_left_get(a_type, a_field, (r_node))); \
+ rbtn_left_set(a_type, a_field, (r_node), (a_node)); \
+} while (0)
+
+#define rbtn_rotate_right(a_type, a_field, a_node, r_node) do { \
+ (r_node) = rbtn_left_get(a_type, a_field, (a_node)); \
+ rbtn_left_set(a_type, a_field, (a_node), \
+ rbtn_right_get(a_type, a_field, (r_node))); \
+ rbtn_right_set(a_type, a_field, (r_node), (a_node)); \
+} while (0)
+
+/*
+ * The rb_proto() macro generates function prototypes that correspond to the
+ * functions generated by an equivalently parameterized call to rb_gen().
+ */
+
+#define rb_proto(a_attr, a_prefix, a_rbt_type, a_type) \
+a_attr void \
+a_prefix##new(a_rbt_type *rbtree); \
+a_attr a_type * \
+a_prefix##first(a_rbt_type *rbtree); \
+a_attr a_type * \
+a_prefix##last(a_rbt_type *rbtree); \
+a_attr a_type * \
+a_prefix##next(a_rbt_type *rbtree, a_type *node); \
+a_attr a_type * \
+a_prefix##prev(a_rbt_type *rbtree, a_type *node); \
+a_attr a_type * \
+a_prefix##search(a_rbt_type *rbtree, a_type *key); \
+a_attr a_type * \
+a_prefix##nsearch(a_rbt_type *rbtree, a_type *key); \
+a_attr a_type * \
+a_prefix##psearch(a_rbt_type *rbtree, a_type *key); \
+a_attr void \
+a_prefix##insert(a_rbt_type *rbtree, a_type *node); \
+a_attr void \
+a_prefix##remove(a_rbt_type *rbtree, a_type *node); \
+a_attr a_type * \
+a_prefix##iter(a_rbt_type *rbtree, a_type *start, a_type *(*cb)( \
+ a_rbt_type *, a_type *, void *), void *arg); \
+a_attr a_type * \
+a_prefix##reverse_iter(a_rbt_type *rbtree, a_type *start, \
+ a_type *(*cb)(a_rbt_type *, a_type *, void *), void *arg);
+
+/*
+ * The rb_gen() macro generates a type-specific red-black tree implementation,
+ * based on the above cpp macros.
+ *
+ * Arguments:
+ *
+ * a_attr : Function attribute for generated functions (ex: static).
+ * a_prefix : Prefix for generated functions (ex: ex_).
+ * a_rb_type : Type for red-black tree data structure (ex: ex_t).
+ * a_type : Type for red-black tree node data structure (ex: ex_node_t).
+ * a_field : Name of red-black tree node linkage (ex: ex_link).
+ * a_cmp : Node comparison function name, with the following prototype:
+ * int (a_cmp *)(a_type *a_node, a_type *a_other);
+ * ^^^^^^
+ * or a_key
+ * Interpretation of comparision function return values:
+ * -1 : a_node < a_other
+ * 0 : a_node == a_other
+ * 1 : a_node > a_other
+ * In all cases, the a_node or a_key macro argument is the first
+ * argument to the comparison function, which makes it possible
+ * to write comparison functions that treat the first argument
+ * specially.
+ *
+ * Assuming the following setup:
+ *
+ * typedef struct ex_node_s ex_node_t;
+ * struct ex_node_s {
+ * rb_node(ex_node_t) ex_link;
+ * };
+ * typedef rb_tree(ex_node_t) ex_t;
+ * rb_gen(static, ex_, ex_t, ex_node_t, ex_link, ex_cmp)
+ *
+ * The following API is generated:
+ *
+ * static void
+ * ex_new(ex_t *extree);
+ * Description: Initialize a red-black tree structure.
+ * Args:
+ * extree: Pointer to an uninitialized red-black tree object.
+ *
+ * static ex_node_t *
+ * ex_first(ex_t *extree);
+ * static ex_node_t *
+ * ex_last(ex_t *extree);
+ * Description: Get the first/last node in extree.
+ * Args:
+ * extree: Pointer to an initialized red-black tree object.
+ * Ret: First/last node in extree, or NULL if extree is empty.
+ *
+ * static ex_node_t *
+ * ex_next(ex_t *extree, ex_node_t *node);
+ * static ex_node_t *
+ * ex_prev(ex_t *extree, ex_node_t *node);
+ * Description: Get node's successor/predecessor.
+ * Args:
+ * extree: Pointer to an initialized red-black tree object.
+ * node : A node in extree.
+ * Ret: node's successor/predecessor in extree, or NULL if node is
+ * last/first.
+ *
+ * static ex_node_t *
+ * ex_search(ex_t *extree, ex_node_t *key);
+ * Description: Search for node that matches key.
+ * Args:
+ * extree: Pointer to an initialized red-black tree object.
+ * key : Search key.
+ * Ret: Node in extree that matches key, or NULL if no match.
+ *
+ * static ex_node_t *
+ * ex_nsearch(ex_t *extree, ex_node_t *key);
+ * static ex_node_t *
+ * ex_psearch(ex_t *extree, ex_node_t *key);
+ * Description: Search for node that matches key. If no match is found,
+ * return what would be key's successor/predecessor, were
+ * key in extree.
+ * Args:
+ * extree: Pointer to an initialized red-black tree object.
+ * key : Search key.
+ * Ret: Node in extree that matches key, or if no match, hypothetical
+ * node's successor/predecessor (NULL if no successor/predecessor).
+ *
+ * static void
+ * ex_insert(ex_t *extree, ex_node_t *node);
+ * Description: Insert node into extree.
+ * Args:
+ * extree: Pointer to an initialized red-black tree object.
+ * node : Node to be inserted into extree.
+ *
+ * static void
+ * ex_remove(ex_t *extree, ex_node_t *node);
+ * Description: Remove node from extree.
+ * Args:
+ * extree: Pointer to an initialized red-black tree object.
+ * node : Node in extree to be removed.
+ *
+ * static ex_node_t *
+ * ex_iter(ex_t *extree, ex_node_t *start, ex_node_t *(*cb)(ex_t *,
+ * ex_node_t *, void *), void *arg);
+ * static ex_node_t *
+ * ex_reverse_iter(ex_t *extree, ex_node_t *start, ex_node *(*cb)(ex_t *,
+ * ex_node_t *, void *), void *arg);
+ * Description: Iterate forward/backward over extree, starting at node.
+ * If extree is modified, iteration must be immediately
+ * terminated by the callback function that causes the
+ * modification.
+ * Args:
+ * extree: Pointer to an initialized red-black tree object.
+ * start : Node at which to start iteration, or NULL to start at
+ * first/last node.
+ * cb : Callback function, which is called for each node during
+ * iteration. Under normal circumstances the callback function
+ * should return NULL, which causes iteration to continue. If a
+ * callback function returns non-NULL, iteration is immediately
+ * terminated and the non-NULL return value is returned by the
+ * iterator. This is useful for re-starting iteration after
+ * modifying extree.
+ * arg : Opaque pointer passed to cb().
+ * Ret: NULL if iteration completed, or the non-NULL callback return value
+ * that caused termination of the iteration.
+ */
+#define rb_gen(a_attr, a_prefix, a_rbt_type, a_type, a_field, a_cmp) \
+a_attr void \
+a_prefix##new(a_rbt_type *rbtree) { \
+ rb_new(a_type, a_field, rbtree); \
+} \
+a_attr a_type * \
+a_prefix##first(a_rbt_type *rbtree) { \
+ a_type *ret; \
+ rbtn_first(a_type, a_field, rbtree, rbtree->rbt_root, ret); \
+ if (ret == &rbtree->rbt_nil) { \
+ ret = NULL; \
+ } \
+ return (ret); \
+} \
+a_attr a_type * \
+a_prefix##last(a_rbt_type *rbtree) { \
+ a_type *ret; \
+ rbtn_last(a_type, a_field, rbtree, rbtree->rbt_root, ret); \
+ if (ret == &rbtree->rbt_nil) { \
+ ret = NULL; \
+ } \
+ return (ret); \
+} \
+a_attr a_type * \
+a_prefix##next(a_rbt_type *rbtree, a_type *node) { \
+ a_type *ret; \
+ if (rbtn_right_get(a_type, a_field, node) != &rbtree->rbt_nil) { \
+ rbtn_first(a_type, a_field, rbtree, rbtn_right_get(a_type, \
+ a_field, node), ret); \
+ } else { \
+ a_type *tnode = rbtree->rbt_root; \
+ assert(tnode != &rbtree->rbt_nil); \
+ ret = &rbtree->rbt_nil; \
+ while (true) { \
+ int cmp = (a_cmp)(node, tnode); \
+ if (cmp < 0) { \
+ ret = tnode; \
+ tnode = rbtn_left_get(a_type, a_field, tnode); \
+ } else if (cmp > 0) { \
+ tnode = rbtn_right_get(a_type, a_field, tnode); \
+ } else { \
+ break; \
+ } \
+ assert(tnode != &rbtree->rbt_nil); \
+ } \
+ } \
+ if (ret == &rbtree->rbt_nil) { \
+ ret = (NULL); \
+ } \
+ return (ret); \
+} \
+a_attr a_type * \
+a_prefix##prev(a_rbt_type *rbtree, a_type *node) { \
+ a_type *ret; \
+ if (rbtn_left_get(a_type, a_field, node) != &rbtree->rbt_nil) { \
+ rbtn_last(a_type, a_field, rbtree, rbtn_left_get(a_type, \
+ a_field, node), ret); \
+ } else { \
+ a_type *tnode = rbtree->rbt_root; \
+ assert(tnode != &rbtree->rbt_nil); \
+ ret = &rbtree->rbt_nil; \
+ while (true) { \
+ int cmp = (a_cmp)(node, tnode); \
+ if (cmp < 0) { \
+ tnode = rbtn_left_get(a_type, a_field, tnode); \
+ } else if (cmp > 0) { \
+ ret = tnode; \
+ tnode = rbtn_right_get(a_type, a_field, tnode); \
+ } else { \
+ break; \
+ } \
+ assert(tnode != &rbtree->rbt_nil); \
+ } \
+ } \
+ if (ret == &rbtree->rbt_nil) { \
+ ret = (NULL); \
+ } \
+ return (ret); \
+} \
+a_attr a_type * \
+a_prefix##search(a_rbt_type *rbtree, a_type *key) { \
+ a_type *ret; \
+ int cmp; \
+ ret = rbtree->rbt_root; \
+ while (ret != &rbtree->rbt_nil \
+ && (cmp = (a_cmp)(key, ret)) != 0) { \
+ if (cmp < 0) { \
+ ret = rbtn_left_get(a_type, a_field, ret); \
+ } else { \
+ ret = rbtn_right_get(a_type, a_field, ret); \
+ } \
+ } \
+ if (ret == &rbtree->rbt_nil) { \
+ ret = (NULL); \
+ } \
+ return (ret); \
+} \
+a_attr a_type * \
+a_prefix##nsearch(a_rbt_type *rbtree, a_type *key) { \
+ a_type *ret; \
+ a_type *tnode = rbtree->rbt_root; \
+ ret = &rbtree->rbt_nil; \
+ while (tnode != &rbtree->rbt_nil) { \
+ int cmp = (a_cmp)(key, tnode); \
+ if (cmp < 0) { \
+ ret = tnode; \
+ tnode = rbtn_left_get(a_type, a_field, tnode); \
+ } else if (cmp > 0) { \
+ tnode = rbtn_right_get(a_type, a_field, tnode); \
+ } else { \
+ ret = tnode; \
+ break; \
+ } \
+ } \
+ if (ret == &rbtree->rbt_nil) { \
+ ret = (NULL); \
+ } \
+ return (ret); \
+} \
+a_attr a_type * \
+a_prefix##psearch(a_rbt_type *rbtree, a_type *key) { \
+ a_type *ret; \
+ a_type *tnode = rbtree->rbt_root; \
+ ret = &rbtree->rbt_nil; \
+ while (tnode != &rbtree->rbt_nil) { \
+ int cmp = (a_cmp)(key, tnode); \
+ if (cmp < 0) { \
+ tnode = rbtn_left_get(a_type, a_field, tnode); \
+ } else if (cmp > 0) { \
+ ret = tnode; \
+ tnode = rbtn_right_get(a_type, a_field, tnode); \
+ } else { \
+ ret = tnode; \
+ break; \
+ } \
+ } \
+ if (ret == &rbtree->rbt_nil) { \
+ ret = (NULL); \
+ } \
+ return (ret); \
+} \
+a_attr void \
+a_prefix##insert(a_rbt_type *rbtree, a_type *node) { \
+ struct { \
+ a_type *node; \
+ int cmp; \
+ } path[sizeof(void *) << 4], *pathp; \
+ rbt_node_new(a_type, a_field, rbtree, node); \
+ /* Wind. */ \
+ path->node = rbtree->rbt_root; \
+ for (pathp = path; pathp->node != &rbtree->rbt_nil; pathp++) { \
+ int cmp = pathp->cmp = a_cmp(node, pathp->node); \
+ assert(cmp != 0); \
+ if (cmp < 0) { \
+ pathp[1].node = rbtn_left_get(a_type, a_field, \
+ pathp->node); \
+ } else { \
+ pathp[1].node = rbtn_right_get(a_type, a_field, \
+ pathp->node); \
+ } \
+ } \
+ pathp->node = node; \
+ /* Unwind. */ \
+ for (pathp--; (uintptr_t)pathp >= (uintptr_t)path; pathp--) { \
+ a_type *cnode = pathp->node; \
+ if (pathp->cmp < 0) { \
+ a_type *left = pathp[1].node; \
+ rbtn_left_set(a_type, a_field, cnode, left); \
+ if (rbtn_red_get(a_type, a_field, left)) { \
+ a_type *leftleft = rbtn_left_get(a_type, a_field, left);\
+ if (rbtn_red_get(a_type, a_field, leftleft)) { \
+ /* Fix up 4-node. */ \
+ a_type *tnode; \
+ rbtn_black_set(a_type, a_field, leftleft); \
+ rbtn_rotate_right(a_type, a_field, cnode, tnode); \
+ cnode = tnode; \
+ } \
+ } else { \
+ return; \
+ } \
+ } else { \
+ a_type *right = pathp[1].node; \
+ rbtn_right_set(a_type, a_field, cnode, right); \
+ if (rbtn_red_get(a_type, a_field, right)) { \
+ a_type *left = rbtn_left_get(a_type, a_field, cnode); \
+ if (rbtn_red_get(a_type, a_field, left)) { \
+ /* Split 4-node. */ \
+ rbtn_black_set(a_type, a_field, left); \
+ rbtn_black_set(a_type, a_field, right); \
+ rbtn_red_set(a_type, a_field, cnode); \
+ } else { \
+ /* Lean left. */ \
+ a_type *tnode; \
+ bool tred = rbtn_red_get(a_type, a_field, cnode); \
+ rbtn_rotate_left(a_type, a_field, cnode, tnode); \
+ rbtn_color_set(a_type, a_field, tnode, tred); \
+ rbtn_red_set(a_type, a_field, cnode); \
+ cnode = tnode; \
+ } \
+ } else { \
+ return; \
+ } \
+ } \
+ pathp->node = cnode; \
+ } \
+ /* Set root, and make it black. */ \
+ rbtree->rbt_root = path->node; \
+ rbtn_black_set(a_type, a_field, rbtree->rbt_root); \
+} \
+a_attr void \
+a_prefix##remove(a_rbt_type *rbtree, a_type *node) { \
+ struct { \
+ a_type *node; \
+ int cmp; \
+ } *pathp, *nodep, path[sizeof(void *) << 4]; \
+ /* Wind. */ \
+ nodep = NULL; /* Silence compiler warning. */ \
+ path->node = rbtree->rbt_root; \
+ for (pathp = path; pathp->node != &rbtree->rbt_nil; pathp++) { \
+ int cmp = pathp->cmp = a_cmp(node, pathp->node); \
+ if (cmp < 0) { \
+ pathp[1].node = rbtn_left_get(a_type, a_field, \
+ pathp->node); \
+ } else { \
+ pathp[1].node = rbtn_right_get(a_type, a_field, \
+ pathp->node); \
+ if (cmp == 0) { \
+ /* Find node's successor, in preparation for swap. */ \
+ pathp->cmp = 1; \
+ nodep = pathp; \
+ for (pathp++; pathp->node != &rbtree->rbt_nil; \
+ pathp++) { \
+ pathp->cmp = -1; \
+ pathp[1].node = rbtn_left_get(a_type, a_field, \
+ pathp->node); \
+ } \
+ break; \
+ } \
+ } \
+ } \
+ assert(nodep->node == node); \
+ pathp--; \
+ if (pathp->node != node) { \
+ /* Swap node with its successor. */ \
+ bool tred = rbtn_red_get(a_type, a_field, pathp->node); \
+ rbtn_color_set(a_type, a_field, pathp->node, \
+ rbtn_red_get(a_type, a_field, node)); \
+ rbtn_left_set(a_type, a_field, pathp->node, \
+ rbtn_left_get(a_type, a_field, node)); \
+ /* If node's successor is its right child, the following code */\
+ /* will do the wrong thing for the right child pointer. */\
+ /* However, it doesn't matter, because the pointer will be */\
+ /* properly set when the successor is pruned. */\
+ rbtn_right_set(a_type, a_field, pathp->node, \
+ rbtn_right_get(a_type, a_field, node)); \
+ rbtn_color_set(a_type, a_field, node, tred); \
+ /* The pruned leaf node's child pointers are never accessed */\
+ /* again, so don't bother setting them to nil. */\
+ nodep->node = pathp->node; \
+ pathp->node = node; \
+ if (nodep == path) { \
+ rbtree->rbt_root = nodep->node; \
+ } else { \
+ if (nodep[-1].cmp < 0) { \
+ rbtn_left_set(a_type, a_field, nodep[-1].node, \
+ nodep->node); \
+ } else { \
+ rbtn_right_set(a_type, a_field, nodep[-1].node, \
+ nodep->node); \
+ } \
+ } \
+ } else { \
+ a_type *left = rbtn_left_get(a_type, a_field, node); \
+ if (left != &rbtree->rbt_nil) { \
+ /* node has no successor, but it has a left child. */\
+ /* Splice node out, without losing the left child. */\
+ assert(rbtn_red_get(a_type, a_field, node) == false); \
+ assert(rbtn_red_get(a_type, a_field, left)); \
+ rbtn_black_set(a_type, a_field, left); \
+ if (pathp == path) { \
+ rbtree->rbt_root = left; \
+ } else { \
+ if (pathp[-1].cmp < 0) { \
+ rbtn_left_set(a_type, a_field, pathp[-1].node, \
+ left); \
+ } else { \
+ rbtn_right_set(a_type, a_field, pathp[-1].node, \
+ left); \
+ } \
+ } \
+ return; \
+ } else if (pathp == path) { \
+ /* The tree only contained one node. */ \
+ rbtree->rbt_root = &rbtree->rbt_nil; \
+ return; \
+ } \
+ } \
+ if (rbtn_red_get(a_type, a_field, pathp->node)) { \
+ /* Prune red node, which requires no fixup. */ \
+ assert(pathp[-1].cmp < 0); \
+ rbtn_left_set(a_type, a_field, pathp[-1].node, \
+ &rbtree->rbt_nil); \
+ return; \
+ } \
+ /* The node to be pruned is black, so unwind until balance is */\
+ /* restored. */\
+ pathp->node = &rbtree->rbt_nil; \
+ for (pathp--; (uintptr_t)pathp >= (uintptr_t)path; pathp--) { \
+ assert(pathp->cmp != 0); \
+ if (pathp->cmp < 0) { \
+ rbtn_left_set(a_type, a_field, pathp->node, \
+ pathp[1].node); \
+ assert(rbtn_red_get(a_type, a_field, pathp[1].node) \
+ == false); \
+ if (rbtn_red_get(a_type, a_field, pathp->node)) { \
+ a_type *right = rbtn_right_get(a_type, a_field, \
+ pathp->node); \
+ a_type *rightleft = rbtn_left_get(a_type, a_field, \
+ right); \
+ a_type *tnode; \
+ if (rbtn_red_get(a_type, a_field, rightleft)) { \
+ /* In the following diagrams, ||, //, and \\ */\
+ /* indicate the path to the removed node. */\
+ /* */\
+ /* || */\
+ /* pathp(r) */\
+ /* // \ */\
+ /* (b) (b) */\
+ /* / */\
+ /* (r) */\
+ /* */\
+ rbtn_black_set(a_type, a_field, pathp->node); \
+ rbtn_rotate_right(a_type, a_field, right, tnode); \
+ rbtn_right_set(a_type, a_field, pathp->node, tnode);\
+ rbtn_rotate_left(a_type, a_field, pathp->node, \
+ tnode); \
+ } else { \
+ /* || */\
+ /* pathp(r) */\
+ /* // \ */\
+ /* (b) (b) */\
+ /* / */\
+ /* (b) */\
+ /* */\
+ rbtn_rotate_left(a_type, a_field, pathp->node, \
+ tnode); \
+ } \
+ /* Balance restored, but rotation modified subtree */\
+ /* root. */\
+ assert((uintptr_t)pathp > (uintptr_t)path); \
+ if (pathp[-1].cmp < 0) { \
+ rbtn_left_set(a_type, a_field, pathp[-1].node, \
+ tnode); \
+ } else { \
+ rbtn_right_set(a_type, a_field, pathp[-1].node, \
+ tnode); \
+ } \
+ return; \
+ } else { \
+ a_type *right = rbtn_right_get(a_type, a_field, \
+ pathp->node); \
+ a_type *rightleft = rbtn_left_get(a_type, a_field, \
+ right); \
+ if (rbtn_red_get(a_type, a_field, rightleft)) { \
+ /* || */\
+ /* pathp(b) */\
+ /* // \ */\
+ /* (b) (b) */\
+ /* / */\
+ /* (r) */\
+ a_type *tnode; \
+ rbtn_black_set(a_type, a_field, rightleft); \
+ rbtn_rotate_right(a_type, a_field, right, tnode); \
+ rbtn_right_set(a_type, a_field, pathp->node, tnode);\
+ rbtn_rotate_left(a_type, a_field, pathp->node, \
+ tnode); \
+ /* Balance restored, but rotation modified */\
+ /* subree root, which may actually be the tree */\
+ /* root. */\
+ if (pathp == path) { \
+ /* Set root. */ \
+ rbtree->rbt_root = tnode; \
+ } else { \
+ if (pathp[-1].cmp < 0) { \
+ rbtn_left_set(a_type, a_field, \
+ pathp[-1].node, tnode); \
+ } else { \
+ rbtn_right_set(a_type, a_field, \
+ pathp[-1].node, tnode); \
+ } \
+ } \
+ return; \
+ } else { \
+ /* || */\
+ /* pathp(b) */\
+ /* // \ */\
+ /* (b) (b) */\
+ /* / */\
+ /* (b) */\
+ a_type *tnode; \
+ rbtn_red_set(a_type, a_field, pathp->node); \
+ rbtn_rotate_left(a_type, a_field, pathp->node, \
+ tnode); \
+ pathp->node = tnode; \
+ } \
+ } \
+ } else { \
+ a_type *left; \
+ rbtn_right_set(a_type, a_field, pathp->node, \
+ pathp[1].node); \
+ left = rbtn_left_get(a_type, a_field, pathp->node); \
+ if (rbtn_red_get(a_type, a_field, left)) { \
+ a_type *tnode; \
+ a_type *leftright = rbtn_right_get(a_type, a_field, \
+ left); \
+ a_type *leftrightleft = rbtn_left_get(a_type, a_field, \
+ leftright); \
+ if (rbtn_red_get(a_type, a_field, leftrightleft)) { \
+ /* || */\
+ /* pathp(b) */\
+ /* / \\ */\
+ /* (r) (b) */\
+ /* \ */\
+ /* (b) */\
+ /* / */\
+ /* (r) */\
+ a_type *unode; \
+ rbtn_black_set(a_type, a_field, leftrightleft); \
+ rbtn_rotate_right(a_type, a_field, pathp->node, \
+ unode); \
+ rbtn_rotate_right(a_type, a_field, pathp->node, \
+ tnode); \
+ rbtn_right_set(a_type, a_field, unode, tnode); \
+ rbtn_rotate_left(a_type, a_field, unode, tnode); \
+ } else { \
+ /* || */\
+ /* pathp(b) */\
+ /* / \\ */\
+ /* (r) (b) */\
+ /* \ */\
+ /* (b) */\
+ /* / */\
+ /* (b) */\
+ assert(leftright != &rbtree->rbt_nil); \
+ rbtn_red_set(a_type, a_field, leftright); \
+ rbtn_rotate_right(a_type, a_field, pathp->node, \
+ tnode); \
+ rbtn_black_set(a_type, a_field, tnode); \
+ } \
+ /* Balance restored, but rotation modified subtree */\
+ /* root, which may actually be the tree root. */\
+ if (pathp == path) { \
+ /* Set root. */ \
+ rbtree->rbt_root = tnode; \
+ } else { \
+ if (pathp[-1].cmp < 0) { \
+ rbtn_left_set(a_type, a_field, pathp[-1].node, \
+ tnode); \
+ } else { \
+ rbtn_right_set(a_type, a_field, pathp[-1].node, \
+ tnode); \
+ } \
+ } \
+ return; \
+ } else if (rbtn_red_get(a_type, a_field, pathp->node)) { \
+ a_type *leftleft = rbtn_left_get(a_type, a_field, left);\
+ if (rbtn_red_get(a_type, a_field, leftleft)) { \
+ /* || */\
+ /* pathp(r) */\
+ /* / \\ */\
+ /* (b) (b) */\
+ /* / */\
+ /* (r) */\
+ a_type *tnode; \
+ rbtn_black_set(a_type, a_field, pathp->node); \
+ rbtn_red_set(a_type, a_field, left); \
+ rbtn_black_set(a_type, a_field, leftleft); \
+ rbtn_rotate_right(a_type, a_field, pathp->node, \
+ tnode); \
+ /* Balance restored, but rotation modified */\
+ /* subtree root. */\
+ assert((uintptr_t)pathp > (uintptr_t)path); \
+ if (pathp[-1].cmp < 0) { \
+ rbtn_left_set(a_type, a_field, pathp[-1].node, \
+ tnode); \
+ } else { \
+ rbtn_right_set(a_type, a_field, pathp[-1].node, \
+ tnode); \
+ } \
+ return; \
+ } else { \
+ /* || */\
+ /* pathp(r) */\
+ /* / \\ */\
+ /* (b) (b) */\
+ /* / */\
+ /* (b) */\
+ rbtn_red_set(a_type, a_field, left); \
+ rbtn_black_set(a_type, a_field, pathp->node); \
+ /* Balance restored. */ \
+ return; \
+ } \
+ } else { \
+ a_type *leftleft = rbtn_left_get(a_type, a_field, left);\
+ if (rbtn_red_get(a_type, a_field, leftleft)) { \
+ /* || */\
+ /* pathp(b) */\
+ /* / \\ */\
+ /* (b) (b) */\
+ /* / */\
+ /* (r) */\
+ a_type *tnode; \
+ rbtn_black_set(a_type, a_field, leftleft); \
+ rbtn_rotate_right(a_type, a_field, pathp->node, \
+ tnode); \
+ /* Balance restored, but rotation modified */\
+ /* subtree root, which may actually be the tree */\
+ /* root. */\
+ if (pathp == path) { \
+ /* Set root. */ \
+ rbtree->rbt_root = tnode; \
+ } else { \
+ if (pathp[-1].cmp < 0) { \
+ rbtn_left_set(a_type, a_field, \
+ pathp[-1].node, tnode); \
+ } else { \
+ rbtn_right_set(a_type, a_field, \
+ pathp[-1].node, tnode); \
+ } \
+ } \
+ return; \
+ } else { \
+ /* || */\
+ /* pathp(b) */\
+ /* / \\ */\
+ /* (b) (b) */\
+ /* / */\
+ /* (b) */\
+ rbtn_red_set(a_type, a_field, left); \
+ } \
+ } \
+ } \
+ } \
+ /* Set root. */ \
+ rbtree->rbt_root = path->node; \
+ assert(rbtn_red_get(a_type, a_field, rbtree->rbt_root) == false); \
+} \
+a_attr a_type * \
+a_prefix##iter_recurse(a_rbt_type *rbtree, a_type *node, \
+ a_type *(*cb)(a_rbt_type *, a_type *, void *), void *arg) { \
+ if (node == &rbtree->rbt_nil) { \
+ return (&rbtree->rbt_nil); \
+ } else { \
+ a_type *ret; \
+ if ((ret = a_prefix##iter_recurse(rbtree, rbtn_left_get(a_type, \
+ a_field, node), cb, arg)) != &rbtree->rbt_nil \
+ || (ret = cb(rbtree, node, arg)) != NULL) { \
+ return (ret); \
+ } \
+ return (a_prefix##iter_recurse(rbtree, rbtn_right_get(a_type, \
+ a_field, node), cb, arg)); \
+ } \
+} \
+a_attr a_type * \
+a_prefix##iter_start(a_rbt_type *rbtree, a_type *start, a_type *node, \
+ a_type *(*cb)(a_rbt_type *, a_type *, void *), void *arg) { \
+ int cmp = a_cmp(start, node); \
+ if (cmp < 0) { \
+ a_type *ret; \
+ if ((ret = a_prefix##iter_start(rbtree, start, \
+ rbtn_left_get(a_type, a_field, node), cb, arg)) != \
+ &rbtree->rbt_nil || (ret = cb(rbtree, node, arg)) != NULL) { \
+ return (ret); \
+ } \
+ return (a_prefix##iter_recurse(rbtree, rbtn_right_get(a_type, \
+ a_field, node), cb, arg)); \
+ } else if (cmp > 0) { \
+ return (a_prefix##iter_start(rbtree, start, \
+ rbtn_right_get(a_type, a_field, node), cb, arg)); \
+ } else { \
+ a_type *ret; \
+ if ((ret = cb(rbtree, node, arg)) != NULL) { \
+ return (ret); \
+ } \
+ return (a_prefix##iter_recurse(rbtree, rbtn_right_get(a_type, \
+ a_field, node), cb, arg)); \
+ } \
+} \
+a_attr a_type * \
+a_prefix##iter(a_rbt_type *rbtree, a_type *start, a_type *(*cb)( \
+ a_rbt_type *, a_type *, void *), void *arg) { \
+ a_type *ret; \
+ if (start != NULL) { \
+ ret = a_prefix##iter_start(rbtree, start, rbtree->rbt_root, \
+ cb, arg); \
+ } else { \
+ ret = a_prefix##iter_recurse(rbtree, rbtree->rbt_root, cb, arg);\
+ } \
+ if (ret == &rbtree->rbt_nil) { \
+ ret = NULL; \
+ } \
+ return (ret); \
+} \
+a_attr a_type * \
+a_prefix##reverse_iter_recurse(a_rbt_type *rbtree, a_type *node, \
+ a_type *(*cb)(a_rbt_type *, a_type *, void *), void *arg) { \
+ if (node == &rbtree->rbt_nil) { \
+ return (&rbtree->rbt_nil); \
+ } else { \
+ a_type *ret; \
+ if ((ret = a_prefix##reverse_iter_recurse(rbtree, \
+ rbtn_right_get(a_type, a_field, node), cb, arg)) != \
+ &rbtree->rbt_nil || (ret = cb(rbtree, node, arg)) != NULL) { \
+ return (ret); \
+ } \
+ return (a_prefix##reverse_iter_recurse(rbtree, \
+ rbtn_left_get(a_type, a_field, node), cb, arg)); \
+ } \
+} \
+a_attr a_type * \
+a_prefix##reverse_iter_start(a_rbt_type *rbtree, a_type *start, \
+ a_type *node, a_type *(*cb)(a_rbt_type *, a_type *, void *), \
+ void *arg) { \
+ int cmp = a_cmp(start, node); \
+ if (cmp > 0) { \
+ a_type *ret; \
+ if ((ret = a_prefix##reverse_iter_start(rbtree, start, \
+ rbtn_right_get(a_type, a_field, node), cb, arg)) != \
+ &rbtree->rbt_nil || (ret = cb(rbtree, node, arg)) != NULL) { \
+ return (ret); \
+ } \
+ return (a_prefix##reverse_iter_recurse(rbtree, \
+ rbtn_left_get(a_type, a_field, node), cb, arg)); \
+ } else if (cmp < 0) { \
+ return (a_prefix##reverse_iter_start(rbtree, start, \
+ rbtn_left_get(a_type, a_field, node), cb, arg)); \
+ } else { \
+ a_type *ret; \
+ if ((ret = cb(rbtree, node, arg)) != NULL) { \
+ return (ret); \
+ } \
+ return (a_prefix##reverse_iter_recurse(rbtree, \
+ rbtn_left_get(a_type, a_field, node), cb, arg)); \
+ } \
+} \
+a_attr a_type * \
+a_prefix##reverse_iter(a_rbt_type *rbtree, a_type *start, \
+ a_type *(*cb)(a_rbt_type *, a_type *, void *), void *arg) { \
+ a_type *ret; \
+ if (start != NULL) { \
+ ret = a_prefix##reverse_iter_start(rbtree, start, \
+ rbtree->rbt_root, cb, arg); \
+ } else { \
+ ret = a_prefix##reverse_iter_recurse(rbtree, rbtree->rbt_root, \
+ cb, arg); \
+ } \
+ if (ret == &rbtree->rbt_nil) { \
+ ret = NULL; \
+ } \
+ return (ret); \
+}
+
+#endif /* RB_H_ */
diff --git a/externals/jemalloc/include/internal/stats.h b/externals/jemalloc/include/internal/stats.h
new file mode 100644
index 00000000000..cbf035ff2b9
--- /dev/null
+++ b/externals/jemalloc/include/internal/stats.h
@@ -0,0 +1,174 @@
+/******************************************************************************/
+#ifdef JEMALLOC_H_TYPES
+
+#define UMAX2S_BUFSIZE 65
+
+#ifdef JEMALLOC_STATS
+typedef struct tcache_bin_stats_s tcache_bin_stats_t;
+typedef struct malloc_bin_stats_s malloc_bin_stats_t;
+typedef struct malloc_large_stats_s malloc_large_stats_t;
+typedef struct arena_stats_s arena_stats_t;
+#endif
+#if (defined(JEMALLOC_STATS) || defined(JEMALLOC_PROF))
+typedef struct chunk_stats_s chunk_stats_t;
+#endif
+
+#endif /* JEMALLOC_H_TYPES */
+/******************************************************************************/
+#ifdef JEMALLOC_H_STRUCTS
+
+#ifdef JEMALLOC_STATS
+
+#ifdef JEMALLOC_TCACHE
+struct tcache_bin_stats_s {
+ /*
+ * Number of allocation requests that corresponded to the size of this
+ * bin.
+ */
+ uint64_t nrequests;
+};
+#endif
+
+struct malloc_bin_stats_s {
+ /*
+ * Current number of bytes allocated, including objects currently
+ * cached by tcache.
+ */
+ size_t allocated;
+
+ /*
+ * Total number of allocation/deallocation requests served directly by
+ * the bin. Note that tcache may allocate an object, then recycle it
+ * many times, resulting many increments to nrequests, but only one
+ * each to nmalloc and ndalloc.
+ */
+ uint64_t nmalloc;
+ uint64_t ndalloc;
+
+ /*
+ * Number of allocation requests that correspond to the size of this
+ * bin. This includes requests served by tcache, though tcache only
+ * periodically merges into this counter.
+ */
+ uint64_t nrequests;
+
+#ifdef JEMALLOC_TCACHE
+ /* Number of tcache fills from this bin. */
+ uint64_t nfills;
+
+ /* Number of tcache flushes to this bin. */
+ uint64_t nflushes;
+#endif
+
+ /* Total number of runs created for this bin's size class. */
+ uint64_t nruns;
+
+ /*
+ * Total number of runs reused by extracting them from the runs tree for
+ * this bin's size class.
+ */
+ uint64_t reruns;
+
+ /* High-water mark for this bin. */
+ size_t highruns;
+
+ /* Current number of runs in this bin. */
+ size_t curruns;
+};
+
+struct malloc_large_stats_s {
+ /*
+ * Total number of allocation/deallocation requests served directly by
+ * the arena. Note that tcache may allocate an object, then recycle it
+ * many times, resulting many increments to nrequests, but only one
+ * each to nmalloc and ndalloc.
+ */
+ uint64_t nmalloc;
+ uint64_t ndalloc;
+
+ /*
+ * Number of allocation requests that correspond to this size class.
+ * This includes requests served by tcache, though tcache only
+ * periodically merges into this counter.
+ */
+ uint64_t nrequests;
+
+ /* High-water mark for this size class. */
+ size_t highruns;
+
+ /* Current number of runs of this size class. */
+ size_t curruns;
+};
+
+struct arena_stats_s {
+ /* Number of bytes currently mapped. */
+ size_t mapped;
+
+ /*
+ * Total number of purge sweeps, total number of madvise calls made,
+ * and total pages purged in order to keep dirty unused memory under
+ * control.
+ */
+ uint64_t npurge;
+ uint64_t nmadvise;
+ uint64_t purged;
+
+ /* Per-size-category statistics. */
+ size_t allocated_large;
+ uint64_t nmalloc_large;
+ uint64_t ndalloc_large;
+ uint64_t nrequests_large;
+
+ /*
+ * One element for each possible size class, including sizes that
+ * overlap with bin size classes. This is necessary because ipalloc()
+ * sometimes has to use such large objects in order to assure proper
+ * alignment.
+ */
+ malloc_large_stats_t *lstats;
+};
+#endif /* JEMALLOC_STATS */
+
+#if (defined(JEMALLOC_STATS) || defined(JEMALLOC_PROF))
+struct chunk_stats_s {
+# ifdef JEMALLOC_STATS
+ /* Number of chunks that were allocated. */
+ uint64_t nchunks;
+# endif
+
+ /* High-water mark for number of chunks allocated. */
+ size_t highchunks;
+
+ /*
+ * Current number of chunks allocated. This value isn't maintained for
+ * any other purpose, so keep track of it in order to be able to set
+ * highchunks.
+ */
+ size_t curchunks;
+};
+#endif /* JEMALLOC_STATS */
+
+#endif /* JEMALLOC_H_STRUCTS */
+/******************************************************************************/
+#ifdef JEMALLOC_H_EXTERNS
+
+extern bool opt_stats_print;
+
+char *umax2s(uintmax_t x, unsigned base, char *s);
+#ifdef JEMALLOC_STATS
+void malloc_cprintf(void (*write)(void *, const char *), void *cbopaque,
+ const char *format, ...) JEMALLOC_ATTR(format(printf, 3, 4));
+void malloc_printf(const char *format, ...)
+ JEMALLOC_ATTR(format(printf, 1, 2));
+#endif
+void stats_print(void (*write)(void *, const char *), void *cbopaque,
+ const char *opts);
+
+#endif /* JEMALLOC_H_EXTERNS */
+/******************************************************************************/
+#ifdef JEMALLOC_STATS
+#ifdef JEMALLOC_H_INLINES
+
+#endif /* JEMALLOC_H_INLINES */
+#endif /* JEMALLOC_STATS */
+/******************************************************************************/
diff --git a/externals/jemalloc/include/internal/tcache.h b/externals/jemalloc/include/internal/tcache.h
new file mode 100644
index 00000000000..c76597fafab
--- /dev/null
+++ b/externals/jemalloc/include/internal/tcache.h
@@ -0,0 +1,380 @@
+#ifdef JEMALLOC_TCACHE
+/******************************************************************************/
+#ifdef JEMALLOC_H_TYPES
+
+typedef struct tcache_bin_s tcache_bin_t;
+typedef struct tcache_s tcache_t;
+
+/*
+ * Absolute maximum number of cache slots for each small bin in the thread
+ * cache. This is an additional constraint beyond that imposed as: twice the
+ * number of regions per run for this size class.
+ *
+ * This constant must be an even number.
+ */
+#define TCACHE_NSLOTS_SMALL_MAX 200
+
+/* Number of cache slots for large size classes. */
+#define TCACHE_NSLOTS_LARGE 20
+
+/* (1U << opt_lg_tcache_maxclass) is used to compute tcache_maxclass. */
+#define LG_TCACHE_MAXCLASS_DEFAULT 15
+
+/*
+ * (1U << opt_lg_tcache_gc_sweep) is the approximate number of allocation
+ * events between full GC sweeps (-1: disabled). Integer rounding may cause
+ * the actual number to be slightly higher, since GC is performed
+ * incrementally.
+ */
+#define LG_TCACHE_GC_SWEEP_DEFAULT 13
+
+#endif /* JEMALLOC_H_TYPES */
+/******************************************************************************/
+#ifdef JEMALLOC_H_STRUCTS
+
+struct tcache_bin_s {
+# ifdef JEMALLOC_STATS
+ tcache_bin_stats_t tstats;
+# endif
+ unsigned low_water; /* Min # cached since last GC. */
+ unsigned high_water; /* Max # cached since last GC. */
+ unsigned ncached; /* # of cached objects. */
+ unsigned ncached_max; /* Upper limit on ncached. */
+ void *avail; /* Chain of available objects. */
+};
+
+struct tcache_s {
+# ifdef JEMALLOC_STATS
+ ql_elm(tcache_t) link; /* Used for aggregating stats. */
+# endif
+# ifdef JEMALLOC_PROF
+ uint64_t prof_accumbytes;/* Cleared after arena_prof_accum() */
+# endif
+ arena_t *arena; /* This thread's arena. */
+ unsigned ev_cnt; /* Event count since incremental GC. */
+ unsigned next_gc_bin; /* Next bin to GC. */
+ tcache_bin_t tbins[1]; /* Dynamically sized. */
+};
+
+#endif /* JEMALLOC_H_STRUCTS */
+/******************************************************************************/
+#ifdef JEMALLOC_H_EXTERNS
+
+extern bool opt_tcache;
+extern ssize_t opt_lg_tcache_maxclass;
+extern ssize_t opt_lg_tcache_gc_sweep;
+
+/* Map of thread-specific caches. */
+extern __thread tcache_t *tcache_tls
+ JEMALLOC_ATTR(tls_model("initial-exec"));
+
+/*
+ * Number of tcache bins. There are nbins small-object bins, plus 0 or more
+ * large-object bins.
+ */
+extern size_t nhbins;
+
+/* Maximum cached size class. */
+extern size_t tcache_maxclass;
+
+/* Number of tcache allocation/deallocation events between incremental GCs. */
+extern unsigned tcache_gc_incr;
+
+void tcache_bin_flush_small(tcache_bin_t *tbin, size_t binind, unsigned rem
+#if (defined(JEMALLOC_STATS) || defined(JEMALLOC_PROF))
+ , tcache_t *tcache
+#endif
+ );
+void tcache_bin_flush_large(tcache_bin_t *tbin, size_t binind, unsigned rem
+#if (defined(JEMALLOC_STATS) || defined(JEMALLOC_PROF))
+ , tcache_t *tcache
+#endif
+ );
+tcache_t *tcache_create(arena_t *arena);
+void *tcache_alloc_small_hard(tcache_t *tcache, tcache_bin_t *tbin,
+ size_t binind);
+void tcache_destroy(tcache_t *tcache);
+#ifdef JEMALLOC_STATS
+void tcache_stats_merge(tcache_t *tcache, arena_t *arena);
+#endif
+void tcache_boot(void);
+
+#endif /* JEMALLOC_H_EXTERNS */
+/******************************************************************************/
+#ifdef JEMALLOC_H_INLINES
+
+#ifndef JEMALLOC_ENABLE_INLINE
+void tcache_event(tcache_t *tcache);
+tcache_t *tcache_get(void);
+void *tcache_alloc_easy(tcache_bin_t *tbin);
+void *tcache_alloc_small(tcache_t *tcache, size_t size, bool zero);
+void *tcache_alloc_large(tcache_t *tcache, size_t size, bool zero);
+void tcache_dalloc_small(tcache_t *tcache, void *ptr);
+void tcache_dalloc_large(tcache_t *tcache, void *ptr, size_t size);
+#endif
+
+#if (defined(JEMALLOC_ENABLE_INLINE) || defined(JEMALLOC_TCACHE_C_))
+JEMALLOC_INLINE tcache_t *
+tcache_get(void)
+{
+ tcache_t *tcache;
+
+ if ((isthreaded & opt_tcache) == false)
+ return (NULL);
+
+ tcache = tcache_tls;
+ if ((uintptr_t)tcache <= (uintptr_t)1) {
+ if (tcache == NULL) {
+ tcache = tcache_create(choose_arena());
+ if (tcache == NULL)
+ return (NULL);
+ } else
+ return (NULL);
+ }
+
+ return (tcache);
+}
+
+JEMALLOC_INLINE void
+tcache_event(tcache_t *tcache)
+{
+
+ if (tcache_gc_incr == 0)
+ return;
+
+ tcache->ev_cnt++;
+ assert(tcache->ev_cnt <= tcache_gc_incr);
+ if (tcache->ev_cnt == tcache_gc_incr) {
+ size_t binind = tcache->next_gc_bin;
+ tcache_bin_t *tbin = &tcache->tbins[binind];
+
+ if (tbin->low_water > 0) {
+ /*
+ * Flush (ceiling) 3/4 of the objects below the low
+ * water mark.
+ */
+ if (binind < nbins) {
+ tcache_bin_flush_small(tbin, binind,
+ tbin->ncached - tbin->low_water +
+ (tbin->low_water >> 2)
+#if (defined(JEMALLOC_STATS) || defined(JEMALLOC_PROF))
+ , tcache
+#endif
+ );
+ } else {
+ tcache_bin_flush_large(tbin, binind,
+ tbin->ncached - tbin->low_water +
+ (tbin->low_water >> 2)
+#if (defined(JEMALLOC_STATS) || defined(JEMALLOC_PROF))
+ , tcache
+#endif
+ );
+ }
+ }
+ tbin->low_water = tbin->ncached;
+ tbin->high_water = tbin->ncached;
+
+ tcache->next_gc_bin++;
+ if (tcache->next_gc_bin == nhbins)
+ tcache->next_gc_bin = 0;
+ tcache->ev_cnt = 0;
+ }
+}
+
+JEMALLOC_INLINE void *
+tcache_alloc_easy(tcache_bin_t *tbin)
+{
+ void *ret;
+
+ if (tbin->ncached == 0)
+ return (NULL);
+ tbin->ncached--;
+ if (tbin->ncached < tbin->low_water)
+ tbin->low_water = tbin->ncached;
+ ret = tbin->avail;
+ tbin->avail = *(void **)ret;
+ return (ret);
+}
+
+JEMALLOC_INLINE void *
+tcache_alloc_small(tcache_t *tcache, size_t size, bool zero)
+{
+ void *ret;
+ size_t binind;
+ tcache_bin_t *tbin;
+
+ binind = small_size2bin[size];
+ assert(binind < nbins);
+ tbin = &tcache->tbins[binind];
+ ret = tcache_alloc_easy(tbin);
+ if (ret == NULL) {
+ ret = tcache_alloc_small_hard(tcache, tbin, binind);
+ if (ret == NULL)
+ return (NULL);
+ }
+ assert(arena_salloc(ret) == tcache->arena->bins[binind].reg_size);
+
+ if (zero == false) {
+#ifdef JEMALLOC_FILL
+ if (opt_junk)
+ memset(ret, 0xa5, size);
+ else if (opt_zero)
+ memset(ret, 0, size);
+#endif
+ } else
+ memset(ret, 0, size);
+
+#ifdef JEMALLOC_STATS
+ tbin->tstats.nrequests++;
+#endif
+#ifdef JEMALLOC_PROF
+ tcache->prof_accumbytes += tcache->arena->bins[binind].reg_size;
+#endif
+ tcache_event(tcache);
+ return (ret);
+}
+
+JEMALLOC_INLINE void *
+tcache_alloc_large(tcache_t *tcache, size_t size, bool zero)
+{
+ void *ret;
+ size_t binind;
+ tcache_bin_t *tbin;
+
+ size = PAGE_CEILING(size);
+ assert(size <= tcache_maxclass);
+ binind = nbins + (size >> PAGE_SHIFT) - 1;
+ assert(binind < nhbins);
+ tbin = &tcache->tbins[binind];
+ ret = tcache_alloc_easy(tbin);
+ if (ret == NULL) {
+ /*
+ * Only allocate one large object at a time, because it's quite
+ * expensive to create one and not use it.
+ */
+ ret = arena_malloc_large(tcache->arena, size, zero);
+ if (ret == NULL)
+ return (NULL);
+ } else {
+#ifdef JEMALLOC_PROF
+ arena_chunk_t *chunk = (arena_chunk_t *)CHUNK_ADDR2BASE(ret);
+ size_t pageind = (unsigned)(((uintptr_t)ret - (uintptr_t)chunk)
+ >> PAGE_SHIFT);
+ chunk->map[pageind].bits |= CHUNK_MAP_CLASS_MASK;
+#endif
+ if (zero == false) {
+#ifdef JEMALLOC_FILL
+ if (opt_junk)
+ memset(ret, 0xa5, size);
+ else if (opt_zero)
+ memset(ret, 0, size);
+#endif
+ } else
+ memset(ret, 0, size);
+
+#ifdef JEMALLOC_STATS
+ tbin->tstats.nrequests++;
+#endif
+#ifdef JEMALLOC_PROF
+ tcache->prof_accumbytes += size;
+#endif
+ }
+
+ tcache_event(tcache);
+ return (ret);
+}
+
+JEMALLOC_INLINE void
+tcache_dalloc_small(tcache_t *tcache, void *ptr)
+{
+ arena_t *arena;
+ arena_chunk_t *chunk;
+ arena_run_t *run;
+ arena_bin_t *bin;
+ tcache_bin_t *tbin;
+ size_t pageind, binind;
+ arena_chunk_map_t *mapelm;
+
+ assert(arena_salloc(ptr) <= small_maxclass);
+
+ chunk = (arena_chunk_t *)CHUNK_ADDR2BASE(ptr);
+ arena = chunk->arena;
+ pageind = (((uintptr_t)ptr - (uintptr_t)chunk) >> PAGE_SHIFT);
+ mapelm = &chunk->map[pageind];
+ run = (arena_run_t *)((uintptr_t)chunk + (uintptr_t)((pageind -
+ (mapelm->bits >> PAGE_SHIFT)) << PAGE_SHIFT));
+ assert(run->magic == ARENA_RUN_MAGIC);
+ bin = run->bin;
+ binind = ((uintptr_t)bin - (uintptr_t)&arena->bins) /
+ sizeof(arena_bin_t);
+ assert(binind < nbins);
+
+#ifdef JEMALLOC_FILL
+ if (opt_junk)
+ memset(ptr, 0x5a, bin->reg_size);
+#endif
+
+ tbin = &tcache->tbins[binind];
+ if (tbin->ncached == tbin->ncached_max) {
+ tcache_bin_flush_small(tbin, binind, (tbin->ncached_max >> 1)
+#if (defined(JEMALLOC_STATS) || defined(JEMALLOC_PROF))
+ , tcache
+#endif
+ );
+ }
+ assert(tbin->ncached < tbin->ncached_max);
+ *(void **)ptr = tbin->avail;
+ tbin->avail = ptr;
+ tbin->ncached++;
+ if (tbin->ncached > tbin->high_water)
+ tbin->high_water = tbin->ncached;
+
+ tcache_event(tcache);
+}
+
+JEMALLOC_INLINE void
+tcache_dalloc_large(tcache_t *tcache, void *ptr, size_t size)
+{
+ arena_t *arena;
+ arena_chunk_t *chunk;
+ size_t pageind, binind;
+ tcache_bin_t *tbin;
+ arena_chunk_map_t *mapelm;
+
+ assert((size & PAGE_MASK) == 0);
+ assert(arena_salloc(ptr) > small_maxclass);
+ assert(arena_salloc(ptr) <= tcache_maxclass);
+
+ chunk = (arena_chunk_t *)CHUNK_ADDR2BASE(ptr);
+ arena = chunk->arena;
+ pageind = (((uintptr_t)ptr - (uintptr_t)chunk) >> PAGE_SHIFT);
+ mapelm = &chunk->map[pageind];
+ binind = nbins + (size >> PAGE_SHIFT) - 1;
+
+#ifdef JEMALLOC_FILL
+ if (opt_junk)
+ memset(ptr, 0x5a, bin->reg_size);
+#endif
+
+ tbin = &tcache->tbins[binind];
+ if (tbin->ncached == tbin->ncached_max) {
+ tcache_bin_flush_large(tbin, binind, (tbin->ncached_max >> 1)
+#if (defined(JEMALLOC_STATS) || defined(JEMALLOC_PROF))
+ , tcache
+#endif
+ );
+ }
+ assert(tbin->ncached < tbin->ncached_max);
+ *(void **)ptr = tbin->avail;
+ tbin->avail = ptr;
+ tbin->ncached++;
+ if (tbin->ncached > tbin->high_water)
+ tbin->high_water = tbin->ncached;
+
+ tcache_event(tcache);
+}
+#endif
+
+#endif /* JEMALLOC_H_INLINES */
+/******************************************************************************/
+#endif /* JEMALLOC_TCACHE */
diff --git a/externals/jemalloc/include/internal/totally_not_p_r_n.h b/externals/jemalloc/include/internal/totally_not_p_r_n.h
new file mode 100644
index 00000000000..0709d708012
--- /dev/null
+++ b/externals/jemalloc/include/internal/totally_not_p_r_n.h
@@ -0,0 +1,60 @@
+/******************************************************************************/
+#ifdef JEMALLOC_H_TYPES
+
+/*
+ * Simple linear congruential pseudo-random number generator:
+ *
+ * prn(y) = (a*x + c) % m
+ *
+ * where the following constants ensure maximal period:
+ *
+ * a == Odd number (relatively prime to 2^n), and (a-1) is a multiple of 4.
+ * c == Odd number (relatively prime to 2^n).
+ * m == 2^32
+ *
+ * See Knuth's TAOCP 3rd Ed., Vol. 2, pg. 17 for details on these constraints.
+ *
+ * This choice of m has the disadvantage that the quality of the bits is
+ * proportional to bit position. For example. the lowest bit has a cycle of 2,
+ * the next has a cycle of 4, etc. For this reason, we prefer to use the upper
+ * bits.
+ *
+ * Macro parameters:
+ * uint32_t r : Result.
+ * unsigned lg_range : (0..32], number of least significant bits to return.
+ * uint32_t state : Seed value.
+ * const uint32_t a, c : See above discussion.
+ */
+#define prn32(r, lg_range, state, a, c) do { \
+ assert(lg_range > 0); \
+ assert(lg_range <= 32); \
+ \
+ r = (state * (a)) + (c); \
+ state = r; \
+ r >>= (32 - lg_range); \
+} while (false)
+
+/* Same as prn32(), but 64 bits of pseudo-randomness, using uint64_t. */
+#define prn64(r, lg_range, state, a, c) do { \
+ assert(lg_range > 0); \
+ assert(lg_range <= 64); \
+ \
+ r = (state * (a)) + (c); \
+ state = r; \
+ r >>= (64 - lg_range); \
+} while (false)
+
+#endif /* JEMALLOC_H_TYPES */
+/******************************************************************************/
+#ifdef JEMALLOC_H_STRUCTS
+
+#endif /* JEMALLOC_H_STRUCTS */
+/******************************************************************************/
+#ifdef JEMALLOC_H_EXTERNS
+
+#endif /* JEMALLOC_H_EXTERNS */
+/******************************************************************************/
+#ifdef JEMALLOC_H_INLINES
+
+#endif /* JEMALLOC_H_INLINES */
+/******************************************************************************/
diff --git a/externals/jemalloc/include/jemalloc.h b/externals/jemalloc/include/jemalloc.h
new file mode 100644
index 00000000000..d9bafbfff55
--- /dev/null
+++ b/externals/jemalloc/include/jemalloc.h
@@ -0,0 +1,42 @@
+#ifndef JEMALLOC_H_
+#define JEMALLOC_H_
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define JEMALLOC_VERSION "1.0.0-0-g5523399"
+#define JEMALLOC_VERSION_MAJOR 1
+#define JEMALLOC_VERSION_MINOR 0
+#define JEMALLOC_VERSION_BUGFIX 0
+#define JEMALLOC_VERSION_NREV 0
+#define JEMALLOC_VERSION_GID "5523399"
+
+#include "jemalloc_defs.h"
+#ifndef JEMALLOC_P
+# define JEMALLOC_P(s) s
+#endif
+
+extern const char *JEMALLOC_P(malloc_options);
+extern void (*JEMALLOC_P(malloc_message))(void *, const char *);
+
+void *JEMALLOC_P(malloc)(size_t size) JEMALLOC_ATTR(malloc);
+void *JEMALLOC_P(calloc)(size_t num, size_t size) JEMALLOC_ATTR(malloc);
+int JEMALLOC_P(posix_memalign)(void **memptr, size_t alignment, size_t size)
+ JEMALLOC_ATTR(nonnull(1));
+void *JEMALLOC_P(realloc)(void *ptr, size_t size);
+void JEMALLOC_P(free)(void *ptr);
+
+size_t JEMALLOC_P(malloc_usable_size)(const void *ptr);
+void JEMALLOC_P(malloc_stats_print)(void (*write_cb)(void *, const char *),
+ void *cbopaque, const char *opts);
+int JEMALLOC_P(mallctl)(const char *name, void *oldp, size_t *oldlenp,
+ void *newp, size_t newlen);
+int JEMALLOC_P(mallctlnametomib)(const char *name, size_t *mibp,
+ size_t *miblenp);
+int JEMALLOC_P(mallctlbymib)(const size_t *mib, size_t miblen, void *oldp,
+ size_t *oldlenp, void *newp, size_t newlen);
+
+#ifdef __cplusplus
+};
+#endif
+#endif /* JEMALLOC_H_ */
diff --git a/externals/jemalloc/include/jemalloc.h.in b/externals/jemalloc/include/jemalloc.h.in
new file mode 100644
index 00000000000..8ef8183686e
--- /dev/null
+++ b/externals/jemalloc/include/jemalloc.h.in
@@ -0,0 +1,42 @@
+#ifndef JEMALLOC_H_
+#define JEMALLOC_H_
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define JEMALLOC_VERSION "@jemalloc_version@"
+#define JEMALLOC_VERSION_MAJOR @jemalloc_version_major@
+#define JEMALLOC_VERSION_MINOR @jemalloc_version_minor@
+#define JEMALLOC_VERSION_BUGFIX @jemalloc_version_bugfix@
+#define JEMALLOC_VERSION_NREV @jemalloc_version_nrev@
+#define JEMALLOC_VERSION_GID "@jemalloc_version_gid@"
+
+#include "jemalloc_defs@install_suffix@.h"
+#ifndef JEMALLOC_P
+# define JEMALLOC_P(s) s
+#endif
+
+extern const char *JEMALLOC_P(malloc_options);
+extern void (*JEMALLOC_P(malloc_message))(void *, const char *);
+
+void *JEMALLOC_P(malloc)(size_t size) JEMALLOC_ATTR(malloc);
+void *JEMALLOC_P(calloc)(size_t num, size_t size) JEMALLOC_ATTR(malloc);
+int JEMALLOC_P(posix_memalign)(void **memptr, size_t alignment, size_t size)
+ JEMALLOC_ATTR(nonnull(1));
+void *JEMALLOC_P(realloc)(void *ptr, size_t size);
+void JEMALLOC_P(free)(void *ptr);
+
+size_t JEMALLOC_P(malloc_usable_size)(const void *ptr);
+void JEMALLOC_P(malloc_stats_print)(void (*write_cb)(void *, const char *),
+ void *cbopaque, const char *opts);
+int JEMALLOC_P(mallctl)(const char *name, void *oldp, size_t *oldlenp,
+ void *newp, size_t newlen);
+int JEMALLOC_P(mallctlnametomib)(const char *name, size_t *mibp,
+ size_t *miblenp);
+int JEMALLOC_P(mallctlbymib)(const size_t *mib, size_t miblen, void *oldp,
+ size_t *oldlenp, void *newp, size_t newlen);
+
+#ifdef __cplusplus
+};
+#endif
+#endif /* JEMALLOC_H_ */
diff --git a/externals/jemalloc/include/jemalloc_defs.h b/externals/jemalloc/include/jemalloc_defs.h
new file mode 100644
index 00000000000..e8acaed3abd
--- /dev/null
+++ b/externals/jemalloc/include/jemalloc_defs.h
@@ -0,0 +1,102 @@
+/* include/jemalloc/jemalloc_defs.h. Generated from jemalloc_defs.h.in by configure. */
+#ifndef JEMALLOC_DEFS_H_
+#define JEMALLOC_DEFS_H_
+
+/*
+ * If JEMALLOC_PREFIX is defined, it will cause all public APIs to be prefixed.
+ * This makes it possible, with some care, to use multiple allocators
+ * simultaneously.
+ *
+ * In many cases it is more convenient to manually prefix allocator function
+ * calls than to let macros do it automatically, particularly when using
+ * multiple allocators simultaneously. Define JEMALLOC_MANGLE before
+ * #include'ing jemalloc.h in order to cause name mangling that corresponds to
+ * the API prefixing.
+ */
+/* #undef JEMALLOC_PREFIX */
+#if (defined(JEMALLOC_PREFIX) && defined(JEMALLOC_MANGLE))
+/* #undef JEMALLOC_P */
+#endif
+
+/*
+ * Hyper-threaded CPUs may need a special instruction inside spin loops in
+ * order to yield to another virtual CPU.
+ */
+#define CPU_SPINWAIT __asm__ volatile("pause")
+
+/* Defined if __attribute__((...)) syntax is supported. */
+#define JEMALLOC_HAVE_ATTR
+#ifdef JEMALLOC_HAVE_ATTR
+# define JEMALLOC_ATTR(s) __attribute__((s))
+#else
+# define JEMALLOC_ATTR(s)
+#endif
+
+/*
+ * JEMALLOC_DEBUG enables assertions and other sanity checks, and disables
+ * inline functions.
+ */
+/* #undef JEMALLOC_DEBUG */
+
+/* JEMALLOC_STATS enables statistics calculation. */
+/* #undef JEMALLOC_STATS */
+
+/* JEMALLOC_PROF enables allocation profiling. */
+/* #undef JEMALLOC_PROF */
+
+/* Use libunwind for profile backtracing if defined. */
+/* #undef JEMALLOC_PROF_LIBUNWIND */
+
+/* Use libgcc for profile backtracing if defined. */
+/* #undef JEMALLOC_PROF_LIBGCC */
+
+/*
+ * JEMALLOC_TINY enables support for tiny objects, which are smaller than one
+ * quantum.
+ */
+#define JEMALLOC_TINY
+
+/*
+ * JEMALLOC_TCACHE enables a thread-specific caching layer for small objects.
+ * This makes it possible to allocate/deallocate objects without any locking
+ * when the cache is in the steady state.
+ */
+#define JEMALLOC_TCACHE
+
+/*
+ * JEMALLOC_DSS enables use of sbrk(2) to allocate chunks from the data storage
+ * segment (DSS).
+ */
+/* #undef JEMALLOC_DSS */
+
+/* JEMALLOC_SWAP enables mmap()ed swap file support. */
+/* #undef JEMALLOC_SWAP */
+
+/* Support memory filling (junk/zero). */
+/* #undef JEMALLOC_FILL */
+
+/* Support optional abort() on OOM. */
+/* #undef JEMALLOC_XMALLOC */
+
+/* Support SYSV semantics. */
+/* #undef JEMALLOC_SYSV */
+
+/* Support lazy locking (avoid locking unless a second thread is launched). */
+#define JEMALLOC_LAZY_LOCK
+
+/* Determine page size at run time if defined. */
+/* #undef DYNAMIC_PAGE_SHIFT */
+
+/* One page is 2^STATIC_PAGE_SHIFT bytes. */
+#define STATIC_PAGE_SHIFT 12
+
+/* TLS is used to map arenas and magazine caches to threads. */
+/* #undef NO_TLS */
+
+/* sizeof(void *) == 2^LG_SIZEOF_PTR. */
+#define LG_SIZEOF_PTR 3
+
+/* sizeof(int) == 2^LG_SIZEOF_INT. */
+#define LG_SIZEOF_INT 2
+
+#endif /* JEMALLOC_DEFS_H_ */
diff --git a/externals/jemalloc/include/jemalloc_defs.h.in b/externals/jemalloc/include/jemalloc_defs.h.in
new file mode 100644
index 00000000000..8b98d670acc
--- /dev/null
+++ b/externals/jemalloc/include/jemalloc_defs.h.in
@@ -0,0 +1,101 @@
+#ifndef JEMALLOC_DEFS_H_
+#define JEMALLOC_DEFS_H_
+
+/*
+ * If JEMALLOC_PREFIX is defined, it will cause all public APIs to be prefixed.
+ * This makes it possible, with some care, to use multiple allocators
+ * simultaneously.
+ *
+ * In many cases it is more convenient to manually prefix allocator function
+ * calls than to let macros do it automatically, particularly when using
+ * multiple allocators simultaneously. Define JEMALLOC_MANGLE before
+ * #include'ing jemalloc.h in order to cause name mangling that corresponds to
+ * the API prefixing.
+ */
+#undef JEMALLOC_PREFIX
+#if (defined(JEMALLOC_PREFIX) && defined(JEMALLOC_MANGLE))
+#undef JEMALLOC_P
+#endif
+
+/*
+ * Hyper-threaded CPUs may need a special instruction inside spin loops in
+ * order to yield to another virtual CPU.
+ */
+#undef CPU_SPINWAIT
+
+/* Defined if __attribute__((...)) syntax is supported. */
+#undef JEMALLOC_HAVE_ATTR
+#ifdef JEMALLOC_HAVE_ATTR
+# define JEMALLOC_ATTR(s) __attribute__((s))
+#else
+# define JEMALLOC_ATTR(s)
+#endif
+
+/*
+ * JEMALLOC_DEBUG enables assertions and other sanity checks, and disables
+ * inline functions.
+ */
+#undef JEMALLOC_DEBUG
+
+/* JEMALLOC_STATS enables statistics calculation. */
+#undef JEMALLOC_STATS
+
+/* JEMALLOC_PROF enables allocation profiling. */
+#undef JEMALLOC_PROF
+
+/* Use libunwind for profile backtracing if defined. */
+#undef JEMALLOC_PROF_LIBUNWIND
+
+/* Use libgcc for profile backtracing if defined. */
+#undef JEMALLOC_PROF_LIBGCC
+
+/*
+ * JEMALLOC_TINY enables support for tiny objects, which are smaller than one
+ * quantum.
+ */
+#undef JEMALLOC_TINY
+
+/*
+ * JEMALLOC_TCACHE enables a thread-specific caching layer for small objects.
+ * This makes it possible to allocate/deallocate objects without any locking
+ * when the cache is in the steady state.
+ */
+#undef JEMALLOC_TCACHE
+
+/*
+ * JEMALLOC_DSS enables use of sbrk(2) to allocate chunks from the data storage
+ * segment (DSS).
+ */
+#undef JEMALLOC_DSS
+
+/* JEMALLOC_SWAP enables mmap()ed swap file support. */
+#undef JEMALLOC_SWAP
+
+/* Support memory filling (junk/zero). */
+#undef JEMALLOC_FILL
+
+/* Support optional abort() on OOM. */
+#undef JEMALLOC_XMALLOC
+
+/* Support SYSV semantics. */
+#undef JEMALLOC_SYSV
+
+/* Support lazy locking (avoid locking unless a second thread is launched). */
+#undef JEMALLOC_LAZY_LOCK
+
+/* Determine page size at run time if defined. */
+#undef DYNAMIC_PAGE_SHIFT
+
+/* One page is 2^STATIC_PAGE_SHIFT bytes. */
+#undef STATIC_PAGE_SHIFT
+
+/* TLS is used to map arenas and magazine caches to threads. */
+#undef NO_TLS
+
+/* sizeof(void *) == 2^LG_SIZEOF_PTR. */
+#undef LG_SIZEOF_PTR
+
+/* sizeof(int) == 2^LG_SIZEOF_INT. */
+#undef LG_SIZEOF_INT
+
+#endif /* JEMALLOC_DEFS_H_ */
diff --git a/externals/jemalloc/jemalloc.c b/externals/jemalloc/jemalloc.c
new file mode 100644
index 00000000000..e01de0d5066
--- /dev/null
+++ b/externals/jemalloc/jemalloc.c
@@ -0,0 +1,1349 @@
+/*-
+ * This allocator implementation is designed to provide scalable performance
+ * for multi-threaded programs on multi-processor systems. The following
+ * features are included for this purpose:
+ *
+ * + Multiple arenas are used if there are multiple CPUs, which reduces lock
+ * contention and cache sloshing.
+ *
+ * + Thread-specific caching is used if there are multiple threads, which
+ * reduces the amount of locking.
+ *
+ * + Cache line sharing between arenas is avoided for internal data
+ * structures.
+ *
+ * + Memory is managed in chunks and runs (chunks can be split into runs),
+ * rather than as individual pages. This provides a constant-time
+ * mechanism for associating allocations with particular arenas.
+ *
+ * Allocation requests are rounded up to the nearest size class, and no record
+ * of the original request size is maintained. Allocations are broken into
+ * categories according to size class. Assuming 1 MiB chunks, 4 KiB pages and
+ * a 16 byte quantum on a 32-bit system, the size classes in each category are
+ * as follows:
+ *
+ * |========================================|
+ * | Category | Subcategory | Size |
+ * |========================================|
+ * | Small | Tiny | 2 |
+ * | | | 4 |
+ * | | | 8 |
+ * | |------------------+----------|
+ * | | Quantum-spaced | 16 |
+ * | | | 32 |
+ * | | | 48 |
+ * | | | ... |
+ * | | | 96 |
+ * | | | 112 |
+ * | | | 128 |
+ * | |------------------+----------|
+ * | | Cacheline-spaced | 192 |
+ * | | | 256 |
+ * | | | 320 |
+ * | | | 384 |
+ * | | | 448 |
+ * | | | 512 |
+ * | |------------------+----------|
+ * | | Sub-page | 760 |
+ * | | | 1024 |
+ * | | | 1280 |
+ * | | | ... |
+ * | | | 3328 |
+ * | | | 3584 |
+ * | | | 3840 |
+ * |========================================|
+ * | Large | 4 KiB |
+ * | | 8 KiB |
+ * | | 12 KiB |
+ * | | ... |
+ * | | 1012 KiB |
+ * | | 1016 KiB |
+ * | | 1020 KiB |
+ * |========================================|
+ * | Huge | 1 MiB |
+ * | | 2 MiB |
+ * | | 3 MiB |
+ * | | ... |
+ * |========================================|
+ *
+ * Different mechanisms are used accoding to category:
+ *
+ * Small: Each size class is segregated into its own set of runs. Each run
+ * maintains a bitmap of which regions are free/allocated.
+ *
+ * Large : Each allocation is backed by a dedicated run. Metadata are stored
+ * in the associated arena chunk header maps.
+ *
+ * Huge : Each allocation is backed by a dedicated contiguous set of chunks.
+ * Metadata are stored in a separate red-black tree.
+ *
+ *******************************************************************************
+ */
+
+#define JEMALLOC_C_
+#include "jemalloc/internal/jemalloc_internal.h"
+
+/******************************************************************************/
+/* Data. */
+
+malloc_mutex_t arenas_lock;
+arena_t **arenas;
+unsigned narenas;
+#ifndef NO_TLS
+static unsigned next_arena;
+#endif
+
+#ifndef NO_TLS
+__thread arena_t *arenas_map JEMALLOC_ATTR(tls_model("initial-exec"));
+#endif
+
+/* Set to true once the allocator has been initialized. */
+static bool malloc_initialized = false;
+
+/* Used to let the initializing thread recursively allocate. */
+static pthread_t malloc_initializer = (unsigned long)0;
+
+/* Used to avoid initialization races. */
+static malloc_mutex_t init_lock = PTHREAD_ADAPTIVE_MUTEX_INITIALIZER_NP;
+
+#ifdef DYNAMIC_PAGE_SHIFT
+size_t pagesize;
+size_t pagesize_mask;
+size_t lg_pagesize;
+#endif
+
+unsigned ncpus;
+
+/* Runtime configuration options. */
+const char *JEMALLOC_P(malloc_options)
+ JEMALLOC_ATTR(visibility("default"));
+#ifdef JEMALLOC_DEBUG
+bool opt_abort = true;
+# ifdef JEMALLOC_FILL
+bool opt_junk = true;
+# endif
+#else
+bool opt_abort = false;
+# ifdef JEMALLOC_FILL
+bool opt_junk = false;
+# endif
+#endif
+#ifdef JEMALLOC_SYSV
+bool opt_sysv = false;
+#endif
+#ifdef JEMALLOC_XMALLOC
+bool opt_xmalloc = false;
+#endif
+#ifdef JEMALLOC_FILL
+bool opt_zero = false;
+#endif
+static int opt_narenas_lshift = 0;
+
+/******************************************************************************/
+/* Function prototypes for non-inline static functions. */
+
+static void wrtmessage(void *cbopaque, const char *s);
+static void stats_print_atexit(void);
+static unsigned malloc_ncpus(void);
+static bool malloc_init_hard(void);
+static void jemalloc_prefork(void);
+static void jemalloc_postfork(void);
+
+/******************************************************************************/
+/* malloc_message() setup. */
+
+#ifdef JEMALLOC_HAVE_ATTR
+JEMALLOC_ATTR(visibility("hidden"))
+#else
+static
+#endif
+void
+wrtmessage(void *cbopaque, const char *s)
+{
+
+ write(STDERR_FILENO, s, strlen(s));
+}
+
+void (*JEMALLOC_P(malloc_message))(void *, const char *s)
+ JEMALLOC_ATTR(visibility("default")) = wrtmessage;
+
+/******************************************************************************/
+/*
+ * Begin miscellaneous support functions.
+ */
+
+/* Create a new arena and insert it into the arenas array at index ind. */
+arena_t *
+arenas_extend(unsigned ind)
+{
+ arena_t *ret;
+
+ /* Allocate enough space for trailing bins. */
+ ret = (arena_t *)base_alloc(sizeof(arena_t)
+ + (sizeof(arena_bin_t) * (nbins - 1)));
+ if (ret != NULL && arena_new(ret, ind) == false) {
+ arenas[ind] = ret;
+ return (ret);
+ }
+ /* Only reached if there is an OOM error. */
+
+ /*
+ * OOM here is quite inconvenient to propagate, since dealing with it
+ * would require a check for failure in the fast path. Instead, punt
+ * by using arenas[0]. In practice, this is an extremely unlikely
+ * failure.
+ */
+ malloc_write("<jemalloc>: Error initializing arena\n");
+ if (opt_abort)
+ abort();
+
+ return (arenas[0]);
+}
+
+#ifndef NO_TLS
+/*
+ * Choose an arena based on a per-thread value (slow-path code only, called
+ * only by choose_arena()).
+ */
+arena_t *
+choose_arena_hard(void)
+{
+ arena_t *ret;
+
+ if (narenas > 1) {
+ malloc_mutex_lock(&arenas_lock);
+ if ((ret = arenas[next_arena]) == NULL)
+ ret = arenas_extend(next_arena);
+ next_arena = (next_arena + 1) % narenas;
+ malloc_mutex_unlock(&arenas_lock);
+ } else
+ ret = arenas[0];
+
+ arenas_map = ret;
+
+ return (ret);
+}
+#endif
+
+static void
+stats_print_atexit(void)
+{
+
+#if (defined(JEMALLOC_TCACHE) && defined(JEMALLOC_STATS))
+ unsigned i;
+
+ /*
+ * Merge stats from extant threads. This is racy, since individual
+ * threads do not lock when recording tcache stats events. As a
+ * consequence, the final stats may be slightly out of date by the time
+ * they are reported, if other threads continue to allocate.
+ */
+ for (i = 0; i < narenas; i++) {
+ arena_t *arena = arenas[i];
+ if (arena != NULL) {
+ tcache_t *tcache;
+
+ /*
+ * tcache_stats_merge() locks bins, so if any code is
+ * introduced that acquires both arena and bin locks in
+ * the opposite order, deadlocks may result.
+ */
+ malloc_mutex_lock(&arena->lock);
+ ql_foreach(tcache, &arena->tcache_ql, link) {
+ tcache_stats_merge(tcache, arena);
+ }
+ malloc_mutex_unlock(&arena->lock);
+ }
+ }
+#endif
+ JEMALLOC_P(malloc_stats_print)(NULL, NULL, NULL);
+}
+
+/*
+ * End miscellaneous support functions.
+ */
+/******************************************************************************/
+/*
+ * Begin initialization functions.
+ */
+
+static unsigned
+malloc_ncpus(void)
+{
+ unsigned ret;
+ long result;
+
+ result = sysconf(_SC_NPROCESSORS_ONLN);
+ if (result == -1) {
+ /* Error. */
+ ret = 1;
+ }
+ ret = (unsigned)result;
+
+ return (ret);
+}
+
+/*
+ * FreeBSD's pthreads implementation calls malloc(3), so the malloc
+ * implementation has to take pains to avoid infinite recursion during
+ * initialization.
+ */
+static inline bool
+malloc_init(void)
+{
+
+ if (malloc_initialized == false)
+ return (malloc_init_hard());
+
+ return (false);
+}
+
+static bool
+malloc_init_hard(void)
+{
+ unsigned i;
+ int linklen;
+ char buf[PATH_MAX + 1];
+ const char *opts;
+ arena_t *init_arenas[1];
+
+ malloc_mutex_lock(&init_lock);
+ if (malloc_initialized || malloc_initializer == pthread_self()) {
+ /*
+ * Another thread initialized the allocator before this one
+ * acquired init_lock, or this thread is the initializing
+ * thread, and it is recursively allocating.
+ */
+ malloc_mutex_unlock(&init_lock);
+ return (false);
+ }
+ if (malloc_initializer != (unsigned long)0) {
+ /* Busy-wait until the initializing thread completes. */
+ do {
+ malloc_mutex_unlock(&init_lock);
+ CPU_SPINWAIT;
+ malloc_mutex_lock(&init_lock);
+ } while (malloc_initialized == false);
+ return (false);
+ }
+
+#ifdef DYNAMIC_PAGE_SHIFT
+ /* Get page size. */
+ {
+ long result;
+
+ result = sysconf(_SC_PAGESIZE);
+ assert(result != -1);
+ pagesize = (unsigned)result;
+
+ /*
+ * We assume that pagesize is a power of 2 when calculating
+ * pagesize_mask and lg_pagesize.
+ */
+ assert(((result - 1) & result) == 0);
+ pagesize_mask = result - 1;
+ lg_pagesize = ffs((int)result) - 1;
+ }
+#endif
+
+ for (i = 0; i < 3; i++) {
+ unsigned j;
+
+ /* Get runtime configuration. */
+ switch (i) {
+ case 0:
+ if ((linklen = readlink("/etc/jemalloc.conf", buf,
+ sizeof(buf) - 1)) != -1) {
+ /*
+ * Use the contents of the "/etc/jemalloc.conf"
+ * symbolic link's name.
+ */
+ buf[linklen] = '\0';
+ opts = buf;
+ } else {
+ /* No configuration specified. */
+ buf[0] = '\0';
+ opts = buf;
+ }
+ break;
+ case 1:
+ if ((opts = getenv("JEMALLOC_OPTIONS")) != NULL) {
+ /*
+ * Do nothing; opts is already initialized to
+ * the value of the JEMALLOC_OPTIONS
+ * environment variable.
+ */
+ } else {
+ /* No configuration specified. */
+ buf[0] = '\0';
+ opts = buf;
+ }
+ break;
+ case 2:
+ if (JEMALLOC_P(malloc_options) != NULL) {
+ /*
+ * Use options that were compiled into the
+ * program.
+ */
+ opts = JEMALLOC_P(malloc_options);
+ } else {
+ /* No configuration specified. */
+ buf[0] = '\0';
+ opts = buf;
+ }
+ break;
+ default:
+ /* NOTREACHED */
+ assert(false);
+ buf[0] = '\0';
+ opts = buf;
+ }
+
+ for (j = 0; opts[j] != '\0'; j++) {
+ unsigned k, nreps;
+ bool nseen;
+
+ /* Parse repetition count, if any. */
+ for (nreps = 0, nseen = false;; j++, nseen = true) {
+ switch (opts[j]) {
+ case '0': case '1': case '2': case '3':
+ case '4': case '5': case '6': case '7':
+ case '8': case '9':
+ nreps *= 10;
+ nreps += opts[j] - '0';
+ break;
+ default:
+ goto MALLOC_OUT;
+ }
+ }
+MALLOC_OUT:
+ if (nseen == false)
+ nreps = 1;
+
+ for (k = 0; k < nreps; k++) {
+ switch (opts[j]) {
+ case 'a':
+ opt_abort = false;
+ break;
+ case 'A':
+ opt_abort = true;
+ break;
+#ifdef JEMALLOC_PROF
+ case 'b':
+ if (opt_lg_prof_bt_max > 0)
+ opt_lg_prof_bt_max--;
+ break;
+ case 'B':
+ if (opt_lg_prof_bt_max < LG_PROF_BT_MAX)
+ opt_lg_prof_bt_max++;
+ break;
+#endif
+ case 'c':
+ if (opt_lg_cspace_max - 1 >
+ opt_lg_qspace_max &&
+ opt_lg_cspace_max >
+ LG_CACHELINE)
+ opt_lg_cspace_max--;
+ break;
+ case 'C':
+ if (opt_lg_cspace_max < PAGE_SHIFT
+ - 1)
+ opt_lg_cspace_max++;
+ break;
+ case 'd':
+ if (opt_lg_dirty_mult + 1 <
+ (sizeof(size_t) << 3))
+ opt_lg_dirty_mult++;
+ break;
+ case 'D':
+ if (opt_lg_dirty_mult >= 0)
+ opt_lg_dirty_mult--;
+ break;
+#ifdef JEMALLOC_PROF
+ case 'e':
+ opt_prof_active = false;
+ break;
+ case 'E':
+ opt_prof_active = true;
+ break;
+ case 'f':
+ opt_prof = false;
+ break;
+ case 'F':
+ opt_prof = true;
+ break;
+#endif
+#ifdef JEMALLOC_TCACHE
+ case 'g':
+ if (opt_lg_tcache_gc_sweep >= 0)
+ opt_lg_tcache_gc_sweep--;
+ break;
+ case 'G':
+ if (opt_lg_tcache_gc_sweep + 1 <
+ (sizeof(size_t) << 3))
+ opt_lg_tcache_gc_sweep++;
+ break;
+ case 'h':
+ opt_tcache = false;
+ break;
+ case 'H':
+ opt_tcache = true;
+ break;
+#endif
+#ifdef JEMALLOC_PROF
+ case 'i':
+ if (opt_lg_prof_interval >= 0)
+ opt_lg_prof_interval--;
+ break;
+ case 'I':
+ if (opt_lg_prof_interval + 1 <
+ (sizeof(uint64_t) << 3))
+ opt_lg_prof_interval++;
+ break;
+#endif
+#ifdef JEMALLOC_FILL
+ case 'j':
+ opt_junk = false;
+ break;
+ case 'J':
+ opt_junk = true;
+ break;
+#endif
+ case 'k':
+ /*
+ * Chunks always require at least one
+ * header page, plus one data page.
+ */
+ if ((1U << (opt_lg_chunk - 1)) >=
+ (2U << PAGE_SHIFT))
+ opt_lg_chunk--;
+ break;
+ case 'K':
+ if (opt_lg_chunk + 1 <
+ (sizeof(size_t) << 3))
+ opt_lg_chunk++;
+ break;
+#ifdef JEMALLOC_PROF
+ case 'l':
+ opt_prof_leak = false;
+ break;
+ case 'L':
+ opt_prof_leak = true;
+ break;
+#endif
+#ifdef JEMALLOC_TCACHE
+ case 'm':
+ if (opt_lg_tcache_maxclass >= 0)
+ opt_lg_tcache_maxclass--;
+ break;
+ case 'M':
+ if (opt_lg_tcache_maxclass + 1 <
+ (sizeof(size_t) << 3))
+ opt_lg_tcache_maxclass++;
+ break;
+#endif
+ case 'n':
+ opt_narenas_lshift--;
+ break;
+ case 'N':
+ opt_narenas_lshift++;
+ break;
+#ifdef JEMALLOC_SWAP
+ case 'o':
+ opt_overcommit = false;
+ break;
+ case 'O':
+ opt_overcommit = true;
+ break;
+#endif
+ case 'p':
+ opt_stats_print = false;
+ break;
+ case 'P':
+ opt_stats_print = true;
+ break;
+ case 'q':
+ if (opt_lg_qspace_max > LG_QUANTUM)
+ opt_lg_qspace_max--;
+ break;
+ case 'Q':
+ if (opt_lg_qspace_max + 1 <
+ opt_lg_cspace_max)
+ opt_lg_qspace_max++;
+ break;
+#ifdef JEMALLOC_PROF
+ case 's':
+ if (opt_lg_prof_sample > 0)
+ opt_lg_prof_sample--;
+ break;
+ case 'S':
+ if (opt_lg_prof_sample + 1 <
+ (sizeof(uint64_t) << 3))
+ opt_lg_prof_sample++;
+ break;
+ case 'u':
+ opt_prof_udump = false;
+ break;
+ case 'U':
+ opt_prof_udump = true;
+ break;
+#endif
+#ifdef JEMALLOC_SYSV
+ case 'v':
+ opt_sysv = false;
+ break;
+ case 'V':
+ opt_sysv = true;
+ break;
+#endif
+#ifdef JEMALLOC_XMALLOC
+ case 'x':
+ opt_xmalloc = false;
+ break;
+ case 'X':
+ opt_xmalloc = true;
+ break;
+#endif
+#ifdef JEMALLOC_FILL
+ case 'z':
+ opt_zero = false;
+ break;
+ case 'Z':
+ opt_zero = true;
+ break;
+#endif
+ default: {
+ char cbuf[2];
+
+ cbuf[0] = opts[j];
+ cbuf[1] = '\0';
+ malloc_write(
+ "<jemalloc>: Unsupported character "
+ "in malloc options: '");
+ malloc_write(cbuf);
+ malloc_write("'\n");
+ }
+ }
+ }
+ }
+ }
+
+ /* Register fork handlers. */
+ if (pthread_atfork(jemalloc_prefork, jemalloc_postfork,
+ jemalloc_postfork) != 0) {
+ malloc_write("<jemalloc>: Error in pthread_atfork()\n");
+ if (opt_abort)
+ abort();
+ }
+
+ if (ctl_boot()) {
+ malloc_mutex_unlock(&init_lock);
+ return (true);
+ }
+
+ if (opt_stats_print) {
+ /* Print statistics at exit. */
+ if (atexit(stats_print_atexit) != 0) {
+ malloc_write("<jemalloc>: Error in atexit()\n");
+ if (opt_abort)
+ abort();
+ }
+ }
+
+ if (chunk_boot()) {
+ malloc_mutex_unlock(&init_lock);
+ return (true);
+ }
+
+ if (base_boot()) {
+ malloc_mutex_unlock(&init_lock);
+ return (true);
+ }
+
+#ifdef JEMALLOC_PROF
+ prof_boot0();
+#endif
+
+ if (arena_boot()) {
+ malloc_mutex_unlock(&init_lock);
+ return (true);
+ }
+
+#ifdef JEMALLOC_TCACHE
+ tcache_boot();
+#endif
+
+ if (huge_boot()) {
+ malloc_mutex_unlock(&init_lock);
+ return (true);
+ }
+
+ /*
+ * Create enough scaffolding to allow recursive allocation in
+ * malloc_ncpus().
+ */
+ narenas = 1;
+ arenas = init_arenas;
+ memset(arenas, 0, sizeof(arena_t *) * narenas);
+
+ /*
+ * Initialize one arena here. The rest are lazily created in
+ * choose_arena_hard().
+ */
+ arenas_extend(0);
+ if (arenas[0] == NULL) {
+ malloc_mutex_unlock(&init_lock);
+ return (true);
+ }
+
+#ifndef NO_TLS
+ /*
+ * Assign the initial arena to the initial thread, in order to avoid
+ * spurious creation of an extra arena if the application switches to
+ * threaded mode.
+ */
+ arenas_map = arenas[0];
+#endif
+
+ malloc_mutex_init(&arenas_lock);
+
+#ifdef JEMALLOC_PROF
+ if (prof_boot1()) {
+ malloc_mutex_unlock(&init_lock);
+ return (true);
+ }
+#endif
+
+ /* Get number of CPUs. */
+ malloc_initializer = pthread_self();
+ malloc_mutex_unlock(&init_lock);
+ ncpus = malloc_ncpus();
+ malloc_mutex_lock(&init_lock);
+
+ if (ncpus > 1) {
+ /*
+ * For SMP systems, create more than one arena per CPU by
+ * default.
+ */
+ opt_narenas_lshift += 2;
+ }
+
+ /* Determine how many arenas to use. */
+ narenas = ncpus;
+ if (opt_narenas_lshift > 0) {
+ if ((narenas << opt_narenas_lshift) > narenas)
+ narenas <<= opt_narenas_lshift;
+ /*
+ * Make sure not to exceed the limits of what base_alloc() can
+ * handle.
+ */
+ if (narenas * sizeof(arena_t *) > chunksize)
+ narenas = chunksize / sizeof(arena_t *);
+ } else if (opt_narenas_lshift < 0) {
+ if ((narenas >> -opt_narenas_lshift) < narenas)
+ narenas >>= -opt_narenas_lshift;
+ /* Make sure there is at least one arena. */
+ if (narenas == 0)
+ narenas = 1;
+ }
+
+#ifdef NO_TLS
+ if (narenas > 1) {
+ static const unsigned primes[] = {1, 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};
+ unsigned nprimes, parenas;
+
+ /*
+ * Pick a prime number of hash arenas that is more than narenas
+ * so that direct hashing of pthread_self() pointers tends to
+ * spread allocations evenly among the arenas.
+ */
+ assert((narenas & 1) == 0); /* narenas must be even. */
+ nprimes = (sizeof(primes) >> LG_SIZEOF_INT);
+ parenas = primes[nprimes - 1]; /* In case not enough primes. */
+ for (i = 1; i < nprimes; i++) {
+ if (primes[i] > narenas) {
+ parenas = primes[i];
+ break;
+ }
+ }
+ narenas = parenas;
+ }
+#endif
+
+#ifndef NO_TLS
+ next_arena = 0;
+#endif
+
+ /* Allocate and initialize arenas. */
+ arenas = (arena_t **)base_alloc(sizeof(arena_t *) * narenas);
+ if (arenas == NULL) {
+ malloc_mutex_unlock(&init_lock);
+ return (true);
+ }
+ /*
+ * Zero the array. In practice, this should always be pre-zeroed,
+ * since it was just mmap()ed, but let's be sure.
+ */
+ memset(arenas, 0, sizeof(arena_t *) * narenas);
+ /* Copy the pointer to the one arena that was already initialized. */
+ arenas[0] = init_arenas[0];
+
+ malloc_initialized = true;
+ malloc_mutex_unlock(&init_lock);
+ return (false);
+}
+
+/*
+ * End initialization functions.
+ */
+/******************************************************************************/
+/*
+ * Begin malloc(3)-compatible functions.
+ */
+
+JEMALLOC_ATTR(malloc)
+JEMALLOC_ATTR(visibility("default"))
+void *
+JEMALLOC_P(malloc)(size_t size)
+{
+ void *ret;
+#ifdef JEMALLOC_PROF
+ prof_thr_cnt_t *cnt;
+#endif
+
+ if (malloc_init()) {
+ ret = NULL;
+ goto OOM;
+ }
+
+ if (size == 0) {
+#ifdef JEMALLOC_SYSV
+ if (opt_sysv == false)
+#endif
+ size = 1;
+#ifdef JEMALLOC_SYSV
+ else {
+# ifdef JEMALLOC_XMALLOC
+ if (opt_xmalloc) {
+ malloc_write("<jemalloc>: Error in malloc(): "
+ "invalid size 0\n");
+ abort();
+ }
+# endif
+ ret = NULL;
+ goto RETURN;
+ }
+#endif
+ }
+
+#ifdef JEMALLOC_PROF
+ if (opt_prof) {
+ if ((cnt = prof_alloc_prep(size)) == NULL) {
+ ret = NULL;
+ goto OOM;
+ }
+ if (prof_promote && (uintptr_t)cnt != (uintptr_t)1U && size <=
+ small_maxclass) {
+ ret = imalloc(small_maxclass+1);
+ if (ret != NULL)
+ arena_prof_promoted(ret, size);
+ } else
+ ret = imalloc(size);
+ } else
+#endif
+ ret = imalloc(size);
+
+OOM:
+ if (ret == NULL) {
+#ifdef JEMALLOC_XMALLOC
+ if (opt_xmalloc) {
+ malloc_write("<jemalloc>: Error in malloc(): "
+ "out of memory\n");
+ abort();
+ }
+#endif
+ errno = ENOMEM;
+ }
+
+#ifdef JEMALLOC_SYSV
+RETURN:
+#endif
+#ifdef JEMALLOC_PROF
+ if (opt_prof && ret != NULL)
+ prof_malloc(ret, cnt);
+#endif
+ return (ret);
+}
+
+JEMALLOC_ATTR(nonnull(1))
+JEMALLOC_ATTR(visibility("default"))
+int
+JEMALLOC_P(posix_memalign)(void **memptr, size_t alignment, size_t size)
+{
+ int ret;
+ void *result;
+#ifdef JEMALLOC_PROF
+ prof_thr_cnt_t *cnt;
+#endif
+
+ if (malloc_init())
+ result = NULL;
+ else {
+ if (size == 0) {
+#ifdef JEMALLOC_SYSV
+ if (opt_sysv == false)
+#endif
+ size = 1;
+#ifdef JEMALLOC_SYSV
+ else {
+# ifdef JEMALLOC_XMALLOC
+ if (opt_xmalloc) {
+ malloc_write("<jemalloc>: Error in "
+ "posix_memalign(): invalid size "
+ "0\n");
+ abort();
+ }
+# endif
+ result = NULL;
+ *memptr = NULL;
+ ret = 0;
+ goto RETURN;
+ }
+#endif
+ }
+
+ /* Make sure that alignment is a large enough power of 2. */
+ if (((alignment - 1) & alignment) != 0
+ || alignment < sizeof(void *)) {
+#ifdef JEMALLOC_XMALLOC
+ if (opt_xmalloc) {
+ malloc_write("<jemalloc>: Error in "
+ "posix_memalign(): invalid alignment\n");
+ abort();
+ }
+#endif
+ result = NULL;
+ ret = EINVAL;
+ goto RETURN;
+ }
+
+#ifdef JEMALLOC_PROF
+ if (opt_prof) {
+ if ((cnt = prof_alloc_prep(size)) == NULL) {
+ result = NULL;
+ ret = EINVAL;
+ } else {
+ if (prof_promote && (uintptr_t)cnt !=
+ (uintptr_t)1U && size <= small_maxclass) {
+ result = ipalloc(alignment,
+ small_maxclass+1);
+ if (result != NULL) {
+ arena_prof_promoted(result,
+ size);
+ }
+ } else
+ result = ipalloc(alignment, size);
+ }
+ } else
+#endif
+ result = ipalloc(alignment, size);
+ }
+
+ if (result == NULL) {
+#ifdef JEMALLOC_XMALLOC
+ if (opt_xmalloc) {
+ malloc_write("<jemalloc>: Error in posix_memalign(): "
+ "out of memory\n");
+ abort();
+ }
+#endif
+ ret = ENOMEM;
+ goto RETURN;
+ }
+
+ *memptr = result;
+ ret = 0;
+
+RETURN:
+#ifdef JEMALLOC_PROF
+ if (opt_prof && result != NULL)
+ prof_malloc(result, cnt);
+#endif
+ return (ret);
+}
+
+JEMALLOC_ATTR(malloc)
+JEMALLOC_ATTR(visibility("default"))
+void *
+JEMALLOC_P(calloc)(size_t num, size_t size)
+{
+ void *ret;
+ size_t num_size;
+#ifdef JEMALLOC_PROF
+ prof_thr_cnt_t *cnt;
+#endif
+
+ if (malloc_init()) {
+ num_size = 0;
+ ret = NULL;
+ goto RETURN;
+ }
+
+ num_size = num * size;
+ if (num_size == 0) {
+#ifdef JEMALLOC_SYSV
+ if ((opt_sysv == false) && ((num == 0) || (size == 0)))
+#endif
+ num_size = 1;
+#ifdef JEMALLOC_SYSV
+ else {
+ ret = NULL;
+ goto RETURN;
+ }
+#endif
+ /*
+ * Try to avoid division here. We know that it isn't possible to
+ * overflow during multiplication if neither operand uses any of the
+ * most significant half of the bits in a size_t.
+ */
+ } else if (((num | size) & (SIZE_T_MAX << (sizeof(size_t) << 2)))
+ && (num_size / size != num)) {
+ /* size_t overflow. */
+ ret = NULL;
+ goto RETURN;
+ }
+
+#ifdef JEMALLOC_PROF
+ if (opt_prof) {
+ if ((cnt = prof_alloc_prep(num_size)) == NULL) {
+ ret = NULL;
+ goto RETURN;
+ }
+ if (prof_promote && (uintptr_t)cnt != (uintptr_t)1U && num_size
+ <= small_maxclass) {
+ ret = icalloc(small_maxclass+1);
+ if (ret != NULL)
+ arena_prof_promoted(ret, num_size);
+ } else
+ ret = icalloc(num_size);
+ } else
+#endif
+ ret = icalloc(num_size);
+
+RETURN:
+ if (ret == NULL) {
+#ifdef JEMALLOC_XMALLOC
+ if (opt_xmalloc) {
+ malloc_write("<jemalloc>: Error in calloc(): out of "
+ "memory\n");
+ abort();
+ }
+#endif
+ errno = ENOMEM;
+ }
+
+#ifdef JEMALLOC_PROF
+ if (opt_prof && ret != NULL)
+ prof_malloc(ret, cnt);
+#endif
+ return (ret);
+}
+
+JEMALLOC_ATTR(visibility("default"))
+void *
+JEMALLOC_P(realloc)(void *ptr, size_t size)
+{
+ void *ret;
+#ifdef JEMALLOC_PROF
+ size_t old_size;
+ prof_thr_cnt_t *cnt, *old_cnt;
+#endif
+
+ if (size == 0) {
+#ifdef JEMALLOC_SYSV
+ if (opt_sysv == false)
+#endif
+ size = 1;
+#ifdef JEMALLOC_SYSV
+ else {
+ if (ptr != NULL) {
+#ifdef JEMALLOC_PROF
+ if (opt_prof) {
+ old_size = isalloc(ptr);
+ old_cnt = prof_cnt_get(ptr);
+ cnt = NULL;
+ }
+#endif
+ idalloc(ptr);
+ }
+#ifdef JEMALLOC_PROF
+ else if (opt_prof) {
+ old_size = 0;
+ old_cnt = NULL;
+ cnt = NULL;
+ }
+#endif
+ ret = NULL;
+ goto RETURN;
+ }
+#endif
+ }
+
+ if (ptr != NULL) {
+ assert(malloc_initialized || malloc_initializer ==
+ pthread_self());
+
+#ifdef JEMALLOC_PROF
+ if (opt_prof) {
+ old_size = isalloc(ptr);
+ old_cnt = prof_cnt_get(ptr);
+ if ((cnt = prof_alloc_prep(size)) == NULL) {
+ ret = NULL;
+ goto OOM;
+ }
+ if (prof_promote && (uintptr_t)cnt != (uintptr_t)1U &&
+ size <= small_maxclass) {
+ ret = iralloc(ptr, small_maxclass+1);
+ if (ret != NULL)
+ arena_prof_promoted(ret, size);
+ } else
+ ret = iralloc(ptr, size);
+ } else
+#endif
+ ret = iralloc(ptr, size);
+
+#ifdef JEMALLOC_PROF
+OOM:
+#endif
+ if (ret == NULL) {
+#ifdef JEMALLOC_XMALLOC
+ if (opt_xmalloc) {
+ malloc_write("<jemalloc>: Error in realloc(): "
+ "out of memory\n");
+ abort();
+ }
+#endif
+ errno = ENOMEM;
+ }
+ } else {
+#ifdef JEMALLOC_PROF
+ if (opt_prof) {
+ old_size = 0;
+ old_cnt = NULL;
+ }
+#endif
+ if (malloc_init()) {
+#ifdef JEMALLOC_PROF
+ if (opt_prof)
+ cnt = NULL;
+#endif
+ ret = NULL;
+ } else {
+#ifdef JEMALLOC_PROF
+ if (opt_prof) {
+ if ((cnt = prof_alloc_prep(size)) == NULL)
+ ret = NULL;
+ else {
+ if (prof_promote && (uintptr_t)cnt !=
+ (uintptr_t)1U && size <=
+ small_maxclass) {
+ ret = imalloc(small_maxclass+1);
+ if (ret != NULL) {
+ arena_prof_promoted(ret,
+ size);
+ }
+ } else
+ ret = imalloc(size);
+ }
+ } else
+#endif
+ ret = imalloc(size);
+ }
+
+ if (ret == NULL) {
+#ifdef JEMALLOC_XMALLOC
+ if (opt_xmalloc) {
+ malloc_write("<jemalloc>: Error in realloc(): "
+ "out of memory\n");
+ abort();
+ }
+#endif
+ errno = ENOMEM;
+ }
+ }
+
+#ifdef JEMALLOC_SYSV
+RETURN:
+#endif
+#ifdef JEMALLOC_PROF
+ if (opt_prof)
+ prof_realloc(ret, cnt, ptr, old_size, old_cnt);
+#endif
+ return (ret);
+}
+
+JEMALLOC_ATTR(visibility("default"))
+void
+JEMALLOC_P(free)(void *ptr)
+{
+
+ if (ptr != NULL) {
+ assert(malloc_initialized || malloc_initializer ==
+ pthread_self());
+
+#ifdef JEMALLOC_PROF
+ if (opt_prof)
+ prof_free(ptr);
+#endif
+ idalloc(ptr);
+ }
+}
+
+/*
+ * End malloc(3)-compatible functions.
+ */
+/******************************************************************************/
+/*
+ * Begin non-standard functions.
+ */
+
+JEMALLOC_ATTR(visibility("default"))
+size_t
+JEMALLOC_P(malloc_usable_size)(const void *ptr)
+{
+ size_t ret;
+
+ assert(ptr != NULL);
+ ret = isalloc(ptr);
+
+ return (ret);
+}
+
+#ifdef JEMALLOC_SWAP
+JEMALLOC_ATTR(visibility("default"))
+int
+JEMALLOC_P(malloc_swap_enable)(const int *fds, unsigned nfds, int prezeroed)
+{
+
+ /*
+ * Make sure malloc is initialized, because we need page size, chunk
+ * size, etc.
+ */
+ if (malloc_init())
+ return (-1);
+
+ return (chunk_swap_enable(fds, nfds, (prezeroed != 0)) ? -1 : 0);
+}
+#endif
+
+JEMALLOC_ATTR(visibility("default"))
+void
+JEMALLOC_P(malloc_stats_print)(void (*write_cb)(void *, const char *),
+ void *cbopaque, const char *opts)
+{
+
+ stats_print(write_cb, cbopaque, opts);
+}
+
+JEMALLOC_ATTR(visibility("default"))
+int
+JEMALLOC_P(mallctl)(const char *name, void *oldp, size_t *oldlenp, void *newp,
+ size_t newlen)
+{
+
+ if (malloc_init())
+ return (EAGAIN);
+
+ return (ctl_byname(name, oldp, oldlenp, newp, newlen));
+}
+
+JEMALLOC_ATTR(visibility("default"))
+int
+JEMALLOC_P(mallctlnametomib)(const char *name, size_t *mibp, size_t *miblenp)
+{
+
+ if (malloc_init())
+ return (EAGAIN);
+
+ return (ctl_nametomib(name, mibp, miblenp));
+}
+
+JEMALLOC_ATTR(visibility("default"))
+int
+JEMALLOC_P(mallctlbymib)(const size_t *mib, size_t miblen, void *oldp,
+ size_t *oldlenp, void *newp, size_t newlen)
+{
+
+ if (malloc_init())
+ return (EAGAIN);
+
+ return (ctl_bymib(mib, miblen, oldp, oldlenp, newp, newlen));
+}
+
+/*
+ * End non-standard functions.
+ */
+/******************************************************************************/
+
+/*
+ * The following functions are used by threading libraries for protection of
+ * malloc during fork(). These functions are only called if the program is
+ * running in threaded mode, so there is no need to check whether the program
+ * is threaded here.
+ */
+
+static void
+jemalloc_prefork(void)
+{
+ unsigned i;
+
+ /* Acquire all mutexes in a safe order. */
+
+ malloc_mutex_lock(&arenas_lock);
+ for (i = 0; i < narenas; i++) {
+ if (arenas[i] != NULL)
+ malloc_mutex_lock(&arenas[i]->lock);
+ }
+
+ malloc_mutex_lock(&base_mtx);
+
+ malloc_mutex_lock(&huge_mtx);
+
+#ifdef JEMALLOC_DSS
+ malloc_mutex_lock(&dss_mtx);
+#endif
+
+#ifdef JEMALLOC_SWAP
+ malloc_mutex_lock(&swap_mtx);
+#endif
+}
+
+static void
+jemalloc_postfork(void)
+{
+ unsigned i;
+
+ /* Release all mutexes, now that fork() has completed. */
+
+#ifdef JEMALLOC_SWAP
+ malloc_mutex_unlock(&swap_mtx);
+#endif
+
+#ifdef JEMALLOC_DSS
+ malloc_mutex_unlock(&dss_mtx);
+#endif
+
+ malloc_mutex_unlock(&huge_mtx);
+
+ malloc_mutex_unlock(&base_mtx);
+
+ for (i = 0; i < narenas; i++) {
+ if (arenas[i] != NULL)
+ malloc_mutex_unlock(&arenas[i]->lock);
+ }
+ malloc_mutex_unlock(&arenas_lock);
+}
diff --git a/externals/jemalloc/mb.c b/externals/jemalloc/mb.c
new file mode 100644
index 00000000000..30a1a2e997a
--- /dev/null
+++ b/externals/jemalloc/mb.c
@@ -0,0 +1,2 @@
+#define MB_C_
+#include "jemalloc/internal/jemalloc_internal.h"
diff --git a/externals/jemalloc/mutex.c b/externals/jemalloc/mutex.c
new file mode 100644
index 00000000000..3b6081a4c4f
--- /dev/null
+++ b/externals/jemalloc/mutex.c
@@ -0,0 +1,70 @@
+#define JEMALLOC_MUTEX_C_
+#include "jemalloc/internal/jemalloc_internal.h"
+
+/******************************************************************************/
+/* Data. */
+
+#ifdef JEMALLOC_LAZY_LOCK
+bool isthreaded = false;
+#endif
+
+#ifdef JEMALLOC_LAZY_LOCK
+static void pthread_create_once(void);
+#endif
+
+/******************************************************************************/
+/*
+ * We intercept pthread_create() calls in order to toggle isthreaded if the
+ * process goes multi-threaded.
+ */
+
+#ifdef JEMALLOC_LAZY_LOCK
+static int (*pthread_create_fptr)(pthread_t *__restrict, const pthread_attr_t *,
+ void *(*)(void *), void *__restrict);
+
+static void
+pthread_create_once(void)
+{
+
+ pthread_create_fptr = dlsym(RTLD_NEXT, "pthread_create");
+ if (pthread_create_fptr == NULL) {
+ malloc_write("<jemalloc>: Error in dlsym(RTLD_NEXT, "
+ "\"pthread_create\")\n");
+ abort();
+ }
+
+ isthreaded = true;
+}
+
+JEMALLOC_ATTR(visibility("default"))
+int
+pthread_create(pthread_t *__restrict thread,
+ const pthread_attr_t *__restrict attr, void *(*start_routine)(void *),
+ void *__restrict arg)
+{
+ static pthread_once_t once_control = PTHREAD_ONCE_INIT;
+
+ pthread_once(&once_control, pthread_create_once);
+
+ return (pthread_create_fptr(thread, attr, start_routine, arg));
+}
+#endif
+
+/******************************************************************************/
+
+bool
+malloc_mutex_init(malloc_mutex_t *mutex)
+{
+ pthread_mutexattr_t attr;
+
+ if (pthread_mutexattr_init(&attr) != 0)
+ return (true);
+ pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_ADAPTIVE_NP);
+ if (pthread_mutex_init(mutex, &attr) != 0) {
+ pthread_mutexattr_destroy(&attr);
+ return (true);
+ }
+ pthread_mutexattr_destroy(&attr);
+
+ return (false);
+}
diff --git a/externals/jemalloc/prof.c b/externals/jemalloc/prof.c
new file mode 100644
index 00000000000..6326188e50f
--- /dev/null
+++ b/externals/jemalloc/prof.c
@@ -0,0 +1,1328 @@
+#define JEMALLOC_PROF_C_
+#include "jemalloc/internal/jemalloc_internal.h"
+#ifdef JEMALLOC_PROF
+/******************************************************************************/
+
+#ifdef JEMALLOC_PROF_LIBGCC
+#include <unwind.h>
+#endif
+
+#ifdef JEMALLOC_PROF_LIBUNWIND
+#define UNW_LOCAL_ONLY
+#include <libunwind.h>
+#endif
+
+#include <math.h>
+
+/******************************************************************************/
+/* Data. */
+
+bool opt_prof = false;
+bool opt_prof_active = true;
+size_t opt_lg_prof_bt_max = LG_PROF_BT_MAX_DEFAULT;
+size_t opt_lg_prof_sample = LG_PROF_SAMPLE_DEFAULT;
+ssize_t opt_lg_prof_interval = LG_PROF_INTERVAL_DEFAULT;
+bool opt_prof_udump = false;
+bool opt_prof_leak = false;
+
+uint64_t prof_interval;
+bool prof_promote;
+
+/*
+ * Global hash of (prof_bt_t *)-->(prof_ctx_t *). This is the master data
+ * structure that knows about all backtraces ever captured.
+ */
+static ckh_t bt2ctx;
+static malloc_mutex_t bt2ctx_mtx;
+
+/*
+ * Thread-specific hash of (prof_bt_t *)-->(prof_thr_cnt_t *). Each thread
+ * keeps a cache of backtraces, with associated thread-specific prof_thr_cnt_t
+ * objects. Other threads may read the prof_thr_cnt_t contents, but no others
+ * will ever write them.
+ *
+ * Upon thread exit, the thread must merge all the prof_thr_cnt_t counter data
+ * into the associated prof_ctx_t objects, and unlink/free the prof_thr_cnt_t
+ * objects.
+ */
+static __thread ckh_t *bt2cnt_tls JEMALLOC_ATTR(tls_model("initial-exec"));
+
+/*
+ * Same contents as b2cnt, but initialized such that the TSD destructor is
+ * called when a thread exits, so that bt2cnt_tls contents can be merged,
+ * unlinked, and deallocated.
+ */
+static pthread_key_t bt2cnt_tsd;
+
+/* (1U << opt_lg_prof_bt_max). */
+static unsigned prof_bt_max;
+
+static __thread uint64_t prof_sample_prn_state
+ JEMALLOC_ATTR(tls_model("initial-exec"));
+static __thread uint64_t prof_sample_threshold
+ JEMALLOC_ATTR(tls_model("initial-exec"));
+static __thread uint64_t prof_sample_accum
+ JEMALLOC_ATTR(tls_model("initial-exec"));
+
+static malloc_mutex_t prof_dump_seq_mtx;
+static uint64_t prof_dump_seq;
+static uint64_t prof_dump_iseq;
+static uint64_t prof_dump_mseq;
+static uint64_t prof_dump_useq;
+
+/*
+ * This buffer is rather large for stack allocation, so use a single buffer for
+ * all profile dumps. The buffer is implicitly protected by bt2ctx_mtx, since
+ * it must be locked anyway during dumping.
+ */
+static char prof_dump_buf[PROF_DUMP_BUF_SIZE];
+static unsigned prof_dump_buf_end;
+static int prof_dump_fd;
+
+/* Do not dump any profiles until bootstrapping is complete. */
+static bool prof_booted = false;
+
+static malloc_mutex_t enq_mtx;
+static bool enq;
+static bool enq_idump;
+static bool enq_udump;
+
+/******************************************************************************/
+/* Function prototypes for non-inline static functions. */
+
+static prof_bt_t *bt_dup(prof_bt_t *bt);
+static void bt_init(prof_bt_t *bt, void **vec);
+#ifdef JEMALLOC_PROF_LIBGCC
+static _Unwind_Reason_Code prof_unwind_init_callback(
+ struct _Unwind_Context *context, void *arg);
+static _Unwind_Reason_Code prof_unwind_callback(
+ struct _Unwind_Context *context, void *arg);
+#endif
+static void prof_backtrace(prof_bt_t *bt, unsigned nignore, unsigned max);
+static prof_thr_cnt_t *prof_lookup(prof_bt_t *bt);
+static void prof_cnt_set(const void *ptr, prof_thr_cnt_t *cnt);
+static bool prof_flush(bool propagate_err);
+static bool prof_write(const char *s, bool propagate_err);
+static void prof_ctx_merge(prof_ctx_t *ctx, prof_cnt_t *cnt_all,
+ size_t *leak_nctx);
+static bool prof_dump_ctx(prof_ctx_t *ctx, prof_bt_t *bt,
+ bool propagate_err);
+static bool prof_dump_maps(bool propagate_err);
+static bool prof_dump(const char *filename, bool leakcheck,
+ bool propagate_err);
+static void prof_dump_filename(char *filename, char v, int64_t vseq);
+static void prof_fdump(void);
+static void prof_bt_hash(const void *key, unsigned minbits, size_t *hash1,
+ size_t *hash2);
+static bool prof_bt_keycomp(const void *k1, const void *k2);
+static void bt2cnt_thread_cleanup(void *arg);
+
+/******************************************************************************/
+
+static void
+bt_init(prof_bt_t *bt, void **vec)
+{
+
+ bt->vec = vec;
+ bt->len = 0;
+}
+
+static prof_bt_t *
+bt_dup(prof_bt_t *bt)
+{
+ prof_bt_t *ret;
+
+ /*
+ * Create a single allocation that has space for vec immediately
+ * following the prof_bt_t structure. The backtraces that get
+ * stored in the backtrace caches are copied from stack-allocated
+ * temporary variables, so size is known at creation time. Making this
+ * a contiguous object improves cache locality.
+ */
+ ret = (prof_bt_t *)imalloc(QUANTUM_CEILING(sizeof(prof_bt_t)) +
+ (bt->len * sizeof(void *)));
+ if (ret == NULL)
+ return (NULL);
+ ret->vec = (void **)((uintptr_t)ret +
+ QUANTUM_CEILING(sizeof(prof_bt_t)));
+ memcpy(ret->vec, bt->vec, bt->len * sizeof(void *));
+ ret->len = bt->len;
+
+ return (ret);
+}
+
+static inline void
+prof_enter(void)
+{
+
+ malloc_mutex_lock(&enq_mtx);
+ enq = true;
+ malloc_mutex_unlock(&enq_mtx);
+
+ malloc_mutex_lock(&bt2ctx_mtx);
+}
+
+static inline void
+prof_leave(void)
+{
+ bool idump, udump;
+
+ malloc_mutex_unlock(&bt2ctx_mtx);
+
+ malloc_mutex_lock(&enq_mtx);
+ enq = false;
+ idump = enq_idump;
+ enq_idump = false;
+ udump = enq_udump;
+ enq_udump = false;
+ malloc_mutex_unlock(&enq_mtx);
+
+ if (idump)
+ prof_idump();
+ if (udump)
+ prof_udump();
+}
+
+#ifdef JEMALLOC_PROF_LIBGCC
+static _Unwind_Reason_Code
+prof_unwind_init_callback(struct _Unwind_Context *context, void *arg)
+{
+
+ return (_URC_NO_REASON);
+}
+
+static _Unwind_Reason_Code
+prof_unwind_callback(struct _Unwind_Context *context, void *arg)
+{
+ prof_unwind_data_t *data = (prof_unwind_data_t *)arg;
+
+ if (data->nignore > 0)
+ data->nignore--;
+ else {
+ data->bt->vec[data->bt->len] = (void *)_Unwind_GetIP(context);
+ data->bt->len++;
+ if (data->bt->len == data->max)
+ return (_URC_END_OF_STACK);
+ }
+
+ return (_URC_NO_REASON);
+}
+
+static void
+prof_backtrace(prof_bt_t *bt, unsigned nignore, unsigned max)
+{
+ prof_unwind_data_t data = {bt, nignore, max};
+
+ _Unwind_Backtrace(prof_unwind_callback, &data);
+}
+#elif defined(JEMALLOC_PROF_LIBUNWIND)
+static void
+prof_backtrace(prof_bt_t *bt, unsigned nignore, unsigned max)
+{
+ unw_context_t uc;
+ unw_cursor_t cursor;
+ unsigned i;
+ int err;
+
+ assert(bt->len == 0);
+ assert(bt->vec != NULL);
+ assert(max <= (1U << opt_lg_prof_bt_max));
+
+ unw_getcontext(&uc);
+ unw_init_local(&cursor, &uc);
+
+ /* Throw away (nignore+1) stack frames, if that many exist. */
+ for (i = 0; i < nignore + 1; i++) {
+ err = unw_step(&cursor);
+ if (err <= 0)
+ return;
+ }
+
+ /*
+ * Iterate over stack frames until there are no more. Heap-allocate
+ * and iteratively grow a larger bt if necessary.
+ */
+ for (i = 0; i < max; i++) {
+ unw_get_reg(&cursor, UNW_REG_IP, (unw_word_t *)&bt->vec[i]);
+ err = unw_step(&cursor);
+ if (err <= 0) {
+ bt->len = i;
+ break;
+ }
+ }
+}
+#else
+static void
+prof_backtrace(prof_bt_t *bt, unsigned nignore, unsigned max)
+{
+#define NIGNORE 3
+#define BT_FRAME(i) \
+ if ((i) < NIGNORE + max) { \
+ void *p; \
+ if (__builtin_frame_address(i) == 0) \
+ return; \
+ p = __builtin_return_address(i); \
+ if (p == NULL) \
+ return; \
+ if (i >= NIGNORE) { \
+ bt->vec[(i) - NIGNORE] = p; \
+ bt->len = (i) - NIGNORE + 1; \
+ } \
+ } else \
+ return;
+
+ assert(max <= (1U << opt_lg_prof_bt_max));
+
+ /*
+ * Ignore the first three frames, since they are:
+ *
+ * 0: prof_backtrace()
+ * 1: prof_alloc_prep()
+ * 2: malloc(), calloc(), etc.
+ */
+#if 1
+ assert(nignore + 1 == NIGNORE);
+#else
+ BT_FRAME(0)
+ BT_FRAME(1)
+ BT_FRAME(2)
+#endif
+ BT_FRAME(3)
+ BT_FRAME(4)
+ BT_FRAME(5)
+ BT_FRAME(6)
+ BT_FRAME(7)
+ BT_FRAME(8)
+ BT_FRAME(9)
+
+ BT_FRAME(10)
+ BT_FRAME(11)
+ BT_FRAME(12)
+ BT_FRAME(13)
+ BT_FRAME(14)
+ BT_FRAME(15)
+ BT_FRAME(16)
+ BT_FRAME(17)
+ BT_FRAME(18)
+ BT_FRAME(19)
+
+ BT_FRAME(20)
+ BT_FRAME(21)
+ BT_FRAME(22)
+ BT_FRAME(23)
+ BT_FRAME(24)
+ BT_FRAME(25)
+ BT_FRAME(26)
+ BT_FRAME(27)
+ BT_FRAME(28)
+ BT_FRAME(29)
+
+ BT_FRAME(30)
+ BT_FRAME(31)
+ BT_FRAME(32)
+ BT_FRAME(33)
+ BT_FRAME(34)
+ BT_FRAME(35)
+ BT_FRAME(36)
+ BT_FRAME(37)
+ BT_FRAME(38)
+ BT_FRAME(39)
+
+ BT_FRAME(40)
+ BT_FRAME(41)
+ BT_FRAME(42)
+ BT_FRAME(43)
+ BT_FRAME(44)
+ BT_FRAME(45)
+ BT_FRAME(46)
+ BT_FRAME(47)
+ BT_FRAME(48)
+ BT_FRAME(49)
+
+ BT_FRAME(50)
+ BT_FRAME(51)
+ BT_FRAME(52)
+ BT_FRAME(53)
+ BT_FRAME(54)
+ BT_FRAME(55)
+ BT_FRAME(56)
+ BT_FRAME(57)
+ BT_FRAME(58)
+ BT_FRAME(59)
+
+ BT_FRAME(60)
+ BT_FRAME(61)
+ BT_FRAME(62)
+ BT_FRAME(63)
+ BT_FRAME(64)
+ BT_FRAME(65)
+ BT_FRAME(66)
+ BT_FRAME(67)
+ BT_FRAME(68)
+ BT_FRAME(69)
+
+ BT_FRAME(70)
+ BT_FRAME(71)
+ BT_FRAME(72)
+ BT_FRAME(73)
+ BT_FRAME(74)
+ BT_FRAME(75)
+ BT_FRAME(76)
+ BT_FRAME(77)
+ BT_FRAME(78)
+ BT_FRAME(79)
+
+ BT_FRAME(80)
+ BT_FRAME(81)
+ BT_FRAME(82)
+ BT_FRAME(83)
+ BT_FRAME(84)
+ BT_FRAME(85)
+ BT_FRAME(86)
+ BT_FRAME(87)
+ BT_FRAME(88)
+ BT_FRAME(89)
+
+ BT_FRAME(90)
+ BT_FRAME(91)
+ BT_FRAME(92)
+ BT_FRAME(93)
+ BT_FRAME(94)
+ BT_FRAME(95)
+ BT_FRAME(96)
+ BT_FRAME(97)
+ BT_FRAME(98)
+ BT_FRAME(99)
+
+ BT_FRAME(100)
+ BT_FRAME(101)
+ BT_FRAME(102)
+ BT_FRAME(103)
+ BT_FRAME(104)
+ BT_FRAME(105)
+ BT_FRAME(106)
+ BT_FRAME(107)
+ BT_FRAME(108)
+ BT_FRAME(109)
+
+ BT_FRAME(110)
+ BT_FRAME(111)
+ BT_FRAME(112)
+ BT_FRAME(113)
+ BT_FRAME(114)
+ BT_FRAME(115)
+ BT_FRAME(116)
+ BT_FRAME(117)
+ BT_FRAME(118)
+ BT_FRAME(119)
+
+ BT_FRAME(120)
+ BT_FRAME(121)
+ BT_FRAME(122)
+ BT_FRAME(123)
+ BT_FRAME(124)
+ BT_FRAME(125)
+ BT_FRAME(126)
+ BT_FRAME(127)
+
+ /* Extras to compensate for NIGNORE. */
+ BT_FRAME(128)
+ BT_FRAME(129)
+ BT_FRAME(130)
+#undef BT_FRAME
+}
+#endif
+
+static prof_thr_cnt_t *
+prof_lookup(prof_bt_t *bt)
+{
+ prof_thr_cnt_t *ret;
+ ckh_t *bt2cnt = bt2cnt_tls;
+
+ if (bt2cnt == NULL) {
+ /* Initialize an empty cache for this thread. */
+ bt2cnt = (ckh_t *)imalloc(sizeof(ckh_t));
+ if (bt2cnt == NULL)
+ return (NULL);
+ if (ckh_new(bt2cnt, PROF_CKH_MINITEMS, prof_bt_hash,
+ prof_bt_keycomp)) {
+ idalloc(bt2cnt);
+ return (NULL);
+ }
+ bt2cnt_tls = bt2cnt;
+ }
+
+ if (ckh_search(bt2cnt, bt, NULL, (void **)&ret)) {
+ prof_bt_t *btkey;
+ prof_ctx_t *ctx;
+
+ /*
+ * This thread's cache lacks bt. Look for it in the global
+ * cache.
+ */
+ prof_enter();
+ if (ckh_search(&bt2ctx, bt, (void **)&btkey, (void **)&ctx)) {
+
+ /* bt has never been seen before. Insert it. */
+ ctx = (prof_ctx_t *)imalloc(sizeof(prof_ctx_t));
+ if (ctx == NULL) {
+ prof_leave();
+ return (NULL);
+ }
+ btkey = bt_dup(bt);
+ if (btkey == NULL) {
+ prof_leave();
+ idalloc(ctx);
+ return (NULL);
+ }
+ if (malloc_mutex_init(&ctx->lock)) {
+ prof_leave();
+ idalloc(btkey);
+ idalloc(ctx);
+ return (NULL);
+ }
+ memset(&ctx->cnt_merged, 0, sizeof(prof_cnt_t));
+ ql_new(&ctx->cnts_ql);
+ if (ckh_insert(&bt2ctx, btkey, ctx)) {
+ /* OOM. */
+ prof_leave();
+ idalloc(btkey);
+ idalloc(ctx);
+ return (NULL);
+ }
+ }
+ prof_leave();
+
+ /* Link a prof_thd_cnt_t into ctx for this thread. */
+ ret = (prof_thr_cnt_t *)imalloc(sizeof(prof_thr_cnt_t));
+ if (ret == NULL)
+ return (NULL);
+ ql_elm_new(ret, link);
+ ret->ctx = ctx;
+ ret->epoch = 0;
+ memset(&ret->cnts, 0, sizeof(prof_cnt_t));
+ if (ckh_insert(bt2cnt, btkey, ret)) {
+ idalloc(ret);
+ return (NULL);
+ }
+ malloc_mutex_lock(&ctx->lock);
+ ql_tail_insert(&ctx->cnts_ql, ret, link);
+ malloc_mutex_unlock(&ctx->lock);
+ }
+
+ return (ret);
+}
+
+static inline void
+prof_sample_threshold_update(void)
+{
+ uint64_t r;
+ double u;
+
+ /*
+ * Compute prof_sample_threshold as a geometrically distributed random
+ * variable with mean (2^opt_lg_prof_sample).
+ */
+ prn64(r, 53, prof_sample_prn_state, (uint64_t)1125899906842625LLU,
+ 1058392653243283975);
+ u = (double)r * (1.0/9007199254740992.0L);
+ prof_sample_threshold = (uint64_t)(log(u) /
+ log(1.0 - (1.0 / (double)((uint64_t)1U << opt_lg_prof_sample))))
+ + (uint64_t)1U;
+}
+
+prof_thr_cnt_t *
+prof_alloc_prep(size_t size)
+{
+ prof_thr_cnt_t *ret;
+ void *vec[prof_bt_max];
+ prof_bt_t bt;
+
+ if (opt_prof_active == false) {
+ /* Sampling is currently inactive, so avoid sampling. */
+ ret = (prof_thr_cnt_t *)(uintptr_t)1U;
+ } else if (opt_lg_prof_sample == 0) {
+ /*
+ * Don't bother with sampling logic, since sampling interval is
+ * 1.
+ */
+ bt_init(&bt, vec);
+ prof_backtrace(&bt, 2, prof_bt_max);
+ ret = prof_lookup(&bt);
+ } else {
+ if (prof_sample_threshold == 0) {
+ /*
+ * Initialize. Seed the prng differently for each
+ * thread.
+ */
+ prof_sample_prn_state = (uint64_t)(uintptr_t)&size;
+ prof_sample_threshold_update();
+ }
+
+ /*
+ * Determine whether to capture a backtrace based on whether
+ * size is enough for prof_accum to reach
+ * prof_sample_threshold. However, delay updating these
+ * variables until prof_{m,re}alloc(), because we don't know
+ * for sure that the allocation will succeed.
+ *
+ * Use subtraction rather than addition to avoid potential
+ * integer overflow.
+ */
+ if (size >= prof_sample_threshold - prof_sample_accum) {
+ bt_init(&bt, vec);
+ prof_backtrace(&bt, 2, prof_bt_max);
+ ret = prof_lookup(&bt);
+ } else
+ ret = (prof_thr_cnt_t *)(uintptr_t)1U;
+ }
+
+ return (ret);
+}
+
+prof_thr_cnt_t *
+prof_cnt_get(const void *ptr)
+{
+ prof_thr_cnt_t *ret;
+ arena_chunk_t *chunk;
+
+ assert(ptr != NULL);
+
+ chunk = (arena_chunk_t *)CHUNK_ADDR2BASE(ptr);
+ if (chunk != ptr) {
+ /* Region. */
+ assert(chunk->arena->magic == ARENA_MAGIC);
+
+ ret = arena_prof_cnt_get(ptr);
+ } else
+ ret = huge_prof_cnt_get(ptr);
+
+ return (ret);
+}
+
+static void
+prof_cnt_set(const void *ptr, prof_thr_cnt_t *cnt)
+{
+ arena_chunk_t *chunk;
+
+ assert(ptr != NULL);
+
+ chunk = (arena_chunk_t *)CHUNK_ADDR2BASE(ptr);
+ if (chunk != ptr) {
+ /* Region. */
+ assert(chunk->arena->magic == ARENA_MAGIC);
+
+ arena_prof_cnt_set(ptr, cnt);
+ } else
+ huge_prof_cnt_set(ptr, cnt);
+}
+
+static inline void
+prof_sample_accum_update(size_t size)
+{
+
+ if (opt_lg_prof_sample == 0) {
+ /*
+ * Don't bother with sampling logic, since sampling interval is
+ * 1.
+ */
+ return;
+ }
+
+ /* Take care to avoid integer overflow. */
+ if (size >= prof_sample_threshold - prof_sample_accum) {
+ prof_sample_accum -= (prof_sample_threshold - size);
+ /* Compute new prof_sample_threshold. */
+ prof_sample_threshold_update();
+ while (prof_sample_accum >= prof_sample_threshold) {
+ prof_sample_accum -= prof_sample_threshold;
+ prof_sample_threshold_update();
+ }
+ } else
+ prof_sample_accum += size;
+}
+
+void
+prof_malloc(const void *ptr, prof_thr_cnt_t *cnt)
+{
+ size_t size = isalloc(ptr);
+
+ assert(ptr != NULL);
+
+ prof_cnt_set(ptr, cnt);
+ prof_sample_accum_update(size);
+
+ if ((uintptr_t)cnt > (uintptr_t)1U) {
+ cnt->epoch++;
+ /*********/
+ mb_write();
+ /*********/
+ cnt->cnts.curobjs++;
+ cnt->cnts.curbytes += size;
+ cnt->cnts.accumobjs++;
+ cnt->cnts.accumbytes += size;
+ /*********/
+ mb_write();
+ /*********/
+ cnt->epoch++;
+ /*********/
+ mb_write();
+ /*********/
+ }
+}
+
+void
+prof_realloc(const void *ptr, prof_thr_cnt_t *cnt, const void *old_ptr,
+ size_t old_size, prof_thr_cnt_t *old_cnt)
+{
+ size_t size = isalloc(ptr);
+
+ if (ptr != NULL) {
+ prof_cnt_set(ptr, cnt);
+ prof_sample_accum_update(size);
+ }
+
+ if ((uintptr_t)old_cnt > (uintptr_t)1U)
+ old_cnt->epoch++;
+ if ((uintptr_t)cnt > (uintptr_t)1U)
+ cnt->epoch++;
+ /*********/
+ mb_write();
+ /*********/
+ if ((uintptr_t)old_cnt > (uintptr_t)1U) {
+ old_cnt->cnts.curobjs--;
+ old_cnt->cnts.curbytes -= old_size;
+ }
+ if ((uintptr_t)cnt > (uintptr_t)1U) {
+ cnt->cnts.curobjs++;
+ cnt->cnts.curbytes += size;
+ cnt->cnts.accumobjs++;
+ cnt->cnts.accumbytes += size;
+ }
+ /*********/
+ mb_write();
+ /*********/
+ if ((uintptr_t)old_cnt > (uintptr_t)1U)
+ old_cnt->epoch++;
+ if ((uintptr_t)cnt > (uintptr_t)1U)
+ cnt->epoch++;
+ /*********/
+ mb_write(); /* Not strictly necessary. */
+}
+
+void
+prof_free(const void *ptr)
+{
+ prof_thr_cnt_t *cnt = prof_cnt_get(ptr);
+
+ if ((uintptr_t)cnt > (uintptr_t)1) {
+ size_t size = isalloc(ptr);
+
+ cnt->epoch++;
+ /*********/
+ mb_write();
+ /*********/
+ cnt->cnts.curobjs--;
+ cnt->cnts.curbytes -= size;
+ /*********/
+ mb_write();
+ /*********/
+ cnt->epoch++;
+ /*********/
+ mb_write();
+ /*********/
+ }
+}
+
+static bool
+prof_flush(bool propagate_err)
+{
+ bool ret = false;
+ ssize_t err;
+
+ err = write(prof_dump_fd, prof_dump_buf, prof_dump_buf_end);
+ if (err == -1) {
+ if (propagate_err == false) {
+ malloc_write("<jemalloc>: write() failed during heap "
+ "profile flush\n");
+ if (opt_abort)
+ abort();
+ }
+ ret = true;
+ }
+ prof_dump_buf_end = 0;
+
+ return (ret);
+}
+
+static bool
+prof_write(const char *s, bool propagate_err)
+{
+ unsigned i, slen, n;
+
+ i = 0;
+ slen = strlen(s);
+ while (i < slen) {
+ /* Flush the buffer if it is full. */
+ if (prof_dump_buf_end == PROF_DUMP_BUF_SIZE)
+ if (prof_flush(propagate_err) && propagate_err)
+ return (true);
+
+ if (prof_dump_buf_end + slen <= PROF_DUMP_BUF_SIZE) {
+ /* Finish writing. */
+ n = slen - i;
+ } else {
+ /* Write as much of s as will fit. */
+ n = PROF_DUMP_BUF_SIZE - prof_dump_buf_end;
+ }
+ memcpy(&prof_dump_buf[prof_dump_buf_end], &s[i], n);
+ prof_dump_buf_end += n;
+ i += n;
+ }
+
+ return (false);
+}
+
+static void
+prof_ctx_merge(prof_ctx_t *ctx, prof_cnt_t *cnt_all, size_t *leak_nctx)
+{
+ prof_thr_cnt_t *thr_cnt;
+ prof_cnt_t tcnt;
+
+ malloc_mutex_lock(&ctx->lock);
+
+ memcpy(&ctx->cnt_dump, &ctx->cnt_merged, sizeof(prof_cnt_t));
+ ql_foreach(thr_cnt, &ctx->cnts_ql, link) {
+ volatile unsigned *epoch = &thr_cnt->epoch;
+
+ while (true) {
+ unsigned epoch0 = *epoch;
+
+ /* Make sure epoch is even. */
+ if (epoch0 & 1U)
+ continue;
+
+ memcpy(&tcnt, &thr_cnt->cnts, sizeof(prof_cnt_t));
+
+ /* Terminate if epoch didn't change while reading. */
+ if (*epoch == epoch0)
+ break;
+ }
+
+ ctx->cnt_dump.curobjs += tcnt.curobjs;
+ ctx->cnt_dump.curbytes += tcnt.curbytes;
+ ctx->cnt_dump.accumobjs += tcnt.accumobjs;
+ ctx->cnt_dump.accumbytes += tcnt.accumbytes;
+
+ if (tcnt.curobjs != 0)
+ (*leak_nctx)++;
+ }
+
+ /* Merge into cnt_all. */
+ cnt_all->curobjs += ctx->cnt_dump.curobjs;
+ cnt_all->curbytes += ctx->cnt_dump.curbytes;
+ cnt_all->accumobjs += ctx->cnt_dump.accumobjs;
+ cnt_all->accumbytes += ctx->cnt_dump.accumbytes;
+
+ malloc_mutex_unlock(&ctx->lock);
+}
+
+static bool
+prof_dump_ctx(prof_ctx_t *ctx, prof_bt_t *bt, bool propagate_err)
+{
+ char buf[UMAX2S_BUFSIZE];
+ unsigned i;
+
+ if (prof_write(umax2s(ctx->cnt_dump.curobjs, 10, buf), propagate_err)
+ || prof_write(": ", propagate_err)
+ || prof_write(umax2s(ctx->cnt_dump.curbytes, 10, buf),
+ propagate_err)
+ || prof_write(" [", propagate_err)
+ || prof_write(umax2s(ctx->cnt_dump.accumobjs, 10, buf),
+ propagate_err)
+ || prof_write(": ", propagate_err)
+ || prof_write(umax2s(ctx->cnt_dump.accumbytes, 10, buf),
+ propagate_err)
+ || prof_write("] @", propagate_err))
+ return (true);
+
+ for (i = 0; i < bt->len; i++) {
+ if (prof_write(" 0x", propagate_err)
+ || prof_write(umax2s((uintptr_t)bt->vec[i], 16, buf),
+ propagate_err))
+ return (true);
+ }
+
+ if (prof_write("\n", propagate_err))
+ return (true);
+
+ return (false);
+}
+
+static bool
+prof_dump_maps(bool propagate_err)
+{
+ int mfd;
+ char buf[UMAX2S_BUFSIZE];
+ char *s;
+ unsigned i, slen;
+ /* /proc/<pid>/maps\0 */
+ char mpath[6 + UMAX2S_BUFSIZE
+ + 5 + 1];
+
+ i = 0;
+
+ s = "/proc/";
+ slen = strlen(s);
+ memcpy(&mpath[i], s, slen);
+ i += slen;
+
+ s = umax2s(getpid(), 10, buf);
+ slen = strlen(s);
+ memcpy(&mpath[i], s, slen);
+ i += slen;
+
+ s = "/maps";
+ slen = strlen(s);
+ memcpy(&mpath[i], s, slen);
+ i += slen;
+
+ mpath[i] = '\0';
+
+ mfd = open(mpath, O_RDONLY);
+ if (mfd != -1) {
+ ssize_t nread;
+
+ if (prof_write("\nMAPPED_LIBRARIES:\n", propagate_err) &&
+ propagate_err)
+ return (true);
+ nread = 0;
+ do {
+ prof_dump_buf_end += nread;
+ if (prof_dump_buf_end == PROF_DUMP_BUF_SIZE) {
+ /* Make space in prof_dump_buf before read(). */
+ if (prof_flush(propagate_err) && propagate_err)
+ return (true);
+ }
+ nread = read(mfd, &prof_dump_buf[prof_dump_buf_end],
+ PROF_DUMP_BUF_SIZE - prof_dump_buf_end);
+ } while (nread > 0);
+ close(mfd);
+ } else
+ return (true);
+
+ return (false);
+}
+
+static bool
+prof_dump(const char *filename, bool leakcheck, bool propagate_err)
+{
+ prof_cnt_t cnt_all;
+ size_t tabind;
+ prof_bt_t *bt;
+ prof_ctx_t *ctx;
+ char buf[UMAX2S_BUFSIZE];
+ size_t leak_nctx;
+
+ prof_enter();
+ prof_dump_fd = creat(filename, 0644);
+ if (prof_dump_fd == -1) {
+ if (propagate_err == false) {
+ malloc_write("<jemalloc>: creat(\"");
+ malloc_write(filename);
+ malloc_write("\", 0644) failed\n");
+ if (opt_abort)
+ abort();
+ }
+ goto ERROR;
+ }
+
+ /* Merge per thread profile stats, and sum them in cnt_all. */
+ memset(&cnt_all, 0, sizeof(prof_cnt_t));
+ leak_nctx = 0;
+ for (tabind = 0; ckh_iter(&bt2ctx, &tabind, NULL, (void **)&ctx)
+ == false;) {
+ prof_ctx_merge(ctx, &cnt_all, &leak_nctx);
+ }
+
+ /* Dump profile header. */
+ if (prof_write("heap profile: ", propagate_err)
+ || prof_write(umax2s(cnt_all.curobjs, 10, buf), propagate_err)
+ || prof_write(": ", propagate_err)
+ || prof_write(umax2s(cnt_all.curbytes, 10, buf), propagate_err)
+ || prof_write(" [", propagate_err)
+ || prof_write(umax2s(cnt_all.accumobjs, 10, buf), propagate_err)
+ || prof_write(": ", propagate_err)
+ || prof_write(umax2s(cnt_all.accumbytes, 10, buf), propagate_err))
+ goto ERROR;
+
+ if (opt_lg_prof_sample == 0) {
+ if (prof_write("] @ heapprofile\n", propagate_err))
+ goto ERROR;
+ } else {
+ if (prof_write("] @ heap_v2/", propagate_err)
+ || prof_write(umax2s((uint64_t)1U << opt_lg_prof_sample, 10,
+ buf), propagate_err)
+ || prof_write("\n", propagate_err))
+ goto ERROR;
+ }
+
+ /* Dump per ctx profile stats. */
+ for (tabind = 0; ckh_iter(&bt2ctx, &tabind, (void **)&bt, (void **)&ctx)
+ == false;) {
+ if (prof_dump_ctx(ctx, bt, propagate_err))
+ goto ERROR;
+ }
+
+ /* Dump /proc/<pid>/maps if possible. */
+ if (prof_dump_maps(propagate_err))
+ goto ERROR;
+
+ if (prof_flush(propagate_err))
+ goto ERROR;
+ close(prof_dump_fd);
+ prof_leave();
+
+ if (leakcheck && cnt_all.curbytes != 0) {
+ malloc_write("<jemalloc>: Leak summary: ");
+ malloc_write(umax2s(cnt_all.curbytes, 10, buf));
+ malloc_write((cnt_all.curbytes != 1) ? " bytes, " : " byte, ");
+ malloc_write(umax2s(cnt_all.curobjs, 10, buf));
+ malloc_write((cnt_all.curobjs != 1) ? " objects, " :
+ " object, ");
+ malloc_write(umax2s(leak_nctx, 10, buf));
+ malloc_write((leak_nctx != 1) ? " contexts\n" : " context\n");
+ malloc_write("<jemalloc>: Run pprof on \"");
+ malloc_write(filename);
+ malloc_write("\" for leak detail\n");
+ }
+
+ return (false);
+ERROR:
+ prof_leave();
+ return (true);
+}
+
+#define DUMP_FILENAME_BUFSIZE (PATH_MAX+ UMAX2S_BUFSIZE \
+ + 1 \
+ + UMAX2S_BUFSIZE \
+ + 2 \
+ + UMAX2S_BUFSIZE \
+ + 5 + 1)
+static void
+prof_dump_filename(char *filename, char v, int64_t vseq)
+{
+ char buf[UMAX2S_BUFSIZE];
+ char *s;
+ unsigned i, slen;
+
+ /*
+ * Construct a filename of the form:
+ *
+ * <prefix>.<pid>.<seq>.v<vseq>.heap\0
+ * or
+ * jeprof.<pid>.<seq>.v<vseq>.heap\0
+ */
+
+ i = 0;
+
+ /*
+ * Use JEMALLOC_PROF_PREFIX if it's set, and if it is short enough to
+ * avoid overflowing DUMP_FILENAME_BUFSIZE. The result may exceed
+ * PATH_MAX, but creat(2) will catch that problem.
+ */
+ if ((s = getenv("JEMALLOC_PROF_PREFIX")) != NULL
+ && strlen(s) + (DUMP_FILENAME_BUFSIZE - PATH_MAX) <= PATH_MAX) {
+ slen = strlen(s);
+ memcpy(&filename[i], s, slen);
+ i += slen;
+
+ s = ".";
+ } else
+ s = "jeprof.";
+ slen = strlen(s);
+ memcpy(&filename[i], s, slen);
+ i += slen;
+
+ s = umax2s(getpid(), 10, buf);
+ slen = strlen(s);
+ memcpy(&filename[i], s, slen);
+ i += slen;
+
+ s = ".";
+ slen = strlen(s);
+ memcpy(&filename[i], s, slen);
+ i += slen;
+
+ s = umax2s(prof_dump_seq, 10, buf);
+ prof_dump_seq++;
+ slen = strlen(s);
+ memcpy(&filename[i], s, slen);
+ i += slen;
+
+ s = ".";
+ slen = strlen(s);
+ memcpy(&filename[i], s, slen);
+ i += slen;
+
+ filename[i] = v;
+ i++;
+
+ if (vseq != 0xffffffffffffffffLLU) {
+ s = umax2s(vseq, 10, buf);
+ slen = strlen(s);
+ memcpy(&filename[i], s, slen);
+ i += slen;
+ }
+
+ s = ".heap";
+ slen = strlen(s);
+ memcpy(&filename[i], s, slen);
+ i += slen;
+
+ filename[i] = '\0';
+}
+
+static void
+prof_fdump(void)
+{
+ char filename[DUMP_FILENAME_BUFSIZE];
+
+ if (prof_booted == false)
+ return;
+
+ malloc_mutex_lock(&prof_dump_seq_mtx);
+ prof_dump_filename(filename, 'f', 0xffffffffffffffffLLU);
+ malloc_mutex_unlock(&prof_dump_seq_mtx);
+ prof_dump(filename, opt_prof_leak, false);
+}
+
+void
+prof_idump(void)
+{
+ char filename[DUMP_FILENAME_BUFSIZE];
+
+ if (prof_booted == false)
+ return;
+ malloc_mutex_lock(&enq_mtx);
+ if (enq) {
+ enq_idump = true;
+ malloc_mutex_unlock(&enq_mtx);
+ return;
+ }
+ malloc_mutex_unlock(&enq_mtx);
+
+ malloc_mutex_lock(&prof_dump_seq_mtx);
+ prof_dump_filename(filename, 'i', prof_dump_iseq);
+ prof_dump_iseq++;
+ malloc_mutex_unlock(&prof_dump_seq_mtx);
+ prof_dump(filename, false, false);
+}
+
+bool
+prof_mdump(const char *filename)
+{
+ char filename_buf[DUMP_FILENAME_BUFSIZE];
+
+ if (opt_prof == false || prof_booted == false)
+ return (true);
+
+ if (filename == NULL) {
+ /* No filename specified, so automatically generate one. */
+ malloc_mutex_lock(&prof_dump_seq_mtx);
+ prof_dump_filename(filename_buf, 'm', prof_dump_mseq);
+ prof_dump_mseq++;
+ malloc_mutex_unlock(&prof_dump_seq_mtx);
+ filename = filename_buf;
+ }
+ return (prof_dump(filename, false, true));
+}
+
+void
+prof_udump(void)
+{
+ char filename[DUMP_FILENAME_BUFSIZE];
+
+ if (prof_booted == false)
+ return;
+ malloc_mutex_lock(&enq_mtx);
+ if (enq) {
+ enq_udump = true;
+ malloc_mutex_unlock(&enq_mtx);
+ return;
+ }
+ malloc_mutex_unlock(&enq_mtx);
+
+ malloc_mutex_lock(&prof_dump_seq_mtx);
+ prof_dump_filename(filename, 'u', prof_dump_useq);
+ prof_dump_useq++;
+ malloc_mutex_unlock(&prof_dump_seq_mtx);
+ prof_dump(filename, false, false);
+}
+
+static void
+prof_bt_hash(const void *key, unsigned minbits, size_t *hash1, size_t *hash2)
+{
+ size_t ret1, ret2;
+ uint64_t h;
+ prof_bt_t *bt = (prof_bt_t *)key;
+
+ assert(minbits <= 32 || (SIZEOF_PTR == 8 && minbits <= 64));
+ assert(hash1 != NULL);
+ assert(hash2 != NULL);
+
+ h = hash(bt->vec, bt->len * sizeof(void *), 0x94122f335b332aeaLLU);
+ if (minbits <= 32) {
+ /*
+ * Avoid doing multiple hashes, since a single hash provides
+ * enough bits.
+ */
+ ret1 = h & ZU(0xffffffffU);
+ ret2 = h >> 32;
+ } else {
+ ret1 = h;
+ ret2 = hash(bt->vec, bt->len * sizeof(void *),
+ 0x8432a476666bbc13U);
+ }
+
+ *hash1 = ret1;
+ *hash2 = ret2;
+}
+
+static bool
+prof_bt_keycomp(const void *k1, const void *k2)
+{
+ const prof_bt_t *bt1 = (prof_bt_t *)k1;
+ const prof_bt_t *bt2 = (prof_bt_t *)k2;
+
+ if (bt1->len != bt2->len)
+ return (false);
+ return (memcmp(bt1->vec, bt2->vec, bt1->len * sizeof(void *)) == 0);
+}
+
+static void
+bt2cnt_thread_cleanup(void *arg)
+{
+ ckh_t *bt2cnt;
+
+ bt2cnt = bt2cnt_tls;
+ if (bt2cnt != NULL) {
+ ql_head(prof_thr_cnt_t) cnts_ql;
+ size_t tabind;
+ prof_thr_cnt_t *cnt;
+
+ /* Iteratively merge cnt's into the global stats. */
+ ql_new(&cnts_ql);
+ tabind = 0;
+ while (ckh_iter(bt2cnt, &tabind, NULL, (void **)&cnt) ==
+ false) {
+ prof_ctx_t *ctx = cnt->ctx;
+ /* Merge stats and detach from ctx. */
+ malloc_mutex_lock(&ctx->lock);
+ ctx->cnt_merged.curobjs += cnt->cnts.curobjs;
+ ctx->cnt_merged.curbytes += cnt->cnts.curbytes;
+ ctx->cnt_merged.accumobjs += cnt->cnts.accumobjs;
+ ctx->cnt_merged.accumbytes += cnt->cnts.accumbytes;
+ ql_remove(&ctx->cnts_ql, cnt, link);
+ malloc_mutex_unlock(&ctx->lock);
+
+ /*
+ * Stash cnt for deletion after finishing with
+ * ckh_iter().
+ */
+ ql_tail_insert(&cnts_ql, cnt, link);
+ }
+
+ /*
+ * Delete the hash table now that cnts_ql has a list of all
+ * cnt's.
+ */
+ ckh_delete(bt2cnt);
+ idalloc(bt2cnt);
+ bt2cnt_tls = NULL;
+
+ /* Delete cnt's. */
+ while ((cnt = ql_last(&cnts_ql, link)) != NULL) {
+ ql_remove(&cnts_ql, cnt, link);
+ idalloc(cnt);
+ }
+ }
+}
+
+void
+prof_boot0(void)
+{
+
+ /*
+ * opt_prof and prof_promote must be in their final state before any
+ * arenas are initialized, so this function must be executed early.
+ */
+
+ if (opt_prof_leak && opt_prof == false) {
+ /*
+ * Enable opt_prof, but in such a way that profiles are never
+ * automatically dumped.
+ */
+ opt_prof = true;
+ opt_prof_udump = false;
+ prof_interval = 0;
+ } else if (opt_prof) {
+ if (opt_lg_prof_interval >= 0) {
+ prof_interval = (((uint64_t)1U) <<
+ opt_lg_prof_interval);
+ } else
+ prof_interval = 0;
+ }
+
+ prof_promote = (opt_prof && opt_lg_prof_sample > PAGE_SHIFT);
+}
+
+bool
+prof_boot1(void)
+{
+
+ if (opt_prof) {
+ if (ckh_new(&bt2ctx, PROF_CKH_MINITEMS, prof_bt_hash,
+ prof_bt_keycomp))
+ return (true);
+ if (malloc_mutex_init(&bt2ctx_mtx))
+ return (true);
+ if (pthread_key_create(&bt2cnt_tsd, bt2cnt_thread_cleanup)
+ != 0) {
+ malloc_write(
+ "<jemalloc>: Error in pthread_key_create()\n");
+ abort();
+ }
+
+ prof_bt_max = (1U << opt_lg_prof_bt_max);
+ if (malloc_mutex_init(&prof_dump_seq_mtx))
+ return (true);
+
+ if (malloc_mutex_init(&enq_mtx))
+ return (true);
+ enq = false;
+ enq_idump = false;
+ enq_udump = false;
+
+ if (atexit(prof_fdump) != 0) {
+ malloc_write("<jemalloc>: Error in atexit()\n");
+ if (opt_abort)
+ abort();
+ }
+ }
+
+#ifdef JEMALLOC_PROF_LIBGCC
+ /*
+ * Cause the backtracing machinery to allocate its internal state
+ * before enabling profiling.
+ */
+ _Unwind_Backtrace(prof_unwind_init_callback, NULL);
+#endif
+
+ prof_booted = true;
+
+ return (false);
+}
+
+/******************************************************************************/
+#endif /* JEMALLOC_PROF */
diff --git a/externals/jemalloc/stats.c b/externals/jemalloc/stats.c
new file mode 100644
index 00000000000..9dc75293731
--- /dev/null
+++ b/externals/jemalloc/stats.c
@@ -0,0 +1,717 @@
+#define JEMALLOC_STATS_C_
+#include "jemalloc/internal/jemalloc_internal.h"
+
+#define CTL_GET(n, v, t) do { \
+ size_t sz = sizeof(t); \
+ xmallctl(n, v, &sz, NULL, 0); \
+} while (0)
+
+#define CTL_I_GET(n, v, t) do { \
+ size_t mib[6]; \
+ size_t miblen = sizeof(mib) / sizeof(size_t); \
+ size_t sz = sizeof(t); \
+ xmallctlnametomib(n, mib, &miblen); \
+ mib[2] = i; \
+ xmallctlbymib(mib, miblen, v, &sz, NULL, 0); \
+} while (0)
+
+#define CTL_J_GET(n, v, t) do { \
+ size_t mib[6]; \
+ size_t miblen = sizeof(mib) / sizeof(size_t); \
+ size_t sz = sizeof(t); \
+ xmallctlnametomib(n, mib, &miblen); \
+ mib[2] = j; \
+ xmallctlbymib(mib, miblen, v, &sz, NULL, 0); \
+} while (0)
+
+#define CTL_IJ_GET(n, v, t) do { \
+ size_t mib[6]; \
+ size_t miblen = sizeof(mib) / sizeof(size_t); \
+ size_t sz = sizeof(t); \
+ xmallctlnametomib(n, mib, &miblen); \
+ mib[2] = i; \
+ mib[4] = j; \
+ xmallctlbymib(mib, miblen, v, &sz, NULL, 0); \
+} while (0)
+
+/******************************************************************************/
+/* Data. */
+
+bool opt_stats_print = false;
+
+/******************************************************************************/
+/* Function prototypes for non-inline static functions. */
+
+#ifdef JEMALLOC_STATS
+static void malloc_vcprintf(void (*write_cb)(void *, const char *),
+ void *cbopaque, const char *format, va_list ap);
+static void stats_arena_bins_print(void (*write_cb)(void *, const char *),
+ void *cbopaque, unsigned i);
+static void stats_arena_lruns_print(void (*write_cb)(void *, const char *),
+ void *cbopaque, unsigned i);
+static void stats_arena_print(void (*write_cb)(void *, const char *),
+ void *cbopaque, unsigned i);
+#endif
+
+/******************************************************************************/
+
+/*
+ * We don't want to depend on vsnprintf() for production builds, since that can
+ * cause unnecessary bloat for static binaries. umax2s() provides minimal
+ * integer printing functionality, so that malloc_printf() use can be limited to
+ * JEMALLOC_STATS code.
+ */
+char *
+umax2s(uintmax_t x, unsigned base, char *s)
+{
+ unsigned i;
+
+ i = UMAX2S_BUFSIZE - 1;
+ s[i] = '\0';
+ switch (base) {
+ case 10:
+ do {
+ i--;
+ s[i] = "0123456789"[x % 10];
+ x /= 10;
+ } while (x > 0);
+ break;
+ case 16:
+ do {
+ i--;
+ s[i] = "0123456789abcdef"[x & 0xf];
+ x >>= 4;
+ } while (x > 0);
+ break;
+ default:
+ do {
+ i--;
+ s[i] = "0123456789abcdefghijklmnopqrstuvwxyz"[x % base];
+ x /= base;
+ } while (x > 0);
+ }
+
+ return (&s[i]);
+}
+
+#ifdef JEMALLOC_STATS
+static void
+malloc_vcprintf(void (*write_cb)(void *, const char *), void *cbopaque,
+ const char *format, va_list ap)
+{
+ char buf[4096];
+
+ if (write_cb == NULL) {
+ /*
+ * The caller did not provide an alternate write_cb callback
+ * function, so use the default one. malloc_write() is an
+ * inline function, so use malloc_message() directly here.
+ */
+ write_cb = JEMALLOC_P(malloc_message);
+ cbopaque = NULL;
+ }
+
+ vsnprintf(buf, sizeof(buf), format, ap);
+ write_cb(cbopaque, buf);
+}
+
+/*
+ * Print to a callback function in such a way as to (hopefully) avoid memory
+ * allocation.
+ */
+JEMALLOC_ATTR(format(printf, 3, 4))
+void
+malloc_cprintf(void (*write_cb)(void *, const char *), void *cbopaque,
+ const char *format, ...)
+{
+ va_list ap;
+
+ va_start(ap, format);
+ malloc_vcprintf(write_cb, cbopaque, format, ap);
+ va_end(ap);
+}
+
+/*
+ * Print to stderr in such a way as to (hopefully) avoid memory allocation.
+ */
+JEMALLOC_ATTR(format(printf, 1, 2))
+void
+malloc_printf(const char *format, ...)
+{
+ va_list ap;
+
+ va_start(ap, format);
+ malloc_vcprintf(NULL, NULL, format, ap);
+ va_end(ap);
+}
+#endif
+
+#ifdef JEMALLOC_STATS
+static void
+stats_arena_bins_print(void (*write_cb)(void *, const char *), void *cbopaque,
+ unsigned i)
+{
+ size_t pagesize;
+ bool config_tcache;
+ unsigned nbins, j, gap_start;
+
+ CTL_GET("arenas.pagesize", &pagesize, size_t);
+
+ CTL_GET("config.tcache", &config_tcache, bool);
+ if (config_tcache) {
+ malloc_cprintf(write_cb, cbopaque,
+ "bins: bin size regs pgs allocated nmalloc"
+ " ndalloc nrequests nfills nflushes"
+ " newruns reruns maxruns curruns\n");
+ } else {
+ malloc_cprintf(write_cb, cbopaque,
+ "bins: bin size regs pgs allocated nmalloc"
+ " ndalloc newruns reruns maxruns"
+ " curruns\n");
+ }
+ CTL_GET("arenas.nbins", &nbins, unsigned);
+ for (j = 0, gap_start = UINT_MAX; j < nbins; j++) {
+ uint64_t nruns;
+
+ CTL_IJ_GET("stats.arenas.0.bins.0.nruns", &nruns, uint64_t);
+ if (nruns == 0) {
+ if (gap_start == UINT_MAX)
+ gap_start = j;
+ } else {
+ unsigned ntbins_, nqbins, ncbins, nsbins;
+ size_t reg_size, run_size, allocated;
+ uint32_t nregs;
+ uint64_t nmalloc, ndalloc, nrequests, nfills, nflushes;
+ uint64_t reruns;
+ size_t highruns, curruns;
+
+ if (gap_start != UINT_MAX) {
+ if (j > gap_start + 1) {
+ /* Gap of more than one size class. */
+ malloc_cprintf(write_cb, cbopaque,
+ "[%u..%u]\n", gap_start,
+ j - 1);
+ } else {
+ /* Gap of one size class. */
+ malloc_cprintf(write_cb, cbopaque,
+ "[%u]\n", gap_start);
+ }
+ gap_start = UINT_MAX;
+ }
+ CTL_GET("arenas.ntbins", &ntbins_, unsigned);
+ CTL_GET("arenas.nqbins", &nqbins, unsigned);
+ CTL_GET("arenas.ncbins", &ncbins, unsigned);
+ CTL_GET("arenas.nsbins", &nsbins, unsigned);
+ CTL_J_GET("arenas.bin.0.size", &reg_size, size_t);
+ CTL_J_GET("arenas.bin.0.nregs", &nregs, uint32_t);
+ CTL_J_GET("arenas.bin.0.run_size", &run_size, size_t);
+ CTL_IJ_GET("stats.arenas.0.bins.0.allocated",
+ &allocated, size_t);
+ CTL_IJ_GET("stats.arenas.0.bins.0.nmalloc",
+ &nmalloc, uint64_t);
+ CTL_IJ_GET("stats.arenas.0.bins.0.ndalloc",
+ &ndalloc, uint64_t);
+ if (config_tcache) {
+ CTL_IJ_GET("stats.arenas.0.bins.0.nrequests",
+ &nrequests, uint64_t);
+ CTL_IJ_GET("stats.arenas.0.bins.0.nfills",
+ &nfills, uint64_t);
+ CTL_IJ_GET("stats.arenas.0.bins.0.nflushes",
+ &nflushes, uint64_t);
+ }
+ CTL_IJ_GET("stats.arenas.0.bins.0.nreruns", &reruns,
+ uint64_t);
+ CTL_IJ_GET("stats.arenas.0.bins.0.highruns", &highruns,
+ size_t);
+ CTL_IJ_GET("stats.arenas.0.bins.0.curruns", &curruns,
+ size_t);
+ if (config_tcache) {
+ malloc_cprintf(write_cb, cbopaque,
+ "%13u %1s %5zu %4u %3zu %12zu %12"PRIu64
+ " %12"PRIu64" %12"PRIu64" %12"PRIu64
+ " %12"PRIu64" %12"PRIu64" %12"PRIu64
+ " %12zu %12zu\n",
+ j,
+ j < ntbins_ ? "T" : j < ntbins_ + nqbins ?
+ "Q" : j < ntbins_ + nqbins + ncbins ? "C" :
+ "S",
+ reg_size, nregs, run_size / pagesize,
+ allocated, nmalloc, ndalloc, nrequests,
+ nfills, nflushes, nruns, reruns, highruns,
+ curruns);
+ } else {
+ malloc_cprintf(write_cb, cbopaque,
+ "%13u %1s %5zu %4u %3zu %12zu %12"PRIu64
+ " %12"PRIu64" %12"PRIu64" %12"PRIu64
+ " %12zu %12zu\n",
+ j,
+ j < ntbins_ ? "T" : j < ntbins_ + nqbins ?
+ "Q" : j < ntbins_ + nqbins + ncbins ? "C" :
+ "S",
+ reg_size, nregs, run_size / pagesize,
+ allocated, nmalloc, ndalloc, nruns, reruns,
+ highruns, curruns);
+ }
+ }
+ }
+ if (gap_start != UINT_MAX) {
+ if (j > gap_start + 1) {
+ /* Gap of more than one size class. */
+ malloc_cprintf(write_cb, cbopaque, "[%u..%u]\n",
+ gap_start, j - 1);
+ } else {
+ /* Gap of one size class. */
+ malloc_cprintf(write_cb, cbopaque, "[%u]\n", gap_start);
+ }
+ }
+}
+
+static void
+stats_arena_lruns_print(void (*write_cb)(void *, const char *), void *cbopaque,
+ unsigned i)
+{
+ size_t pagesize, nlruns, j;
+ ssize_t gap_start;
+
+ CTL_GET("arenas.pagesize", &pagesize, size_t);
+
+ malloc_cprintf(write_cb, cbopaque,
+ "large: size pages nmalloc ndalloc nrequests"
+ " maxruns curruns\n");
+ CTL_GET("arenas.nlruns", &nlruns, size_t);
+ for (j = 0, gap_start = -1; j < nlruns; j++) {
+ uint64_t nmalloc, ndalloc, nrequests;
+ size_t run_size, highruns, curruns;
+
+ CTL_IJ_GET("stats.arenas.0.lruns.0.nmalloc", &nmalloc,
+ uint64_t);
+ CTL_IJ_GET("stats.arenas.0.lruns.0.ndalloc", &ndalloc,
+ uint64_t);
+ CTL_IJ_GET("stats.arenas.0.lruns.0.nrequests", &nrequests,
+ uint64_t);
+ if (nrequests == 0) {
+ if (gap_start == -1)
+ gap_start = j;
+ } else {
+ CTL_J_GET("arenas.lrun.0.size", &run_size, size_t);
+ CTL_IJ_GET("stats.arenas.0.lruns.0.highruns", &highruns,
+ size_t);
+ CTL_IJ_GET("stats.arenas.0.lruns.0.curruns", &curruns,
+ size_t);
+ if (gap_start != -1) {
+ malloc_cprintf(write_cb, cbopaque, "[%zu]\n",
+ j - gap_start);
+ gap_start = -1;
+ }
+ malloc_cprintf(write_cb, cbopaque,
+ "%13zu %5zu %12"PRIu64" %12"PRIu64" %12"PRIu64
+ " %12zu %12zu\n",
+ run_size, run_size / pagesize, nmalloc, ndalloc,
+ nrequests, highruns, curruns);
+ }
+ }
+ if (gap_start != -1)
+ malloc_cprintf(write_cb, cbopaque, "[%zu]\n", j - gap_start);
+}
+
+static void
+stats_arena_print(void (*write_cb)(void *, const char *), void *cbopaque,
+ unsigned i)
+{
+ size_t pagesize, pactive, pdirty, mapped;
+ uint64_t npurge, nmadvise, purged;
+ size_t small_allocated;
+ uint64_t small_nmalloc, small_ndalloc, small_nrequests;
+ size_t large_allocated;
+ uint64_t large_nmalloc, large_ndalloc, large_nrequests;
+
+ CTL_GET("arenas.pagesize", &pagesize, size_t);
+
+ CTL_I_GET("stats.arenas.0.pactive", &pactive, size_t);
+ CTL_I_GET("stats.arenas.0.pdirty", &pdirty, size_t);
+ CTL_I_GET("stats.arenas.0.npurge", &npurge, uint64_t);
+ CTL_I_GET("stats.arenas.0.nmadvise", &nmadvise, uint64_t);
+ CTL_I_GET("stats.arenas.0.purged", &purged, uint64_t);
+ malloc_cprintf(write_cb, cbopaque,
+ "dirty pages: %zu:%zu active:dirty, %"PRIu64" sweep%s,"
+ " %"PRIu64" madvise%s, %"PRIu64" purged\n",
+ pactive, pdirty, npurge, npurge == 1 ? "" : "s",
+ nmadvise, nmadvise == 1 ? "" : "s", purged);
+
+ malloc_cprintf(write_cb, cbopaque,
+ " allocated nmalloc ndalloc nrequests\n");
+ CTL_I_GET("stats.arenas.0.small.allocated", &small_allocated, size_t);
+ CTL_I_GET("stats.arenas.0.small.nmalloc", &small_nmalloc, uint64_t);
+ CTL_I_GET("stats.arenas.0.small.ndalloc", &small_ndalloc, uint64_t);
+ CTL_I_GET("stats.arenas.0.small.nrequests", &small_nrequests, uint64_t);
+ malloc_cprintf(write_cb, cbopaque,
+ "small: %12zu %12"PRIu64" %12"PRIu64" %12"PRIu64"\n",
+ small_allocated, small_nmalloc, small_ndalloc, small_nrequests);
+ CTL_I_GET("stats.arenas.0.large.allocated", &large_allocated, size_t);
+ CTL_I_GET("stats.arenas.0.large.nmalloc", &large_nmalloc, uint64_t);
+ CTL_I_GET("stats.arenas.0.large.ndalloc", &large_ndalloc, uint64_t);
+ CTL_I_GET("stats.arenas.0.large.nrequests", &large_nrequests, uint64_t);
+ malloc_cprintf(write_cb, cbopaque,
+ "large: %12zu %12"PRIu64" %12"PRIu64" %12"PRIu64"\n",
+ large_allocated, large_nmalloc, large_ndalloc, large_nrequests);
+ malloc_cprintf(write_cb, cbopaque,
+ "total: %12zu %12"PRIu64" %12"PRIu64" %12"PRIu64"\n",
+ small_allocated + large_allocated,
+ small_nmalloc + large_nmalloc,
+ small_ndalloc + large_ndalloc,
+ small_nrequests + large_nrequests);
+ malloc_cprintf(write_cb, cbopaque, "active: %12zu\n",
+ pactive * pagesize );
+ CTL_I_GET("stats.arenas.0.mapped", &mapped, size_t);
+ malloc_cprintf(write_cb, cbopaque, "mapped: %12zu\n", mapped);
+
+ stats_arena_bins_print(write_cb, cbopaque, i);
+ stats_arena_lruns_print(write_cb, cbopaque, i);
+}
+#endif
+
+void
+stats_print(void (*write_cb)(void *, const char *), void *cbopaque,
+ const char *opts)
+{
+ uint64_t epoch;
+ size_t u64sz;
+ char s[UMAX2S_BUFSIZE];
+ bool general = true;
+ bool merged = true;
+ bool unmerged = true;
+ bool bins = true;
+ bool large = true;
+
+ /* Refresh stats, in case mallctl() was called by the application. */
+ epoch = 1;
+ u64sz = sizeof(uint64_t);
+ xmallctl("epoch", &epoch, &u64sz, &epoch, sizeof(uint64_t));
+
+ if (write_cb == NULL) {
+ /*
+ * The caller did not provide an alternate write_cb callback
+ * function, so use the default one. malloc_write() is an
+ * inline function, so use malloc_message() directly here.
+ */
+ write_cb = JEMALLOC_P(malloc_message);
+ cbopaque = NULL;
+ }
+
+ if (opts != NULL) {
+ unsigned i;
+
+ for (i = 0; opts[i] != '\0'; i++) {
+ switch (opts[i]) {
+ case 'g':
+ general = false;
+ break;
+ case 'm':
+ merged = false;
+ break;
+ case 'a':
+ unmerged = false;
+ break;
+ case 'b':
+ bins = false;
+ break;
+ case 'l':
+ large = false;
+ break;
+ default:;
+ }
+ }
+ }
+
+ write_cb(cbopaque, "___ Begin jemalloc statistics ___\n");
+ if (general) {
+ int err;
+ const char *cpv;
+ bool bv;
+ unsigned uv;
+ ssize_t ssv;
+ size_t sv, bsz, ssz;
+
+ bsz = sizeof(bool);
+ ssz = sizeof(size_t);
+
+ CTL_GET("version", &cpv, const char *);
+ write_cb(cbopaque, "Version: ");
+ write_cb(cbopaque, cpv);
+ write_cb(cbopaque, "\n");
+ CTL_GET("config.debug", &bv, bool);
+ write_cb(cbopaque, "Assertions ");
+ write_cb(cbopaque, bv ? "enabled" : "disabled");
+ write_cb(cbopaque, "\n");
+
+ write_cb(cbopaque, "Boolean JEMALLOC_OPTIONS: ");
+ if ((err = JEMALLOC_P(mallctl)("opt.abort", &bv, &bsz, NULL, 0))
+ == 0)
+ write_cb(cbopaque, bv ? "A" : "a");
+ if ((err = JEMALLOC_P(mallctl)("prof.active", &bv, &bsz,
+ NULL, 0)) == 0)
+ write_cb(cbopaque, bv ? "E" : "e");
+ if ((err = JEMALLOC_P(mallctl)("opt.prof", &bv, &bsz, NULL, 0))
+ == 0)
+ write_cb(cbopaque, bv ? "F" : "f");
+ if ((err = JEMALLOC_P(mallctl)("opt.tcache", &bv, &bsz, NULL,
+ 0)) == 0)
+ write_cb(cbopaque, bv ? "H" : "h");
+ if ((err = JEMALLOC_P(mallctl)("opt.junk", &bv, &bsz, NULL, 0))
+ == 0)
+ write_cb(cbopaque, bv ? "J" : "j");
+ if ((err = JEMALLOC_P(mallctl)("opt.prof_leak", &bv, &bsz, NULL,
+ 0)) == 0)
+ write_cb(cbopaque, bv ? "L" : "l");
+ if ((err = JEMALLOC_P(mallctl)("opt.overcommit", &bv, &bsz,
+ NULL, 0)) == 0)
+ write_cb(cbopaque, bv ? "O" : "o");
+ if ((err = JEMALLOC_P(mallctl)("opt.stats_print", &bv, &bsz,
+ NULL, 0)) == 0)
+ write_cb(cbopaque, bv ? "P" : "p");
+ if ((err = JEMALLOC_P(mallctl)("opt.prof_udump", &bv, &bsz,
+ NULL, 0)) == 0)
+ write_cb(cbopaque, bv ? "U" : "u");
+ if ((err = JEMALLOC_P(mallctl)("opt.sysv", &bv, &bsz, NULL, 0))
+ == 0)
+ write_cb(cbopaque, bv ? "V" : "v");
+ if ((err = JEMALLOC_P(mallctl)("opt.xmalloc", &bv, &bsz, NULL,
+ 0)) == 0)
+ write_cb(cbopaque, bv ? "X" : "x");
+ if ((err = JEMALLOC_P(mallctl)("opt.zero", &bv, &bsz, NULL, 0))
+ == 0)
+ write_cb(cbopaque, bv ? "Z" : "z");
+ write_cb(cbopaque, "\n");
+
+ write_cb(cbopaque, "CPUs: ");
+ write_cb(cbopaque, umax2s(ncpus, 10, s));
+ write_cb(cbopaque, "\n");
+
+ CTL_GET("arenas.narenas", &uv, unsigned);
+ write_cb(cbopaque, "Max arenas: ");
+ write_cb(cbopaque, umax2s(uv, 10, s));
+ write_cb(cbopaque, "\n");
+
+ write_cb(cbopaque, "Pointer size: ");
+ write_cb(cbopaque, umax2s(sizeof(void *), 10, s));
+ write_cb(cbopaque, "\n");
+
+ CTL_GET("arenas.quantum", &sv, size_t);
+ write_cb(cbopaque, "Quantum size: ");
+ write_cb(cbopaque, umax2s(sv, 10, s));
+ write_cb(cbopaque, "\n");
+
+ CTL_GET("arenas.cacheline", &sv, size_t);
+ write_cb(cbopaque, "Cacheline size (assumed): ");
+ write_cb(cbopaque, umax2s(sv, 10, s));
+ write_cb(cbopaque, "\n");
+
+ CTL_GET("arenas.subpage", &sv, size_t);
+ write_cb(cbopaque, "Subpage spacing: ");
+ write_cb(cbopaque, umax2s(sv, 10, s));
+ write_cb(cbopaque, "\n");
+
+ if ((err = JEMALLOC_P(mallctl)("arenas.tspace_min", &sv, &ssz,
+ NULL, 0)) == 0) {
+ write_cb(cbopaque, "Tiny 2^n-spaced sizes: [");
+ write_cb(cbopaque, umax2s(sv, 10, s));
+ write_cb(cbopaque, "..");
+
+ CTL_GET("arenas.tspace_max", &sv, size_t);
+ write_cb(cbopaque, umax2s(sv, 10, s));
+ write_cb(cbopaque, "]\n");
+ }
+
+ CTL_GET("arenas.qspace_min", &sv, size_t);
+ write_cb(cbopaque, "Quantum-spaced sizes: [");
+ write_cb(cbopaque, umax2s(sv, 10, s));
+ write_cb(cbopaque, "..");
+ CTL_GET("arenas.qspace_max", &sv, size_t);
+ write_cb(cbopaque, umax2s(sv, 10, s));
+ write_cb(cbopaque, "]\n");
+
+ CTL_GET("arenas.cspace_min", &sv, size_t);
+ write_cb(cbopaque, "Cacheline-spaced sizes: [");
+ write_cb(cbopaque, umax2s(sv, 10, s));
+ write_cb(cbopaque, "..");
+ CTL_GET("arenas.cspace_max", &sv, size_t);
+ write_cb(cbopaque, umax2s(sv, 10, s));
+ write_cb(cbopaque, "]\n");
+
+ CTL_GET("arenas.sspace_min", &sv, size_t);
+ write_cb(cbopaque, "Subpage-spaced sizes: [");
+ write_cb(cbopaque, umax2s(sv, 10, s));
+ write_cb(cbopaque, "..");
+ CTL_GET("arenas.sspace_max", &sv, size_t);
+ write_cb(cbopaque, umax2s(sv, 10, s));
+ write_cb(cbopaque, "]\n");
+
+ CTL_GET("opt.lg_dirty_mult", &ssv, ssize_t);
+ if (ssv >= 0) {
+ write_cb(cbopaque,
+ "Min active:dirty page ratio per arena: ");
+ write_cb(cbopaque, umax2s((1U << ssv), 10, s));
+ write_cb(cbopaque, ":1\n");
+ } else {
+ write_cb(cbopaque,
+ "Min active:dirty page ratio per arena: N/A\n");
+ }
+ if ((err = JEMALLOC_P(mallctl)("arenas.tcache_max", &sv,
+ &ssz, NULL, 0)) == 0) {
+ write_cb(cbopaque,
+ "Maximum thread-cached size class: ");
+ write_cb(cbopaque, umax2s(sv, 10, s));
+ write_cb(cbopaque, "\n");
+ }
+ if ((err = JEMALLOC_P(mallctl)("opt.lg_tcache_gc_sweep", &ssv,
+ &ssz, NULL, 0)) == 0) {
+ size_t tcache_gc_sweep = (1U << ssv);
+ bool tcache_enabled;
+ CTL_GET("opt.tcache", &tcache_enabled, bool);
+ write_cb(cbopaque, "Thread cache GC sweep interval: ");
+ write_cb(cbopaque, tcache_enabled && ssv >= 0 ?
+ umax2s(tcache_gc_sweep, 10, s) : "N/A");
+ write_cb(cbopaque, "\n");
+ }
+ if ((err = JEMALLOC_P(mallctl)("opt.prof", &bv, &bsz, NULL, 0))
+ == 0 && bv) {
+ CTL_GET("opt.lg_prof_bt_max", &sv, size_t);
+ write_cb(cbopaque, "Maximum profile backtrace depth: ");
+ write_cb(cbopaque, umax2s((1U << sv), 10, s));
+ write_cb(cbopaque, "\n");
+
+ CTL_GET("opt.lg_prof_sample", &sv, size_t);
+ write_cb(cbopaque, "Average profile sample interval: ");
+ write_cb(cbopaque, umax2s((1U << sv), 10, s));
+ write_cb(cbopaque, " (2^");
+ write_cb(cbopaque, umax2s(sv, 10, s));
+ write_cb(cbopaque, ")\n");
+
+ CTL_GET("opt.lg_prof_interval", &ssv, ssize_t);
+ write_cb(cbopaque, "Average profile dump interval: ");
+ if (ssv >= 0) {
+ write_cb(cbopaque, umax2s((1U << ssv), 10, s));
+ write_cb(cbopaque, " (2^");
+ write_cb(cbopaque, umax2s(ssv, 10, s));
+ write_cb(cbopaque, ")\n");
+ } else
+ write_cb(cbopaque, "N/A\n");
+ }
+ CTL_GET("arenas.chunksize", &sv, size_t);
+ write_cb(cbopaque, "Chunk size: ");
+ write_cb(cbopaque, umax2s(sv, 10, s));
+ CTL_GET("opt.lg_chunk", &sv, size_t);
+ write_cb(cbopaque, " (2^");
+ write_cb(cbopaque, umax2s(sv, 10, s));
+ write_cb(cbopaque, ")\n");
+ }
+
+#ifdef JEMALLOC_STATS
+ {
+ int err;
+ size_t ssz;
+ size_t allocated, active, mapped;
+ size_t chunks_current, chunks_high, swap_avail;
+ uint64_t chunks_total;
+ size_t huge_allocated;
+ uint64_t huge_nmalloc, huge_ndalloc;
+
+ ssz = sizeof(size_t);
+
+ CTL_GET("stats.allocated", &allocated, size_t);
+ CTL_GET("stats.active", &active, size_t);
+ CTL_GET("stats.mapped", &mapped, size_t);
+ malloc_cprintf(write_cb, cbopaque,
+ "Allocated: %zu, active: %zu, mapped: %zu\n", allocated,
+ active, mapped);
+
+ /* Print chunk stats. */
+ CTL_GET("stats.chunks.total", &chunks_total, uint64_t);
+ CTL_GET("stats.chunks.high", &chunks_high, size_t);
+ CTL_GET("stats.chunks.current", &chunks_current, size_t);
+ if ((err = JEMALLOC_P(mallctl)("swap.avail", &swap_avail, &ssz,
+ NULL, 0)) == 0) {
+ size_t lg_chunk;
+
+ malloc_cprintf(write_cb, cbopaque, "chunks: nchunks "
+ "highchunks curchunks swap_avail\n");
+ CTL_GET("opt.lg_chunk", &lg_chunk, size_t);
+ malloc_cprintf(write_cb, cbopaque,
+ " %13"PRIu64"%13zu%13zu%13zu\n",
+ chunks_total, chunks_high, chunks_current,
+ swap_avail << lg_chunk);
+ } else {
+ malloc_cprintf(write_cb, cbopaque, "chunks: nchunks "
+ "highchunks curchunks\n");
+ malloc_cprintf(write_cb, cbopaque,
+ " %13"PRIu64"%13zu%13zu\n",
+ chunks_total, chunks_high, chunks_current);
+ }
+
+ /* Print huge stats. */
+ CTL_GET("stats.huge.nmalloc", &huge_nmalloc, uint64_t);
+ CTL_GET("stats.huge.ndalloc", &huge_ndalloc, uint64_t);
+ CTL_GET("stats.huge.allocated", &huge_allocated, size_t);
+ malloc_cprintf(write_cb, cbopaque,
+ "huge: nmalloc ndalloc allocated\n");
+ malloc_cprintf(write_cb, cbopaque,
+ " %12"PRIu64" %12"PRIu64" %12zu\n",
+ huge_nmalloc, huge_ndalloc, huge_allocated);
+
+ if (merged) {
+ unsigned narenas;
+
+ CTL_GET("arenas.narenas", &narenas, unsigned);
+ {
+ bool initialized[narenas];
+ size_t isz;
+ unsigned i, ninitialized;
+
+ isz = sizeof(initialized);
+ xmallctl("arenas.initialized", initialized,
+ &isz, NULL, 0);
+ for (i = ninitialized = 0; i < narenas; i++) {
+ if (initialized[i])
+ ninitialized++;
+ }
+
+ if (ninitialized > 1) {
+ /* Print merged arena stats. */
+ malloc_cprintf(write_cb, cbopaque,
+ "\nMerged arenas stats:\n");
+ stats_arena_print(write_cb, cbopaque,
+ narenas);
+ }
+ }
+ }
+
+ if (unmerged) {
+ unsigned narenas;
+
+ /* Print stats for each arena. */
+
+ CTL_GET("arenas.narenas", &narenas, unsigned);
+ {
+ bool initialized[narenas];
+ size_t isz;
+ unsigned i;
+
+ isz = sizeof(initialized);
+ xmallctl("arenas.initialized", initialized,
+ &isz, NULL, 0);
+
+ for (i = 0; i < narenas; i++) {
+ if (initialized[i]) {
+ malloc_cprintf(write_cb,
+ cbopaque,
+ "\narenas[%u]:\n", i);
+ stats_arena_print(write_cb,
+ cbopaque, i);
+ }
+ }
+ }
+ }
+ }
+#endif /* #ifdef JEMALLOC_STATS */
+ write_cb(cbopaque, "--- End jemalloc statistics ---\n");
+}
diff --git a/externals/jemalloc/tcache.c b/externals/jemalloc/tcache.c
new file mode 100644
index 00000000000..ce6ec996159
--- /dev/null
+++ b/externals/jemalloc/tcache.c
@@ -0,0 +1,403 @@
+#define JEMALLOC_TCACHE_C_
+#include "jemalloc/internal/jemalloc_internal.h"
+#ifdef JEMALLOC_TCACHE
+/******************************************************************************/
+/* Data. */
+
+bool opt_tcache = true;
+ssize_t opt_lg_tcache_maxclass = LG_TCACHE_MAXCLASS_DEFAULT;
+ssize_t opt_lg_tcache_gc_sweep = LG_TCACHE_GC_SWEEP_DEFAULT;
+
+/* Map of thread-specific caches. */
+__thread tcache_t *tcache_tls JEMALLOC_ATTR(tls_model("initial-exec"));
+
+/*
+ * Same contents as tcache, but initialized such that the TSD destructor is
+ * called when a thread exits, so that the cache can be cleaned up.
+ */
+static pthread_key_t tcache_tsd;
+
+size_t nhbins;
+size_t tcache_maxclass;
+unsigned tcache_gc_incr;
+
+/******************************************************************************/
+/* Function prototypes for non-inline static functions. */
+
+static void tcache_thread_cleanup(void *arg);
+
+/******************************************************************************/
+
+void *
+tcache_alloc_small_hard(tcache_t *tcache, tcache_bin_t *tbin, size_t binind)
+{
+ void *ret;
+
+ arena_tcache_fill_small(tcache->arena, tbin, binind
+#ifdef JEMALLOC_PROF
+ , tcache->prof_accumbytes
+#endif
+ );
+#ifdef JEMALLOC_PROF
+ tcache->prof_accumbytes = 0;
+#endif
+ ret = tcache_alloc_easy(tbin);
+
+ return (ret);
+}
+
+void
+tcache_bin_flush_small(tcache_bin_t *tbin, size_t binind, unsigned rem
+#if (defined(JEMALLOC_STATS) || defined(JEMALLOC_PROF))
+ , tcache_t *tcache
+#endif
+ )
+{
+ void *flush, *deferred, *ptr;
+ unsigned i, nflush, ndeferred;
+
+ assert(binind < nbins);
+ assert(rem <= tbin->ncached);
+
+ for (flush = tbin->avail, nflush = tbin->ncached - rem; flush != NULL;
+ flush = deferred, nflush = ndeferred) {
+ /* Lock the arena bin associated with the first object. */
+ arena_chunk_t *chunk = (arena_chunk_t *)CHUNK_ADDR2BASE(flush);
+ arena_t *arena = chunk->arena;
+ arena_bin_t *bin = &arena->bins[binind];
+
+#ifdef JEMALLOC_PROF
+ if (arena == tcache->arena) {
+ malloc_mutex_lock(&arena->lock);
+ arena_prof_accum(arena, tcache->prof_accumbytes);
+ malloc_mutex_unlock(&arena->lock);
+ tcache->prof_accumbytes = 0;
+ }
+#endif
+
+ malloc_mutex_lock(&bin->lock);
+#ifdef JEMALLOC_STATS
+ if (arena == tcache->arena) {
+ bin->stats.nflushes++;
+ bin->stats.nrequests += tbin->tstats.nrequests;
+ tbin->tstats.nrequests = 0;
+ }
+#endif
+ deferred = NULL;
+ ndeferred = 0;
+ for (i = 0; i < nflush; i++) {
+ ptr = flush;
+ assert(ptr != NULL);
+ flush = *(void **)ptr;
+ chunk = (arena_chunk_t *)CHUNK_ADDR2BASE(ptr);
+ if (chunk->arena == arena) {
+ size_t pageind = (((uintptr_t)ptr -
+ (uintptr_t)chunk) >> PAGE_SHIFT);
+ arena_chunk_map_t *mapelm =
+ &chunk->map[pageind];
+ arena_dalloc_bin(arena, chunk, ptr, mapelm);
+ } else {
+ /*
+ * This object was allocated via a different
+ * arena bin than the one that is currently
+ * locked. Stash the object, so that it can be
+ * handled in a future pass.
+ */
+ *(void **)ptr = deferred;
+ deferred = ptr;
+ ndeferred++;
+ }
+ }
+ malloc_mutex_unlock(&bin->lock);
+
+ if (flush != NULL) {
+ /*
+ * This was the first pass, and rem cached objects
+ * remain.
+ */
+ tbin->avail = flush;
+ }
+ }
+
+ tbin->ncached = rem;
+ if (tbin->ncached < tbin->low_water)
+ tbin->low_water = tbin->ncached;
+}
+
+void
+tcache_bin_flush_large(tcache_bin_t *tbin, size_t binind, unsigned rem
+#if (defined(JEMALLOC_STATS) || defined(JEMALLOC_PROF))
+ , tcache_t *tcache
+#endif
+ )
+{
+ void *flush, *deferred, *ptr;
+ unsigned i, nflush, ndeferred;
+
+ assert(binind < nhbins);
+ assert(rem <= tbin->ncached);
+
+ for (flush = tbin->avail, nflush = tbin->ncached - rem; flush != NULL;
+ flush = deferred, nflush = ndeferred) {
+ /* Lock the arena associated with the first object. */
+ arena_chunk_t *chunk = (arena_chunk_t *)CHUNK_ADDR2BASE(flush);
+ arena_t *arena = chunk->arena;
+
+ malloc_mutex_lock(&arena->lock);
+#if (defined(JEMALLOC_PROF) || defined(JEMALLOC_STATS))
+ if (arena == tcache->arena) {
+#endif
+#ifdef JEMALLOC_PROF
+ arena_prof_accum(arena, tcache->prof_accumbytes);
+ tcache->prof_accumbytes = 0;
+#endif
+#ifdef JEMALLOC_STATS
+ arena->stats.nrequests_large += tbin->tstats.nrequests;
+ arena->stats.lstats[binind - nbins].nrequests +=
+ tbin->tstats.nrequests;
+ tbin->tstats.nrequests = 0;
+#endif
+#if (defined(JEMALLOC_PROF) || defined(JEMALLOC_STATS))
+ }
+#endif
+ deferred = NULL;
+ ndeferred = 0;
+ for (i = 0; i < nflush; i++) {
+ ptr = flush;
+ assert(ptr != NULL);
+ flush = *(void **)ptr;
+ chunk = (arena_chunk_t *)CHUNK_ADDR2BASE(ptr);
+ if (chunk->arena == arena)
+ arena_dalloc_large(arena, chunk, ptr);
+ else {
+ /*
+ * This object was allocated via a different
+ * arena than the one that is currently locked.
+ * Stash the object, so that it can be handled
+ * in a future pass.
+ */
+ *(void **)ptr = deferred;
+ deferred = ptr;
+ ndeferred++;
+ }
+ }
+ malloc_mutex_unlock(&arena->lock);
+
+ if (flush != NULL) {
+ /*
+ * This was the first pass, and rem cached objects
+ * remain.
+ */
+ tbin->avail = flush;
+ }
+ }
+
+ tbin->ncached = rem;
+ if (tbin->ncached < tbin->low_water)
+ tbin->low_water = tbin->ncached;
+}
+
+tcache_t *
+tcache_create(arena_t *arena)
+{
+ tcache_t *tcache;
+ size_t size;
+ unsigned i;
+
+ size = sizeof(tcache_t) + (sizeof(tcache_bin_t) * (nhbins - 1));
+ /*
+ * Round up to the nearest multiple of the cacheline size, in order to
+ * avoid the possibility of false cacheline sharing.
+ *
+ * That this works relies on the same logic as in ipalloc().
+ */
+ size = (size + CACHELINE_MASK) & (-CACHELINE);
+
+ if (size <= small_maxclass)
+ tcache = (tcache_t *)arena_malloc_small(arena, size, true);
+ else
+ tcache = (tcache_t *)icalloc(size);
+
+ if (tcache == NULL)
+ return (NULL);
+
+#ifdef JEMALLOC_STATS
+ /* Link into list of extant tcaches. */
+ malloc_mutex_lock(&arena->lock);
+ ql_elm_new(tcache, link);
+ ql_tail_insert(&arena->tcache_ql, tcache, link);
+ malloc_mutex_unlock(&arena->lock);
+#endif
+
+ tcache->arena = arena;
+ assert((TCACHE_NSLOTS_SMALL_MAX & 1U) == 0);
+ for (i = 0; i < nbins; i++) {
+ if ((arena->bins[i].nregs << 1) <= TCACHE_NSLOTS_SMALL_MAX) {
+ tcache->tbins[i].ncached_max = (arena->bins[i].nregs <<
+ 1);
+ } else
+ tcache->tbins[i].ncached_max = TCACHE_NSLOTS_SMALL_MAX;
+ }
+ for (; i < nhbins; i++)
+ tcache->tbins[i].ncached_max = TCACHE_NSLOTS_LARGE;
+
+ tcache_tls = tcache;
+ pthread_setspecific(tcache_tsd, tcache);
+
+ return (tcache);
+}
+
+void
+tcache_destroy(tcache_t *tcache)
+{
+ unsigned i;
+
+#ifdef JEMALLOC_STATS
+ /* Unlink from list of extant tcaches. */
+ malloc_mutex_lock(&tcache->arena->lock);
+ ql_remove(&tcache->arena->tcache_ql, tcache, link);
+ malloc_mutex_unlock(&tcache->arena->lock);
+ tcache_stats_merge(tcache, tcache->arena);
+#endif
+
+ for (i = 0; i < nbins; i++) {
+ tcache_bin_t *tbin = &tcache->tbins[i];
+ tcache_bin_flush_small(tbin, i, 0
+#if (defined(JEMALLOC_STATS) || defined(JEMALLOC_PROF))
+ , tcache
+#endif
+ );
+
+#ifdef JEMALLOC_STATS
+ if (tbin->tstats.nrequests != 0) {
+ arena_t *arena = tcache->arena;
+ arena_bin_t *bin = &arena->bins[i];
+ malloc_mutex_lock(&bin->lock);
+ bin->stats.nrequests += tbin->tstats.nrequests;
+ malloc_mutex_unlock(&bin->lock);
+ }
+#endif
+ }
+
+ for (; i < nhbins; i++) {
+ tcache_bin_t *tbin = &tcache->tbins[i];
+ tcache_bin_flush_large(tbin, i, 0
+#if (defined(JEMALLOC_STATS) || defined(JEMALLOC_PROF))
+ , tcache
+#endif
+ );
+
+#ifdef JEMALLOC_STATS
+ if (tbin->tstats.nrequests != 0) {
+ arena_t *arena = tcache->arena;
+ malloc_mutex_lock(&arena->lock);
+ arena->stats.nrequests_large += tbin->tstats.nrequests;
+ arena->stats.lstats[i - nbins].nrequests +=
+ tbin->tstats.nrequests;
+ malloc_mutex_unlock(&arena->lock);
+ }
+#endif
+ }
+
+#ifdef JEMALLOC_PROF
+ if (tcache->prof_accumbytes > 0) {
+ malloc_mutex_lock(&tcache->arena->lock);
+ arena_prof_accum(tcache->arena, tcache->prof_accumbytes);
+ malloc_mutex_unlock(&tcache->arena->lock);
+ }
+#endif
+
+ if (arena_salloc(tcache) <= small_maxclass) {
+ arena_chunk_t *chunk = CHUNK_ADDR2BASE(tcache);
+ arena_t *arena = chunk->arena;
+ size_t pageind = (((uintptr_t)tcache - (uintptr_t)chunk) >>
+ PAGE_SHIFT);
+ arena_chunk_map_t *mapelm = &chunk->map[pageind];
+ arena_run_t *run = (arena_run_t *)((uintptr_t)chunk +
+ (uintptr_t)((pageind - (mapelm->bits >> PAGE_SHIFT)) <<
+ PAGE_SHIFT));
+ arena_bin_t *bin = run->bin;
+
+ malloc_mutex_lock(&bin->lock);
+ arena_dalloc_bin(arena, chunk, tcache, mapelm);
+ malloc_mutex_unlock(&bin->lock);
+ } else
+ idalloc(tcache);
+}
+
+static void
+tcache_thread_cleanup(void *arg)
+{
+ tcache_t *tcache = (tcache_t *)arg;
+
+ assert(tcache == tcache_tls);
+ if (tcache != NULL) {
+ assert(tcache != (void *)(uintptr_t)1);
+ tcache_destroy(tcache);
+ tcache_tls = (void *)(uintptr_t)1;
+ }
+}
+
+#ifdef JEMALLOC_STATS
+void
+tcache_stats_merge(tcache_t *tcache, arena_t *arena)
+{
+ unsigned i;
+
+ /* Merge and reset tcache stats. */
+ for (i = 0; i < nbins; i++) {
+ arena_bin_t *bin = &arena->bins[i];
+ tcache_bin_t *tbin = &tcache->tbins[i];
+ malloc_mutex_lock(&bin->lock);
+ bin->stats.nrequests += tbin->tstats.nrequests;
+ malloc_mutex_unlock(&bin->lock);
+ tbin->tstats.nrequests = 0;
+ }
+
+ for (; i < nhbins; i++) {
+ malloc_large_stats_t *lstats = &arena->stats.lstats[i - nbins];
+ tcache_bin_t *tbin = &tcache->tbins[i];
+ arena->stats.nrequests_large += tbin->tstats.nrequests;
+ lstats->nrequests += tbin->tstats.nrequests;
+ tbin->tstats.nrequests = 0;
+ }
+}
+#endif
+
+void
+tcache_boot(void)
+{
+
+ if (opt_tcache) {
+ /*
+ * If necessary, clamp opt_lg_tcache_maxclass, now that
+ * small_maxclass and arena_maxclass are known.
+ */
+ if (opt_lg_tcache_maxclass < 0 || (1U <<
+ opt_lg_tcache_maxclass) < small_maxclass)
+ tcache_maxclass = small_maxclass;
+ else if ((1U << opt_lg_tcache_maxclass) > arena_maxclass)
+ tcache_maxclass = arena_maxclass;
+ else
+ tcache_maxclass = (1U << opt_lg_tcache_maxclass);
+
+ nhbins = nbins + (tcache_maxclass >> PAGE_SHIFT);
+
+ /* Compute incremental GC event threshold. */
+ if (opt_lg_tcache_gc_sweep >= 0) {
+ tcache_gc_incr = ((1U << opt_lg_tcache_gc_sweep) /
+ nbins) + (((1U << opt_lg_tcache_gc_sweep) % nbins ==
+ 0) ? 0 : 1);
+ } else
+ tcache_gc_incr = 0;
+
+ if (pthread_key_create(&tcache_tsd, tcache_thread_cleanup) !=
+ 0) {
+ malloc_write(
+ "<jemalloc>: Error in pthread_key_create()\n");
+ abort();
+ }
+ }
+}
+/******************************************************************************/
+#endif /* JEMALLOC_TCACHE */
diff --git a/externals/sockets/Base64.cpp b/externals/sockets/Base64.cpp
new file mode 100644
index 00000000000..f7f12f5edff
--- /dev/null
+++ b/externals/sockets/Base64.cpp
@@ -0,0 +1,262 @@
+/** \file Base64.cpp
+ ** \date 2004-02-13
+ ** \author grymse@alhem.net
+**/
+/*
+Copyright (C) 2004-2007 Anders Hedstrom
+
+This library is made available under the terms of the GNU GPL.
+
+If you would like to use this library in a closed-source application,
+a separate license agreement is available. For information about
+the closed-source license agreement for the C++ sockets library,
+please visit http://www.alhem.net/Sockets/license.html and/or
+email license@alhem.net.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+#include "Base64.h"
+
+#ifdef SOCKETS_NAMESPACE
+namespace SOCKETS_NAMESPACE {
+#endif
+
+const char *Base64::bstr =
+ "ABCDEFGHIJKLMNOPQ"
+ "RSTUVWXYZabcdefgh"
+ "ijklmnopqrstuvwxy"
+ "z0123456789+/";
+
+const char Base64::rstr[] = {
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 62, 0, 0, 0, 63,
+ 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 0, 0, 0, 0, 0, 0,
+ 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14,
+ 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 0, 0, 0, 0, 0,
+ 0, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40,
+ 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 0, 0, 0, 0, 0};
+
+Base64::Base64()
+{
+}
+
+void Base64::encode(FILE *fil, std::string& output, bool add_crlf)
+{
+ size_t remain;
+ size_t i = 0;
+ size_t o = 0;
+ char input[4];
+
+ output = "";
+ remain = fread(input,1,3,fil);
+ while (remain > 0)
+ {
+ if (add_crlf && o && o % 76 == 0)
+ output += "\n";
+ switch (remain)
+ {
+ case 1:
+ output += bstr[ ((input[i] >> 2) & 0x3f) ];
+ output += bstr[ ((input[i] << 4) & 0x30) ];
+ output += "==";
+ break;
+ case 2:
+ output += bstr[ ((input[i] >> 2) & 0x3f) ];
+ output += bstr[ ((input[i] << 4) & 0x30) + ((input[i + 1] >> 4) & 0x0f) ];
+ output += bstr[ ((input[i + 1] << 2) & 0x3c) ];
+ output += "=";
+ break;
+ default:
+ output += bstr[ ((input[i] >> 2) & 0x3f) ];
+ output += bstr[ ((input[i] << 4) & 0x30) + ((input[i + 1] >> 4) & 0x0f) ];
+ output += bstr[ ((input[i + 1] << 2) & 0x3c) + ((input[i + 2] >> 6) & 0x03) ];
+ output += bstr[ (input[i + 2] & 0x3f) ];
+ }
+ o += 4;
+ //
+ remain = fread(input,1,3,fil);
+ }
+}
+
+void Base64::encode(const std::string& str_in, std::string& str_out, bool add_crlf)
+{
+ encode(str_in.c_str(), str_in.size(), str_out, add_crlf);
+}
+
+void Base64::encode(const char* input,size_t l,std::string& output, bool add_crlf)
+{
+ size_t i = 0;
+ size_t o = 0;
+
+ output = "";
+ while (i < l)
+ {
+ size_t remain = l - i;
+ if (add_crlf && o && o % 76 == 0)
+ output += "\n";
+ switch (remain)
+ {
+ case 1:
+ output += bstr[ ((input[i] >> 2) & 0x3f) ];
+ output += bstr[ ((input[i] << 4) & 0x30) ];
+ output += "==";
+ break;
+ case 2:
+ output += bstr[ ((input[i] >> 2) & 0x3f) ];
+ output += bstr[ ((input[i] << 4) & 0x30) + ((input[i + 1] >> 4) & 0x0f) ];
+ output += bstr[ ((input[i + 1] << 2) & 0x3c) ];
+ output += "=";
+ break;
+ default:
+ output += bstr[ ((input[i] >> 2) & 0x3f) ];
+ output += bstr[ ((input[i] << 4) & 0x30) + ((input[i + 1] >> 4) & 0x0f) ];
+ output += bstr[ ((input[i + 1] << 2) & 0x3c) + ((input[i + 2] >> 6) & 0x03) ];
+ output += bstr[ (input[i + 2] & 0x3f) ];
+ }
+ o += 4;
+ i += 3;
+ }
+}
+
+void Base64::encode(const unsigned char* input,size_t l,std::string& output,bool add_crlf)
+{
+ size_t i = 0;
+ size_t o = 0;
+
+ output = "";
+ while (i < l)
+ {
+ size_t remain = l - i;
+ if (add_crlf && o && o % 76 == 0)
+ output += "\n";
+ switch (remain)
+ {
+ case 1:
+ output += bstr[ ((input[i] >> 2) & 0x3f) ];
+ output += bstr[ ((input[i] << 4) & 0x30) ];
+ output += "==";
+ break;
+ case 2:
+ output += bstr[ ((input[i] >> 2) & 0x3f) ];
+ output += bstr[ ((input[i] << 4) & 0x30) + ((input[i + 1] >> 4) & 0x0f) ];
+ output += bstr[ ((input[i + 1] << 2) & 0x3c) ];
+ output += "=";
+ break;
+ default:
+ output += bstr[ ((input[i] >> 2) & 0x3f) ];
+ output += bstr[ ((input[i] << 4) & 0x30) + ((input[i + 1] >> 4) & 0x0f) ];
+ output += bstr[ ((input[i + 1] << 2) & 0x3c) + ((input[i + 2] >> 6) & 0x03) ];
+ output += bstr[ (input[i + 2] & 0x3f) ];
+ }
+ o += 4;
+ i += 3;
+ }
+}
+
+void Base64::decode(const std::string& input,std::string& output)
+{
+ size_t i = 0;
+ size_t l = input.size();
+
+ output = "";
+ while (i < l)
+ {
+ while (i < l && (input[i] == 13 || input[i] == 10))
+ i++;
+ if (i < l)
+ {
+ char b1 = (char)((rstr[(int)input[i]] << 2 & 0xfc) +
+ (rstr[(int)input[i + 1]] >> 4 & 0x03));
+ output += b1;
+ if (input[i + 2] != '=')
+ {
+ char b2 = (char)((rstr[(int)input[i + 1]] << 4 & 0xf0) +
+ (rstr[(int)input[i + 2]] >> 2 & 0x0f));
+ output += b2;
+ }
+ if (input[i + 3] != '=')
+ {
+ char b3 = (char)((rstr[(int)input[i + 2]] << 6 & 0xc0) +
+ rstr[(int)input[i + 3]]);
+ output += b3;
+ }
+ i += 4;
+ }
+ }
+}
+
+void Base64::decode(const std::string& input, unsigned char *output, size_t& sz)
+{
+ size_t i = 0;
+ size_t l = input.size();
+ size_t j = 0;
+
+ while (i < l)
+ {
+ while (i < l && (input[i] == 13 || input[i] == 10))
+ i++;
+ if (i < l)
+ {
+ unsigned char b1 = (unsigned char)((rstr[(int)input[i]] << 2 & 0xfc) +
+ (rstr[(int)input[i + 1]] >> 4 & 0x03));
+ if (output)
+ {
+ output[j] = b1;
+ }
+ j++;
+ if (input[i + 2] != '=')
+ {
+ unsigned char b2 = (unsigned char)((rstr[(int)input[i + 1]] << 4 & 0xf0) +
+ (rstr[(int)input[i + 2]] >> 2 & 0x0f));
+ if (output)
+ {
+ output[j] = b2;
+ }
+ j++;
+ }
+ if (input[i + 3] != '=')
+ {
+ unsigned char b3 = (unsigned char)((rstr[(int)input[i + 2]] << 6 & 0xc0) +
+ rstr[(int)input[i + 3]]);
+ if (output)
+ {
+ output[j] = b3;
+ }
+ j++;
+ }
+ i += 4;
+ }
+ }
+ sz = j;
+}
+
+size_t Base64::decode_length(const std::string& str64)
+{
+ if (str64.empty() || str64.size() % 4)
+ return 0;
+ size_t l = 3 * (str64.size() / 4 - 1) + 1;
+ if (str64[str64.size() - 2] != '=')
+ l++;
+ if (str64[str64.size() - 1] != '=')
+ l++;
+ return l;
+}
+
+#ifdef SOCKETS_NAMESPACE
+}
+#endif
+
+
diff --git a/externals/sockets/CMakeLists.txt b/externals/sockets/CMakeLists.txt
new file mode 100644
index 00000000000..a47c8dee75f
--- /dev/null
+++ b/externals/sockets/CMakeLists.txt
@@ -0,0 +1,26 @@
+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
+)
+
+include_directories(
+ ${CMAKE_SOURCE_DIR}/dep/include/sockets
+)
+
+add_library(trinitysockets STATIC ${trinitysockets_STAT_SRCS})
diff --git a/externals/sockets/Exception.cpp b/externals/sockets/Exception.cpp
new file mode 100644
index 00000000000..4d79aeef813
--- /dev/null
+++ b/externals/sockets/Exception.cpp
@@ -0,0 +1,45 @@
+/**
+ ** \file Exception.cpp
+ ** \date 2007-09-28
+ ** \author grymse@alhem.net
+**/
+/*
+Copyright (C) 2007 Anders Hedstrom
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+#ifdef _MSC_VER
+#pragma warning(disable:4786)
+#endif
+#include "Exception.h"
+
+#ifdef SOCKETS_NAMESPACE
+namespace SOCKETS_NAMESPACE {
+#endif
+
+Exception::Exception(const std::string& description) : m_description(description)
+{
+}
+
+const std::string Exception::ToString() const
+{
+ return m_description;
+}
+
+#ifdef SOCKETS_NAMESPACE
+} // namespace SOCKETS_NAMESPACE {
+#endif
+
+
diff --git a/externals/sockets/Ipv4Address.cpp b/externals/sockets/Ipv4Address.cpp
new file mode 100644
index 00000000000..03935038951
--- /dev/null
+++ b/externals/sockets/Ipv4Address.cpp
@@ -0,0 +1,192 @@
+/**
+ ** \file Ipv4Address.cpp
+ ** \date 2006-09-21
+ ** \author grymse@alhem.net
+**/
+/*
+Copyright (C) 2007 Anders Hedstrom
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+#include "Ipv4Address.h"
+#include "Utility.h"
+#include "Parse.h"
+#ifndef _WIN32
+#include <netdb.h>
+#endif
+
+#ifdef SOCKETS_NAMESPACE
+namespace SOCKETS_NAMESPACE {
+#endif
+
+Ipv4Address::Ipv4Address(port_t port) : m_valid(true)
+{
+ memset(&m_addr, 0, sizeof(m_addr));
+ m_addr.sin_family = AF_INET;
+ m_addr.sin_port = htons( port );
+}
+
+Ipv4Address::Ipv4Address(ipaddr_t a,port_t port) : m_valid(true)
+{
+ memset(&m_addr, 0, sizeof(m_addr));
+ m_addr.sin_family = AF_INET;
+ m_addr.sin_port = htons( port );
+ memcpy(&m_addr.sin_addr, &a, sizeof(struct in_addr));
+}
+
+Ipv4Address::Ipv4Address(struct in_addr& a,port_t port) : m_valid(true)
+{
+ memset(&m_addr, 0, sizeof(m_addr));
+ m_addr.sin_family = AF_INET;
+ m_addr.sin_port = htons( port );
+ m_addr.sin_addr = a;
+}
+
+Ipv4Address::Ipv4Address(const std::string& host,port_t port) : m_valid(false)
+{
+ memset(&m_addr, 0, sizeof(m_addr));
+ m_addr.sin_family = AF_INET;
+ m_addr.sin_port = htons( port );
+ {
+ ipaddr_t a;
+ if (Utility::u2ip(host, a))
+ {
+ memcpy(&m_addr.sin_addr, &a, sizeof(struct in_addr));
+ m_valid = true;
+ }
+ }
+}
+
+Ipv4Address::Ipv4Address(struct sockaddr_in& sa)
+{
+ m_addr = sa;
+ m_valid = sa.sin_family == AF_INET;
+}
+
+Ipv4Address::~Ipv4Address()
+{
+}
+
+Ipv4Address::operator struct sockaddr *()
+{
+ return (struct sockaddr *)&m_addr;
+}
+
+Ipv4Address::operator socklen_t()
+{
+ return sizeof(struct sockaddr_in);
+}
+
+void Ipv4Address::SetPort(port_t port)
+{
+ m_addr.sin_port = htons( port );
+}
+
+port_t Ipv4Address::GetPort()
+{
+ return ntohs( m_addr.sin_port );
+}
+
+bool Ipv4Address::Resolve(const std::string& hostname,struct in_addr& a)
+{
+ struct sockaddr_in sa;
+ memset(&a, 0, sizeof(a));
+ if (Utility::isipv4(hostname))
+ {
+ if (!Utility::u2ip(hostname, sa, AI_NUMERICHOST))
+ return false;
+ a = sa.sin_addr;
+ return true;
+ }
+ if (!Utility::u2ip(hostname, sa))
+ return false;
+ a = sa.sin_addr;
+ return true;
+}
+
+bool Ipv4Address::Reverse(struct in_addr& a,std::string& name)
+{
+ struct sockaddr_in sa;
+ memset(&sa, 0, sizeof(sa));
+ sa.sin_family = AF_INET;
+ sa.sin_addr = a;
+ return Utility::reverse((struct sockaddr *)&sa, sizeof(sa), name);
+}
+
+std::string Ipv4Address::Convert(bool include_port)
+{
+ if (include_port)
+ return Convert(m_addr.sin_addr) + ":" + Utility::l2string(GetPort());
+ return Convert(m_addr.sin_addr);
+}
+
+std::string Ipv4Address::Convert(struct in_addr& a)
+{
+ struct sockaddr_in sa;
+ memset(&sa, 0, sizeof(sa));
+ sa.sin_family = AF_INET;
+ sa.sin_addr = a;
+ std::string name;
+ Utility::reverse((struct sockaddr *)&sa, sizeof(sa), name, NI_NUMERICHOST);
+ return name;
+}
+
+void Ipv4Address::SetAddress(struct sockaddr *sa)
+{
+ memcpy(&m_addr, sa, sizeof(struct sockaddr_in));
+}
+
+int Ipv4Address::GetFamily()
+{
+ return m_addr.sin_family;
+}
+
+bool Ipv4Address::IsValid()
+{
+ return m_valid;
+}
+
+bool Ipv4Address::operator==(SocketAddress& a)
+{
+ if (a.GetFamily() != GetFamily())
+ return false;
+ if ((socklen_t)a != sizeof(m_addr))
+ return false;
+ struct sockaddr *sa = a;
+ struct sockaddr_in *p = (struct sockaddr_in *)sa;
+ if (p -> sin_port != m_addr.sin_port)
+ return false;
+ if (memcmp(&p -> sin_addr, &m_addr.sin_addr, 4))
+ return false;
+ return true;
+}
+
+std::auto_ptr<SocketAddress> Ipv4Address::GetCopy()
+{
+ return std::auto_ptr<SocketAddress>(new Ipv4Address(m_addr));
+}
+
+std::string Ipv4Address::Reverse()
+{
+ std::string tmp;
+ Reverse(m_addr.sin_addr, tmp);
+ return tmp;
+}
+
+#ifdef SOCKETS_NAMESPACE
+} // namespace SOCKETS_NAMESPACE {
+#endif
+
+
diff --git a/externals/sockets/Ipv6Address.cpp b/externals/sockets/Ipv6Address.cpp
new file mode 100644
index 00000000000..3208b5098fa
--- /dev/null
+++ b/externals/sockets/Ipv6Address.cpp
@@ -0,0 +1,247 @@
+/**
+ ** \file Ipv6Address.cpp
+ ** \date 2006-09-21
+ ** \author grymse@alhem.net
+**/
+/*
+Copyright (C) 2007 Anders Hedstrom
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+#include "Ipv6Address.h"
+#ifdef ENABLE_IPV6
+
+#include "Utility.h"
+#include "Parse.h"
+#ifndef _WIN32
+#include <netdb.h>
+#endif
+#ifdef IPPROTO_IPV6
+
+#ifdef SOCKETS_NAMESPACE
+namespace SOCKETS_NAMESPACE {
+#endif
+
+Ipv6Address::Ipv6Address(port_t port) : m_valid(true)
+{
+ memset(&m_addr, 0, sizeof(m_addr));
+ m_addr.sin6_family = AF_INET6;
+ m_addr.sin6_port = htons( port );
+}
+
+Ipv6Address::Ipv6Address(struct in6_addr& a,port_t port) : m_valid(true)
+{
+ memset(&m_addr, 0, sizeof(m_addr));
+ m_addr.sin6_family = AF_INET6;
+ m_addr.sin6_port = htons( port );
+ m_addr.sin6_addr = a;
+}
+
+Ipv6Address::Ipv6Address(const std::string& host,port_t port) : m_valid(false)
+{
+ memset(&m_addr, 0, sizeof(m_addr));
+ m_addr.sin6_family = AF_INET6;
+ m_addr.sin6_port = htons( port );
+ {
+ struct in6_addr a;
+ if (Utility::u2ip(host, a))
+ {
+ m_addr.sin6_addr = a;
+ m_valid = true;
+ }
+ }
+}
+
+Ipv6Address::Ipv6Address(struct sockaddr_in6& sa)
+{
+ m_addr = sa;
+ m_valid = sa.sin6_family == AF_INET6;
+}
+
+Ipv6Address::~Ipv6Address()
+{
+}
+
+Ipv6Address::operator struct sockaddr *()
+{
+ return (struct sockaddr *)&m_addr;
+}
+
+Ipv6Address::operator socklen_t()
+{
+ return sizeof(struct sockaddr_in6);
+}
+
+void Ipv6Address::SetPort(port_t port)
+{
+ m_addr.sin6_port = htons( port );
+}
+
+port_t Ipv6Address::GetPort()
+{
+ return ntohs( m_addr.sin6_port );
+}
+
+bool Ipv6Address::Resolve(const std::string& hostname,struct in6_addr& a)
+{
+ struct sockaddr_in6 sa;
+ memset(&a, 0, sizeof(a));
+ if (Utility::isipv6(hostname))
+ {
+ if (!Utility::u2ip(hostname, sa, AI_NUMERICHOST))
+ return false;
+ a = sa.sin6_addr;
+ return true;
+ }
+ if (!Utility::u2ip(hostname, sa))
+ return false;
+ a = sa.sin6_addr;
+ return true;
+}
+
+bool Ipv6Address::Reverse(struct in6_addr& a,std::string& name)
+{
+ struct sockaddr_in6 sa;
+ memset(&sa, 0, sizeof(sa));
+ sa.sin6_family = AF_INET6;
+ sa.sin6_addr = a;
+ return Utility::reverse((struct sockaddr *)&sa, sizeof(sa), name);
+}
+
+std::string Ipv6Address::Convert(bool include_port)
+{
+ if (include_port)
+ return Convert(m_addr.sin6_addr) + ":" + Utility::l2string(GetPort());
+ return Convert(m_addr.sin6_addr);
+}
+
+std::string Ipv6Address::Convert(struct in6_addr& a,bool mixed)
+{
+ char slask[100]; // l2ip temporary
+ *slask = 0;
+ unsigned int prev = 0;
+ bool skipped = false;
+ bool ok_to_skip = true;
+ if (mixed)
+ {
+ unsigned short x;
+ unsigned short addr16[8];
+ memcpy(addr16, &a, sizeof(addr16));
+ for (size_t i = 0; i < 6; i++)
+ {
+ x = ntohs(addr16[i]);
+ if (*slask && (x || !ok_to_skip || prev))
+ strcat(slask,":");
+ if (x || !ok_to_skip)
+ {
+ sprintf(slask + strlen(slask),"%x", x);
+ if (x && skipped)
+ ok_to_skip = false;
+ }
+ else
+ {
+ skipped = true;
+ }
+ prev = x;
+ }
+ x = ntohs(addr16[6]);
+ sprintf(slask + strlen(slask),":%u.%u",x / 256,x & 255);
+ x = ntohs(addr16[7]);
+ sprintf(slask + strlen(slask),".%u.%u",x / 256,x & 255);
+ }
+ else
+ {
+ struct sockaddr_in6 sa;
+ memset(&sa, 0, sizeof(sa));
+ sa.sin6_family = AF_INET6;
+ sa.sin6_addr = a;
+ std::string name;
+ Utility::reverse((struct sockaddr *)&sa, sizeof(sa), name, NI_NUMERICHOST);
+ return name;
+ }
+ return slask;
+}
+
+void Ipv6Address::SetAddress(struct sockaddr *sa)
+{
+ memcpy(&m_addr, sa, sizeof(struct sockaddr_in6));
+}
+
+int Ipv6Address::GetFamily()
+{
+ return m_addr.sin6_family;
+}
+
+void Ipv6Address::SetFlowinfo(uint32_t x)
+{
+ m_addr.sin6_flowinfo = x;
+}
+
+uint32_t Ipv6Address::GetFlowinfo()
+{
+ return m_addr.sin6_flowinfo;
+}
+
+#ifndef _WIN32
+void Ipv6Address::SetScopeId(uint32_t x)
+{
+ m_addr.sin6_scope_id = x;
+}
+
+uint32_t Ipv6Address::GetScopeId()
+{
+ return m_addr.sin6_scope_id;
+}
+#endif
+
+bool Ipv6Address::IsValid()
+{
+ return m_valid;
+}
+
+bool Ipv6Address::operator==(SocketAddress& a)
+{
+ if (a.GetFamily() != GetFamily())
+ return false;
+ if ((socklen_t)a != sizeof(m_addr))
+ return false;
+ struct sockaddr *sa = a;
+ struct sockaddr_in6 *p = (struct sockaddr_in6 *)sa;
+ if (p -> sin6_port != m_addr.sin6_port)
+ return false;
+ if (memcmp(&p -> sin6_addr, &m_addr.sin6_addr, sizeof(struct in6_addr)))
+ return false;
+ return true;
+}
+
+std::auto_ptr<SocketAddress> Ipv6Address::GetCopy()
+{
+ return std::auto_ptr<SocketAddress>(new Ipv6Address(m_addr));
+}
+
+std::string Ipv6Address::Reverse()
+{
+ std::string tmp;
+ Reverse(m_addr.sin6_addr, tmp);
+ return tmp;
+}
+
+#ifdef SOCKETS_NAMESPACE
+} // namespace SOCKETS_NAMESPACE {
+#endif
+#endif // IPPROTO_IPV6
+#endif // ENABLE_IPV6
+
+
diff --git a/externals/sockets/Lock.cpp b/externals/sockets/Lock.cpp
new file mode 100644
index 00000000000..b75664cfbdc
--- /dev/null
+++ b/externals/sockets/Lock.cpp
@@ -0,0 +1,52 @@
+/** \file Lock.cpp
+ ** \date 2005-08-22
+ ** \author grymse@alhem.net
+**/
+/*
+Copyright (C) 2005,2007 Anders Hedstrom
+
+This library is made available under the terms of the GNU GPL.
+
+If you would like to use this library in a closed-source application,
+a separate license agreement is available. For information about
+the closed-source license agreement for the C++ sockets library,
+please visit http://www.alhem.net/Sockets/license.html and/or
+email license@alhem.net.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+#include "Mutex.h"
+#include "Lock.h"
+
+#ifdef SOCKETS_NAMESPACE
+namespace SOCKETS_NAMESPACE {
+#endif
+
+Lock::Lock(Mutex& m) : m_mutex(m)
+{
+ m_mutex.Lock();
+}
+
+Lock::~Lock()
+{
+ m_mutex.Unlock();
+}
+
+
+#ifdef SOCKETS_NAMESPACE
+}
+#endif
+
+
diff --git a/externals/sockets/Mutex.cpp b/externals/sockets/Mutex.cpp
new file mode 100644
index 00000000000..681e85cee5b
--- /dev/null
+++ b/externals/sockets/Mutex.cpp
@@ -0,0 +1,77 @@
+/** \file Mutex.cpp
+ ** \date 2004-10-30
+ ** \author grymse@alhem.net
+**/
+/*
+Copyright (C) 2004-2007 Anders Hedstrom
+
+This library is made available under the terms of the GNU GPL.
+
+If you would like to use this library in a closed-source application,
+a separate license agreement is available. For information about
+the closed-source license agreement for the C++ sockets library,
+please visit http://www.alhem.net/Sockets/license.html and/or
+email license@alhem.net.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+#include "Mutex.h"
+
+#ifdef SOCKETS_NAMESPACE
+namespace SOCKETS_NAMESPACE {
+#endif
+
+Mutex::Mutex()
+{
+#ifdef _WIN32
+ m_mutex = ::CreateMutex(NULL, FALSE, NULL);
+#else
+ pthread_mutex_init(&m_mutex, NULL);
+#endif
+}
+
+Mutex::~Mutex()
+{
+#ifdef _WIN32
+ ::CloseHandle(m_mutex);
+#else
+ pthread_mutex_destroy(&m_mutex);
+#endif
+}
+
+void Mutex::Lock()
+{
+#ifdef _WIN32
+ /*DWORD d =*/ WaitForSingleObject(m_mutex, INFINITE);
+ /// \todo check 'd' for result
+#else
+ pthread_mutex_lock(&m_mutex);
+#endif
+}
+
+void Mutex::Unlock()
+{
+#ifdef _WIN32
+ ::ReleaseMutex(m_mutex);
+#else
+ pthread_mutex_unlock(&m_mutex);
+#endif
+}
+
+#ifdef SOCKETS_NAMESPACE
+}
+#endif
+
+
diff --git a/externals/sockets/Parse.cpp b/externals/sockets/Parse.cpp
new file mode 100644
index 00000000000..2967859f23d
--- /dev/null
+++ b/externals/sockets/Parse.cpp
@@ -0,0 +1,318 @@
+/** \file Parse.cpp - parse a string
+ **
+ ** Written: 1999-Feb-10 grymse@alhem.net
+ **/
+
+/*
+Copyright (C) 1999-2007 Anders Hedstrom
+
+This library is made available under the terms of the GNU GPL.
+
+If you would like to use this library in a closed-source application,
+a separate license agreement is available. For information about
+the closed-source license agreement for the C++ sockets library,
+please visit http://www.alhem.net/Sockets/license.html and/or
+email license@alhem.net.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+#include <stdlib.h>
+#include <string.h>
+
+#include "Parse.h"
+
+#ifdef SOCKETS_NAMESPACE
+namespace SOCKETS_NAMESPACE {
+#endif
+
+/* implementation of class Parse */
+
+Parse::Parse()
+:pa_the_str("")
+,pa_splits("")
+,pa_ord("")
+,pa_the_ptr(0)
+,pa_breakchar(0)
+,pa_enable(0)
+,pa_disable(0)
+,pa_nospace(0)
+,pa_quote(false)
+{
+}
+
+Parse::Parse(const std::string&s)
+:pa_the_str(s)
+,pa_splits("")
+,pa_ord("")
+,pa_the_ptr(0)
+,pa_breakchar(0)
+,pa_enable(0)
+,pa_disable(0)
+,pa_nospace(0)
+,pa_quote(false)
+{
+}
+
+Parse::Parse(const std::string&s,const std::string&sp)
+:pa_the_str(s)
+,pa_splits(sp)
+,pa_ord("")
+,pa_the_ptr(0)
+,pa_breakchar(0)
+,pa_enable(0)
+,pa_disable(0)
+,pa_nospace(0)
+,pa_quote(false)
+{
+}
+
+Parse::Parse(const std::string&s,const std::string&sp,short /*nospace*/)
+:pa_the_str(s)
+,pa_splits(sp)
+,pa_ord("")
+,pa_the_ptr(0)
+,pa_breakchar(0)
+,pa_enable(0)
+,pa_disable(0)
+,pa_nospace(1)
+,pa_quote(false)
+{
+}
+
+Parse::~Parse()
+{
+}
+
+#define C ((pa_the_ptr<pa_the_str.size()) ? pa_the_str[pa_the_ptr] : 0)
+
+short Parse::issplit(const char c)
+{
+ for (size_t i = 0; i < pa_splits.size(); i++)
+ if (pa_splits[i] == c)
+ return 1;
+ return 0;
+}
+
+void Parse::getsplit()
+{
+ size_t x;
+
+ if (C == '=')
+ {
+ x = pa_the_ptr++;
+ } else
+ {
+ while (C && (issplit(C)))
+ pa_the_ptr++;
+ x = pa_the_ptr;
+ while (C && !issplit(C) && C != '=')
+ pa_the_ptr++;
+ }
+ if (x == pa_the_ptr && C == '=')
+ pa_the_ptr++;
+ pa_ord = (x < pa_the_str.size()) ? pa_the_str.substr(x,pa_the_ptr - x) : "";
+}
+
+std::string Parse::getword()
+{
+ size_t x;
+ int disabled = 0;
+ int quote = 0;
+ int rem = 0;
+
+ if (pa_nospace)
+ {
+ while (C && issplit(C))
+ pa_the_ptr++;
+ x = pa_the_ptr;
+ while (C && !issplit(C) && (C != pa_breakchar || !pa_breakchar || disabled))
+ {
+ if (pa_breakchar && C == pa_disable)
+ disabled = 1;
+ if (pa_breakchar && C == pa_enable)
+ disabled = 0;
+ if (pa_quote && C == '"')
+ quote = 1;
+ pa_the_ptr++;
+ while (quote && C && C != '"')
+ {
+ pa_the_ptr++;
+ }
+ if (pa_quote && C == '"')
+ {
+ pa_the_ptr++;
+ }
+ quote = 0;
+ }
+ } else
+ {
+ if (C == pa_breakchar && pa_breakchar)
+ {
+ x = pa_the_ptr++;
+ rem = 1;
+ } else
+ {
+ while (C && (C == ' ' || C == 9 || C == 13 || C == 10 || issplit(C)))
+ pa_the_ptr++;
+ x = pa_the_ptr;
+ while (C && C != ' ' && C != 9 && C != 13 && C != 10 && !issplit(C) &&
+ (C != pa_breakchar || !pa_breakchar || disabled))
+ {
+ if (pa_breakchar && C == pa_disable)
+ disabled = 1;
+ if (pa_breakchar && C == pa_enable)
+ disabled = 0;
+ if (pa_quote && C == '"')
+ {
+ quote = 1;
+ pa_the_ptr++;
+ while (quote && C && C != '"')
+ {
+ pa_the_ptr++;
+ }
+ if (pa_quote && C == '"')
+ {
+ pa_the_ptr++;
+ }
+ }
+ else
+ pa_the_ptr++;
+ quote = 0;
+ }
+ pa_the_ptr++;
+ rem = 1;
+ }
+ if (x == pa_the_ptr && C == pa_breakchar && pa_breakchar)
+ pa_the_ptr++;
+ }
+ if (x < pa_the_str.size())
+ {
+ pa_ord = pa_the_str.substr(x,pa_the_ptr - x - rem);
+ }
+ else
+ {
+ pa_ord = "";
+ }
+ return pa_ord;
+}
+
+void Parse::getword(std::string&s)
+{
+ s = Parse::getword();
+}
+
+void Parse::getsplit(std::string&s)
+{
+ Parse::getsplit();
+ s = pa_ord;
+}
+
+void Parse::getword(std::string&s,std::string&fill,int l)
+{
+ Parse::getword();
+ s = "";
+ while (s.size() + pa_ord.size() < (size_t)l)
+ s += fill;
+ s += pa_ord;
+}
+
+std::string Parse::getrest()
+{
+ std::string s;
+ while (C && (C == ' ' || C == 9 || issplit(C)))
+ pa_the_ptr++;
+ s = (pa_the_ptr < pa_the_str.size()) ? pa_the_str.substr(pa_the_ptr) : "";
+ return s;
+}
+
+void Parse::getrest(std::string&s)
+{
+ while (C && (C == ' ' || C == 9 || issplit(C)))
+ pa_the_ptr++;
+ s = (pa_the_ptr < pa_the_str.size()) ? pa_the_str.substr(pa_the_ptr) : "";
+}
+
+long Parse::getvalue()
+{
+ Parse::getword();
+ return atol(pa_ord.c_str());
+}
+
+void Parse::setbreak(const char c)
+{
+ pa_breakchar = c;
+}
+
+int Parse::getwordlen()
+{
+ size_t x,y = pa_the_ptr,len;
+
+ if (C == pa_breakchar && pa_breakchar)
+ {
+ x = pa_the_ptr++;
+ } else
+ {
+ while (C && (C == ' ' || C == 9 || C == 13 || C == 10 || issplit(C)))
+ pa_the_ptr++;
+ x = pa_the_ptr;
+ while (C && C != ' ' && C != 9 && C != 13 && C != 10 && !issplit(C) && (C != pa_breakchar || !pa_breakchar))
+ pa_the_ptr++;
+ }
+ if (x == pa_the_ptr && C == pa_breakchar && pa_breakchar)
+ pa_the_ptr++;
+ len = pa_the_ptr - x;
+ pa_the_ptr = y;
+ return (int)len;
+}
+
+int Parse::getrestlen()
+{
+ size_t y = pa_the_ptr;
+ size_t len;
+
+ while (C && (C == ' ' || C == 9 || issplit(C)))
+ pa_the_ptr++;
+ len = strlen(pa_the_str.c_str() + pa_the_ptr);
+ pa_the_ptr = y;
+ return (int)len;
+}
+
+void Parse::getline()
+{
+ size_t x;
+
+ x = pa_the_ptr;
+ while (C && C != 13 && C != 10)
+ pa_the_ptr++;
+ pa_ord = (x < pa_the_str.size()) ? pa_the_str.substr(x,pa_the_ptr - x) : "";
+ if (C == 13)
+ pa_the_ptr++;
+ if (C == 10)
+ pa_the_ptr++;
+}
+
+void Parse::getline(std::string&s)
+{
+ getline();
+ s = pa_ord;
+}
+
+/* end of implementation of class Parse */
+/***************************************************/
+#ifdef SOCKETS_NAMESPACE
+}
+#endif
+
+
diff --git a/externals/sockets/ResolvServer.cpp b/externals/sockets/ResolvServer.cpp
new file mode 100644
index 00000000000..3c8a7de6bc0
--- /dev/null
+++ b/externals/sockets/ResolvServer.cpp
@@ -0,0 +1,92 @@
+/** \file ResolvServer.cpp
+ ** \date 2005-03-24
+ ** \author grymse@alhem.net
+**/
+/*
+Copyright (C) 2004-2007 Anders Hedstrom
+
+This library is made available under the terms of the GNU GPL.
+
+If you would like to use this library in a closed-source application,
+a separate license agreement is available. For information about
+the closed-source license agreement for the C++ sockets library,
+please visit http://www.alhem.net/Sockets/license.html and/or
+email license@alhem.net.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+#ifdef _MSC_VER
+#pragma warning(disable:4786)
+#endif
+#include "ResolvServer.h"
+#ifdef ENABLE_RESOLVER
+#include "StdoutLog.h"
+#include "ListenSocket.h"
+#include "ResolvSocket.h"
+#include "SocketHandler.h"
+
+#ifdef SOCKETS_NAMESPACE
+namespace SOCKETS_NAMESPACE {
+#endif
+
+ResolvServer::ResolvServer(port_t port)
+:Thread()
+,m_quit(false)
+,m_port(port)
+,m_ready(false)
+{
+}
+
+ResolvServer::~ResolvServer()
+{
+}
+
+void ResolvServer::Run()
+{
+// StdoutLog log;
+ SocketHandler h;
+ ListenSocket<ResolvSocket> l(h);
+
+ if (l.Bind("127.0.0.1", m_port))
+ {
+ return;
+ }
+ h.Add(&l);
+
+ m_ready = true;
+ while (!m_quit && IsRunning() )
+ {
+ h.Select(0, 500000);
+ }
+ SetRunning(false);
+}
+
+void ResolvServer::Quit()
+{
+ m_quit = true;
+}
+
+bool ResolvServer::Ready()
+{
+ return m_ready;
+}
+
+#ifdef SOCKETS_NAMESPACE
+}
+#endif
+
+#endif // ENABLE_RESOLVER
+
+
diff --git a/externals/sockets/ResolvSocket.cpp b/externals/sockets/ResolvSocket.cpp
new file mode 100644
index 00000000000..636de276426
--- /dev/null
+++ b/externals/sockets/ResolvSocket.cpp
@@ -0,0 +1,426 @@
+/** \file ResolvSocket.cpp
+ ** \date 2005-03-24
+ ** \author grymse@alhem.net
+**/
+/*
+Copyright (C) 2004-2007 Anders Hedstrom
+
+This library is made available under the terms of the GNU GPL.
+
+If you would like to use this library in a closed-source application,
+a separate license agreement is available. For information about
+the closed-source license agreement for the C++ sockets library,
+please visit http://www.alhem.net/Sockets/license.html and/or
+email license@alhem.net.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+#ifdef _WIN32
+#ifdef _MSC_VER
+#pragma warning(disable:4786)
+#pragma warning(disable:4503)
+#endif
+#else
+#include <netdb.h>
+#endif
+#include "ResolvSocket.h"
+#ifdef ENABLE_RESOLVER
+#include "Utility.h"
+#include "Parse.h"
+#include "ISocketHandler.h"
+#include "Lock.h"
+#include "Mutex.h"
+
+#ifdef SOCKETS_NAMESPACE
+namespace SOCKETS_NAMESPACE {
+#endif
+
+//#ifdef _DEBUG
+//#define DEB(x) x
+//#else
+#define DEB(x)
+//#endif
+
+// static
+ResolvSocket::cache_t ResolvSocket::m_cache;
+ResolvSocket::timeout_t ResolvSocket::m_cache_to;
+Mutex ResolvSocket::m_cache_mutex;
+
+ResolvSocket::ResolvSocket(ISocketHandler& h)
+:TcpSocket(h)
+,m_bServer(false)
+,m_parent(NULL)
+#ifdef ENABLE_IPV6
+,m_resolve_ipv6(false)
+#endif
+,m_cached(false)
+{
+ SetLineProtocol();
+}
+
+ResolvSocket::ResolvSocket(ISocketHandler& h, Socket *parent, const std::string& host, port_t port, bool ipv6)
+:TcpSocket(h)
+,m_bServer(false)
+,m_parent(parent)
+,m_resolv_host(host)
+,m_resolv_port(port)
+#ifdef ENABLE_IPV6
+,m_resolve_ipv6(ipv6)
+#endif
+,m_cached(false)
+{
+ SetLineProtocol();
+}
+
+ResolvSocket::ResolvSocket(ISocketHandler& h, Socket *parent, ipaddr_t a)
+:TcpSocket(h)
+,m_bServer(false)
+,m_parent(parent)
+,m_resolv_port(0)
+,m_resolv_address(a)
+#ifdef ENABLE_IPV6
+,m_resolve_ipv6(false)
+#endif
+,m_cached(false)
+{
+ SetLineProtocol();
+}
+
+#ifdef ENABLE_IPV6
+ResolvSocket::ResolvSocket(ISocketHandler& h, Socket *parent, in6_addr& a)
+:TcpSocket(h)
+,m_bServer(false)
+,m_parent(parent)
+,m_resolv_port(0)
+,m_resolve_ipv6(true)
+,m_resolv_address6(a)
+,m_cached(false)
+{
+ SetLineProtocol();
+}
+#endif
+
+ResolvSocket::~ResolvSocket()
+{
+}
+
+void ResolvSocket::OnLine(const std::string& line)
+{
+ Parse pa(line, ":");
+ if (m_bServer)
+ {
+ m_query = pa.getword();
+ m_data = pa.getrest();
+DEB( fprintf(stderr, " *** ResolvSocket server; query=%s, data=%s\n", m_query.c_str(), m_data.c_str());)
+ // %! check cache
+ {
+ Lock lock(m_cache_mutex);
+ if (m_cache[m_query].find(m_data) != m_cache[m_query].end())
+ {
+ if (time(NULL) - m_cache_to[m_query][m_data] < 3600) // ttl
+ {
+ std::string result = m_cache[m_query][m_data];
+DEB(fprintf(stderr, " *** Returning cache for [%s][%s] = '%s'\n", m_query.c_str(), m_data.c_str(), result.c_str());)
+ Send("Cached\n");
+ if (!result.size()) /* failed */
+ {
+ Send("Failed\n\n");
+ SetCloseAndDelete();
+ return;
+ }
+ else
+ if (m_query == "gethostbyname")
+ {
+ Send("A: " + result + "\n\n");
+ SetCloseAndDelete();
+ return;
+ }
+ else
+#ifdef ENABLE_IPV6
+#ifdef IPPROTO_IPV6
+ if (m_query == "gethostbyname2")
+ {
+ Send("AAAA: " + result + "\n\n");
+ SetCloseAndDelete();
+ return;
+ }
+ else
+#endif
+#endif
+ if (m_query == "gethostbyaddr")
+ {
+ Send("Name: " + result + "\n\n");
+ SetCloseAndDelete();
+ return;
+ }
+ }
+ }
+ }
+ if (!Detach()) // detach failed?
+ {
+ SetCloseAndDelete();
+ }
+ return;
+ }
+ std::string key = pa.getword();
+ std::string value = pa.getrest();
+DEB( fprintf(stderr, " *** ResolvSocket response; %s: %s\n", key.c_str(), value.c_str());)
+
+ if (key == "Cached")
+ {
+ m_cached = true;
+ }
+ else
+ if (key == "Failed" && m_parent)
+ {
+DEB( fprintf(stderr, " ************ Resolve failed\n");)
+ if (Handler().Resolving(m_parent) || Handler().Valid(m_parent))
+ {
+ m_parent -> OnResolveFailed(m_resolv_id);
+ }
+ // update cache
+ if (!m_cached)
+ {
+ Lock lock(m_cache_mutex);
+DEB(fprintf(stderr, " *** Update cache for [%s][%s] = '%s'\n", m_query.c_str(), m_data.c_str(), value.c_str());)
+ m_cache[m_query][m_data] = value;
+ m_cache_to[m_query][m_data] = time(NULL);
+ }
+ m_parent = NULL;
+ }
+ else
+ if (key == "Name" && !m_resolv_host.size() && m_parent)
+ {
+ if (Handler().Resolving(m_parent) || Handler().Valid(m_parent))
+ {
+ m_parent -> OnReverseResolved(m_resolv_id, value);
+ }
+ // update cache
+ if (!m_cached)
+ {
+ Lock lock(m_cache_mutex);
+DEB(fprintf(stderr, " *** Update cache for [%s][%s] = '%s'\n", m_query.c_str(), m_data.c_str(), value.c_str());)
+ m_cache[m_query][m_data] = value;
+ m_cache_to[m_query][m_data] = time(NULL);
+ }
+ m_parent = NULL;
+ }
+ else
+ if (key == "A" && m_parent)
+ {
+ if (Handler().Resolving(m_parent) || Handler().Valid(m_parent))
+ {
+ ipaddr_t l;
+ Utility::u2ip(value, l); // ip2ipaddr_t
+ m_parent -> OnResolved(m_resolv_id, l, m_resolv_port);
+ }
+ // update cache
+ if (!m_cached)
+ {
+ Lock lock(m_cache_mutex);
+DEB(fprintf(stderr, " *** Update cache for [%s][%s] = '%s'\n", m_query.c_str(), m_data.c_str(), value.c_str());)
+ m_cache[m_query][m_data] = value;
+ m_cache_to[m_query][m_data] = time(NULL);
+ }
+ m_parent = NULL; // always use first ip in case there are several
+ }
+#ifdef ENABLE_IPV6
+#ifdef IPPROTO_IPV6
+ else
+ if (key == "AAAA" && m_parent)
+ {
+ if (Handler().Resolving(m_parent) || Handler().Valid(m_parent))
+ {
+ in6_addr a;
+ Utility::u2ip(value, a);
+ m_parent -> OnResolved(m_resolv_id, a, m_resolv_port);
+ }
+ // update cache
+ if (!m_cached)
+ {
+ Lock lock(m_cache_mutex);
+DEB(fprintf(stderr, " *** Update cache for [%s][%s] = '%s'\n", m_query.c_str(), m_data.c_str(), value.c_str());)
+ m_cache[m_query][m_data] = value;
+ m_cache_to[m_query][m_data] = time(NULL);
+ }
+ m_parent = NULL;
+ }
+#endif
+#endif
+}
+
+void ResolvSocket::OnDetached()
+{
+DEB( fprintf(stderr, " *** ResolvSocket::OnDetached(); query=%s, data=%s\n", m_query.c_str(), m_data.c_str());)
+ if (m_query == "gethostbyname")
+ {
+ struct sockaddr_in sa;
+ if (Utility::u2ip(m_data, sa))
+ {
+ std::string ip;
+ Utility::l2ip(sa.sin_addr, ip);
+ Send("A: " + ip + "\n");
+ }
+ else
+ {
+ Send("Failed\n");
+ }
+ Send("\n");
+ }
+ else
+#ifdef ENABLE_IPV6
+#ifdef IPPROTO_IPV6
+ if (m_query == "gethostbyname2")
+ {
+ struct sockaddr_in6 sa;
+ if (Utility::u2ip(m_data, sa))
+ {
+ std::string ip;
+ Utility::l2ip(sa.sin6_addr, ip);
+ Send("AAAA: " + ip + "\n");
+ }
+ else
+ {
+ Send("Failed\n");
+ }
+ Send("\n");
+ }
+ else
+#endif
+#endif
+ if (m_query == "gethostbyaddr")
+ {
+ if (Utility::isipv4( m_data ))
+ {
+ struct sockaddr_in sa;
+ if (!Utility::u2ip(m_data, sa, AI_NUMERICHOST))
+ {
+ Send("Failed: convert to sockaddr_in failed\n");
+ }
+ else
+ {
+ std::string name;
+ if (!Utility::reverse( (struct sockaddr *)&sa, sizeof(sa), name))
+ {
+ Send("Failed: ipv4 reverse lookup of " + m_data + "\n");
+ }
+ else
+ {
+ Send("Name: " + name + "\n");
+ }
+ }
+ }
+ else
+#ifdef ENABLE_IPV6
+#ifdef IPPROTO_IPV6
+ if (Utility::isipv6( m_data ))
+ {
+ struct sockaddr_in6 sa;
+ if (!Utility::u2ip(m_data, sa, AI_NUMERICHOST))
+ {
+ Send("Failed: convert to sockaddr_in6 failed\n");
+ }
+ else
+ {
+ std::string name;
+ if (!Utility::reverse( (struct sockaddr *)&sa, sizeof(sa), name))
+ {
+ Send("Failed: ipv6 reverse lookup of " + m_data + "\n");
+ }
+ else
+ {
+ Send("Name: " + name + "\n");
+ }
+ }
+ }
+ else
+#endif
+#endif
+ {
+ Send("Failed: malformed address\n");
+ }
+ Send("\n");
+ }
+ else
+ {
+ std::string msg = "Unknown query type: " + m_query;
+ Handler().LogError(this, "OnDetached", 0, msg);
+ Send("Unknown\n\n");
+ }
+ SetCloseAndDelete();
+}
+
+void ResolvSocket::OnConnect()
+{
+ if (!m_resolv_host.empty())
+ {
+#ifdef ENABLE_IPV6
+ std::string msg = (m_resolve_ipv6 ? "gethostbyname2 " : "gethostbyname ") + m_resolv_host + "\n";
+ m_query = m_resolve_ipv6 ? "gethostbyname2" : "gethostbyname";
+#else
+ std::string msg = "gethostbyname " + m_resolv_host + "\n";
+ m_query = "gethostbyname";
+#endif
+ m_data = m_resolv_host;
+ Send( msg );
+ return;
+ }
+#ifdef ENABLE_IPV6
+ if (m_resolve_ipv6)
+ {
+ std::string tmp;
+ Utility::l2ip(m_resolv_address6, tmp);
+ m_query = "gethostbyaddr";
+ m_data = tmp;
+ std::string msg = "gethostbyaddr " + tmp + "\n";
+ Send( msg );
+ }
+#endif
+ std::string tmp;
+ Utility::l2ip(m_resolv_address, tmp);
+ m_query = "gethostbyaddr";
+ m_data = tmp;
+ std::string msg = "gethostbyaddr " + tmp + "\n";
+ Send( msg );
+}
+
+void ResolvSocket::OnDelete()
+{
+ if (m_parent)
+ {
+ if (Handler().Resolving(m_parent) || Handler().Valid(m_parent))
+ {
+ m_parent -> OnResolveFailed(m_resolv_id);
+ }
+ // update cache
+ if (!m_cached)
+ {
+ Lock lock(m_cache_mutex);
+ std::string value;
+DEB(fprintf(stderr, " *** Update cache for [%s][%s] = '%s'\n", m_query.c_str(), m_data.c_str(), value.c_str());)
+ m_cache[m_query][m_data] = value;
+ m_cache_to[m_query][m_data] = time(NULL);
+ }
+ m_parent = NULL;
+ }
+}
+
+#ifdef SOCKETS_NAMESPACE
+}
+#endif
+
+#endif // ENABLE_RESOLVER
+
+
diff --git a/externals/sockets/Socket.cpp b/externals/sockets/Socket.cpp
new file mode 100644
index 00000000000..f53cd27621e
--- /dev/null
+++ b/externals/sockets/Socket.cpp
@@ -0,0 +1,1726 @@
+/** \file Socket.cpp
+ ** \date 2004-02-13
+ ** \author grymse@alhem.net
+**/
+/*
+Copyright (C) 2004-2007 Anders Hedstrom
+
+This library is made available under the terms of the GNU GPL.
+
+If you would like to use this library in a closed-source application,
+a separate license agreement is available. For information about
+the closed-source license agreement for the C++ sockets library,
+please visit http://www.alhem.net/Sockets/license.html and/or
+email license@alhem.net.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+#include "Socket.h"
+#ifdef _WIN32
+#ifdef _MSC_VER
+#pragma warning(disable:4786)
+#endif
+#include <stdlib.h>
+#else
+#include <errno.h>
+#include <netdb.h>
+#endif
+#include <ctype.h>
+#include <fcntl.h>
+
+#include "ISocketHandler.h"
+#include "Utility.h"
+
+#include "SocketAddress.h"
+#include "SocketHandler.h"
+#ifdef ENABLE_EXCEPTIONS
+#include "Exception.h"
+#endif
+#include "Ipv4Address.h"
+
+//#ifdef _DEBUG
+//#define DEB(x) x; fflush(stderr);
+//#else
+#define DEB(x)
+//#endif
+
+#ifdef SOCKETS_NAMESPACE
+namespace SOCKETS_NAMESPACE {
+#endif
+
+// statics
+#ifdef _WIN32
+WSAInitializer Socket::m_winsock_init;
+#endif
+
+Socket::Socket(ISocketHandler& h)
+//:m_flags(0)
+:m_handler(h)
+,m_socket( INVALID_SOCKET )
+,m_bDel(false)
+,m_bClose(false)
+,m_tCreate(time(NULL))
+,m_parent(NULL)
+,m_b_disable_read(false)
+,m_connected(false)
+,m_b_erased_by_handler(false)
+,m_tClose(0)
+,m_client_remote_address(NULL)
+,m_remote_address(NULL)
+,m_traffic_monitor(NULL)
+,m_bLost(false)
+#ifdef HAVE_OPENSSL
+,m_b_enable_ssl(false)
+,m_b_ssl(false)
+,m_b_ssl_server(false)
+#endif
+#ifdef ENABLE_IPV6
+,m_ipv6(false)
+#endif
+#ifdef ENABLE_POOL
+,m_socket_type(0)
+,m_bClient(false)
+,m_bRetain(false)
+#endif
+#ifdef ENABLE_SOCKS4
+,m_bSocks4(false)
+,m_socks4_host(h.GetSocks4Host())
+,m_socks4_port(h.GetSocks4Port())
+,m_socks4_userid(h.GetSocks4Userid())
+#endif
+#ifdef ENABLE_DETACH
+,m_detach(false)
+,m_detached(false)
+,m_pThread(NULL)
+,m_slave_handler(NULL)
+#endif
+{
+}
+
+Socket::~Socket()
+{
+ Handler().Remove(this);
+ if (m_socket != INVALID_SOCKET
+#ifdef ENABLE_POOL
+ && !m_bRetain
+#endif
+ )
+ {
+ Close();
+ }
+}
+
+void Socket::Init()
+{
+}
+
+void Socket::OnRead()
+{
+}
+
+void Socket::OnWrite()
+{
+}
+
+void Socket::OnException()
+{
+ // %! exception doesn't always mean something bad happened, this code should be reworked
+ // errno valid here?
+ int err = SoError();
+ Handler().LogError(this, "exception on select", err, StrError(err), LOG_LEVEL_FATAL);
+ SetCloseAndDelete();
+}
+
+void Socket::OnDelete()
+{
+}
+
+void Socket::OnConnect()
+{
+}
+
+void Socket::OnAccept()
+{
+}
+
+int Socket::Close()
+{
+ if (m_socket == INVALID_SOCKET) // this could happen
+ {
+ Handler().LogError(this, "Socket::Close", 0, "file descriptor invalid", LOG_LEVEL_WARNING);
+ return 0;
+ }
+ int n;
+ if ((n = closesocket(m_socket)) == -1)
+ {
+ // failed...
+ Handler().LogError(this, "close", Errno, StrError(Errno), LOG_LEVEL_ERROR);
+ }
+ Handler().Set(m_socket, false, false, false); // remove from fd_set's
+ Handler().AddList(m_socket, LIST_CALLONCONNECT, false);
+#ifdef ENABLE_DETACH
+ Handler().AddList(m_socket, LIST_DETACH, false);
+#endif
+ Handler().AddList(m_socket, LIST_TIMEOUT, false);
+ Handler().AddList(m_socket, LIST_RETRY, false);
+ Handler().AddList(m_socket, LIST_CLOSE, false);
+ m_socket = INVALID_SOCKET;
+ return n;
+}
+
+SOCKET Socket::CreateSocket(int af,int type, const std::string& protocol)
+{
+ struct protoent *p = NULL;
+ SOCKET s;
+
+#ifdef ENABLE_POOL
+ m_socket_type = type;
+ m_socket_protocol = protocol;
+#endif
+ if (!protocol.empty())
+ {
+ p = getprotobyname( protocol.c_str() );
+ if (!p)
+ {
+ Handler().LogError(this, "getprotobyname", Errno, StrError(Errno), LOG_LEVEL_FATAL);
+ SetCloseAndDelete();
+#ifdef ENABLE_EXCEPTIONS
+ throw Exception(std::string("getprotobyname() failed: ") + StrError(Errno));
+#endif
+ return INVALID_SOCKET;
+ }
+ }
+ int protno = p ? p -> p_proto : 0;
+
+ s = socket(af, type, protno);
+ if (s == INVALID_SOCKET)
+ {
+ Handler().LogError(this, "socket", Errno, StrError(Errno), LOG_LEVEL_FATAL);
+ SetCloseAndDelete();
+#ifdef ENABLE_EXCEPTIONS
+ throw Exception(std::string("socket() failed: ") + StrError(Errno));
+#endif
+ return INVALID_SOCKET;
+ }
+ Attach(s);
+ OnOptions(af, type, protno, s);
+ Attach(INVALID_SOCKET);
+ return s;
+}
+
+void Socket::Attach(SOCKET s)
+{
+ m_socket = s;
+}
+
+SOCKET Socket::GetSocket()
+{
+ return m_socket;
+}
+
+void Socket::SetDeleteByHandler(bool x)
+{
+ m_bDel = x;
+}
+
+bool Socket::DeleteByHandler()
+{
+ return m_bDel;
+}
+
+void Socket::SetCloseAndDelete(bool x)
+{
+ if (x != m_bClose)
+ {
+ Handler().AddList(m_socket, LIST_CLOSE, x);
+ m_bClose = x;
+ if (x)
+ {
+ m_tClose = time(NULL);
+ }
+ }
+}
+
+bool Socket::CloseAndDelete()
+{
+ return m_bClose;
+}
+
+void Socket::SetRemoteAddress(SocketAddress& ad) //struct sockaddr* sa, socklen_t l)
+{
+ m_remote_address = ad.GetCopy();
+}
+
+std::auto_ptr<SocketAddress> Socket::GetRemoteSocketAddress()
+{
+ return m_remote_address -> GetCopy();
+}
+
+ISocketHandler& Socket::Handler() const
+{
+#ifdef ENABLE_DETACH
+ if (IsDetached())
+ return *m_slave_handler;
+#endif
+ return m_handler;
+}
+
+ISocketHandler& Socket::MasterHandler() const
+{
+ return m_handler;
+}
+
+ipaddr_t Socket::GetRemoteIP4()
+{
+ ipaddr_t l = 0;
+#ifdef ENABLE_IPV6
+ if (m_ipv6)
+ {
+ Handler().LogError(this, "GetRemoteIP4", 0, "get ipv4 address for ipv6 socket", LOG_LEVEL_WARNING);
+ }
+#endif
+ if (m_remote_address.get() != NULL)
+ {
+ struct sockaddr *p = *m_remote_address;
+ struct sockaddr_in *sa = (struct sockaddr_in *)p;
+ memcpy(&l, &sa -> sin_addr, sizeof(struct in_addr));
+ }
+ return l;
+}
+
+#ifdef ENABLE_IPV6
+#ifdef IPPROTO_IPV6
+struct in6_addr Socket::GetRemoteIP6()
+{
+ if (!m_ipv6)
+ {
+ Handler().LogError(this, "GetRemoteIP6", 0, "get ipv6 address for ipv4 socket", LOG_LEVEL_WARNING);
+ }
+ struct sockaddr_in6 fail;
+ if (m_remote_address.get() != NULL)
+ {
+ struct sockaddr *p = *m_remote_address;
+ memcpy(&fail, p, sizeof(struct sockaddr_in6));
+ }
+ else
+ {
+ memset(&fail, 0, sizeof(struct sockaddr_in6));
+ }
+ return fail.sin6_addr;
+}
+#endif
+#endif
+
+port_t Socket::GetRemotePort()
+{
+ if (!m_remote_address.get())
+ {
+ return 0;
+ }
+ return m_remote_address -> GetPort();
+}
+
+std::string Socket::GetRemoteAddress()
+{
+ if (!m_remote_address.get())
+ {
+ return "";
+ }
+ return m_remote_address -> Convert(false);
+}
+
+std::string Socket::GetRemoteHostname()
+{
+ if (!m_remote_address.get())
+ {
+ return "";
+ }
+ return m_remote_address -> Reverse();
+}
+
+bool Socket::SetNonblocking(bool bNb)
+{
+#ifdef _WIN32
+ unsigned long l = bNb ? 1 : 0;
+ int n = ioctlsocket(m_socket, FIONBIO, &l);
+ if (n != 0)
+ {
+ Handler().LogError(this, "ioctlsocket(FIONBIO)", Errno, "");
+ return false;
+ }
+ return true;
+#else
+ if (bNb)
+ {
+ if (fcntl(m_socket, F_SETFL, O_NONBLOCK) == -1)
+ {
+ Handler().LogError(this, "fcntl(F_SETFL, O_NONBLOCK)", Errno, StrError(Errno), LOG_LEVEL_ERROR);
+ return false;
+ }
+ }
+ else
+ {
+ if (fcntl(m_socket, F_SETFL, 0) == -1)
+ {
+ Handler().LogError(this, "fcntl(F_SETFL, 0)", Errno, StrError(Errno), LOG_LEVEL_ERROR);
+ return false;
+ }
+ }
+ return true;
+#endif
+}
+
+bool Socket::SetNonblocking(bool bNb, SOCKET s)
+{
+#ifdef _WIN32
+ unsigned long l = bNb ? 1 : 0;
+ int n = ioctlsocket(s, FIONBIO, &l);
+ if (n != 0)
+ {
+ Handler().LogError(this, "ioctlsocket(FIONBIO)", Errno, "");
+ return false;
+ }
+ return true;
+#else
+ if (bNb)
+ {
+ if (fcntl(s, F_SETFL, O_NONBLOCK) == -1)
+ {
+ Handler().LogError(this, "fcntl(F_SETFL, O_NONBLOCK)", Errno, StrError(Errno), LOG_LEVEL_ERROR);
+ return false;
+ }
+ }
+ else
+ {
+ if (fcntl(s, F_SETFL, 0) == -1)
+ {
+ Handler().LogError(this, "fcntl(F_SETFL, 0)", Errno, StrError(Errno), LOG_LEVEL_ERROR);
+ return false;
+ }
+ }
+ return true;
+#endif
+}
+
+void Socket::Set(bool bRead, bool bWrite, bool bException)
+{
+ Handler().Set(m_socket, bRead, bWrite, bException);
+}
+
+bool Socket::Ready()
+{
+ if (m_socket != INVALID_SOCKET && !CloseAndDelete())
+ return true;
+ return false;
+}
+
+void Socket::OnLine(const std::string& )
+{
+}
+
+void Socket::OnConnectFailed()
+{
+}
+
+Socket *Socket::GetParent()
+{
+ return m_parent;
+}
+
+void Socket::SetParent(Socket *x)
+{
+ m_parent = x;
+}
+
+port_t Socket::GetPort()
+{
+ Handler().LogError(this, "GetPort", 0, "GetPort only implemented for ListenSocket", LOG_LEVEL_WARNING);
+ return 0;
+}
+
+bool Socket::OnConnectRetry()
+{
+ return true;
+}
+
+#ifdef ENABLE_RECONNECT
+void Socket::OnReconnect()
+{
+}
+#endif
+
+time_t Socket::Uptime()
+{
+ return time(NULL) - m_tCreate;
+}
+
+#ifdef ENABLE_IPV6
+void Socket::SetIpv6(bool x)
+{
+ m_ipv6 = x;
+}
+
+bool Socket::IsIpv6()
+{
+ return m_ipv6;
+}
+#endif
+
+void Socket::DisableRead(bool x)
+{
+ m_b_disable_read = x;
+}
+
+bool Socket::IsDisableRead()
+{
+ return m_b_disable_read;
+}
+
+void Socket::SendBuf(const char *,size_t,int)
+{
+}
+
+void Socket::Send(const std::string&,int)
+{
+}
+
+void Socket::SetConnected(bool x)
+{
+ m_connected = x;
+}
+
+bool Socket::IsConnected()
+{
+ return m_connected;
+}
+
+void Socket::OnDisconnect()
+{
+}
+
+void Socket::SetLost()
+{
+ m_bLost = true;
+}
+
+bool Socket::Lost()
+{
+ return m_bLost;
+}
+
+void Socket::SetErasedByHandler(bool x)
+{
+ m_b_erased_by_handler = x;
+}
+
+bool Socket::ErasedByHandler()
+{
+ return m_b_erased_by_handler;
+}
+
+time_t Socket::TimeSinceClose()
+{
+ return time(NULL) - m_tClose;
+}
+
+void Socket::SetClientRemoteAddress(SocketAddress& ad)
+{
+ if (!ad.IsValid())
+ {
+ Handler().LogError(this, "SetClientRemoteAddress", 0, "remote address not valid", LOG_LEVEL_ERROR);
+ }
+ m_client_remote_address = ad.GetCopy();
+}
+
+std::auto_ptr<SocketAddress> Socket::GetClientRemoteAddress()
+{
+ if (!m_client_remote_address.get())
+ {
+ Handler().LogError(this, "GetClientRemoteAddress", 0, "remote address not yet set", LOG_LEVEL_ERROR);
+ }
+ return m_client_remote_address -> GetCopy();
+}
+
+uint64_t Socket::GetBytesSent(bool)
+{
+ return 0;
+}
+
+uint64_t Socket::GetBytesReceived(bool)
+{
+ return 0;
+}
+
+#ifdef HAVE_OPENSSL
+void Socket::OnSSLConnect()
+{
+}
+
+void Socket::OnSSLAccept()
+{
+}
+
+bool Socket::SSLNegotiate()
+{
+ return false;
+}
+
+bool Socket::IsSSL()
+{
+ return m_b_enable_ssl;
+}
+
+void Socket::EnableSSL(bool x)
+{
+ m_b_enable_ssl = x;
+}
+
+bool Socket::IsSSLNegotiate()
+{
+ return m_b_ssl;
+}
+
+void Socket::SetSSLNegotiate(bool x)
+{
+ m_b_ssl = x;
+}
+
+bool Socket::IsSSLServer()
+{
+ return m_b_ssl_server;
+}
+
+void Socket::SetSSLServer(bool x)
+{
+ m_b_ssl_server = x;
+}
+
+void Socket::OnSSLConnectFailed()
+{
+}
+
+void Socket::OnSSLAcceptFailed()
+{
+}
+#endif // HAVE_OPENSSL
+
+#ifdef ENABLE_POOL
+void Socket::CopyConnection(Socket *sock)
+{
+ Attach( sock -> GetSocket() );
+#ifdef ENABLE_IPV6
+ SetIpv6( sock -> IsIpv6() );
+#endif
+ SetSocketType( sock -> GetSocketType() );
+ SetSocketProtocol( sock -> GetSocketProtocol() );
+
+ SetClientRemoteAddress( *sock -> GetClientRemoteAddress() );
+ SetRemoteAddress( *sock -> GetRemoteSocketAddress() );
+}
+
+void Socket::SetIsClient()
+{
+ m_bClient = true;
+}
+
+void Socket::SetSocketType(int x)
+{
+ m_socket_type = x;
+}
+
+int Socket::GetSocketType()
+{
+ return m_socket_type;
+}
+
+void Socket::SetSocketProtocol(const std::string& x)
+{
+ m_socket_protocol = x;
+}
+
+const std::string& Socket::GetSocketProtocol()
+{
+ return m_socket_protocol;
+}
+
+void Socket::SetRetain()
+{
+ if (m_bClient) m_bRetain = true;
+}
+
+bool Socket::Retain()
+{
+ return m_bRetain;
+}
+
+#endif // ENABLE_POOL
+
+#ifdef ENABLE_SOCKS4
+void Socket::OnSocks4Connect()
+{
+ Handler().LogError(this, "OnSocks4Connect", 0, "Use with TcpSocket only");
+}
+
+void Socket::OnSocks4ConnectFailed()
+{
+ Handler().LogError(this, "OnSocks4ConnectFailed", 0, "Use with TcpSocket only");
+}
+
+bool Socket::OnSocks4Read()
+{
+ Handler().LogError(this, "OnSocks4Read", 0, "Use with TcpSocket only");
+ return true;
+}
+
+void Socket::SetSocks4Host(const std::string& host)
+{
+ Utility::u2ip(host, m_socks4_host);
+}
+
+bool Socket::Socks4()
+{
+ return m_bSocks4;
+}
+
+void Socket::SetSocks4(bool x)
+{
+ m_bSocks4 = x;
+}
+
+void Socket::SetSocks4Host(ipaddr_t a)
+{
+ m_socks4_host = a;
+}
+
+void Socket::SetSocks4Port(port_t p)
+{
+ m_socks4_port = p;
+}
+
+void Socket::SetSocks4Userid(const std::string& x)
+{
+ m_socks4_userid = x;
+}
+
+ipaddr_t Socket::GetSocks4Host()
+{
+ return m_socks4_host;
+}
+
+port_t Socket::GetSocks4Port()
+{
+ return m_socks4_port;
+}
+
+const std::string& Socket::GetSocks4Userid()
+{
+ return m_socks4_userid;
+}
+#endif // ENABLE_SOCKS4
+
+#ifdef ENABLE_DETACH
+bool Socket::Detach()
+{
+ if (!DeleteByHandler())
+ return false;
+ if (m_pThread)
+ return false;
+ if (m_detached)
+ return false;
+ SetDetach();
+ return true;
+}
+
+void Socket::DetachSocket()
+{
+ SetDetached();
+ m_pThread = new SocketThread(this);
+ m_pThread -> SetRelease(true);
+}
+
+void Socket::OnDetached()
+{
+}
+
+void Socket::SetDetach(bool x)
+{
+ Handler().AddList(m_socket, LIST_DETACH, x);
+ m_detach = x;
+}
+
+bool Socket::IsDetach()
+{
+ return m_detach;
+}
+
+void Socket::SetDetached(bool x)
+{
+ m_detached = x;
+}
+
+const bool Socket::IsDetached() const
+{
+ return m_detached;
+}
+
+void Socket::SetSlaveHandler(ISocketHandler *p)
+{
+ m_slave_handler = p;
+}
+
+Socket::SocketThread::SocketThread(Socket *p)
+:Thread(false)
+,m_socket(p)
+{
+ // Creator will release
+}
+
+Socket::SocketThread::~SocketThread()
+{
+ if (IsRunning())
+ {
+ SetRelease(true);
+ SetRunning(false);
+#ifdef _WIN32
+ Sleep(1000);
+#else
+ sleep(1);
+#endif
+ }
+}
+
+void Socket::SocketThread::Run()
+{
+ SocketHandler h;
+ h.SetSlave();
+ h.Add(m_socket);
+ m_socket -> SetSlaveHandler(&h);
+ m_socket -> OnDetached();
+ while (h.GetCount() && IsRunning())
+ {
+ h.Select(0, 500000);
+ }
+ // m_socket now deleted oops
+ // yeah oops m_socket delete its socket thread, that means this
+ // so Socket will no longer delete its socket thread, instead we do this:
+ SetDeleteOnExit();
+}
+#endif // ENABLE_DETACH
+
+#ifdef ENABLE_RESOLVER
+int Socket::Resolve(const std::string& host,port_t port)
+{
+ return Handler().Resolve(this, host, port);
+}
+
+#ifdef ENABLE_IPV6
+int Socket::Resolve6(const std::string& host,port_t port)
+{
+ return Handler().Resolve6(this, host, port);
+}
+#endif
+
+int Socket::Resolve(ipaddr_t a)
+{
+ return Handler().Resolve(this, a);
+}
+
+#ifdef ENABLE_IPV6
+int Socket::Resolve(in6_addr& a)
+{
+ return Handler().Resolve(this, a);
+}
+#endif
+
+void Socket::OnResolved(int,ipaddr_t,port_t)
+{
+}
+
+#ifdef ENABLE_IPV6
+void Socket::OnResolved(int,in6_addr&,port_t)
+{
+}
+#endif
+
+void Socket::OnReverseResolved(int,const std::string&)
+{
+}
+
+void Socket::OnResolveFailed(int)
+{
+}
+#endif // ENABLE_RESOLVER
+
+/* IP options */
+
+bool Socket::SetIpOptions(const void *p, socklen_t len)
+{
+#ifdef IP_OPTIONS
+ if (setsockopt(GetSocket(), IPPROTO_IP, IP_OPTIONS, (char *)p, len) == -1)
+ {
+ Handler().LogError(this, "setsockopt(IPPROTO_IP, IP_OPTIONS)", Errno, StrError(Errno), LOG_LEVEL_FATAL);
+ return false;
+ }
+ return true;
+#else
+ Handler().LogError(this, "ip option not available", 0, "IP_OPTIONS", LOG_LEVEL_INFO);
+ return false;
+#endif
+}
+
+#ifdef IP_PKTINFO
+bool Socket::SetIpPktinfo(bool x)
+{
+ int optval = x ? 1 : 0;
+ if (setsockopt(GetSocket(), IPPROTO_IP, IP_PKTINFO, (char *)&optval, sizeof(optval)) == -1)
+ {
+ Handler().LogError(this, "setsockopt(IPPROTO_IP, IP_PKTINFO)", Errno, StrError(Errno), LOG_LEVEL_FATAL);
+ return false;
+ }
+ return true;
+}
+#endif
+
+#ifdef IP_RECVTOS
+bool Socket::SetIpRecvTOS(bool x)
+{
+ int optval = x ? 1 : 0;
+ if (setsockopt(GetSocket(), IPPROTO_IP, IP_RECVTOS, (char *)&optval, sizeof(optval)) == -1)
+ {
+ Handler().LogError(this, "setsockopt(IPPROTO_IP, IP_RECVTOS)", Errno, StrError(Errno), LOG_LEVEL_FATAL);
+ return false;
+ }
+ return true;
+}
+#endif
+
+#ifdef IP_RECVTTL
+bool Socket::SetIpRecvTTL(bool x)
+{
+ int optval = x ? 1 : 0;
+ if (setsockopt(GetSocket(), IPPROTO_IP, IP_RECVTTL, (char *)&optval, sizeof(optval)) == -1)
+ {
+ Handler().LogError(this, "setsockopt(IPPROTO_IP, IP_RECVTTL)", Errno, StrError(Errno), LOG_LEVEL_FATAL);
+ return false;
+ }
+ return true;
+}
+#endif
+
+#ifdef IP_RECVOPTS
+bool Socket::SetIpRecvopts(bool x)
+{
+ int optval = x ? 1 : 0;
+ if (setsockopt(GetSocket(), IPPROTO_IP, IP_RECVOPTS, (char *)&optval, sizeof(optval)) == -1)
+ {
+ Handler().LogError(this, "setsockopt(IPPROTO_IP, IP_RECVOPTS)", Errno, StrError(Errno), LOG_LEVEL_FATAL);
+ return false;
+ }
+ return true;
+}
+#endif
+
+#ifdef IP_RETOPTS
+bool Socket::SetIpRetopts(bool x)
+{
+ int optval = x ? 1 : 0;
+ if (setsockopt(GetSocket(), IPPROTO_IP, IP_RETOPTS, (char *)&optval, sizeof(optval)) == -1)
+ {
+ Handler().LogError(this, "setsockopt(IPPROTO_IP, IP_RETOPTS)", Errno, StrError(Errno), LOG_LEVEL_FATAL);
+ return false;
+ }
+ return true;
+}
+#endif
+
+bool Socket::SetIpTOS(unsigned char tos)
+{
+#ifdef IP_TOS
+ if (setsockopt(GetSocket(), IPPROTO_IP, IP_TOS, (char *)&tos, sizeof(tos)) == -1)
+ {
+ Handler().LogError(this, "setsockopt(IPPROTO_IP, IP_TOS)", Errno, StrError(Errno), LOG_LEVEL_FATAL);
+ return false;
+ }
+ return true;
+#else
+ Handler().LogError(this, "ip option not available", 0, "IP_TOS", LOG_LEVEL_INFO);
+ return false;
+#endif
+}
+
+unsigned char Socket::IpTOS()
+{
+ unsigned char tos = 0;
+#ifdef IP_TOS
+ socklen_t len = sizeof(tos);
+ if (getsockopt(GetSocket(), IPPROTO_IP, IP_TOS, (char *)&tos, &len) == -1)
+ {
+ Handler().LogError(this, "getsockopt(IPPROTO_IP, IP_TOS)", Errno, StrError(Errno), LOG_LEVEL_FATAL);
+ }
+#else
+ Handler().LogError(this, "ip option not available", 0, "IP_TOS", LOG_LEVEL_INFO);
+#endif
+ return tos;
+}
+
+bool Socket::SetIpTTL(int ttl)
+{
+#ifdef IP_TTL
+ if (setsockopt(GetSocket(), IPPROTO_IP, IP_TTL, (char *)&ttl, sizeof(ttl)) == -1)
+ {
+ Handler().LogError(this, "setsockopt(IPPROTO_IP, IP_TTL)", Errno, StrError(Errno), LOG_LEVEL_FATAL);
+ return false;
+ }
+ return true;
+#else
+ Handler().LogError(this, "ip option not available", 0, "IP_TTL", LOG_LEVEL_INFO);
+ return false;
+#endif
+}
+
+int Socket::IpTTL()
+{
+ int ttl = 0;
+#ifdef IP_TTL
+ socklen_t len = sizeof(ttl);
+ if (getsockopt(GetSocket(), IPPROTO_IP, IP_TTL, (char *)&ttl, &len) == -1)
+ {
+ Handler().LogError(this, "getsockopt(IPPROTO_IP, IP_TTL)", Errno, StrError(Errno), LOG_LEVEL_FATAL);
+ }
+#else
+ Handler().LogError(this, "ip option not available", 0, "IP_TTL", LOG_LEVEL_INFO);
+#endif
+ return ttl;
+}
+
+bool Socket::SetIpHdrincl(bool x)
+{
+#ifdef IP_HDRINCL
+ int optval = x ? 1 : 0;
+ if (setsockopt(GetSocket(), IPPROTO_IP, IP_HDRINCL, (char *)&optval, sizeof(optval)) == -1)
+ {
+ Handler().LogError(this, "setsockopt(IPPROTO_IP, IP_HDRINCL)", Errno, StrError(Errno), LOG_LEVEL_FATAL);
+ return false;
+ }
+ return true;
+#else
+ Handler().LogError(this, "ip option not available", 0, "IP_HDRINCL", LOG_LEVEL_INFO);
+ return false;
+#endif
+}
+
+#ifdef IP_RECVERR
+bool Socket::SetIpRecverr(bool x)
+{
+ int optval = x ? 1 : 0;
+ if (setsockopt(GetSocket(), IPPROTO_IP, IP_RECVERR, (char *)&optval, sizeof(optval)) == -1)
+ {
+ Handler().LogError(this, "setsockopt(IPPROTO_IP, IP_RECVERR)", Errno, StrError(Errno), LOG_LEVEL_FATAL);
+ return false;
+ }
+ return true;
+}
+#endif
+
+#ifdef IP_MTU_DISCOVER
+bool Socket::SetIpMtudiscover(bool x)
+{
+ int optval = x ? 1 : 0;
+ if (setsockopt(GetSocket(), IPPROTO_IP, IP_MTU_DISCOVER, (char *)&optval, sizeof(optval)) == -1)
+ {
+ Handler().LogError(this, "setsockopt(IPPROTO_IP, IP_MTU_DISCOVER)", Errno, StrError(Errno), LOG_LEVEL_FATAL);
+ return false;
+ }
+ return true;
+}
+#endif
+
+#ifdef IP_MTU
+int Socket::IpMtu()
+{
+ int mtu = 0;
+ socklen_t len = sizeof(mtu);
+ if (getsockopt(GetSocket(), IPPROTO_IP, IP_MTU, (char *)&mtu, &len) == -1)
+ {
+ Handler().LogError(this, "getsockopt(IPPROTO_IP, IP_MTU)", Errno, StrError(Errno), LOG_LEVEL_FATAL);
+ }
+ return mtu;
+}
+#endif
+
+#ifdef IP_ROUTER_ALERT
+bool Socket::SetIpRouterAlert(bool x)
+{
+ int optval = x ? 1 : 0;
+ if (setsockopt(GetSocket(), IPPROTO_IP, IP_ROUTER_ALERT, (char *)&optval, sizeof(optval)) == -1)
+ {
+ Handler().LogError(this, "setsockopt(IPPROTO_IP, IP_ROUTER_ALERT)", Errno, StrError(Errno), LOG_LEVEL_FATAL);
+ return false;
+ }
+ return true;
+}
+#endif
+
+bool Socket::SetIpMulticastTTL(int ttl)
+{
+#ifdef IP_MULTICAST_TTL
+ if (setsockopt(GetSocket(), IPPROTO_IP, IP_MULTICAST_TTL, (char *)&ttl, sizeof(ttl)) == -1)
+ {
+ Handler().LogError(this, "setsockopt(IPPROTO_IP, IP_MULTICAST_TTL)", Errno, StrError(Errno), LOG_LEVEL_FATAL);
+ return false;
+ }
+ return true;
+#else
+ Handler().LogError(this, "ip option not available", 0, "IP_MULTICAST_TTL", LOG_LEVEL_INFO);
+ return false;
+#endif
+}
+
+int Socket::IpMulticastTTL()
+{
+ int ttl = 0;
+#ifdef IP_MULTICAST_TTL
+ socklen_t len = sizeof(ttl);
+ if (getsockopt(GetSocket(), IPPROTO_IP, IP_MULTICAST_TTL, (char *)&ttl, &len) == -1)
+ {
+ Handler().LogError(this, "getsockopt(IPPROTO_IP, IP_MULTICAST_TTL)", Errno, StrError(Errno), LOG_LEVEL_FATAL);
+ }
+#else
+ Handler().LogError(this, "ip option not available", 0, "IP_MULTICAST_TTL", LOG_LEVEL_INFO);
+#endif
+ return ttl;
+}
+
+bool Socket::SetMulticastLoop(bool x)
+{
+#ifdef IP_MULTICAST_LOOP
+ int optval = x ? 1 : 0;
+ if (setsockopt(GetSocket(), IPPROTO_IP, IP_MULTICAST_LOOP, (char *)&optval, sizeof(optval)) == -1)
+ {
+ Handler().LogError(this, "setsockopt(IPPROTO_IP, IP_MULTICAST_LOOP)", Errno, StrError(Errno), LOG_LEVEL_FATAL);
+ return false;
+ }
+ return true;
+#else
+ Handler().LogError(this, "ip option not available", 0, "IP_MULTICAST_LOOP", LOG_LEVEL_INFO);
+ return false;
+#endif
+}
+
+#ifdef LINUX
+bool Socket::IpAddMembership(struct ip_mreqn& ref)
+{
+#ifdef IP_ADD_MEMBERSHIP
+ if (setsockopt(GetSocket(), IPPROTO_IP, IP_ADD_MEMBERSHIP, (char *)&ref, sizeof(struct ip_mreqn)) == -1)
+ {
+ Handler().LogError(this, "setsockopt(IPPROTO_IP, IP_ADD_MEMBERSHIP)", Errno, StrError(Errno), LOG_LEVEL_FATAL);
+ return false;
+ }
+ return true;
+#else
+ Handler().LogError(this, "ip option not available", 0, "IP_ADD_MEMBERSHIP", LOG_LEVEL_INFO);
+ return false;
+#endif
+}
+#endif
+
+bool Socket::IpAddMembership(struct ip_mreq& ref)
+{
+#ifdef IP_ADD_MEMBERSHIP
+ if (setsockopt(GetSocket(), IPPROTO_IP, IP_ADD_MEMBERSHIP, (char *)&ref, sizeof(struct ip_mreq)) == -1)
+ {
+ Handler().LogError(this, "setsockopt(IPPROTO_IP, IP_ADD_MEMBERSHIP)", Errno, StrError(Errno), LOG_LEVEL_FATAL);
+ return false;
+ }
+ return true;
+#else
+ Handler().LogError(this, "ip option not available", 0, "IP_ADD_MEMBERSHIP", LOG_LEVEL_INFO);
+ return false;
+#endif
+}
+
+#ifdef LINUX
+bool Socket::IpDropMembership(struct ip_mreqn& ref)
+{
+#ifdef IP_DROP_MEMBERSHIP
+ if (setsockopt(GetSocket(), IPPROTO_IP, IP_DROP_MEMBERSHIP, (char *)&ref, sizeof(struct ip_mreqn)) == -1)
+ {
+ Handler().LogError(this, "setsockopt(IPPROTO_IP, IP_DROP_MEMBERSHIP)", Errno, StrError(Errno), LOG_LEVEL_FATAL);
+ return false;
+ }
+ return true;
+#else
+ Handler().LogError(this, "ip option not available", 0, "IP_DROP_MEMBERSHIP", LOG_LEVEL_INFO);
+ return false;
+#endif
+}
+#endif
+
+bool Socket::IpDropMembership(struct ip_mreq& ref)
+{
+#ifdef IP_DROP_MEMBERSHIP
+ if (setsockopt(GetSocket(), IPPROTO_IP, IP_DROP_MEMBERSHIP, (char *)&ref, sizeof(struct ip_mreq)) == -1)
+ {
+ Handler().LogError(this, "setsockopt(IPPROTO_IP, IP_DROP_MEMBERSHIP)", Errno, StrError(Errno), LOG_LEVEL_FATAL);
+ return false;
+ }
+ return true;
+#else
+ Handler().LogError(this, "ip option not available", 0, "IP_DROP_MEMBERSHIP", LOG_LEVEL_INFO);
+ return false;
+#endif
+}
+
+/* SOCKET options */
+
+bool Socket::SetSoReuseaddr(bool x)
+{
+#ifdef SO_REUSEADDR
+ int optval = x ? 1 : 0;
+ if (setsockopt(GetSocket(), SOL_SOCKET, SO_REUSEADDR, (char *)&optval, sizeof(optval)) == -1)
+ {
+ Handler().LogError(this, "setsockopt(SOL_SOCKET, SO_REUSEADDR)", Errno, StrError(Errno), LOG_LEVEL_FATAL);
+ return false;
+ }
+ return true;
+#else
+ Handler().LogError(this, "socket option not available", 0, "SO_REUSEADDR", LOG_LEVEL_INFO);
+ return false;
+#endif
+}
+
+bool Socket::SetSoKeepalive(bool x)
+{
+#ifdef SO_KEEPALIVE
+ int optval = x ? 1 : 0;
+ if (setsockopt(GetSocket(), SOL_SOCKET, SO_KEEPALIVE, (char *)&optval, sizeof(optval)) == -1)
+ {
+ Handler().LogError(this, "setsockopt(SOL_SOCKET, SO_KEEPALIVE)", Errno, StrError(Errno), LOG_LEVEL_FATAL);
+ return false;
+ }
+ return true;
+#else
+ Handler().LogError(this, "socket option not available", 0, "SO_KEEPALIVE", LOG_LEVEL_INFO);
+ return false;
+#endif
+}
+
+#ifdef SO_NOSIGPIPE
+bool Socket::SetSoNosigpipe(bool x)
+{
+ int optval = x ? 1 : 0;
+ if (setsockopt(GetSocket(), SOL_SOCKET, SO_NOSIGPIPE, (char *)&optval, sizeof(optval)) == -1)
+ {
+ Handler().LogError(this, "setsockopt(SOL_SOCKET, SO_NOSIGPIPE)", Errno, StrError(Errno), LOG_LEVEL_FATAL);
+ return false;
+ }
+ return true;
+}
+#endif
+
+bool Socket::SoAcceptconn()
+{
+ int value = 0;
+#ifdef SO_ACCEPTCONN
+ socklen_t len = sizeof(value);
+ if (getsockopt(GetSocket(), SOL_SOCKET, SO_ACCEPTCONN, (char *)&value, &len) == -1)
+ {
+ Handler().LogError(this, "getsockopt(SOL_SOCKET, SO_ACCEPTCONN)", Errno, StrError(Errno), LOG_LEVEL_FATAL);
+ }
+#else
+ Handler().LogError(this, "socket option not available", 0, "SO_ACCEPTCONN", LOG_LEVEL_INFO);
+#endif
+ return value ? true : false;
+}
+
+#ifdef SO_BSDCOMPAT
+bool Socket::SetSoBsdcompat(bool x)
+{
+ int optval = x ? 1 : 0;
+ if (setsockopt(GetSocket(), SOL_SOCKET, SO_BSDCOMPAT, (char *)&optval, sizeof(optval)) == -1)
+ {
+ Handler().LogError(this, "setsockopt(SOL_SOCKET, SO_BSDCOMPAT)", Errno, StrError(Errno), LOG_LEVEL_FATAL);
+ return false;
+ }
+ return true;
+}
+#endif
+
+#ifdef SO_BINDTODEVICE
+bool Socket::SetSoBindtodevice(const std::string& intf)
+{
+ if (setsockopt(GetSocket(), SOL_SOCKET, SO_BINDTODEVICE, (char *)intf.c_str(), intf.size()) == -1)
+ {
+ Handler().LogError(this, "setsockopt(SOL_SOCKET, SO_BINDTODEVICE)", Errno, StrError(Errno), LOG_LEVEL_FATAL);
+ return false;
+ }
+ return true;
+}
+#endif
+
+bool Socket::SetSoBroadcast(bool x)
+{
+#ifdef SO_BROADCAST
+ int optval = x ? 1 : 0;
+ if (setsockopt(GetSocket(), SOL_SOCKET, SO_BROADCAST, (char *)&optval, sizeof(optval)) == -1)
+ {
+ Handler().LogError(this, "setsockopt(SOL_SOCKET, SO_BROADCAST)", Errno, StrError(Errno), LOG_LEVEL_FATAL);
+ return false;
+ }
+ return true;
+#else
+ Handler().LogError(this, "socket option not available", 0, "SO_BROADCAST", LOG_LEVEL_INFO);
+ return false;
+#endif
+}
+
+bool Socket::SetSoDebug(bool x)
+{
+#ifdef SO_DEBUG
+ int optval = x ? 1 : 0;
+ if (setsockopt(GetSocket(), SOL_SOCKET, SO_DEBUG, (char *)&optval, sizeof(optval)) == -1)
+ {
+ Handler().LogError(this, "setsockopt(SOL_SOCKET, SO_DEBUG)", Errno, StrError(Errno), LOG_LEVEL_FATAL);
+ return false;
+ }
+ return true;
+#else
+ Handler().LogError(this, "socket option not available", 0, "SO_DEBUG", LOG_LEVEL_INFO);
+ return false;
+#endif
+}
+
+int Socket::SoError()
+{
+ int value = 0;
+#ifdef SO_ERROR
+ socklen_t len = sizeof(value);
+ if (getsockopt(GetSocket(), SOL_SOCKET, SO_ERROR, (char *)&value, &len) == -1)
+ {
+ Handler().LogError(this, "getsockopt(SOL_SOCKET, SO_ERROR)", Errno, StrError(Errno), LOG_LEVEL_FATAL);
+ }
+#else
+ Handler().LogError(this, "socket option not available", 0, "SO_ERROR", LOG_LEVEL_INFO);
+#endif
+ return value;
+}
+
+bool Socket::SetSoDontroute(bool x)
+{
+#ifdef SO_DONTROUTE
+ int optval = x ? 1 : 0;
+ if (setsockopt(GetSocket(), SOL_SOCKET, SO_DONTROUTE, (char *)&optval, sizeof(optval)) == -1)
+ {
+ Handler().LogError(this, "setsockopt(SOL_SOCKET, SO_DONTROUTE)", Errno, StrError(Errno), LOG_LEVEL_FATAL);
+ return false;
+ }
+ return true;
+#else
+ Handler().LogError(this, "socket option not available", 0, "SO_DONTROUTE", LOG_LEVEL_INFO);
+ return false;
+#endif
+}
+
+bool Socket::SetSoLinger(int onoff, int linger)
+{
+#ifdef SO_LINGER
+ struct linger stl;
+ stl.l_onoff = onoff;
+ stl.l_linger = linger;
+ if (setsockopt(GetSocket(), SOL_SOCKET, SO_LINGER, (char *)&stl, sizeof(stl)) == -1)
+ {
+ Handler().LogError(this, "setsockopt(SOL_SOCKET, SO_LINGER)", Errno, StrError(Errno), LOG_LEVEL_FATAL);
+ return false;
+ }
+ return true;
+#else
+ Handler().LogError(this, "socket option not available", 0, "SO_LINGER", LOG_LEVEL_INFO);
+ return false;
+#endif
+}
+
+bool Socket::SetSoOobinline(bool x)
+{
+#ifdef SO_OOBINLINE
+ int optval = x ? 1 : 0;
+ if (setsockopt(GetSocket(), SOL_SOCKET, SO_OOBINLINE, (char *)&optval, sizeof(optval)) == -1)
+ {
+ Handler().LogError(this, "setsockopt(SOL_SOCKET, SO_OOBINLINE)", Errno, StrError(Errno), LOG_LEVEL_FATAL);
+ return false;
+ }
+ return true;
+#else
+ Handler().LogError(this, "socket option not available", 0, "SO_OOBINLINE", LOG_LEVEL_INFO);
+ return false;
+#endif
+}
+
+#ifdef SO_PASSCRED
+bool Socket::SetSoPasscred(bool x)
+{
+ int optval = x ? 1 : 0;
+ if (setsockopt(GetSocket(), SOL_SOCKET, SO_PASSCRED, (char *)&optval, sizeof(optval)) == -1)
+ {
+ Handler().LogError(this, "setsockopt(SOL_SOCKET, SO_PASSCRED)", Errno, StrError(Errno), LOG_LEVEL_FATAL);
+ return false;
+ }
+ return true;
+}
+#endif
+
+#ifdef SO_PEERCRED
+bool Socket::SoPeercred(struct ucred& ucr)
+{
+ if (setsockopt(GetSocket(), SOL_SOCKET, SO_PEERCRED, (char *)&ucr, sizeof(ucr)) == -1)
+ {
+ Handler().LogError(this, "setsockopt(SOL_SOCKET, SO_PEERCRED)", Errno, StrError(Errno), LOG_LEVEL_FATAL);
+ return false;
+ }
+ return true;
+}
+#endif
+
+#ifdef SO_PRIORITY
+bool Socket::SetSoPriority(int x)
+{
+ if (setsockopt(GetSocket(), SOL_SOCKET, SO_PRIORITY, (char *)&x, sizeof(x)) == -1)
+ {
+ Handler().LogError(this, "setsockopt(SOL_SOCKET, SO_PRIORITY)", Errno, StrError(Errno), LOG_LEVEL_FATAL);
+ return false;
+ }
+ return true;
+}
+#endif
+
+bool Socket::SetSoRcvlowat(int x)
+{
+#ifdef SO_RCVLOWAT
+ if (setsockopt(GetSocket(), SOL_SOCKET, SO_RCVLOWAT, (char *)&x, sizeof(x)) == -1)
+ {
+ Handler().LogError(this, "setsockopt(SOL_SOCKET, SO_RCVLOWAT)", Errno, StrError(Errno), LOG_LEVEL_FATAL);
+ return false;
+ }
+ return true;
+#else
+ Handler().LogError(this, "socket option not available", 0, "SO_RCVLOWAT", LOG_LEVEL_INFO);
+ return false;
+#endif
+}
+
+bool Socket::SetSoSndlowat(int x)
+{
+#ifdef SO_SNDLOWAT
+ if (setsockopt(GetSocket(), SOL_SOCKET, SO_SNDLOWAT, (char *)&x, sizeof(x)) == -1)
+ {
+ Handler().LogError(this, "setsockopt(SOL_SOCKET, SO_SNDLOWAT)", Errno, StrError(Errno), LOG_LEVEL_FATAL);
+ return false;
+ }
+ return true;
+#else
+ Handler().LogError(this, "socket option not available", 0, "SO_SNDLOWAT", LOG_LEVEL_INFO);
+ return false;
+#endif
+}
+
+bool Socket::SetSoRcvtimeo(struct timeval& tv)
+{
+#ifdef SO_RCVTIMEO
+ if (setsockopt(GetSocket(), SOL_SOCKET, SO_RCVTIMEO, (char *)&tv, sizeof(tv)) == -1)
+ {
+ Handler().LogError(this, "setsockopt(SOL_SOCKET, SO_RCVTIMEO)", Errno, StrError(Errno), LOG_LEVEL_FATAL);
+ return false;
+ }
+ return true;
+#else
+ Handler().LogError(this, "socket option not available", 0, "SO_RCVTIMEO", LOG_LEVEL_INFO);
+ return false;
+#endif
+}
+
+bool Socket::SetSoSndtimeo(struct timeval& tv)
+{
+#ifdef SO_SNDTIMEO
+ if (setsockopt(GetSocket(), SOL_SOCKET, SO_SNDTIMEO, (char *)&tv, sizeof(tv)) == -1)
+ {
+ Handler().LogError(this, "setsockopt(SOL_SOCKET, SO_SNDTIMEO)", Errno, StrError(Errno), LOG_LEVEL_FATAL);
+ return false;
+ }
+ return true;
+#else
+ Handler().LogError(this, "socket option not available", 0, "SO_SNDTIMEO", LOG_LEVEL_INFO);
+ return false;
+#endif
+}
+
+bool Socket::SetSoRcvbuf(int x)
+{
+#ifdef SO_RCVBUF
+ if (setsockopt(GetSocket(), SOL_SOCKET, SO_RCVBUF, (char *)&x, sizeof(x)) == -1)
+ {
+ Handler().LogError(this, "setsockopt(SOL_SOCKET, SO_RCVBUF)", Errno, StrError(Errno), LOG_LEVEL_FATAL);
+ return false;
+ }
+ return true;
+#else
+ Handler().LogError(this, "socket option not available", 0, "SO_RCVBUF", LOG_LEVEL_INFO);
+ return false;
+#endif
+}
+
+int Socket::SoRcvbuf()
+{
+ int value = 0;
+#ifdef SO_RCVBUF
+ socklen_t len = sizeof(value);
+ if (getsockopt(GetSocket(), SOL_SOCKET, SO_RCVBUF, (char *)&value, &len) == -1)
+ {
+ Handler().LogError(this, "getsockopt(SOL_SOCKET, SO_RCVBUF)", Errno, StrError(Errno), LOG_LEVEL_FATAL);
+ }
+#else
+ Handler().LogError(this, "socket option not available", 0, "SO_RCVBUF", LOG_LEVEL_INFO);
+#endif
+ return value;
+}
+
+#ifdef SO_RCVBUFFORCE
+bool Socket::SetSoRcvbufforce(int x)
+{
+ if (setsockopt(GetSocket(), SOL_SOCKET, SO_RCVBUFFORCE, (char *)&x, sizeof(x)) == -1)
+ {
+ Handler().LogError(this, "setsockopt(SOL_SOCKET, SO_RCVBUFFORCE)", Errno, StrError(Errno), LOG_LEVEL_FATAL);
+ return false;
+ }
+ return true;
+}
+#endif
+
+bool Socket::SetSoSndbuf(int x)
+{
+#ifdef SO_SNDBUF
+ if (setsockopt(GetSocket(), SOL_SOCKET, SO_SNDBUF, (char *)&x, sizeof(x)) == -1)
+ {
+ Handler().LogError(this, "setsockopt(SOL_SOCKET, SO_SNDBUF)", Errno, StrError(Errno), LOG_LEVEL_FATAL);
+ return false;
+ }
+ return true;
+#else
+ Handler().LogError(this, "socket option not available", 0, "SO_SNDBUF", LOG_LEVEL_INFO);
+ return false;
+#endif
+}
+
+int Socket::SoSndbuf()
+{
+ int value = 0;
+#ifdef SO_SNDBUF
+ socklen_t len = sizeof(value);
+ if (getsockopt(GetSocket(), SOL_SOCKET, SO_SNDBUF, (char *)&value, &len) == -1)
+ {
+ Handler().LogError(this, "getsockopt(SOL_SOCKET, SO_SNDBUF)", Errno, StrError(Errno), LOG_LEVEL_FATAL);
+ }
+#else
+ Handler().LogError(this, "socket option not available", 0, "SO_SNDBUF", LOG_LEVEL_INFO);
+#endif
+ return value;
+}
+
+#ifdef SO_SNDBUFFORCE
+bool Socket::SetSoSndbufforce(int x)
+{
+ if (setsockopt(GetSocket(), SOL_SOCKET, SO_SNDBUFFORCE, (char *)&x, sizeof(x)) == -1)
+ {
+ Handler().LogError(this, "setsockopt(SOL_SOCKET, SO_SNDBUFFORCE)", Errno, StrError(Errno), LOG_LEVEL_FATAL);
+ return false;
+ }
+ return true;
+}
+#endif
+
+#ifdef SO_TIMESTAMP
+bool Socket::SetSoTimestamp(bool x)
+{
+ int optval = x ? 1 : 0;
+ if (setsockopt(GetSocket(), SOL_SOCKET, SO_TIMESTAMP, (char *)&optval, sizeof(optval)) == -1)
+ {
+ Handler().LogError(this, "setsockopt(SOL_SOCKET, SO_TIMESTAMP)", Errno, StrError(Errno), LOG_LEVEL_FATAL);
+ return false;
+ }
+ return true;
+}
+#endif
+
+int Socket::SoType()
+{
+ int value = 0;
+#ifdef SO_TYPE
+ socklen_t len = sizeof(value);
+ if (getsockopt(GetSocket(), SOL_SOCKET, SO_TYPE, (char *)&value, &len) == -1)
+ {
+ Handler().LogError(this, "getsockopt(SOL_SOCKET, SO_TYPE)", Errno, StrError(Errno), LOG_LEVEL_FATAL);
+ }
+#else
+ Handler().LogError(this, "socket option not available", 0, "SO_TYPE", LOG_LEVEL_INFO);
+#endif
+ return value;
+}
+
+#ifdef ENABLE_TRIGGERS
+void Socket::Subscribe(int id)
+{
+ Handler().Subscribe(id, this);
+}
+
+void Socket::Unsubscribe(int id)
+{
+ Handler().Unsubscribe(id, this);
+}
+
+void Socket::OnTrigger(int, const TriggerData&)
+{
+}
+
+void Socket::OnCancelled(int)
+{
+}
+#endif
+
+void Socket::SetTimeout(time_t secs)
+{
+ if (!secs)
+ {
+ Handler().AddList(m_socket, LIST_TIMEOUT, false);
+ return;
+ }
+ Handler().AddList(m_socket, LIST_TIMEOUT, true);
+ m_timeout_start = time(NULL);
+ m_timeout_limit = secs;
+}
+
+void Socket::OnTimeout()
+{
+}
+
+void Socket::OnConnectTimeout()
+{
+}
+
+bool Socket::Timeout(time_t tnow)
+{
+ if (tnow - m_timeout_start > m_timeout_limit)
+ return true;
+ return false;
+}
+
+/** Returns local port number for bound socket file descriptor. */
+port_t Socket::GetSockPort()
+{
+#ifdef ENABLE_IPV6
+#ifdef IPPROTO_IPV6
+ if (IsIpv6())
+ {
+ struct sockaddr_in6 sa;
+ socklen_t sockaddr_length = sizeof(struct sockaddr_in6);
+ if (getsockname(GetSocket(), (struct sockaddr *)&sa, (socklen_t*)&sockaddr_length) == -1)
+ memset(&sa, 0, sizeof(sa));
+ return ntohs(sa.sin6_port);
+ }
+#endif
+#endif
+ struct sockaddr_in sa;
+ socklen_t sockaddr_length = sizeof(struct sockaddr_in);
+ if (getsockname(GetSocket(), (struct sockaddr *)&sa, (socklen_t*)&sockaddr_length) == -1)
+ memset(&sa, 0, sizeof(sa));
+ return ntohs(sa.sin_port);
+}
+
+/** Returns local ipv4 address for bound socket file descriptor. */
+ipaddr_t Socket::GetSockIP4()
+{
+#ifdef ENABLE_IPV6
+#ifdef IPPROTO_IPV6
+ if (IsIpv6())
+ {
+ return 0;
+ }
+#endif
+#endif
+ struct sockaddr_in sa;
+ socklen_t sockaddr_length = sizeof(struct sockaddr_in);
+ if (getsockname(GetSocket(), (struct sockaddr *)&sa, (socklen_t*)&sockaddr_length) == -1)
+ memset(&sa, 0, sizeof(sa));
+ ipaddr_t a;
+ memcpy(&a, &sa.sin_addr, 4);
+ return a;
+}
+
+/** Returns local ipv4 address as text for bound socket file descriptor. */
+std::string Socket::GetSockAddress()
+{
+#ifdef ENABLE_IPV6
+#ifdef IPPROTO_IPV6
+ if (IsIpv6())
+ {
+ return "";
+ }
+#endif
+#endif
+ struct sockaddr_in sa;
+ socklen_t sockaddr_length = sizeof(struct sockaddr_in);
+ if (getsockname(GetSocket(), (struct sockaddr *)&sa, (socklen_t*)&sockaddr_length) == -1)
+ memset(&sa, 0, sizeof(sa));
+ Ipv4Address addr( sa );
+ return addr.Convert();
+}
+
+#ifdef ENABLE_IPV6
+#ifdef IPPROTO_IPV6
+/** Returns local ipv6 address for bound socket file descriptor. */
+struct in6_addr Socket::GetSockIP6()
+{
+ if (IsIpv6())
+ {
+ struct sockaddr_in6 sa;
+ socklen_t sockaddr_length = sizeof(struct sockaddr_in6);
+ if (getsockname(GetSocket(), (struct sockaddr *)&sa, (socklen_t*)&sockaddr_length) == -1)
+ memset(&sa, 0, sizeof(sa));
+ return sa.sin6_addr;
+ }
+ struct in6_addr a;
+ memset(&a, 0, sizeof(a));
+ return a;
+}
+
+/** Returns local ipv6 address as text for bound socket file descriptor. */
+std::string Socket::GetSockAddress6()
+{
+ if (IsIpv6())
+ {
+ struct sockaddr_in6 sa;
+ socklen_t sockaddr_length = sizeof(struct sockaddr_in6);
+ if (getsockname(GetSocket(), (struct sockaddr *)&sa, (socklen_t*)&sockaddr_length) == -1)
+ memset(&sa, 0, sizeof(sa));
+ Ipv6Address addr( sa );
+ return addr.Convert();
+ }
+ return "";
+}
+#endif
+#endif
+
+#ifdef SOCKETS_NAMESPACE
+}
+#endif
+
+
diff --git a/externals/sockets/SocketHandler.cpp b/externals/sockets/SocketHandler.cpp
new file mode 100644
index 00000000000..acf71fb2efa
--- /dev/null
+++ b/externals/sockets/SocketHandler.cpp
@@ -0,0 +1,1377 @@
+/** \file SocketHandler.cpp
+ ** \date 2004-02-13
+ ** \author grymse@alhem.net
+**/
+/*
+Copyright (C) 2004-2007 Anders Hedstrom
+
+This library is made available under the terms of the GNU GPL.
+
+If you would like to use this library in a closed-source application,
+a separate license agreement is available. For information about
+the closed-source license agreement for the C++ sockets library,
+please visit http://www.alhem.net/Sockets/license.html and/or
+email license@alhem.net.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+#ifdef _WIN32
+#ifdef _MSC_VER
+#pragma warning(disable:4786)
+#endif
+#endif
+#include <stdlib.h>
+#include <stdio.h>
+#include <errno.h>
+#include <cstdio>
+
+#include "SocketHandler.h"
+#include "UdpSocket.h"
+#include "ResolvSocket.h"
+#include "ResolvServer.h"
+#include "TcpSocket.h"
+#include "Mutex.h"
+#include "Utility.h"
+#include "SocketAddress.h"
+
+#ifdef SOCKETS_NAMESPACE
+namespace SOCKETS_NAMESPACE {
+#endif
+
+//#ifdef _DEBUG
+//#define DEB(x) x; fflush(stderr);
+//#else
+#define DEB(x)
+//#endif
+
+SocketHandler::SocketHandler(StdLog *p)
+:m_stdlog(p)
+,m_mutex(m_mutex)
+,m_b_use_mutex(false)
+,m_maxsock(0)
+,m_preverror(-1)
+,m_errcnt(0)
+,m_tlast(0)
+#ifdef ENABLE_SOCKS4
+,m_socks4_host(0)
+,m_socks4_port(0)
+,m_bTryDirect(false)
+#endif
+#ifdef ENABLE_RESOLVER
+,m_resolv_id(0)
+,m_resolver(NULL)
+#endif
+#ifdef ENABLE_POOL
+,m_b_enable_pool(false)
+#endif
+#ifdef ENABLE_TRIGGERS
+,m_next_trigger_id(0)
+#endif
+#ifdef ENABLE_DETACH
+,m_slave(false)
+#endif
+{
+ FD_ZERO(&m_rfds);
+ FD_ZERO(&m_wfds);
+ FD_ZERO(&m_efds);
+}
+
+SocketHandler::SocketHandler(Mutex& mutex,StdLog *p)
+:m_stdlog(p)
+,m_mutex(mutex)
+,m_b_use_mutex(true)
+,m_maxsock(0)
+,m_preverror(-1)
+,m_errcnt(0)
+,m_tlast(0)
+#ifdef ENABLE_SOCKS4
+,m_socks4_host(0)
+,m_socks4_port(0)
+,m_bTryDirect(false)
+#endif
+#ifdef ENABLE_RESOLVER
+,m_resolv_id(0)
+,m_resolver(NULL)
+#endif
+#ifdef ENABLE_POOL
+,m_b_enable_pool(false)
+#endif
+#ifdef ENABLE_TRIGGERS
+,m_next_trigger_id(0)
+#endif
+#ifdef ENABLE_DETACH
+,m_slave(false)
+#endif
+{
+ m_mutex.Lock();
+ FD_ZERO(&m_rfds);
+ FD_ZERO(&m_wfds);
+ FD_ZERO(&m_efds);
+}
+
+SocketHandler::~SocketHandler()
+{
+#ifdef ENABLE_RESOLVER
+ if (m_resolver)
+ {
+ m_resolver -> Quit();
+ }
+#endif
+ {
+ while (m_sockets.size())
+ {
+DEB( fprintf(stderr, "Emptying sockets list in SocketHandler destructor, %d instances\n", (int)m_sockets.size());)
+ socket_m::iterator it = m_sockets.begin();
+ Socket *p = it -> second;
+ if (p)
+ {
+DEB( fprintf(stderr, " fd %d\n", p -> GetSocket());)
+ p -> Close();
+DEB( fprintf(stderr, " fd closed %d\n", p -> GetSocket());)
+// p -> OnDelete(); // hey, I turn this back on. what's the worst that could happen??!!
+ // MinionSocket breaks, calling MinderHandler methods in OnDelete -
+ // MinderHandler is already gone when that happens...
+
+ // only delete socket when controlled
+ // ie master sockethandler can delete non-detached sockets
+ // and a slave sockethandler can only delete a detach socket
+ if (p -> DeleteByHandler()
+#ifdef ENABLE_DETACH
+ && !(m_slave ^ p -> IsDetached())
+#endif
+ )
+ {
+ p -> SetErasedByHandler();
+ delete p;
+ }
+ m_sockets.erase(it);
+ }
+ else
+ {
+ m_sockets.erase(it);
+ }
+DEB( fprintf(stderr, "next\n");)
+ }
+DEB( fprintf(stderr, "/Emptying sockets list in SocketHandler destructor, %d instances\n", (int)m_sockets.size());)
+ }
+#ifdef ENABLE_RESOLVER
+ if (m_resolver)
+ {
+ delete m_resolver;
+ }
+#endif
+ if (m_b_use_mutex)
+ {
+ m_mutex.Unlock();
+ }
+}
+
+Mutex& SocketHandler::GetMutex() const
+{
+ return m_mutex;
+}
+
+#ifdef ENABLE_DETACH
+void SocketHandler::SetSlave(bool x)
+{
+ m_slave = x;
+}
+
+bool SocketHandler::IsSlave()
+{
+ return m_slave;
+}
+#endif
+
+void SocketHandler::RegStdLog(StdLog *log)
+{
+ m_stdlog = log;
+}
+
+void SocketHandler::LogError(Socket *p,const std::string& user_text,int err,const std::string& sys_err,loglevel_t t)
+{
+ if (m_stdlog)
+ {
+ m_stdlog -> error(this, p, user_text, err, sys_err, t);
+ }
+}
+
+void SocketHandler::Add(Socket *p)
+{
+ if (p -> GetSocket() == INVALID_SOCKET)
+ {
+ LogError(p, "Add", -1, "Invalid socket", LOG_LEVEL_WARNING);
+ if (p -> CloseAndDelete())
+ {
+ m_delete.push_back(p);
+ }
+ return;
+ }
+ if (m_add.find(p -> GetSocket()) != m_add.end())
+ {
+ LogError(p, "Add", (int)p -> GetSocket(), "Attempt to add socket already in add queue", LOG_LEVEL_FATAL);
+ m_delete.push_back(p);
+ return;
+ }
+ m_add[p -> GetSocket()] = p;
+}
+
+void SocketHandler::Get(SOCKET s,bool& r,bool& w,bool& e)
+{
+ if (s >= 0)
+ {
+ r = FD_ISSET(s, &m_rfds) ? true : false;
+ w = FD_ISSET(s, &m_wfds) ? true : false;
+ e = FD_ISSET(s, &m_efds) ? true : false;
+ }
+}
+
+void SocketHandler::Set(SOCKET s,bool bRead,bool bWrite,bool bException)
+{
+DEB( fprintf(stderr, "Set(%d, %s, %s, %s)\n", s, bRead ? "true" : "false", bWrite ? "true" : "false", bException ? "true" : "false");)
+ if (s >= 0)
+ {
+ if (bRead)
+ {
+ if (!FD_ISSET(s, &m_rfds))
+ {
+ FD_SET(s, &m_rfds);
+ }
+ }
+ else
+ {
+ FD_CLR(s, &m_rfds);
+ }
+ if (bWrite)
+ {
+ if (!FD_ISSET(s, &m_wfds))
+ {
+ FD_SET(s, &m_wfds);
+ }
+ }
+ else
+ {
+ FD_CLR(s, &m_wfds);
+ }
+ if (bException)
+ {
+ if (!FD_ISSET(s, &m_efds))
+ {
+ FD_SET(s, &m_efds);
+ }
+ }
+ else
+ {
+ FD_CLR(s, &m_efds);
+ }
+ }
+}
+
+int SocketHandler::Select(long sec,long usec)
+{
+ struct timeval tv;
+ tv.tv_sec = sec;
+ tv.tv_usec = usec;
+ return Select(&tv);
+}
+
+int SocketHandler::Select()
+{
+ if (!m_fds_callonconnect.empty() ||
+#ifdef ENABLE_DETACH
+ (!m_slave && !m_fds_detach.empty()) ||
+#endif
+ !m_fds_timeout.empty() ||
+ !m_fds_retry.empty() ||
+ !m_fds_close.empty() ||
+ !m_fds_erase.empty())
+ {
+ return Select(0, 200000);
+ }
+ return Select(NULL);
+}
+
+int SocketHandler::Select(struct timeval *tsel)
+{
+ size_t ignore = 0;
+ while (m_add.size() > ignore)
+ {
+ if (m_sockets.size() >= FD_SETSIZE)
+ {
+ LogError(NULL, "Select", (int)m_sockets.size(), "FD_SETSIZE reached", LOG_LEVEL_WARNING);
+ break;
+ }
+ socket_m::iterator it = m_add.begin();
+ SOCKET s = it -> first;
+ Socket *p = it -> second;
+DEB( fprintf(stderr, "Trying to add fd %d, m_add.size() %d, ignore %d\n", (int)s, (int)m_add.size(), (int)ignore);)
+ //
+ if (m_sockets.find(p -> GetSocket()) != m_sockets.end())
+ {
+ LogError(p, "Add", (int)p -> GetSocket(), "Attempt to add socket already in controlled queue", LOG_LEVEL_FATAL);
+ // %! it's a dup, don't add to delete queue, just ignore it
+ m_delete.push_back(p);
+ m_add.erase(it);
+// ignore++;
+ continue;
+ }
+ if (!p -> CloseAndDelete())
+ {
+ StreamSocket *scp = dynamic_cast<StreamSocket *>(p);
+ if (scp && scp -> Connecting()) // 'Open' called before adding socket
+ {
+ Set(s,false,true);
+ }
+ else
+ {
+ TcpSocket *tcp = dynamic_cast<TcpSocket *>(p);
+ bool bWrite = tcp ? tcp -> GetOutputLength() != 0 : false;
+ if (p -> IsDisableRead())
+ {
+ Set(s, false, bWrite);
+ }
+ else
+ {
+ Set(s, true, bWrite);
+ }
+ }
+ m_maxsock = (s > m_maxsock) ? s : m_maxsock;
+ }
+ else
+ {
+ LogError(p, "Add", (int)p -> GetSocket(), "Trying to add socket with SetCloseAndDelete() true", LOG_LEVEL_WARNING);
+ }
+ // only add to m_fds (process fd_set events) if
+ // slave handler and detached/detaching socket
+ // master handler and non-detached socket
+#ifdef ENABLE_DETACH
+ if (!(m_slave ^ p -> IsDetach()))
+#endif
+ {
+ m_fds.push_back(s);
+ }
+ m_sockets[s] = p;
+ //
+ m_add.erase(it);
+ }
+#ifdef MACOSX
+ fd_set rfds;
+ fd_set wfds;
+ fd_set efds;
+ FD_COPY(&m_rfds, &rfds);
+ FD_COPY(&m_wfds, &wfds);
+ FD_COPY(&m_efds, &efds);
+#else
+ fd_set rfds = m_rfds;
+ fd_set wfds = m_wfds;
+ fd_set efds = m_efds;
+#endif
+ int n;
+ if (m_b_use_mutex)
+ {
+ m_mutex.Unlock();
+ n = select( (int)(m_maxsock + 1),&rfds,&wfds,&efds,tsel);
+ m_mutex.Lock();
+ }
+ else
+ {
+ n = select( (int)(m_maxsock + 1),&rfds,&wfds,&efds,tsel);
+ }
+ if (n == -1)
+ {
+ /*
+ EBADF An invalid file descriptor was given in one of the sets.
+ EINTR A non blocked signal was caught.
+ EINVAL n is negative. Or struct timeval contains bad time values (<0).
+ ENOMEM select was unable to allocate memory for internal tables.
+ */
+ if (Errno != m_preverror || m_errcnt++ % 10000 == 0)
+ {
+ LogError(NULL, "select", Errno, StrError(Errno));
+DEB( fprintf(stderr, "m_maxsock: %d\n", m_maxsock);
+ fprintf(stderr, "%s\n", Errno == EINVAL ? "EINVAL" :
+ Errno == EINTR ? "EINTR" :
+ Errno == EBADF ? "EBADF" :
+ Errno == ENOMEM ? "ENOMEM" : "<another>");
+ // test bad fd
+ for (SOCKET i = 0; i <= m_maxsock; i++)
+ {
+ bool t = false;
+ FD_ZERO(&rfds);
+ FD_ZERO(&wfds);
+ FD_ZERO(&efds);
+ if (FD_ISSET(i, &m_rfds))
+ {
+ FD_SET(i, &rfds);
+ t = true;
+ }
+ if (FD_ISSET(i, &m_wfds))
+ {
+ FD_SET(i, &wfds);
+ t = true;
+ }
+ if (FD_ISSET(i, &m_efds))
+ {
+ FD_SET(i, &efds);
+ t = true;
+ }
+ if (t && m_sockets.find(i) == m_sockets.end())
+ {
+ fprintf(stderr, "Bad fd in fd_set: %d\n", i);
+ }
+ }
+) // DEB
+ m_preverror = Errno;
+ }
+ /// \todo rebuild fd_set's from active sockets list (m_sockets) here
+ }
+ else
+ if (!n)
+ {
+ m_preverror = -1;
+ }
+ else
+ if (n > 0)
+ {
+ for (socket_v::iterator it2 = m_fds.begin(); it2 != m_fds.end() && n; it2++)
+ {
+ SOCKET i = *it2;
+ if (FD_ISSET(i, &rfds))
+ {
+ socket_m::iterator itmp = m_sockets.find(i);
+ if (itmp != m_sockets.end()) // found
+ {
+ Socket *p = itmp -> second;
+ // new SSL negotiate method
+#ifdef HAVE_OPENSSL
+ if (p -> IsSSLNegotiate())
+ {
+ p -> SSLNegotiate();
+ }
+ else
+#endif
+ {
+ p -> OnRead();
+ }
+ }
+ else
+ {
+ LogError(NULL, "GetSocket/handler/1", (int)i, "Did not find expected socket using file descriptor", LOG_LEVEL_WARNING);
+ }
+ n--;
+ }
+ if (FD_ISSET(i, &wfds))
+ {
+ socket_m::iterator itmp = m_sockets.find(i);
+ if (itmp != m_sockets.end()) // found
+ {
+ Socket *p = itmp -> second;
+ // new SSL negotiate method
+#ifdef HAVE_OPENSSL
+ if (p -> IsSSLNegotiate())
+ {
+ p -> SSLNegotiate();
+ }
+ else
+#endif
+ {
+ p -> OnWrite();
+ }
+ }
+ else
+ {
+ LogError(NULL, "GetSocket/handler/2", (int)i, "Did not find expected socket using file descriptor", LOG_LEVEL_WARNING);
+ }
+ n--;
+ }
+ if (FD_ISSET(i, &efds))
+ {
+ socket_m::iterator itmp = m_sockets.find(i);
+ if (itmp != m_sockets.end()) // found
+ {
+ Socket *p = itmp -> second;
+ p -> OnException();
+ }
+ else
+ {
+ LogError(NULL, "GetSocket/handler/3", (int)i, "Did not find expected socket using file descriptor", LOG_LEVEL_WARNING);
+ }
+ n--;
+ }
+ } // m_fds loop
+ m_preverror = -1;
+ } // if (n > 0)
+
+ // check CallOnConnect - EVENT
+ if (!m_fds_callonconnect.empty())
+ {
+ socket_v tmp = m_fds_callonconnect;
+ for (socket_v::iterator it = tmp.begin(); it != tmp.end(); it++)
+ {
+ Socket *p = NULL;
+ {
+ socket_m::iterator itmp = m_sockets.find(*it);
+ if (itmp != m_sockets.end()) // found
+ {
+ p = itmp -> second;
+ }
+ else
+ {
+ LogError(NULL, "GetSocket/handler/4", (int)*it, "Did not find expected socket using file descriptor", LOG_LEVEL_WARNING);
+ }
+ }
+ if (p)
+ {
+// if (p -> CallOnConnect() && p -> Ready() )
+ {
+ p -> SetConnected(); // moved here from inside if (tcp) check below
+#ifdef HAVE_OPENSSL
+ if (p -> IsSSL()) // SSL Enabled socket
+ p -> OnSSLConnect();
+ else
+#endif
+#ifdef ENABLE_SOCKS4
+ if (p -> Socks4())
+ p -> OnSocks4Connect();
+ else
+#endif
+ {
+ TcpSocket *tcp = dynamic_cast<TcpSocket *>(p);
+ if (tcp)
+ {
+ if (tcp -> GetOutputLength())
+ {
+ p -> OnWrite();
+ }
+ }
+#ifdef ENABLE_RECONNECT
+ if (tcp && tcp -> IsReconnect())
+ p -> OnReconnect();
+ else
+#endif
+ {
+// LogError(p, "Calling OnConnect", 0, "Because CallOnConnect", LOG_LEVEL_INFO);
+ p -> OnConnect();
+ }
+ }
+// p -> SetCallOnConnect( false );
+ AddList(p -> GetSocket(), LIST_CALLONCONNECT, false);
+ }
+ }
+ }
+ }
+#ifdef ENABLE_DETACH
+ // check detach of socket if master handler - EVENT
+ if (!m_slave && !m_fds_detach.empty())
+ {
+ // %! why not using tmp list here??!?
+ for (socket_v::iterator it = m_fds_detach.begin(); it != m_fds_detach.end(); it++)
+ {
+ Socket *p = NULL;
+ {
+ socket_m::iterator itmp = m_sockets.find(*it);
+ if (itmp != m_sockets.end()) // found
+ {
+ p = itmp -> second;
+ }
+ else
+ {
+ LogError(NULL, "GetSocket/handler/5", (int)*it, "Did not find expected socket using file descriptor", LOG_LEVEL_WARNING);
+ }
+ }
+ if (p)
+ {
+// if (p -> IsDetach())
+ {
+ Set(p -> GetSocket(), false, false, false);
+ // After DetachSocket(), all calls to Handler() will return a reference
+ // to the new slave SocketHandler running in the new thread.
+ p -> DetachSocket();
+ // Adding the file descriptor to m_fds_erase will now also remove the
+ // socket from the detach queue - tnx knightmad
+ m_fds_erase.push_back(p -> GetSocket());
+ }
+ }
+ }
+ }
+#endif
+ // check Connecting - connection timeout - conditional event
+ if (m_fds_timeout.size())
+ {
+ time_t tnow = time(NULL);
+ if (tnow != m_tlast)
+ {
+ socket_v tmp = m_fds_timeout;
+DEB( fprintf(stderr, "Checking %d socket(s) for timeout\n", tmp.size());)
+ for (socket_v::iterator it = tmp.begin(); it != tmp.end(); it++)
+ {
+ Socket *p = NULL;
+ {
+ socket_m::iterator itmp = m_sockets.find(*it);
+ if (itmp != m_sockets.end()) // found
+ {
+ p = itmp -> second;
+ }
+ else
+ {
+ itmp = m_add.find(*it);
+ if (itmp != m_add.end())
+ {
+ p = itmp -> second;
+ }
+ else
+ {
+ LogError(NULL, "GetSocket/handler/6", (int)*it, "Did not find expected socket using file descriptor", LOG_LEVEL_WARNING);
+ }
+ }
+ }
+ if (p)
+ {
+ if (p -> Timeout(tnow))
+ {
+ StreamSocket *scp = dynamic_cast<StreamSocket *>(p);
+ if (scp && scp -> Connecting())
+ p -> OnConnectTimeout();
+ else
+ p -> OnTimeout();
+ p -> SetTimeout(0);
+ }
+ }
+ }
+ m_tlast = tnow;
+ } // tnow != tlast
+ }
+ // check retry client connect - EVENT
+ if (!m_fds_retry.empty())
+ {
+ socket_v tmp = m_fds_retry;
+ for (socket_v::iterator it = tmp.begin(); it != tmp.end(); it++)
+ {
+ Socket *p = NULL;
+ {
+ socket_m::iterator itmp = m_sockets.find(*it);
+ if (itmp != m_sockets.end()) // found
+ {
+ p = itmp -> second;
+ }
+ else
+ {
+ LogError(NULL, "GetSocket/handler/7", (int)*it, "Did not find expected socket using file descriptor", LOG_LEVEL_WARNING);
+ }
+ }
+ if (p)
+ {
+// if (p -> RetryClientConnect())
+ {
+ TcpSocket *tcp = dynamic_cast<TcpSocket *>(p);
+ SOCKET nn = *it; //(*it3).first;
+ tcp -> SetRetryClientConnect(false);
+DEB( fprintf(stderr, "Close() before retry client connect\n");)
+ p -> Close(); // removes from m_fds_retry
+ std::auto_ptr<SocketAddress> ad = p -> GetClientRemoteAddress();
+ if (ad.get())
+ {
+ tcp -> Open(*ad);
+ }
+ else
+ {
+ LogError(p, "RetryClientConnect", 0, "no address", LOG_LEVEL_ERROR);
+ }
+ Add(p);
+ m_fds_erase.push_back(nn);
+ }
+ }
+ }
+ }
+ // check close and delete - conditional event
+ if (!m_fds_close.empty())
+ {
+ socket_v tmp = m_fds_close;
+DEB( fprintf(stderr, "m_fds_close.size() == %d\n", (int)m_fds_close.size());)
+ for (socket_v::iterator it = tmp.begin(); it != tmp.end(); it++)
+ {
+ Socket *p = NULL;
+ {
+ socket_m::iterator itmp = m_sockets.find(*it);
+ if (itmp != m_sockets.end()) // found
+ {
+ p = itmp -> second;
+ }
+ else
+ {
+ itmp = m_add.find(*it);
+ if (itmp != m_add.end())
+ {
+ p = itmp -> second;
+ }
+ else
+ {
+ LogError(NULL, "GetSocket/handler/8", (int)*it, "Did not find expected socket using file descriptor", LOG_LEVEL_WARNING);
+ }
+ }
+ }
+ if (p)
+ {
+// if (p -> CloseAndDelete() )
+ {
+ TcpSocket *tcp = dynamic_cast<TcpSocket *>(p);
+ // new graceful tcp - flush and close timeout 5s
+ if (tcp && p -> IsConnected() && tcp -> GetFlushBeforeClose() &&
+#ifdef HAVE_OPENSSL
+ !tcp -> IsSSL() &&
+#endif
+ p -> TimeSinceClose() < 5)
+ {
+DEB( fprintf(stderr, " close(1)\n");)
+ if (tcp -> GetOutputLength())
+ {
+ LogError(p, "Closing", (int)tcp -> GetOutputLength(), "Sending all data before closing", LOG_LEVEL_INFO);
+ }
+ else // shutdown write when output buffer is empty
+ if (!(tcp -> GetShutdown() & SHUT_WR))
+ {
+ SOCKET nn = *it;
+ if (nn != INVALID_SOCKET && shutdown(nn, SHUT_WR) == -1)
+ {
+ LogError(p, "graceful shutdown", Errno, StrError(Errno), LOG_LEVEL_ERROR);
+ }
+ tcp -> SetShutdown(SHUT_WR);
+ }
+ }
+ else
+#ifdef ENABLE_RECONNECT
+ if (tcp && p -> IsConnected() && tcp -> Reconnect())
+ {
+ SOCKET nn = *it; //(*it3).first;
+DEB( fprintf(stderr, " close(2) fd %d\n", nn);)
+ p -> SetCloseAndDelete(false);
+ tcp -> SetIsReconnect();
+ p -> SetConnected(false);
+DEB( fprintf(stderr, "Close() before reconnect\n");)
+ p -> Close(); // dispose of old file descriptor (Open creates a new)
+ p -> OnDisconnect();
+ std::auto_ptr<SocketAddress> ad = p -> GetClientRemoteAddress();
+ if (ad.get())
+ {
+ tcp -> Open(*ad);
+ }
+ else
+ {
+ LogError(p, "Reconnect", 0, "no address", LOG_LEVEL_ERROR);
+ }
+ tcp -> ResetConnectionRetries();
+ Add(p);
+ m_fds_erase.push_back(nn);
+ }
+ else
+#endif
+ {
+ SOCKET nn = *it; //(*it3).first;
+DEB( fprintf(stderr, " close(3) fd %d GetSocket() %d\n", nn, p -> GetSocket());)
+ if (tcp && p -> IsConnected() && tcp -> GetOutputLength())
+ {
+ LogError(p, "Closing", (int)tcp -> GetOutputLength(), "Closing socket while data still left to send", LOG_LEVEL_WARNING);
+ }
+#ifdef ENABLE_POOL
+ if (p -> Retain() && !p -> Lost())
+ {
+ PoolSocket *p2 = new PoolSocket(*this, p);
+ p2 -> SetDeleteByHandler();
+ Add(p2);
+ //
+ p -> SetCloseAndDelete(false); // added - remove from m_fds_close
+ }
+ else
+#endif // ENABLE_POOL
+ {
+ Set(p -> GetSocket(),false,false,false);
+DEB( fprintf(stderr, "Close() before OnDelete\n");)
+ p -> Close();
+ }
+ p -> OnDelete();
+ if (p -> DeleteByHandler())
+ {
+ p -> SetErasedByHandler();
+ }
+ m_fds_erase.push_back(nn);
+ }
+ }
+ }
+ }
+ }
+
+ // check erased sockets
+ bool check_max_fd = false;
+ while (!m_fds_erase.empty())
+ {
+ socket_v::iterator it = m_fds_erase.begin();
+ SOCKET nn = *it;
+#ifdef ENABLE_DETACH
+ {
+ for (socket_v::iterator it = m_fds_detach.begin(); it != m_fds_detach.end(); it++)
+ {
+ if (*it == nn)
+ {
+ m_fds_detach.erase(it);
+ break;
+ }
+ }
+ }
+#endif
+ {
+ for (socket_v::iterator it = m_fds.begin(); it != m_fds.end(); it++)
+ {
+ if (*it == nn)
+ {
+ m_fds.erase(it);
+ break;
+ }
+ }
+ }
+ {
+ socket_m::iterator it = m_sockets.find(nn);
+ if (it != m_sockets.end())
+ {
+ Socket *p = it -> second;
+ /* Sometimes a SocketThread class can finish its run before the master
+ sockethandler gets here. In that case, the SocketThread has set the
+ 'ErasedByHandler' flag on the socket which will make us end up with a
+ double delete on the socket instance.
+ The fix is to make sure that the master sockethandler only can delete
+ non-detached sockets, and a slave sockethandler only can delete
+ detach sockets. */
+ if (p -> ErasedByHandler()
+#ifdef ENABLE_DETACH
+ && !(m_slave ^ p -> IsDetached())
+#endif
+ )
+ {
+#ifdef ENABLE_TRIGGERS
+ bool again = false;
+ do
+ {
+ again = false;
+ for (std::map<int, Socket *>::iterator it = m_trigger_src.begin(); it != m_trigger_src.end(); it++)
+ {
+ int id = it -> first;
+ Socket *src = it -> second;
+ if (src == p)
+ {
+ for (std::map<Socket *, bool>::iterator it = m_trigger_dst[id].begin(); it != m_trigger_dst[id].end(); it++)
+ {
+ Socket *dst = it -> first;
+ if (Valid(dst))
+ {
+ dst -> OnCancelled(id);
+ }
+ }
+ m_trigger_src.erase(m_trigger_src.find(id));
+ m_trigger_dst.erase(m_trigger_dst.find(id));
+ again = true;
+ break;
+ }
+ }
+ } while (again);
+#endif
+ delete p;
+ }
+ m_sockets.erase(it);
+ }
+ }
+ m_fds_erase.erase(it);
+ check_max_fd = true;
+ }
+ // calculate max file descriptor for select() call
+ if (check_max_fd)
+ {
+ m_maxsock = 0;
+ for (socket_v::iterator it = m_fds.begin(); it != m_fds.end(); it++)
+ {
+ SOCKET s = *it;
+ m_maxsock = s > m_maxsock ? s : m_maxsock;
+ }
+ }
+ // remove Add's that fizzed
+ while (!m_delete.empty())
+ {
+ std::list<Socket *>::iterator it = m_delete.begin();
+ Socket *p = *it;
+ p -> OnDelete();
+ m_delete.erase(it);
+ if (p -> DeleteByHandler()
+#ifdef ENABLE_DETACH
+ && !(m_slave ^ p -> IsDetached())
+#endif
+ )
+ {
+ p -> SetErasedByHandler();
+#ifdef ENABLE_TRIGGERS
+ bool again = false;
+ do
+ {
+ again = false;
+ for (std::map<int, Socket *>::iterator it = m_trigger_src.begin(); it != m_trigger_src.end(); it++)
+ {
+ int id = it -> first;
+ Socket *src = it -> second;
+ if (src == p)
+ {
+ for (std::map<Socket *, bool>::iterator it = m_trigger_dst[id].begin(); it != m_trigger_dst[id].end(); it++)
+ {
+ Socket *dst = it -> first;
+ if (Valid(dst))
+ {
+ dst -> OnCancelled(id);
+ }
+ }
+ m_trigger_src.erase(m_trigger_src.find(id));
+ m_trigger_dst.erase(m_trigger_dst.find(id));
+ again = true;
+ break;
+ }
+ }
+ } while (again);
+#endif
+ delete p;
+ }
+ }
+ return n;
+}
+
+#ifdef ENABLE_RESOLVER
+bool SocketHandler::Resolving(Socket *p0)
+{
+ std::map<Socket *, bool>::iterator it = m_resolve_q.find(p0);
+ return it != m_resolve_q.end();
+}
+#endif
+
+bool SocketHandler::Valid(Socket *p0)
+{
+ for (socket_m::iterator it = m_sockets.begin(); it != m_sockets.end(); it++)
+ {
+ Socket *p = it -> second;
+ if (p0 == p)
+ return true;
+ }
+ return false;
+}
+
+bool SocketHandler::OkToAccept(Socket *)
+{
+ return true;
+}
+
+size_t SocketHandler::GetCount()
+{
+/*
+printf(" m_sockets : %d\n", m_sockets.size());
+printf(" m_add : %d\n", m_add.size());
+printf(" m_delete : %d\n", m_delete.size());
+*/
+ return m_sockets.size() + m_add.size() + m_delete.size();
+}
+
+#ifdef ENABLE_SOCKS4
+void SocketHandler::SetSocks4Host(ipaddr_t a)
+{
+ m_socks4_host = a;
+}
+
+void SocketHandler::SetSocks4Host(const std::string& host)
+{
+ Utility::u2ip(host, m_socks4_host);
+}
+
+void SocketHandler::SetSocks4Port(port_t port)
+{
+ m_socks4_port = port;
+}
+
+void SocketHandler::SetSocks4Userid(const std::string& id)
+{
+ m_socks4_userid = id;
+}
+#endif
+
+#ifdef ENABLE_RESOLVER
+int SocketHandler::Resolve(Socket *p,const std::string& host,port_t port)
+{
+ // check cache
+ ResolvSocket *resolv = new ResolvSocket(*this, p, host, port);
+ resolv -> SetId(++m_resolv_id);
+ resolv -> SetDeleteByHandler();
+ ipaddr_t local;
+ Utility::u2ip("127.0.0.1", local);
+ if (!resolv -> Open(local, m_resolver_port))
+ {
+ LogError(resolv, "Resolve", -1, "Can't connect to local resolve server", LOG_LEVEL_FATAL);
+ }
+ Add(resolv);
+ m_resolve_q[p] = true;
+DEB( fprintf(stderr, " *** Resolve '%s:%d' id#%d m_resolve_q size: %d p: %p\n", host.c_str(), port, resolv -> GetId(), m_resolve_q.size(), p);)
+ return resolv -> GetId();
+}
+
+#ifdef ENABLE_IPV6
+int SocketHandler::Resolve6(Socket *p,const std::string& host,port_t port)
+{
+ // check cache
+ ResolvSocket *resolv = new ResolvSocket(*this, p, host, port, true);
+ resolv -> SetId(++m_resolv_id);
+ resolv -> SetDeleteByHandler();
+ ipaddr_t local;
+ Utility::u2ip("127.0.0.1", local);
+ if (!resolv -> Open(local, m_resolver_port))
+ {
+ LogError(resolv, "Resolve", -1, "Can't connect to local resolve server", LOG_LEVEL_FATAL);
+ }
+ Add(resolv);
+ m_resolve_q[p] = true;
+ return resolv -> GetId();
+}
+#endif
+
+int SocketHandler::Resolve(Socket *p,ipaddr_t a)
+{
+ // check cache
+ ResolvSocket *resolv = new ResolvSocket(*this, p, a);
+ resolv -> SetId(++m_resolv_id);
+ resolv -> SetDeleteByHandler();
+ ipaddr_t local;
+ Utility::u2ip("127.0.0.1", local);
+ if (!resolv -> Open(local, m_resolver_port))
+ {
+ LogError(resolv, "Resolve", -1, "Can't connect to local resolve server", LOG_LEVEL_FATAL);
+ }
+ Add(resolv);
+ m_resolve_q[p] = true;
+ return resolv -> GetId();
+}
+
+#ifdef ENABLE_IPV6
+int SocketHandler::Resolve(Socket *p,in6_addr& a)
+{
+ // check cache
+ ResolvSocket *resolv = new ResolvSocket(*this, p, a);
+ resolv -> SetId(++m_resolv_id);
+ resolv -> SetDeleteByHandler();
+ ipaddr_t local;
+ Utility::u2ip("127.0.0.1", local);
+ if (!resolv -> Open(local, m_resolver_port))
+ {
+ LogError(resolv, "Resolve", -1, "Can't connect to local resolve server", LOG_LEVEL_FATAL);
+ }
+ Add(resolv);
+ m_resolve_q[p] = true;
+ return resolv -> GetId();
+}
+#endif
+
+void SocketHandler::EnableResolver(port_t port)
+{
+ if (!m_resolver)
+ {
+ m_resolver_port = port;
+ m_resolver = new ResolvServer(port);
+ }
+}
+
+bool SocketHandler::ResolverReady()
+{
+ return m_resolver ? m_resolver -> Ready() : false;
+}
+#endif // ENABLE_RESOLVER
+
+#ifdef ENABLE_SOCKS4
+void SocketHandler::SetSocks4TryDirect(bool x)
+{
+ m_bTryDirect = x;
+}
+
+ipaddr_t SocketHandler::GetSocks4Host()
+{
+ return m_socks4_host;
+}
+
+port_t SocketHandler::GetSocks4Port()
+{
+ return m_socks4_port;
+}
+
+const std::string& SocketHandler::GetSocks4Userid()
+{
+ return m_socks4_userid;
+}
+
+bool SocketHandler::Socks4TryDirect()
+{
+ return m_bTryDirect;
+}
+#endif
+
+#ifdef ENABLE_RESOLVER
+bool SocketHandler::ResolverEnabled()
+{
+ return m_resolver ? true : false;
+}
+
+port_t SocketHandler::GetResolverPort()
+{
+ return m_resolver_port;
+}
+#endif // ENABLE_RESOLVER
+
+#ifdef ENABLE_POOL
+ISocketHandler::PoolSocket *SocketHandler::FindConnection(int type,const std::string& protocol,SocketAddress& ad)
+{
+ for (socket_m::iterator it = m_sockets.begin(); it != m_sockets.end() && !m_sockets.empty(); it++)
+ {
+ PoolSocket *pools = dynamic_cast<PoolSocket *>(it -> second);
+ if (pools)
+ {
+ if (pools -> GetSocketType() == type &&
+ pools -> GetSocketProtocol() == protocol &&
+// %! pools -> GetClientRemoteAddress() &&
+ *pools -> GetClientRemoteAddress() == ad)
+ {
+ m_sockets.erase(it);
+ pools -> SetRetain(); // avoid Close in Socket destructor
+ return pools; // Caller is responsible that this socket is deleted
+ }
+ }
+ }
+ return NULL;
+}
+
+void SocketHandler::EnablePool(bool x)
+{
+ m_b_enable_pool = x;
+}
+
+bool SocketHandler::PoolEnabled()
+{
+ return m_b_enable_pool;
+}
+#endif
+
+void SocketHandler::Remove(Socket *p)
+{
+#ifdef ENABLE_RESOLVER
+ std::map<Socket *, bool>::iterator it4 = m_resolve_q.find(p);
+ if (it4 != m_resolve_q.end())
+ m_resolve_q.erase(it4);
+#endif
+ if (p -> ErasedByHandler())
+ {
+ return;
+ }
+ for (socket_m::iterator it = m_sockets.begin(); it != m_sockets.end(); it++)
+ {
+ if (it -> second == p)
+ {
+ LogError(p, "Remove", -1, "Socket destructor called while still in use", LOG_LEVEL_WARNING);
+ m_sockets.erase(it);
+ return;
+ }
+ }
+ for (socket_m::iterator it2 = m_add.begin(); it2 != m_add.end(); it2++)
+ {
+ if ((*it2).second == p)
+ {
+ LogError(p, "Remove", -2, "Socket destructor called while still in use", LOG_LEVEL_WARNING);
+ m_add.erase(it2);
+ return;
+ }
+ }
+ for (std::list<Socket *>::iterator it3 = m_delete.begin(); it3 != m_delete.end(); it3++)
+ {
+ if (*it3 == p)
+ {
+ LogError(p, "Remove", -3, "Socket destructor called while still in use", LOG_LEVEL_WARNING);
+ m_delete.erase(it3);
+ return;
+ }
+ }
+}
+
+void SocketHandler::CheckSanity()
+{
+ CheckList(m_fds, "active sockets"); // active sockets
+ CheckList(m_fds_erase, "sockets to be erased"); // should always be empty anyway
+ CheckList(m_fds_callonconnect, "checklist CallOnConnect");
+#ifdef ENABLE_DETACH
+ CheckList(m_fds_detach, "checklist Detach");
+#endif
+ CheckList(m_fds_timeout, "checklist Timeout");
+ CheckList(m_fds_retry, "checklist retry client connect");
+ CheckList(m_fds_close, "checklist close and delete");
+}
+
+void SocketHandler::CheckList(socket_v& ref,const std::string& listname)
+{
+ for (socket_v::iterator it = ref.begin(); it != ref.end(); it++)
+ {
+ SOCKET s = *it;
+ if (m_sockets.find(s) != m_sockets.end())
+ continue;
+ if (m_add.find(s) != m_add.end())
+ continue;
+ bool found = false;
+ for (std::list<Socket *>::iterator it = m_delete.begin(); it != m_delete.end(); it++)
+ {
+ Socket *p = *it;
+ if (p -> GetSocket() == s)
+ {
+ found = true;
+ break;
+ }
+ }
+ if (!found)
+ {
+ fprintf(stderr, "CheckList failed for \"%s\": fd %d\n", listname.c_str(), s);
+ }
+ }
+}
+
+void SocketHandler::AddList(SOCKET s,list_t which_one,bool add)
+{
+ if (s == INVALID_SOCKET)
+ {
+DEB( fprintf(stderr, "AddList: invalid_socket\n");)
+ return;
+ }
+ socket_v& ref =
+ (which_one == LIST_CALLONCONNECT) ? m_fds_callonconnect :
+#ifdef ENABLE_DETACH
+ (which_one == LIST_DETACH) ? m_fds_detach :
+#endif
+ (which_one == LIST_TIMEOUT) ? m_fds_timeout :
+ (which_one == LIST_RETRY) ? m_fds_retry :
+ (which_one == LIST_CLOSE) ? m_fds_close : m_fds_close;
+ if (add)
+ {
+#ifdef ENABLE_DETACH
+DEB( fprintf(stderr, "AddList; %5d: %s: %s\n", s, (which_one == LIST_CALLONCONNECT) ? "CallOnConnect" :
+ (which_one == LIST_DETACH) ? "Detach" :
+ (which_one == LIST_TIMEOUT) ? "Timeout" :
+ (which_one == LIST_RETRY) ? "Retry" :
+ (which_one == LIST_CLOSE) ? "Close" : "<undef>",
+ add ? "Add" : "Remove");)
+#else
+DEB( fprintf(stderr, "AddList; %5d: %s: %s\n", s, (which_one == LIST_CALLONCONNECT) ? "CallOnConnect" :
+ (which_one == LIST_TIMEOUT) ? "Timeout" :
+ (which_one == LIST_RETRY) ? "Retry" :
+ (which_one == LIST_CLOSE) ? "Close" : "<undef>",
+ add ? "Add" : "Remove");)
+#endif
+ }
+ if (add)
+ {
+ for (socket_v::iterator it = ref.begin(); it != ref.end(); it++)
+ {
+ if (*it == s) // already there
+ {
+ return;
+ }
+ }
+ ref.push_back(s);
+ return;
+ }
+ // remove
+ for (socket_v::iterator it = ref.begin(); it != ref.end(); it++)
+ {
+ if (*it == s)
+ {
+ ref.erase(it);
+ break;
+ }
+ }
+//DEB( fprintf(stderr, "/AddList\n");)
+}
+
+#ifdef ENABLE_TRIGGERS
+int SocketHandler::TriggerID(Socket *src)
+{
+ int id = m_next_trigger_id++;
+ m_trigger_src[id] = src;
+ return id;
+}
+
+bool SocketHandler::Subscribe(int id, Socket *dst)
+{
+ if (m_trigger_src.find(id) != m_trigger_src.end())
+ {
+ std::map<Socket *, bool>::iterator it = m_trigger_dst[id].find(dst);
+ if (it != m_trigger_dst[id].end())
+ {
+ m_trigger_dst[id][dst] = true;
+ return true;
+ }
+ LogError(dst, "Subscribe", id, "Already subscribed", LOG_LEVEL_INFO);
+ return false;
+ }
+ LogError(dst, "Subscribe", id, "Trigger id not found", LOG_LEVEL_INFO);
+ return false;
+}
+
+bool SocketHandler::Unsubscribe(int id, Socket *dst)
+{
+ if (m_trigger_src.find(id) != m_trigger_src.end())
+ {
+ std::map<Socket *, bool>::iterator it = m_trigger_dst[id].find(dst);
+ if (it != m_trigger_dst[id].end())
+ {
+ m_trigger_dst[id].erase(it);
+ return true;
+ }
+ LogError(dst, "Unsubscribe", id, "Not subscribed", LOG_LEVEL_INFO);
+ return false;
+ }
+ LogError(dst, "Unsubscribe", id, "Trigger id not found", LOG_LEVEL_INFO);
+ return false;
+}
+
+void SocketHandler::Trigger(int id, Socket::TriggerData& data, bool erase)
+{
+ if (m_trigger_src.find(id) != m_trigger_src.end())
+ {
+ data.SetSource( m_trigger_src[id] );
+ for (std::map<Socket *, bool>::iterator it = m_trigger_dst[id].begin(); it != m_trigger_dst[id].end(); it++)
+ {
+ Socket *dst = it -> first;
+ if (Valid(dst))
+ {
+ dst -> OnTrigger(id, data);
+ }
+ }
+ if (erase)
+ {
+ m_trigger_src.erase(m_trigger_src.find(id));
+ m_trigger_dst.erase(m_trigger_dst.find(id));
+ }
+ }
+ else
+ {
+ LogError(NULL, "Trigger", id, "Trigger id not found", LOG_LEVEL_INFO);
+ }
+}
+#endif // ENABLE_TRIGGERS
+
+#ifdef SOCKETS_NAMESPACE
+}
+#endif
+
+
diff --git a/externals/sockets/StdoutLog.cpp b/externals/sockets/StdoutLog.cpp
new file mode 100644
index 00000000000..e745a6d3358
--- /dev/null
+++ b/externals/sockets/StdoutLog.cpp
@@ -0,0 +1,98 @@
+/** \file StdoutLog.cpp
+ ** \date 2004-06-01
+ ** \author grymse@alhem.net
+**/
+/*
+Copyright (C) 2004-2007 Anders Hedstrom
+
+This library is made available under the terms of the GNU GPL.
+
+If you would like to use this library in a closed-source application,
+a separate license agreement is available. For information about
+the closed-source license agreement for the C++ sockets library,
+please visit http://www.alhem.net/Sockets/license.html and/or
+email license@alhem.net.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+#include <stdio.h>
+
+#ifdef _MSC_VER
+#pragma warning(disable:4786)
+#endif
+
+#include <cstdio>
+
+#include "ISocketHandler.h"
+#include "Socket.h"
+#include "StdoutLog.h"
+
+#ifdef SOCKETS_NAMESPACE
+namespace SOCKETS_NAMESPACE {
+#endif
+
+
+void StdoutLog::error(ISocketHandler *,Socket *sock,const std::string& call,int err,const std::string& sys_err,loglevel_t lvl)
+{
+ time_t t = time(NULL);
+ struct tm tp;
+#ifdef _WIN32
+ memcpy(&tp, localtime(&t), sizeof(tp));
+#else
+ localtime_r(&t, &tp);
+#endif
+ std::string level;
+
+ switch (lvl)
+ {
+ case LOG_LEVEL_WARNING:
+ level = "Warning";
+ break;
+ case LOG_LEVEL_ERROR:
+ level = "Error";
+ break;
+ case LOG_LEVEL_FATAL:
+ level = "Fatal";
+ break;
+ case LOG_LEVEL_INFO:
+ level = "Info";
+ break;
+ }
+ if (sock)
+ {
+ printf("%d-%02d-%02d %02d:%02d:%02d :: fd %d :: %s: %d %s (%s)\n",
+ tp.tm_year + 1900,
+ tp.tm_mon + 1,
+ tp.tm_mday,
+ tp.tm_hour,tp.tm_min,tp.tm_sec,
+ sock -> GetSocket(),
+ call.c_str(),err,sys_err.c_str(),level.c_str());
+ }
+ else
+ {
+ printf("%d-%02d-%02d %02d:%02d:%02d :: %s: %d %s (%s)\n",
+ tp.tm_year + 1900,
+ tp.tm_mon + 1,
+ tp.tm_mday,
+ tp.tm_hour,tp.tm_min,tp.tm_sec,
+ call.c_str(),err,sys_err.c_str(),level.c_str());
+ }
+}
+
+#ifdef SOCKETS_NAMESPACE
+}
+#endif
+
+
diff --git a/externals/sockets/StreamSocket.cpp b/externals/sockets/StreamSocket.cpp
new file mode 100644
index 00000000000..009abadad8f
--- /dev/null
+++ b/externals/sockets/StreamSocket.cpp
@@ -0,0 +1,145 @@
+#include "StreamSocket.h"
+#include "ISocketHandler.h"
+
+#ifdef SOCKETS_NAMESPACE
+namespace SOCKETS_NAMESPACE {
+#endif
+
+StreamSocket::StreamSocket(ISocketHandler& h) : Socket(h)
+,m_bConnecting(false)
+,m_connect_timeout(5)
+,m_flush_before_close(true)
+,m_connection_retry(0)
+,m_retries(0)
+,m_call_on_connect(false)
+,m_b_retry_connect(false)
+,m_line_protocol(false)
+,m_shutdown(0)
+{
+}
+
+StreamSocket::~StreamSocket()
+{
+}
+
+void StreamSocket::SetConnecting(bool x)
+{
+ if (x != m_bConnecting)
+ {
+ m_bConnecting = x;
+ if (x)
+ {
+ SetTimeout( GetConnectTimeout() );
+ }
+ else
+ {
+ SetTimeout( 0 );
+ }
+ }
+}
+
+bool StreamSocket::Connecting()
+{
+ return m_bConnecting;
+}
+
+bool StreamSocket::Ready()
+{
+ if (GetSocket() != INVALID_SOCKET && !Connecting() && !CloseAndDelete())
+ return true;
+ return false;
+}
+
+void StreamSocket::SetConnectTimeout(int x)
+{
+ m_connect_timeout = x;
+}
+
+int StreamSocket::GetConnectTimeout()
+{
+ return m_connect_timeout;
+}
+
+void StreamSocket::SetFlushBeforeClose(bool x)
+{
+ m_flush_before_close = x;
+}
+
+bool StreamSocket::GetFlushBeforeClose()
+{
+ return m_flush_before_close;
+}
+
+int StreamSocket::GetConnectionRetry()
+{
+ return m_connection_retry;
+}
+
+void StreamSocket::SetConnectionRetry(int x)
+{
+ m_connection_retry = x;
+}
+
+int StreamSocket::GetConnectionRetries()
+{
+ return m_retries;
+}
+
+void StreamSocket::IncreaseConnectionRetries()
+{
+ m_retries++;
+}
+
+void StreamSocket::ResetConnectionRetries()
+{
+ m_retries = 0;
+}
+
+void StreamSocket::SetCallOnConnect(bool x)
+{
+ Handler().AddList(GetSocket(), LIST_CALLONCONNECT, x);
+ m_call_on_connect = x;
+}
+
+bool StreamSocket::CallOnConnect()
+{
+ return m_call_on_connect;
+}
+
+void StreamSocket::SetRetryClientConnect(bool x)
+{
+ Handler().AddList(GetSocket(), LIST_RETRY, x);
+ m_b_retry_connect = x;
+}
+
+bool StreamSocket::RetryClientConnect()
+{
+ return m_b_retry_connect;
+}
+
+void StreamSocket::SetLineProtocol(bool x)
+{
+ m_line_protocol = x;
+}
+
+bool StreamSocket::LineProtocol()
+{
+ return m_line_protocol;
+}
+
+void StreamSocket::SetShutdown(int x)
+{
+ m_shutdown = x;
+}
+
+int StreamSocket::GetShutdown()
+{
+ return m_shutdown;
+}
+
+
+#ifdef SOCKETS_NAMESPACE
+} // namespace SOCKETS_NAMESPACE {
+#endif
+
+
diff --git a/externals/sockets/TcpSocket.cpp b/externals/sockets/TcpSocket.cpp
new file mode 100644
index 00000000000..5f067b53124
--- /dev/null
+++ b/externals/sockets/TcpSocket.cpp
@@ -0,0 +1,1681 @@
+/** \file TcpSocket.cpp
+ ** \date 2004-02-13
+ ** \author grymse@alhem.net
+**/
+/*
+Copyright (C) 2004-2007 Anders Hedstrom
+
+This library is made available under the terms of the GNU GPL.
+
+If you would like to use this library in a closed-source application,
+a separate license agreement is available. For information about
+the closed-source license agreement for the C++ sockets library,
+please visit http://www.alhem.net/Sockets/license.html and/or
+email license@alhem.net.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+#ifdef _WIN32
+#ifdef _MSC_VER
+#pragma warning(disable:4786)
+#endif
+#include <stdlib.h>
+#else
+#include <errno.h>
+#endif
+#include "ISocketHandler.h"
+#include <fcntl.h>
+#include <assert.h>
+#include <stdarg.h>
+#include <stdio.h>
+#ifdef HAVE_OPENSSL
+#include <openssl/rand.h>
+#include <openssl/err.h>
+#endif
+#include <map>
+#include <cstdio>
+
+#include "TcpSocket.h"
+#include "Utility.h"
+#include "Ipv4Address.h"
+#include "Ipv6Address.h"
+#include "Mutex.h"
+#include "IFile.h"
+
+#ifdef SOCKETS_NAMESPACE
+namespace SOCKETS_NAMESPACE {
+#endif
+
+//#ifdef _DEBUG
+//#define DEB(x) x
+//#else
+#define DEB(x)
+//#endif
+
+// statics
+#ifdef HAVE_OPENSSL
+SSLInitializer TcpSocket::m_ssl_init;
+#endif
+
+// thanks, q
+#ifdef _MSC_VER
+#pragma warning(disable:4355)
+#endif
+TcpSocket::TcpSocket(ISocketHandler& h) : StreamSocket(h)
+,ibuf(TCP_BUFSIZE_READ)
+,m_b_input_buffer_disabled(false)
+,m_bytes_sent(0)
+,m_bytes_received(0)
+,m_skip_c(false)
+#ifdef SOCKETS_DYNAMIC_TEMP
+,m_buf(new char[TCP_BUFSIZE_READ + 1])
+#endif
+,m_obuf_top(NULL)
+,m_transfer_limit(0)
+,m_output_length(0)
+#ifdef HAVE_OPENSSL
+,m_ssl_ctx(NULL)
+,m_ssl(NULL)
+,m_sbio(NULL)
+#endif
+#ifdef ENABLE_SOCKS4
+,m_socks4_state(0)
+#endif
+#ifdef ENABLE_RESOLVER
+,m_resolver_id(0)
+#endif
+#ifdef ENABLE_RECONNECT
+,m_b_reconnect(false)
+,m_b_is_reconnect(false)
+#endif
+{
+}
+#ifdef _MSC_VER
+#pragma warning(default:4355)
+#endif
+
+#ifdef _MSC_VER
+#pragma warning(disable:4355)
+#endif
+TcpSocket::TcpSocket(ISocketHandler& h,size_t isize,size_t osize) : StreamSocket(h)
+,ibuf(isize)
+,m_b_input_buffer_disabled(false)
+,m_bytes_sent(0)
+,m_bytes_received(0)
+,m_skip_c(false)
+#ifdef SOCKETS_DYNAMIC_TEMP
+,m_buf(new char[TCP_BUFSIZE_READ + 1])
+#endif
+,m_obuf_top(NULL)
+,m_transfer_limit(0)
+,m_output_length(0)
+#ifdef HAVE_OPENSSL
+,m_ssl_ctx(NULL)
+,m_ssl(NULL)
+,m_sbio(NULL)
+#endif
+#ifdef ENABLE_SOCKS4
+,m_socks4_state(0)
+#endif
+#ifdef ENABLE_RESOLVER
+,m_resolver_id(0)
+#endif
+#ifdef ENABLE_RECONNECT
+,m_b_reconnect(false)
+,m_b_is_reconnect(false)
+#endif
+{
+}
+#ifdef _MSC_VER
+#pragma warning(default:4355)
+#endif
+
+TcpSocket::~TcpSocket()
+{
+#ifdef SOCKETS_DYNAMIC_TEMP
+ delete[] m_buf;
+#endif
+ // %! empty m_obuf
+ while (m_obuf.size())
+ {
+ output_l::iterator it = m_obuf.begin();
+ OUTPUT *p = *it;
+ delete p;
+ m_obuf.erase(it);
+ }
+#ifdef HAVE_OPENSSL
+ if (m_ssl)
+ {
+ SSL_free(m_ssl);
+ }
+#endif
+}
+
+bool TcpSocket::Open(ipaddr_t ip,port_t port,bool skip_socks)
+{
+ Ipv4Address ad(ip, port);
+ Ipv4Address local;
+ return Open(ad, local, skip_socks);
+}
+
+#ifdef ENABLE_IPV6
+#ifdef IPPROTO_IPV6
+bool TcpSocket::Open(in6_addr ip,port_t port,bool skip_socks)
+{
+ Ipv6Address ad(ip, port);
+ return Open(ad, skip_socks);
+}
+#endif
+#endif
+
+bool TcpSocket::Open(SocketAddress& ad,bool skip_socks)
+{
+ Ipv4Address bind_ad("0.0.0.0", 0);
+ return Open(ad, bind_ad, skip_socks);
+}
+
+bool TcpSocket::Open(SocketAddress& ad,SocketAddress& bind_ad,bool skip_socks)
+{
+ if (!ad.IsValid())
+ {
+ Handler().LogError(this, "Open", 0, "Invalid SocketAddress", LOG_LEVEL_FATAL);
+ SetCloseAndDelete();
+ return false;
+ }
+ if (Handler().GetCount() >= FD_SETSIZE)
+ {
+ Handler().LogError(this, "Open", 0, "no space left in fd_set", LOG_LEVEL_FATAL);
+ SetCloseAndDelete();
+ return false;
+ }
+ SetConnecting(false);
+#ifdef ENABLE_SOCKS4
+ SetSocks4(false);
+#endif
+ // check for pooling
+#ifdef ENABLE_POOL
+ if (Handler().PoolEnabled())
+ {
+ ISocketHandler::PoolSocket *pools = Handler().FindConnection(SOCK_STREAM, "tcp", ad);
+ if (pools)
+ {
+ CopyConnection( pools );
+ delete pools;
+
+ SetIsClient();
+ SetCallOnConnect(); // ISocketHandler must call OnConnect
+ Handler().LogError(this, "SetCallOnConnect", 0, "Found pooled connection", LOG_LEVEL_INFO);
+ return true;
+ }
+ }
+#endif
+ // if not, create new connection
+ SOCKET s = CreateSocket(ad.GetFamily(), SOCK_STREAM, "tcp");
+ if (s == INVALID_SOCKET)
+ {
+ return false;
+ }
+ // socket must be nonblocking for async connect
+ if (!SetNonblocking(true, s))
+ {
+ SetCloseAndDelete();
+ closesocket(s);
+ return false;
+ }
+#ifdef ENABLE_POOL
+ SetIsClient(); // client because we connect
+#endif
+ SetClientRemoteAddress(ad);
+ int n = 0;
+ if (bind_ad.GetPort() != 0)
+ {
+ bind(s, bind_ad, bind_ad);
+ }
+#ifdef ENABLE_SOCKS4
+ if (!skip_socks && GetSocks4Host() && GetSocks4Port())
+ {
+ Ipv4Address sa(GetSocks4Host(), GetSocks4Port());
+ {
+ std::string sockshost;
+ Utility::l2ip(GetSocks4Host(), sockshost);
+ Handler().LogError(this, "Open", 0, "Connecting to socks4 server @ " + sockshost + ":" +
+ Utility::l2string(GetSocks4Port()), LOG_LEVEL_INFO);
+ }
+ SetSocks4();
+ n = connect(s, sa, sa);
+ SetRemoteAddress(sa);
+ }
+ else
+#endif
+ {
+ n = connect(s, ad, ad);
+ SetRemoteAddress(ad);
+ }
+ if (n == -1)
+ {
+ // check error code that means a connect is in progress
+#ifdef _WIN32
+ if (Errno == WSAEWOULDBLOCK)
+#else
+ if (Errno == EINPROGRESS)
+#endif
+ {
+ Attach(s);
+ SetConnecting( true ); // this flag will control fd_set's
+ }
+ else
+#ifdef ENABLE_SOCKS4
+ if (Socks4() && Handler().Socks4TryDirect() ) // retry
+ {
+ closesocket(s);
+ return Open(ad, true);
+ }
+ else
+#endif
+#ifdef ENABLE_RECONNECT
+ if (Reconnect())
+ {
+ Handler().LogError(this, "connect: failed, reconnect pending", Errno, StrError(Errno), LOG_LEVEL_INFO);
+ Attach(s);
+ SetConnecting( true ); // this flag will control fd_set's
+ }
+ else
+#endif
+ {
+ Handler().LogError(this, "connect: failed", Errno, StrError(Errno), LOG_LEVEL_FATAL);
+ SetCloseAndDelete();
+ closesocket(s);
+ return false;
+ }
+ }
+ else
+ {
+ Attach(s);
+ SetCallOnConnect(); // ISocketHandler must call OnConnect
+ }
+
+ // 'true' means connected or connecting(not yet connected)
+ // 'false' means something failed
+ return true; //!Connecting();
+}
+
+bool TcpSocket::Open(const std::string &host,port_t port)
+{
+#ifdef ENABLE_IPV6
+#ifdef IPPROTO_IPV6
+ if (IsIpv6())
+ {
+#ifdef ENABLE_RESOLVER
+ if (!Handler().ResolverEnabled() || Utility::isipv6(host) )
+ {
+#endif
+ in6_addr a;
+ if (!Utility::u2ip(host, a))
+ {
+ SetCloseAndDelete();
+ return false;
+ }
+ Ipv6Address ad(a, port);
+ Ipv6Address local;
+ return Open(ad, local);
+#ifdef ENABLE_RESOLVER
+ }
+ m_resolver_id = Resolve6(host, port);
+ return true;
+#endif
+ }
+#endif
+#endif
+#ifdef ENABLE_RESOLVER
+ if (!Handler().ResolverEnabled() || Utility::isipv4(host) )
+ {
+#endif
+ ipaddr_t l;
+ if (!Utility::u2ip(host,l))
+ {
+ SetCloseAndDelete();
+ return false;
+ }
+ Ipv4Address ad(l, port);
+ Ipv4Address local;
+ return Open(ad, local);
+#ifdef ENABLE_RESOLVER
+ }
+ // resolve using async resolver thread
+ m_resolver_id = Resolve(host, port);
+ return true;
+#endif
+}
+
+#ifdef ENABLE_RESOLVER
+void TcpSocket::OnResolved(int id,ipaddr_t a,port_t port)
+{
+DEB( fprintf(stderr, "TcpSocket::OnResolved id %d addr %x port %d\n", id, a, port);)
+ if (id == m_resolver_id)
+ {
+ if (a && port)
+ {
+ Ipv4Address ad(a, port);
+ Ipv4Address local;
+ if (Open(ad, local))
+ {
+ if (!Handler().Valid(this))
+ {
+ Handler().Add(this);
+ }
+ }
+ }
+ else
+ {
+ Handler().LogError(this, "OnResolved", 0, "Resolver failed", LOG_LEVEL_FATAL);
+ SetCloseAndDelete();
+ }
+ }
+ else
+ {
+ Handler().LogError(this, "OnResolved", id, "Resolver returned wrong job id", LOG_LEVEL_FATAL);
+ SetCloseAndDelete();
+ }
+}
+
+#ifdef ENABLE_IPV6
+void TcpSocket::OnResolved(int id,in6_addr& a,port_t port)
+{
+ if (id == m_resolver_id)
+ {
+ Ipv6Address ad(a, port);
+ if (ad.IsValid())
+ {
+ Ipv6Address local;
+ if (Open(ad, local))
+ {
+ if (!Handler().Valid(this))
+ {
+ Handler().Add(this);
+ }
+ }
+ }
+ }
+ else
+ {
+ Handler().LogError(this, "OnResolved", id, "Resolver returned wrong job id", LOG_LEVEL_FATAL);
+ SetCloseAndDelete();
+ }
+}
+#endif
+#endif
+
+void TcpSocket::OnRead()
+{
+ int n = 0;
+#ifdef SOCKETS_DYNAMIC_TEMP
+ char *buf = m_buf;
+#else
+ char buf[TCP_BUFSIZE_READ];
+#endif
+#ifdef HAVE_OPENSSL
+ if (IsSSL())
+ {
+ if (!Ready())
+ return;
+ n = SSL_read(m_ssl, buf, TCP_BUFSIZE_READ);
+ if (n == -1)
+ {
+ n = SSL_get_error(m_ssl, n);
+ switch (n)
+ {
+ case SSL_ERROR_NONE:
+ case SSL_ERROR_WANT_READ:
+ case SSL_ERROR_WANT_WRITE:
+ break;
+ case SSL_ERROR_ZERO_RETURN:
+DEB( fprintf(stderr, "SSL_read() returns zero - closing socket\n");)
+ OnDisconnect();
+ SetCloseAndDelete(true);
+ SetFlushBeforeClose(false);
+ SetLost();
+ break;
+ default:
+DEB( fprintf(stderr, "SSL read problem, errcode = %d\n",n);)
+ OnDisconnect();
+ SetCloseAndDelete(true);
+ SetFlushBeforeClose(false);
+ SetLost();
+ }
+ return;
+ }
+ else
+ if (!n)
+ {
+ OnDisconnect();
+ SetCloseAndDelete(true);
+ SetFlushBeforeClose(false);
+ SetLost();
+ SetShutdown(SHUT_WR);
+ return;
+ }
+ else
+ if (n > 0 && n <= TCP_BUFSIZE_READ)
+ {
+ m_bytes_received += n;
+ if (GetTrafficMonitor())
+ {
+ GetTrafficMonitor() -> fwrite(buf, 1, n);
+ }
+ if (!m_b_input_buffer_disabled && !ibuf.Write(buf,n))
+ {
+ Handler().LogError(this, "OnRead(ssl)", 0, "ibuf overflow", LOG_LEVEL_WARNING);
+ }
+ }
+ else
+ {
+ Handler().LogError(this, "OnRead(ssl)", n, "abnormal value from SSL_read", LOG_LEVEL_ERROR);
+ }
+ }
+ else
+#endif // HAVE_OPENSSL
+ {
+ n = recv(GetSocket(), buf, TCP_BUFSIZE_READ, MSG_NOSIGNAL);
+ if (n == -1)
+ {
+ Handler().LogError(this, "read", Errno, StrError(Errno), LOG_LEVEL_FATAL);
+ OnDisconnect();
+ SetCloseAndDelete(true);
+ SetFlushBeforeClose(false);
+ SetLost();
+ return;
+ }
+ else
+ if (!n)
+ {
+ OnDisconnect();
+ SetCloseAndDelete(true);
+ SetFlushBeforeClose(false);
+ SetLost();
+ SetShutdown(SHUT_WR);
+ return;
+ }
+ else
+ if (n > 0 && n <= TCP_BUFSIZE_READ)
+ {
+ m_bytes_received += n;
+ if (GetTrafficMonitor())
+ {
+ GetTrafficMonitor() -> fwrite(buf, 1, n);
+ }
+ if (!m_b_input_buffer_disabled && !ibuf.Write(buf,n))
+ {
+ Handler().LogError(this, "OnRead", 0, "ibuf overflow", LOG_LEVEL_WARNING);
+ }
+ }
+ else
+ {
+ Handler().LogError(this, "OnRead", n, "abnormal value from recv", LOG_LEVEL_ERROR);
+ }
+ }
+ //
+ OnRead( buf, n );
+}
+
+void TcpSocket::OnRead( char *buf, size_t n )
+{
+ // unbuffered
+ if (n > 0 && n <= TCP_BUFSIZE_READ)
+ {
+ if (LineProtocol())
+ {
+ buf[n] = 0;
+ size_t i = 0;
+ if (m_skip_c && (buf[i] == 13 || buf[i] == 10) && buf[i] != m_c)
+ {
+ m_skip_c = false;
+ i++;
+ }
+ size_t x = i;
+ for (; i < n && LineProtocol(); i++)
+ {
+ while ((buf[i] == 13 || buf[i] == 10) && LineProtocol())
+ {
+ char c = buf[i];
+ buf[i] = 0;
+ if (buf[x])
+ {
+ m_line += (buf + x);
+ }
+ OnLine( m_line );
+ i++;
+ m_skip_c = true;
+ m_c = c;
+ if (i < n && (buf[i] == 13 || buf[i] == 10) && buf[i] != c)
+ {
+ m_skip_c = false;
+ i++;
+ }
+ x = i;
+ m_line = "";
+ }
+ if (!LineProtocol())
+ {
+ break;
+ }
+ }
+ if (!LineProtocol())
+ {
+ if (i < n)
+ {
+ OnRawData(buf + i, n - i);
+ }
+ }
+ else
+ if (buf[x])
+ {
+ m_line += (buf + x);
+ }
+ }
+ else
+ {
+ OnRawData(buf, n);
+ }
+ }
+ if (m_b_input_buffer_disabled)
+ {
+ return;
+ }
+ // further processing: socks4
+#ifdef ENABLE_SOCKS4
+ if (Socks4())
+ {
+ bool need_more = false;
+ while (GetInputLength() && !need_more && !CloseAndDelete())
+ {
+ need_more = OnSocks4Read();
+ }
+ }
+#endif
+}
+
+void TcpSocket::OnWriteComplete()
+{
+}
+
+void TcpSocket::OnWrite()
+{
+ if (Connecting())
+ {
+ int err = SoError();
+
+ // don't reset connecting flag on error here, we want the OnConnectFailed timeout later on
+ if (!err) // ok
+ {
+ Set(!IsDisableRead(), false);
+ SetConnecting(false);
+ SetCallOnConnect();
+ return;
+ }
+ Handler().LogError(this, "tcp: connect failed", err, StrError(err), LOG_LEVEL_FATAL);
+ Set(false, false); // no more monitoring because connection failed
+
+ // failed
+#ifdef ENABLE_SOCKS4
+ if (Socks4())
+ {
+ // %! leave 'Connecting' flag set?
+ OnSocks4ConnectFailed();
+ return;
+ }
+#endif
+ if (GetConnectionRetry() == -1 ||
+ (GetConnectionRetry() && GetConnectionRetries() < GetConnectionRetry()) )
+ {
+ // even though the connection failed at once, only retry after
+ // the connection timeout.
+ // should we even try to connect again, when CheckConnect returns
+ // false it's because of a connection error - not a timeout...
+ return;
+ }
+ SetConnecting(false);
+ SetCloseAndDelete( true );
+ /// \todo state reason why connect failed
+ OnConnectFailed();
+ return;
+ }
+ // try send next block in buffer
+ // if full block is sent, repeat
+ // if all blocks are sent, reset m_wfds
+
+ bool repeat = false;
+ size_t sz = m_transfer_limit ? GetOutputLength() : 0;
+ do
+ {
+ output_l::iterator it = m_obuf.begin();
+ OUTPUT *p = *it;
+ repeat = false;
+ int n = TryWrite(p -> Buf(), p -> Len());
+ if (n > 0)
+ {
+ size_t left = p -> Remove(n);
+ m_output_length -= n;
+ if (!left)
+ {
+ delete p;
+ m_obuf.erase(it);
+ if (!m_obuf.size())
+ {
+ m_obuf_top = NULL;
+ OnWriteComplete();
+ }
+ else
+ {
+ repeat = true;
+ }
+ }
+ }
+ } while (repeat);
+
+ if (m_transfer_limit && sz > m_transfer_limit && GetOutputLength() < m_transfer_limit)
+ {
+ OnTransferLimit();
+ }
+
+ // check output buffer set, set/reset m_wfds accordingly
+ {
+ bool br;
+ bool bw;
+ bool bx;
+ Handler().Get(GetSocket(), br, bw, bx);
+ if (m_obuf.size())
+ Set(br, true);
+ else
+ Set(br, false);
+ }
+}
+
+int TcpSocket::TryWrite(const char *buf, size_t len)
+{
+ int n = 0;
+#ifdef HAVE_OPENSSL
+ if (IsSSL())
+ {
+ n = SSL_write(m_ssl, buf, (int)len);
+ if (n == -1)
+ {
+ int errnr = SSL_get_error(m_ssl, n);
+ if ( errnr != SSL_ERROR_WANT_READ && errnr != SSL_ERROR_WANT_WRITE )
+ {
+ OnDisconnect();
+ SetCloseAndDelete(true);
+ SetFlushBeforeClose(false);
+ SetLost();
+ const char *errbuf = ERR_error_string(errnr, NULL);
+ Handler().LogError(this, "OnWrite/SSL_write", errnr, errbuf, LOG_LEVEL_FATAL);
+ }
+ return 0;
+ }
+ else
+ if (!n)
+ {
+ OnDisconnect();
+ SetCloseAndDelete(true);
+ SetFlushBeforeClose(false);
+ SetLost();
+DEB( int errnr = SSL_get_error(m_ssl, n);
+ const char *errbuf = ERR_error_string(errnr, NULL);
+ fprintf(stderr, "SSL_write() returns 0: %d : %s\n",errnr, errbuf);)
+ }
+ }
+ else
+#endif // HAVE_OPENSSL
+ {
+ n = send(GetSocket(), buf, (int)len, MSG_NOSIGNAL);
+ if (n == -1)
+ {
+ // normal error codes:
+ // WSAEWOULDBLOCK
+ // EAGAIN or EWOULDBLOCK
+#ifdef _WIN32
+ if (Errno != WSAEWOULDBLOCK)
+#else
+ if (Errno != EWOULDBLOCK)
+#endif
+ {
+ Handler().LogError(this, "send", Errno, StrError(Errno), LOG_LEVEL_FATAL);
+ OnDisconnect();
+ SetCloseAndDelete(true);
+ SetFlushBeforeClose(false);
+ SetLost();
+ }
+ return 0;
+ }
+ }
+ if (n > 0)
+ {
+ m_bytes_sent += n;
+ if (GetTrafficMonitor())
+ {
+ GetTrafficMonitor() -> fwrite(buf, 1, n);
+ }
+ }
+ return n;
+}
+
+void TcpSocket::Buffer(const char *buf, size_t len)
+{
+ size_t ptr = 0;
+ m_output_length += len;
+ while (ptr < len)
+ {
+ // buf/len => pbuf/sz
+ size_t space = 0;
+ if (m_obuf_top && (space = m_obuf_top -> Space()) > 0)
+ {
+ const char *pbuf = buf + ptr;
+ size_t sz = len - ptr;
+ if (space >= sz)
+ {
+ m_obuf_top -> Add(pbuf, sz);
+ ptr += sz;
+ }
+ else
+ {
+ m_obuf_top -> Add(pbuf, space);
+ ptr += space;
+ }
+ }
+ else
+ {
+ m_obuf_top = new OUTPUT;
+ m_obuf.push_back( m_obuf_top );
+ }
+ }
+}
+
+void TcpSocket::Send(const std::string &str,int i)
+{
+ SendBuf(str.c_str(),str.size(),i);
+}
+
+void TcpSocket::SendBuf(const char *buf,size_t len,int)
+{
+ if (!Ready() && !Connecting())
+ {
+ Handler().LogError(this, "SendBuf", -1, "Attempt to write to a non-ready socket" ); // warning
+ if (GetSocket() == INVALID_SOCKET)
+ Handler().LogError(this, "SendBuf", 0, " * GetSocket() == INVALID_SOCKET", LOG_LEVEL_INFO);
+ if (Connecting())
+ Handler().LogError(this, "SendBuf", 0, " * Connecting()", LOG_LEVEL_INFO);
+ if (CloseAndDelete())
+ Handler().LogError(this, "SendBuf", 0, " * CloseAndDelete()", LOG_LEVEL_INFO);
+ return;
+ }
+ if (!IsConnected())
+ {
+ Handler().LogError(this, "SendBuf", -1, "Attempt to write to a non-connected socket, will be sent on connect" ); // warning
+ Buffer(buf, len);
+ return;
+ }
+ if (m_obuf_top)
+ {
+ Buffer(buf, len);
+ return;
+ }
+ int n = TryWrite(buf, len);
+ if (n >= 0 && n < (int)len)
+ {
+ Buffer(buf + n, len - n);
+ }
+ // if ( data in buffer || !IsConnected )
+ // {
+ // add to buffer
+ // }
+ // else
+ // try_send
+ // if any data is unsent, buffer it and set m_wfds
+
+ // check output buffer set, set/reset m_wfds accordingly
+ {
+ bool br;
+ bool bw;
+ bool bx;
+ Handler().Get(GetSocket(), br, bw, bx);
+ if (m_obuf.size())
+ Set(br, true);
+ else
+ Set(br, false);
+ }
+}
+
+void TcpSocket::OnLine(const std::string& )
+{
+}
+
+#ifdef _MSC_VER
+#pragma warning(disable:4355)
+#endif
+TcpSocket::TcpSocket(const TcpSocket& s)
+:StreamSocket(s)
+,ibuf(0)
+{
+}
+#ifdef _MSC_VER
+#pragma warning(default:4355)
+#endif
+
+#ifdef ENABLE_SOCKS4
+void TcpSocket::OnSocks4Connect()
+{
+ char request[1000];
+ memset(request, 0, sizeof(request));
+ request[0] = 4; // socks v4
+ request[1] = 1; // command code: CONNECT
+ {
+ std::auto_ptr<SocketAddress> ad = GetClientRemoteAddress();
+ if (ad.get())
+ {
+ struct sockaddr *p0 = (struct sockaddr *)*ad;
+ struct sockaddr_in *p = (struct sockaddr_in *)p0;
+ if (p -> sin_family == AF_INET)
+ {
+ memcpy(request + 2, &p -> sin_port, 2); // nwbo is ok here
+ memcpy(request + 4, &p -> sin_addr, sizeof(struct in_addr));
+ }
+ else
+ {
+ /// \todo warn
+ }
+ }
+ else
+ {
+ /// \todo warn
+ }
+ }
+ strcpy(request + 8, GetSocks4Userid().c_str());
+ size_t length = GetSocks4Userid().size() + 8 + 1;
+ SendBuf(request, length);
+ m_socks4_state = 0;
+}
+
+void TcpSocket::OnSocks4ConnectFailed()
+{
+ Handler().LogError(this,"OnSocks4ConnectFailed",0,"connection to socks4 server failed, trying direct connection",LOG_LEVEL_WARNING);
+ if (!Handler().Socks4TryDirect())
+ {
+ SetConnecting(false);
+ SetCloseAndDelete();
+ OnConnectFailed(); // just in case
+ }
+ else
+ {
+ SetRetryClientConnect();
+ }
+}
+
+bool TcpSocket::OnSocks4Read()
+{
+ switch (m_socks4_state)
+ {
+ case 0:
+ ibuf.Read(&m_socks4_vn, 1);
+ m_socks4_state = 1;
+ break;
+ case 1:
+ ibuf.Read(&m_socks4_cd, 1);
+ m_socks4_state = 2;
+ break;
+ case 2:
+ if (GetInputLength() > 1)
+ {
+ ibuf.Read( (char *)&m_socks4_dstport, 2);
+ m_socks4_state = 3;
+ }
+ else
+ {
+ return true;
+ }
+ break;
+ case 3:
+ if (GetInputLength() > 3)
+ {
+ ibuf.Read( (char *)&m_socks4_dstip, 4);
+ SetSocks4(false);
+
+ switch (m_socks4_cd)
+ {
+ case 90:
+ OnConnect();
+ Handler().LogError(this, "OnSocks4Read", 0, "Connection established", LOG_LEVEL_INFO);
+ break;
+ case 91:
+ case 92:
+ case 93:
+ Handler().LogError(this,"OnSocks4Read",m_socks4_cd,"socks4 server reports connect failed",LOG_LEVEL_FATAL);
+ SetConnecting(false);
+ SetCloseAndDelete();
+ OnConnectFailed();
+ break;
+ default:
+ Handler().LogError(this,"OnSocks4Read",m_socks4_cd,"socks4 server unrecognized response",LOG_LEVEL_FATAL);
+ SetCloseAndDelete();
+ break;
+ }
+ }
+ else
+ {
+ return true;
+ }
+ break;
+ }
+ return false;
+}
+#endif
+
+void TcpSocket::Sendf(const char *format, ...)
+{
+ va_list ap;
+ va_start(ap, format);
+ char slask[5000]; // vsprintf / vsnprintf temporary
+#ifdef _WIN32
+ vsprintf(slask, format, ap);
+#else
+ vsnprintf(slask, 5000, format, ap);
+#endif
+ va_end(ap);
+ Send( slask );
+}
+
+#ifdef HAVE_OPENSSL
+void TcpSocket::OnSSLConnect()
+{
+ SetNonblocking(true);
+ {
+ if (m_ssl_ctx)
+ {
+DEB( fprintf(stderr, "SSL Context already initialized - closing socket\n");)
+ SetCloseAndDelete(true);
+ return;
+ }
+ InitSSLClient();
+ }
+ if (m_ssl_ctx)
+ {
+ /* Connect the SSL socket */
+ m_ssl = SSL_new(m_ssl_ctx);
+ if (!m_ssl)
+ {
+DEB( fprintf(stderr, " m_ssl is NULL\n");)
+ SetCloseAndDelete(true);
+ return;
+ }
+ SSL_set_mode(m_ssl, SSL_MODE_AUTO_RETRY);
+ m_sbio = BIO_new_socket((int)GetSocket(), BIO_NOCLOSE);
+ if (!m_sbio)
+ {
+DEB( fprintf(stderr, " m_sbio is NULL\n");)
+ SetCloseAndDelete(true);
+ return;
+ }
+ SSL_set_bio(m_ssl, m_sbio, m_sbio);
+ if (!SSLNegotiate())
+ {
+ SetSSLNegotiate();
+ }
+ }
+ else
+ {
+ SetCloseAndDelete();
+ }
+}
+
+void TcpSocket::OnSSLAccept()
+{
+ SetNonblocking(true);
+ {
+ if (m_ssl_ctx)
+ {
+DEB( fprintf(stderr, "SSL Context already initialized - closing socket\n");)
+ SetCloseAndDelete(true);
+ return;
+ }
+ InitSSLServer();
+ SetSSLServer();
+ }
+ if (m_ssl_ctx)
+ {
+ m_ssl = SSL_new(m_ssl_ctx);
+ if (!m_ssl)
+ {
+DEB( fprintf(stderr, " m_ssl is NULL\n");)
+ SetCloseAndDelete(true);
+ return;
+ }
+ SSL_set_mode(m_ssl, SSL_MODE_AUTO_RETRY);
+ m_sbio = BIO_new_socket((int)GetSocket(), BIO_NOCLOSE);
+ if (!m_sbio)
+ {
+DEB( fprintf(stderr, " m_sbio is NULL\n");)
+ SetCloseAndDelete(true);
+ return;
+ }
+ SSL_set_bio(m_ssl, m_sbio, m_sbio);
+// if (!SSLNegotiate())
+ {
+ SetSSLNegotiate();
+ }
+ }
+}
+
+bool TcpSocket::SSLNegotiate()
+{
+ if (!IsSSLServer()) // client
+ {
+ int r = SSL_connect(m_ssl);
+ if (r > 0)
+ {
+ SetSSLNegotiate(false);
+ /// \todo: resurrect certificate check... client
+// CheckCertificateChain( "");//ServerHOST);
+ SetNonblocking(false);
+ //
+ {
+ SetConnected();
+ if (GetOutputLength())
+ {
+ OnWrite();
+ }
+ }
+#ifdef ENABLE_RECONNECT
+ if (IsReconnect())
+ OnReconnect();
+ else
+#endif
+ {
+ OnConnect();
+ }
+ Handler().LogError(this, "SSLNegotiate/SSL_connect", 0, "Connection established", LOG_LEVEL_INFO);
+ return true;
+ }
+ else
+ if (!r)
+ {
+ Handler().LogError(this, "SSLNegotiate/SSL_connect", 0, "Connection failed", LOG_LEVEL_INFO);
+ SetSSLNegotiate(false);
+ SetCloseAndDelete();
+ OnSSLConnectFailed();
+ }
+ else
+ {
+ r = SSL_get_error(m_ssl, r);
+ if (r != SSL_ERROR_WANT_READ && r != SSL_ERROR_WANT_WRITE)
+ {
+ Handler().LogError(this, "SSLNegotiate/SSL_connect", -1, "Connection failed", LOG_LEVEL_INFO);
+DEB( fprintf(stderr, "SSL_connect() failed - closing socket, return code: %d\n",r);)
+ SetSSLNegotiate(false);
+ SetCloseAndDelete(true);
+ OnSSLConnectFailed();
+ }
+ }
+ }
+ else // server
+ {
+ int r = SSL_accept(m_ssl);
+ if (r > 0)
+ {
+ SetSSLNegotiate(false);
+ /// \todo: resurrect certificate check... server
+// CheckCertificateChain( "");//ClientHOST);
+ SetNonblocking(false);
+ //
+ {
+ SetConnected();
+ if (GetOutputLength())
+ {
+ OnWrite();
+ }
+ }
+ OnAccept();
+ Handler().LogError(this, "SSLNegotiate/SSL_accept", 0, "Connection established", LOG_LEVEL_INFO);
+ return true;
+ }
+ else
+ if (!r)
+ {
+ Handler().LogError(this, "SSLNegotiate/SSL_accept", 0, "Connection failed", LOG_LEVEL_INFO);
+ SetSSLNegotiate(false);
+ SetCloseAndDelete();
+ OnSSLAcceptFailed();
+ }
+ else
+ {
+ r = SSL_get_error(m_ssl, r);
+ if (r != SSL_ERROR_WANT_READ && r != SSL_ERROR_WANT_WRITE)
+ {
+ Handler().LogError(this, "SSLNegotiate/SSL_accept", -1, "Connection failed", LOG_LEVEL_INFO);
+DEB( fprintf(stderr, "SSL_accept() failed - closing socket, return code: %d\n",r);)
+ SetSSLNegotiate(false);
+ SetCloseAndDelete(true);
+ OnSSLAcceptFailed();
+ }
+ }
+ }
+ return false;
+}
+
+void TcpSocket::InitSSLClient()
+{
+ InitializeContext("", SSLv23_method());
+}
+
+void TcpSocket::InitSSLServer()
+{
+ Handler().LogError(this, "InitSSLServer", 0, "You MUST implement your own InitSSLServer method", LOG_LEVEL_FATAL);
+ SetCloseAndDelete();
+}
+
+void TcpSocket::InitializeContext(const std::string& context, SSL_METHOD *meth_in)
+{
+ /* Create our context*/
+ static std::map<std::string, SSL_CTX *> client_contexts;
+ if (client_contexts.find(context) == client_contexts.end())
+ {
+ SSL_METHOD *meth = meth_in ? meth_in : SSLv3_method();
+ m_ssl_ctx = client_contexts[context] = SSL_CTX_new(meth);
+ SSL_CTX_set_mode(m_ssl_ctx, SSL_MODE_AUTO_RETRY);
+ }
+ else
+ {
+ m_ssl_ctx = client_contexts[context];
+ }
+}
+
+void TcpSocket::InitializeContext(const std::string& context,const std::string& keyfile,const std::string& password,SSL_METHOD *meth_in)
+{
+ /* Create our context*/
+ static std::map<std::string, SSL_CTX *> server_contexts;
+ if (server_contexts.find(context) == server_contexts.end())
+ {
+ SSL_METHOD *meth = meth_in ? meth_in : SSLv3_method();
+ m_ssl_ctx = server_contexts[context] = SSL_CTX_new(meth);
+ SSL_CTX_set_mode(m_ssl_ctx, SSL_MODE_AUTO_RETRY);
+ // session id
+ if (!context.empty())
+ SSL_CTX_set_session_id_context(m_ssl_ctx, (const unsigned char *)context.c_str(), (unsigned int)context.size());
+ else
+ SSL_CTX_set_session_id_context(m_ssl_ctx, (const unsigned char *)"--empty--", 9);
+ }
+ else
+ {
+ m_ssl_ctx = server_contexts[context];
+ }
+
+ /* Load our keys and certificates*/
+ if (!(SSL_CTX_use_certificate_file(m_ssl_ctx, keyfile.c_str(), SSL_FILETYPE_PEM)))
+ {
+ Handler().LogError(this, "TcpSocket InitializeContext", 0, "Couldn't read certificate file " + keyfile, LOG_LEVEL_FATAL);
+ }
+
+ m_password = password;
+ SSL_CTX_set_default_passwd_cb(m_ssl_ctx, SSL_password_cb);
+ SSL_CTX_set_default_passwd_cb_userdata(m_ssl_ctx, this);
+ if (!(SSL_CTX_use_PrivateKey_file(m_ssl_ctx, keyfile.c_str(), SSL_FILETYPE_PEM)))
+ {
+ Handler().LogError(this, "TcpSocket InitializeContext", 0, "Couldn't read private key file " + keyfile, LOG_LEVEL_FATAL);
+ }
+}
+
+void TcpSocket::InitializeContext(const std::string& context,const std::string& certfile,const std::string& keyfile,const std::string& password,SSL_METHOD *meth_in)
+{
+ /* Create our context*/
+ static std::map<std::string, SSL_CTX *> server_contexts;
+ if (server_contexts.find(context) == server_contexts.end())
+ {
+ SSL_METHOD *meth = meth_in ? meth_in : SSLv3_method();
+ m_ssl_ctx = server_contexts[context] = SSL_CTX_new(meth);
+ SSL_CTX_set_mode(m_ssl_ctx, SSL_MODE_AUTO_RETRY);
+ // session id
+ if (context.size())
+ SSL_CTX_set_session_id_context(m_ssl_ctx, (const unsigned char *)context.c_str(), (unsigned int)context.size());
+ else
+ SSL_CTX_set_session_id_context(m_ssl_ctx, (const unsigned char *)"--empty--", 9);
+ }
+ else
+ {
+ m_ssl_ctx = server_contexts[context];
+ }
+
+ /* Load our keys and certificates*/
+ if (!(SSL_CTX_use_certificate_file(m_ssl_ctx, certfile.c_str(), SSL_FILETYPE_PEM)))
+ {
+ Handler().LogError(this, "TcpSocket InitializeContext", 0, "Couldn't read certificate file " + keyfile, LOG_LEVEL_FATAL);
+ }
+
+ m_password = password;
+ SSL_CTX_set_default_passwd_cb(m_ssl_ctx, SSL_password_cb);
+ SSL_CTX_set_default_passwd_cb_userdata(m_ssl_ctx, this);
+ if (!(SSL_CTX_use_PrivateKey_file(m_ssl_ctx, keyfile.c_str(), SSL_FILETYPE_PEM)))
+ {
+ Handler().LogError(this, "TcpSocket InitializeContext", 0, "Couldn't read private key file " + keyfile, LOG_LEVEL_FATAL);
+ }
+}
+
+int TcpSocket::SSL_password_cb(char *buf,int num,int rwflag,void *userdata)
+{
+ Socket *p0 = static_cast<Socket *>(userdata);
+ TcpSocket *p = dynamic_cast<TcpSocket *>(p0);
+ std::string pw = p ? p -> GetPassword() : "";
+ if ( (size_t)num < pw.size() + 1)
+ {
+ return 0;
+ }
+ strcpy(buf,pw.c_str());
+ return (int)pw.size();
+}
+#endif // HAVE_OPENSSL
+
+int TcpSocket::Close()
+{
+ if (GetSocket() == INVALID_SOCKET) // this could happen
+ {
+ Handler().LogError(this, "Socket::Close", 0, "file descriptor invalid", LOG_LEVEL_WARNING);
+ return 0;
+ }
+ int n;
+ SetNonblocking(true);
+ if (!Lost() && IsConnected() && !(GetShutdown() & SHUT_WR))
+ {
+ if (shutdown(GetSocket(), SHUT_WR) == -1)
+ {
+ // failed...
+ Handler().LogError(this, "shutdown", Errno, StrError(Errno), LOG_LEVEL_ERROR);
+ }
+ }
+ //
+ char tmp[1000];
+ if (!Lost() && (n = recv(GetSocket(),tmp,1000,0)) >= 0)
+ {
+ if (n)
+ {
+ Handler().LogError(this, "read() after shutdown", n, "bytes read", LOG_LEVEL_WARNING);
+ }
+ }
+#ifdef HAVE_OPENSSL
+ if (IsSSL() && m_ssl)
+ SSL_shutdown(m_ssl);
+ if (m_ssl)
+ {
+ SSL_free(m_ssl);
+ m_ssl = NULL;
+ }
+#endif
+ return Socket::Close();
+}
+
+#ifdef HAVE_OPENSSL
+SSL_CTX *TcpSocket::GetSslContext()
+{
+ if (!m_ssl_ctx)
+ Handler().LogError(this, "GetSslContext", 0, "SSL Context is NULL; check InitSSLServer/InitSSLClient", LOG_LEVEL_WARNING);
+ return m_ssl_ctx;
+}
+
+SSL *TcpSocket::GetSsl()
+{
+ if (!m_ssl)
+ Handler().LogError(this, "GetSsl", 0, "SSL is NULL; check InitSSLServer/InitSSLClient", LOG_LEVEL_WARNING);
+ return m_ssl;
+}
+#endif
+
+#ifdef ENABLE_RECONNECT
+void TcpSocket::SetReconnect(bool x)
+{
+ m_b_reconnect = x;
+}
+#endif
+
+void TcpSocket::OnRawData(const char *buf_in,size_t len)
+{
+}
+
+size_t TcpSocket::GetInputLength()
+{
+ return ibuf.GetLength();
+}
+
+size_t TcpSocket::GetOutputLength()
+{
+ return m_output_length;
+}
+
+uint64_t TcpSocket::GetBytesReceived(bool clear)
+{
+ uint64_t z = m_bytes_received;
+ if (clear)
+ m_bytes_received = 0;
+ return z;
+}
+
+uint64_t TcpSocket::GetBytesSent(bool clear)
+{
+ uint64_t z = m_bytes_sent;
+ if (clear)
+ m_bytes_sent = 0;
+ return z;
+}
+
+#ifdef ENABLE_RECONNECT
+bool TcpSocket::Reconnect()
+{
+ return m_b_reconnect;
+}
+
+void TcpSocket::SetIsReconnect(bool x)
+{
+ m_b_is_reconnect = x;
+}
+
+bool TcpSocket::IsReconnect()
+{
+ return m_b_is_reconnect;
+}
+#endif
+
+#ifdef HAVE_OPENSSL
+const std::string& TcpSocket::GetPassword()
+{
+ return m_password;
+}
+#endif
+
+void TcpSocket::DisableInputBuffer(bool x)
+{
+ m_b_input_buffer_disabled = x;
+}
+
+void TcpSocket::OnOptions(int family,int type,int protocol,SOCKET s)
+{
+DEB( fprintf(stderr, "Socket::OnOptions()\n");)
+#ifdef SO_NOSIGPIPE
+ SetSoNosigpipe(true);
+#endif
+ SetSoReuseaddr(true);
+ SetSoKeepalive(true);
+}
+
+void TcpSocket::SetLineProtocol(bool x)
+{
+ StreamSocket::SetLineProtocol(x);
+ DisableInputBuffer(x);
+}
+
+bool TcpSocket::SetTcpNodelay(bool x)
+{
+#ifdef TCP_NODELAY
+ int optval = x ? 1 : 0;
+ if (setsockopt(GetSocket(), IPPROTO_TCP, TCP_NODELAY, (char *)&optval, sizeof(optval)) == -1)
+ {
+ Handler().LogError(this, "setsockopt(IPPROTO_TCP, TCP_NODELAY)", Errno, StrError(Errno), LOG_LEVEL_FATAL);
+ return false;
+ }
+ return true;
+#else
+ Handler().LogError(this, "socket option not available", 0, "TCP_NODELAY", LOG_LEVEL_INFO);
+ return false;
+#endif
+}
+
+TcpSocket::CircularBuffer::CircularBuffer(size_t size)
+:buf(new char[2 * size])
+,m_max(size)
+,m_q(0)
+,m_b(0)
+,m_t(0)
+,m_count(0)
+{
+}
+
+TcpSocket::CircularBuffer::~CircularBuffer()
+{
+ delete[] buf;
+}
+
+bool TcpSocket::CircularBuffer::Write(const char *s,size_t l)
+{
+ if (m_q + l > m_max)
+ {
+ return false; // overflow
+ }
+ m_count += (unsigned long)l;
+ if (m_t + l > m_max) // block crosses circular border
+ {
+ size_t l1 = m_max - m_t; // size left until circular border crossing
+ // always copy full block to buffer(buf) + top pointer(m_t)
+ // because we have doubled the buffer size for performance reasons
+ memcpy(buf + m_t, s, l);
+ memcpy(buf, s + l1, l - l1);
+ m_t = l - l1;
+ m_q += l;
+ }
+ else
+ {
+ memcpy(buf + m_t, s, l);
+ memcpy(buf + m_max + m_t, s, l);
+ m_t += l;
+ if (m_t >= m_max)
+ m_t -= m_max;
+ m_q += l;
+ }
+ return true;
+}
+
+bool TcpSocket::CircularBuffer::Read(char *s,size_t l)
+{
+ if (l > m_q)
+ {
+ return false; // not enough chars
+ }
+ if (m_b + l > m_max) // block crosses circular border
+ {
+ size_t l1 = m_max - m_b;
+ if (s)
+ {
+ memcpy(s, buf + m_b, l1);
+ memcpy(s + l1, buf, l - l1);
+ }
+ m_b = l - l1;
+ m_q -= l;
+ }
+ else
+ {
+ if (s)
+ {
+ memcpy(s, buf + m_b, l);
+ }
+ m_b += l;
+ if (m_b >= m_max)
+ m_b -= m_max;
+ m_q -= l;
+ }
+ if (!m_q)
+ {
+ m_b = m_t = 0;
+ }
+ return true;
+}
+
+bool TcpSocket::CircularBuffer::SoftRead(char *s, size_t l)
+{
+ if (l > m_q)
+ {
+ return false;
+ }
+ if (m_b + l > m_max) // block crosses circular border
+ {
+ size_t l1 = m_max - m_b;
+ if (s)
+ {
+ memcpy(s, buf + m_b, l1);
+ memcpy(s + l1, buf, l - l1);
+ }
+ }
+ else
+ {
+ if (s)
+ {
+ memcpy(s, buf + m_b, l);
+ }
+ }
+ return true;
+}
+
+bool TcpSocket::CircularBuffer::Remove(size_t l)
+{
+ return Read(NULL, l);
+}
+
+size_t TcpSocket::CircularBuffer::GetLength()
+{
+ return m_q;
+}
+
+const char *TcpSocket::CircularBuffer::GetStart()
+{
+ return buf + m_b;
+}
+
+size_t TcpSocket::CircularBuffer::GetL()
+{
+ return (m_b + m_q > m_max) ? m_max - m_b : m_q;
+}
+
+size_t TcpSocket::CircularBuffer::Space()
+{
+ return m_max - m_q;
+}
+
+unsigned long TcpSocket::CircularBuffer::ByteCounter(bool clear)
+{
+ if (clear)
+ {
+ unsigned long x = m_count;
+ m_count = 0;
+ return x;
+ }
+ return m_count;
+}
+
+std::string TcpSocket::CircularBuffer::ReadString(size_t l)
+{
+ char *sz = new char[l + 1];
+ if (!Read(sz, l)) // failed, debug printout in Read() method
+ {
+ delete[] sz;
+ return "";
+ }
+ sz[l] = 0;
+ std::string tmp = sz;
+ delete[] sz;
+ return tmp;
+}
+
+void TcpSocket::OnConnectTimeout()
+{
+ Handler().LogError(this, "connect", -1, "connect timeout", LOG_LEVEL_FATAL);
+#ifdef ENABLE_SOCKS4
+ if (Socks4())
+ {
+ OnSocks4ConnectFailed();
+ // retry direct connection
+ }
+ else
+#endif
+ if (GetConnectionRetry() == -1 ||
+ (GetConnectionRetry() && GetConnectionRetries() < GetConnectionRetry()) )
+ {
+ IncreaseConnectionRetries();
+ // ask socket via OnConnectRetry callback if we should continue trying
+ if (OnConnectRetry())
+ {
+ SetRetryClientConnect();
+ }
+ else
+ {
+ SetCloseAndDelete( true );
+ /// \todo state reason why connect failed
+ OnConnectFailed();
+ }
+ }
+ else
+ {
+ SetCloseAndDelete(true);
+ /// \todo state reason why connect failed
+ OnConnectFailed();
+ }
+ //
+ SetConnecting(false);
+}
+
+#ifdef _WIN32
+void TcpSocket::OnException()
+{
+ if (Connecting())
+ {
+#ifdef ENABLE_SOCKS4
+ if (Socks4())
+ OnSocks4ConnectFailed();
+ else
+#endif
+ if (GetConnectionRetry() == -1 ||
+ (GetConnectionRetry() &&
+ GetConnectionRetries() < GetConnectionRetry() ))
+ {
+ // even though the connection failed at once, only retry after
+ // the connection timeout
+ // should we even try to connect again, when CheckConnect returns
+ // false it's because of a connection error - not a timeout...
+ }
+ else
+ {
+ SetConnecting(false); // tnx snibbe
+ SetCloseAndDelete();
+ OnConnectFailed();
+ }
+ return;
+ }
+ // %! exception doesn't always mean something bad happened, this code should be reworked
+ // errno valid here?
+ int err = SoError();
+ Handler().LogError(this, "exception on select", err, StrError(err), LOG_LEVEL_FATAL);
+ SetCloseAndDelete();
+}
+#endif // _WIN32
+
+int TcpSocket::Protocol()
+{
+ return IPPROTO_TCP;
+}
+
+void TcpSocket::SetTransferLimit(size_t sz)
+{
+ m_transfer_limit = sz;
+}
+
+void TcpSocket::OnTransferLimit()
+{
+}
+
+#ifdef SOCKETS_NAMESPACE
+}
+#endif
+
+
diff --git a/externals/sockets/Thread.cpp b/externals/sockets/Thread.cpp
new file mode 100644
index 00000000000..773e9f214fa
--- /dev/null
+++ b/externals/sockets/Thread.cpp
@@ -0,0 +1,154 @@
+/** \file Thread.cpp
+ ** \date 2004-10-30
+ ** \author grymse@alhem.net
+**/
+/*
+Copyright (C) 2004-2007 Anders Hedstrom
+
+This library is made available under the terms of the GNU GPL.
+
+If you would like to use this library in a closed-source application,
+a separate license agreement is available. For information about
+the closed-source license agreement for the C++ sockets library,
+please visit http://www.alhem.net/Sockets/license.html and/or
+email license@alhem.net.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+#include <stdio.h>
+#ifdef _WIN32
+#include <process.h>
+#include "socket_include.h"
+#else
+#include <unistd.h>
+#endif
+
+#include "Thread.h"
+
+#ifdef SOCKETS_NAMESPACE
+namespace SOCKETS_NAMESPACE {
+#endif
+
+Thread::Thread(bool release)
+:m_thread(0)
+,m_running(true)
+,m_release(false)
+,m_b_delete_on_exit(false)
+,m_b_destructor(false)
+{
+#ifdef _WIN32
+// m_thread = ::CreateThread(NULL, 0, StartThread, this, 0, &m_dwThreadId);
+ m_thread = (HANDLE)_beginthreadex(NULL, 0, &StartThread, this, 0, &m_dwThreadId);
+#else
+ pthread_attr_t attr;
+
+ pthread_attr_init(&attr);
+ pthread_attr_setdetachstate(&attr,PTHREAD_CREATE_DETACHED);
+ if (pthread_create(&m_thread,&attr, StartThread,this) == -1)
+ {
+ perror("Thread: create failed");
+ SetRunning(false);
+ }
+// pthread_attr_destroy(&attr);
+#endif
+ m_release = release;
+}
+
+Thread::~Thread()
+{
+ m_b_destructor = true;
+ if (m_running)
+ {
+ SetRelease(true);
+ SetRunning(false);
+#ifdef _WIN32
+ Sleep(1000);
+#else
+ sleep(1);
+#endif
+ }
+#ifdef _WIN32
+ if (m_thread)
+ ::CloseHandle(m_thread);
+#endif
+}
+
+threadfunc_t STDPREFIX Thread::StartThread(threadparam_t zz)
+{
+ Thread *p = (Thread *)zz;
+
+ while (p -> m_running && !p -> m_release)
+ {
+#ifdef _WIN32
+ Sleep(1000);
+#else
+ sleep(1);
+#endif
+ }
+ if (p -> m_running)
+ {
+ p -> Run();
+ }
+ p -> SetRunning(false); // if return
+ if (p -> DeleteOnExit() && !p -> IsDestructor())
+ {
+ delete p;
+ }
+#ifdef _WIN32
+ _endthreadex(0);
+#endif
+ return (threadfunc_t)NULL;
+}
+
+bool Thread::IsRunning()
+{
+ return m_running;
+}
+
+void Thread::SetRunning(bool x)
+{
+ m_running = x;
+}
+
+bool Thread::IsReleased()
+{
+ return m_release;
+}
+
+void Thread::SetRelease(bool x)
+{
+ m_release = x;
+}
+
+bool Thread::DeleteOnExit()
+{
+ return m_b_delete_on_exit;
+}
+
+void Thread::SetDeleteOnExit(bool x)
+{
+ m_b_delete_on_exit = x;
+}
+
+bool Thread::IsDestructor()
+{
+ return m_b_destructor;
+}
+
+#ifdef SOCKETS_NAMESPACE
+}
+#endif
+
+
diff --git a/externals/sockets/UdpSocket.cpp b/externals/sockets/UdpSocket.cpp
new file mode 100644
index 00000000000..a3d393c00e2
--- /dev/null
+++ b/externals/sockets/UdpSocket.cpp
@@ -0,0 +1,810 @@
+/** \file UdpSocket.cpp
+ ** \date 2004-02-13
+ ** \author grymse@alhem.net
+**/
+/*
+Copyright (C) 2004-2007 Anders Hedstrom
+
+This library is made available under the terms of the GNU GPL.
+
+If you would like to use this library in a closed-source application,
+a separate license agreement is available. For information about
+the closed-source license agreement for the C++ sockets library,
+please visit http://www.alhem.net/Sockets/license.html and/or
+email license@alhem.net.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+#ifdef _WIN32
+#ifdef _MSC_VER
+#pragma warning(disable:4786)
+#endif
+#include <stdlib.h>
+#else
+#include <errno.h>
+#endif
+
+#include "ISocketHandler.h"
+#include "UdpSocket.h"
+#include "Utility.h"
+#include "Ipv4Address.h"
+#include "Ipv6Address.h"
+#ifdef ENABLE_EXCEPTIONS
+#include "Exception.h"
+#endif
+// include this to see strange sights
+//#include <linux/in6.h>
+
+#ifdef SOCKETS_NAMESPACE
+namespace SOCKETS_NAMESPACE {
+#endif
+
+UdpSocket::UdpSocket(ISocketHandler& h, int ibufsz, bool ipv6, int retries) : Socket(h)
+, m_ibuf(new char[ibufsz])
+, m_ibufsz(ibufsz)
+, m_bind_ok(false)
+, m_port(0)
+, m_last_size_written(-1)
+, m_retries(retries)
+, m_b_read_ts(false)
+{
+#ifdef ENABLE_IPV6
+#ifdef IPPROTO_IPV6
+ SetIpv6(ipv6);
+#endif
+#endif
+}
+
+UdpSocket::~UdpSocket()
+{
+ Close();
+ delete[] m_ibuf;
+}
+
+int UdpSocket::Bind(port_t &port, int range)
+{
+#ifdef ENABLE_IPV6
+#ifdef IPPROTO_IPV6
+ if (IsIpv6())
+ {
+ Ipv6Address ad(port);
+ return Bind(ad, range);
+ }
+#endif
+#endif
+ Ipv4Address ad(port);
+ return Bind(ad, range);
+}
+
+int UdpSocket::Bind(const std::string& intf, port_t &port, int range)
+{
+#ifdef ENABLE_IPV6
+#ifdef IPPROTO_IPV6
+ if (IsIpv6())
+ {
+ Ipv6Address ad(intf, port);
+ if (ad.IsValid())
+ {
+ return Bind(ad, range);
+ }
+ SetCloseAndDelete();
+ return -1;
+ }
+#endif
+#endif
+ Ipv4Address ad(intf, port);
+ if (ad.IsValid())
+ {
+ return Bind(ad, range);
+ }
+ SetCloseAndDelete();
+ return -1;
+}
+
+int UdpSocket::Bind(ipaddr_t a, port_t &port, int range)
+{
+ Ipv4Address ad(a, port);
+ return Bind(ad, range);
+}
+
+#ifdef ENABLE_IPV6
+#ifdef IPPROTO_IPV6
+int UdpSocket::Bind(in6_addr a, port_t &port, int range)
+{
+ Ipv6Address ad(a, port);
+ return Bind(ad, range);
+}
+#endif
+#endif
+
+int UdpSocket::Bind(SocketAddress& ad, int range)
+{
+ if (GetSocket() == INVALID_SOCKET)
+ {
+ Attach(CreateSocket(ad.GetFamily(), SOCK_DGRAM, "udp"));
+ }
+ if (GetSocket() != INVALID_SOCKET)
+ {
+ SetNonblocking(true);
+ int n = bind(GetSocket(), ad, ad);
+ int tries = range;
+ while (n == -1 && tries--)
+ {
+ ad.SetPort(ad.GetPort() + 1);
+ n = bind(GetSocket(), ad, ad);
+ }
+ if (n == -1)
+ {
+ Handler().LogError(this, "bind", Errno, StrError(Errno), LOG_LEVEL_FATAL);
+ SetCloseAndDelete();
+#ifdef ENABLE_EXCEPTIONS
+ throw Exception("bind() failed for UdpSocket, port:range: " + Utility::l2string(ad.GetPort()) + ":" + Utility::l2string(range));
+#endif
+ return -1;
+ }
+ m_bind_ok = true;
+ m_port = ad.GetPort();
+ return 0;
+ }
+ return -1;
+}
+
+/** if you wish to use Send, first Open a connection */
+bool UdpSocket::Open(ipaddr_t l, port_t port)
+{
+ Ipv4Address ad(l, port);
+ return Open(ad);
+}
+
+bool UdpSocket::Open(const std::string& host, port_t port)
+{
+#ifdef ENABLE_IPV6
+#ifdef IPPROTO_IPV6
+ if (IsIpv6())
+ {
+ Ipv6Address ad(host, port);
+ if (ad.IsValid())
+ {
+ return Open(ad);
+ }
+ return false;
+ }
+#endif
+#endif
+ Ipv4Address ad(host, port);
+ if (ad.IsValid())
+ {
+ return Open(ad);
+ }
+ return false;
+}
+
+#ifdef ENABLE_IPV6
+#ifdef IPPROTO_IPV6
+bool UdpSocket::Open(struct in6_addr& a, port_t port)
+{
+ Ipv6Address ad(a, port);
+ return Open(ad);
+}
+#endif
+#endif
+
+bool UdpSocket::Open(SocketAddress& ad)
+{
+ if (GetSocket() == INVALID_SOCKET)
+ {
+ Attach(CreateSocket(ad.GetFamily(), SOCK_DGRAM, "udp"));
+ }
+ if (GetSocket() != INVALID_SOCKET)
+ {
+ SetNonblocking(true);
+ if (connect(GetSocket(), ad, ad) == -1)
+ {
+ Handler().LogError(this, "connect", Errno, StrError(Errno), LOG_LEVEL_FATAL);
+ SetCloseAndDelete();
+ return false;
+ }
+ SetConnected();
+ return true;
+ }
+ return false;
+}
+
+void UdpSocket::CreateConnection()
+{
+#ifdef ENABLE_IPV6
+#ifdef IPPROTO_IPV6
+ if (IsIpv6())
+ {
+ if (GetSocket() == INVALID_SOCKET)
+ {
+ SOCKET s = CreateSocket(AF_INET6, SOCK_DGRAM, "udp");
+ if (s == INVALID_SOCKET)
+ {
+ return;
+ }
+ SetNonblocking(true, s);
+ Attach(s);
+ }
+ return;
+ }
+#endif
+#endif
+ if (GetSocket() == INVALID_SOCKET)
+ {
+ SOCKET s = CreateSocket(AF_INET, SOCK_DGRAM, "udp");
+ if (s == INVALID_SOCKET)
+ {
+ return;
+ }
+ SetNonblocking(true, s);
+ Attach(s);
+ }
+}
+
+/** send to specified address */
+void UdpSocket::SendToBuf(const std::string& h, port_t p, const char *data, int len, int flags)
+{
+#ifdef ENABLE_IPV6
+#ifdef IPPROTO_IPV6
+ if (IsIpv6())
+ {
+ Ipv6Address ad(h, p);
+ if (ad.IsValid())
+ {
+ SendToBuf(ad, data, len, flags);
+ }
+ return;
+ }
+#endif
+#endif
+ Ipv4Address ad(h, p);
+ if (ad.IsValid())
+ {
+ SendToBuf(ad, data, len, flags);
+ }
+}
+
+/** send to specified address */
+void UdpSocket::SendToBuf(ipaddr_t a, port_t p, const char *data, int len, int flags)
+{
+ Ipv4Address ad(a, p);
+ SendToBuf(ad, data, len, flags);
+}
+
+#ifdef ENABLE_IPV6
+#ifdef IPPROTO_IPV6
+void UdpSocket::SendToBuf(in6_addr a, port_t p, const char *data, int len, int flags)
+{
+ Ipv6Address ad(a, p);
+ SendToBuf(ad, data, len, flags);
+}
+#endif
+#endif
+
+void UdpSocket::SendToBuf(SocketAddress& ad, const char *data, int len, int flags)
+{
+ if (GetSocket() == INVALID_SOCKET)
+ {
+ Attach(CreateSocket(ad.GetFamily(), SOCK_DGRAM, "udp"));
+ }
+ if (GetSocket() != INVALID_SOCKET)
+ {
+ SetNonblocking(true);
+ if ((m_last_size_written = sendto(GetSocket(), data, len, flags, ad, ad)) == -1)
+ {
+ Handler().LogError(this, "sendto", Errno, StrError(Errno), LOG_LEVEL_ERROR);
+ }
+ }
+}
+
+void UdpSocket::SendTo(const std::string& a, port_t p, const std::string& str, int flags)
+{
+ SendToBuf(a, p, str.c_str(), (int)str.size(), flags);
+}
+
+void UdpSocket::SendTo(ipaddr_t a, port_t p, const std::string& str, int flags)
+{
+ SendToBuf(a, p, str.c_str(), (int)str.size(), flags);
+}
+
+#ifdef ENABLE_IPV6
+#ifdef IPPROTO_IPV6
+void UdpSocket::SendTo(in6_addr a, port_t p, const std::string& str, int flags)
+{
+ SendToBuf(a, p, str.c_str(), (int)str.size(), flags);
+}
+#endif
+#endif
+
+void UdpSocket::SendTo(SocketAddress& ad, const std::string& str, int flags)
+{
+ SendToBuf(ad, str.c_str(), (int)str.size(), flags);
+}
+
+/** send to connected address */
+void UdpSocket::SendBuf(const char *data, size_t len, int flags)
+{
+ if (!IsConnected())
+ {
+ Handler().LogError(this, "SendBuf", 0, "not connected", LOG_LEVEL_ERROR);
+ return;
+ }
+ if ((m_last_size_written = send(GetSocket(), data, (int)len, flags)) == -1)
+ {
+ Handler().LogError(this, "send", Errno, StrError(Errno), LOG_LEVEL_ERROR);
+ }
+}
+
+void UdpSocket::Send(const std::string& str, int flags)
+{
+ SendBuf(str.c_str(), (int)str.size(), flags);
+}
+
+#if defined(LINUX) || defined(MACOSX)
+int UdpSocket::ReadTS(char *ioBuf, int inBufSize, struct sockaddr *from, socklen_t fromlen, struct timeval *ts)
+{
+ struct msghdr msg;
+ struct iovec vec[1];
+ union {
+ struct cmsghdr cm;
+#ifdef MACOSX
+#ifdef __DARWIN_UNIX03
+#define ALIGNBYTES __DARWIN_ALIGNBYTES
+#endif
+#define myALIGN(p) (((unsigned int)(p) + ALIGNBYTES) &~ ALIGNBYTES)
+#define myCMSG_SPACE(l) (myALIGN(sizeof(struct cmsghdr)) + myALIGN(l))
+ char data[ myCMSG_SPACE(sizeof(struct timeval)) ];
+#else
+ char data[ CMSG_SPACE(sizeof(struct timeval)) ];
+#endif
+ } cmsg_un;
+ struct cmsghdr *cmsg;
+ struct timeval *tv;
+
+ vec[0].iov_base = ioBuf;
+ vec[0].iov_len = inBufSize;
+
+ memset(&msg, 0, sizeof(msg));
+ memset(from, 0, fromlen);
+ memset(ioBuf, 0, inBufSize);
+ memset(&cmsg_un, 0, sizeof(cmsg_un));
+
+ msg.msg_name = (caddr_t)from;
+ msg.msg_namelen = fromlen;
+ msg.msg_iov = vec;
+ msg.msg_iovlen = 1;
+ msg.msg_control = cmsg_un.data;
+ msg.msg_controllen = sizeof(cmsg_un.data);
+ msg.msg_flags = 0;
+
+ // Original version - for reference only
+ //int n = recvfrom(GetSocket(), m_ibuf, m_ibufsz, 0, (struct sockaddr *)&sa, &sa_len);
+
+ int n = recvmsg(GetSocket(), &msg, MSG_DONTWAIT);
+
+ // now ioBuf will contain the data, as if we used recvfrom
+
+ // Now get the time
+ if(n != -1 && msg.msg_controllen >= sizeof(struct cmsghdr) && !(msg.msg_flags & MSG_CTRUNC))
+ {
+ tv = 0;
+ for (cmsg = CMSG_FIRSTHDR(&msg); cmsg != NULL; cmsg = CMSG_NXTHDR(&msg, cmsg))
+ {
+ if (cmsg->cmsg_level == SOL_SOCKET && cmsg->cmsg_type == SCM_TIMESTAMP)
+ {
+ tv = (struct timeval *)CMSG_DATA(cmsg);
+ }
+ }
+ if (tv)
+ {
+ memcpy(ts, tv, sizeof(struct timeval));
+ }
+ }
+ // The address is in network order, but that's OK right now
+ return n;
+}
+#endif
+
+void UdpSocket::OnRead()
+{
+#ifdef ENABLE_IPV6
+#ifdef IPPROTO_IPV6
+ if (IsIpv6())
+ {
+ struct sockaddr_in6 sa;
+ socklen_t sa_len = sizeof(sa);
+ if (m_b_read_ts)
+ {
+ struct timeval ts;
+ Utility::GetTime(&ts);
+#if !defined(LINUX) && !defined(MACOSX)
+ int n = recvfrom(GetSocket(), m_ibuf, m_ibufsz, 0, (struct sockaddr *)&sa, &sa_len);
+#else
+ int n = ReadTS(m_ibuf, m_ibufsz, (struct sockaddr *)&sa, sa_len, &ts);
+#endif
+ if (n > 0)
+ {
+ this -> OnRawData(m_ibuf, n, (struct sockaddr *)&sa, sa_len, &ts);
+ }
+ else
+ if (n == -1)
+ {
+#ifdef _WIN32
+ if (Errno != WSAEWOULDBLOCK)
+#else
+ if (Errno != EWOULDBLOCK)
+#endif
+ Handler().LogError(this, "recvfrom", Errno, StrError(Errno), LOG_LEVEL_ERROR);
+ }
+ return;
+ }
+ int n = recvfrom(GetSocket(), m_ibuf, m_ibufsz, 0, (struct sockaddr *)&sa, &sa_len);
+ int q = m_retries; // receive max 10 at one cycle
+ while (n > 0)
+ {
+ if (sa_len != sizeof(sa))
+ {
+ Handler().LogError(this, "recvfrom", 0, "unexpected address struct size", LOG_LEVEL_WARNING);
+ }
+ this -> OnRawData(m_ibuf, n, (struct sockaddr *)&sa, sa_len);
+ if (!q--)
+ break;
+ //
+ n = recvfrom(GetSocket(), m_ibuf, m_ibufsz, 0, (struct sockaddr *)&sa, &sa_len);
+ }
+ if (n == -1)
+ {
+#ifdef _WIN32
+ if (Errno != WSAEWOULDBLOCK)
+#else
+ if (Errno != EWOULDBLOCK)
+#endif
+ Handler().LogError(this, "recvfrom", Errno, StrError(Errno), LOG_LEVEL_ERROR);
+ }
+ return;
+ }
+#endif
+#endif
+ struct sockaddr_in sa;
+ socklen_t sa_len = sizeof(sa);
+ if (m_b_read_ts)
+ {
+ struct timeval ts;
+ Utility::GetTime(&ts);
+#if !defined(LINUX) && !defined(MACOSX)
+ int n = recvfrom(GetSocket(), m_ibuf, m_ibufsz, 0, (struct sockaddr *)&sa, &sa_len);
+#else
+ int n = ReadTS(m_ibuf, m_ibufsz, (struct sockaddr *)&sa, sa_len, &ts);
+#endif
+ if (n > 0)
+ {
+ this -> OnRawData(m_ibuf, n, (struct sockaddr *)&sa, sa_len, &ts);
+ }
+ else
+ if (n == -1)
+ {
+#ifdef _WIN32
+ if (Errno != WSAEWOULDBLOCK)
+#else
+ if (Errno != EWOULDBLOCK)
+#endif
+ Handler().LogError(this, "recvfrom", Errno, StrError(Errno), LOG_LEVEL_ERROR);
+ }
+ return;
+ }
+ int n = recvfrom(GetSocket(), m_ibuf, m_ibufsz, 0, (struct sockaddr *)&sa, &sa_len);
+ int q = m_retries;
+ while (n > 0)
+ {
+ if (sa_len != sizeof(sa))
+ {
+ Handler().LogError(this, "recvfrom", 0, "unexpected address struct size", LOG_LEVEL_WARNING);
+ }
+ this -> OnRawData(m_ibuf, n, (struct sockaddr *)&sa, sa_len);
+ if (!q--)
+ break;
+ //
+ n = recvfrom(GetSocket(), m_ibuf, m_ibufsz, 0, (struct sockaddr *)&sa, &sa_len);
+ }
+ if (n == -1)
+ {
+#ifdef _WIN32
+ if (Errno != WSAEWOULDBLOCK)
+#else
+ if (Errno != EWOULDBLOCK)
+#endif
+ Handler().LogError(this, "recvfrom", Errno, StrError(Errno), LOG_LEVEL_ERROR);
+ }
+}
+
+void UdpSocket::SetBroadcast(bool b)
+{
+ int one = 1;
+ int zero = 0;
+
+ if (GetSocket() == INVALID_SOCKET)
+ {
+ CreateConnection();
+ }
+ if (b)
+ {
+ if (setsockopt(GetSocket(), SOL_SOCKET, SO_BROADCAST, (char *) &one, sizeof(one)) == -1)
+ {
+ Handler().LogError(this, "SetBroadcast", Errno, StrError(Errno), LOG_LEVEL_WARNING);
+ }
+ }
+ else
+ {
+ if (setsockopt(GetSocket(), SOL_SOCKET, SO_BROADCAST, (char *) &zero, sizeof(zero)) == -1)
+ {
+ Handler().LogError(this, "SetBroadcast", Errno, StrError(Errno), LOG_LEVEL_WARNING);
+ }
+ }
+}
+
+bool UdpSocket::IsBroadcast()
+{
+ int is_broadcast = 0;
+ socklen_t size;
+
+ if (GetSocket() == INVALID_SOCKET)
+ {
+ CreateConnection();
+ }
+ if (getsockopt(GetSocket(), SOL_SOCKET, SO_BROADCAST, (char *)&is_broadcast, &size) == -1)
+ {
+ Handler().LogError(this, "IsBroadcast", Errno, StrError(Errno), LOG_LEVEL_WARNING);
+ }
+ return is_broadcast != 0;
+}
+
+void UdpSocket::SetMulticastTTL(int ttl)
+{
+ if (GetSocket() == INVALID_SOCKET)
+ {
+ CreateConnection();
+ }
+ if (setsockopt(GetSocket(), SOL_IP, IP_MULTICAST_TTL, (char *)&ttl, sizeof(int)) == -1)
+ {
+ Handler().LogError(this, "SetMulticastTTL", Errno, StrError(Errno), LOG_LEVEL_WARNING);
+ }
+}
+
+int UdpSocket::GetMulticastTTL()
+{
+ int ttl = 0;
+ socklen_t size = sizeof(int);
+
+ if (GetSocket() == INVALID_SOCKET)
+ {
+ CreateConnection();
+ }
+ if (getsockopt(GetSocket(), SOL_IP, IP_MULTICAST_TTL, (char *)&ttl, &size) == -1)
+ {
+ Handler().LogError(this, "GetMulticastTTL", Errno, StrError(Errno), LOG_LEVEL_WARNING);
+ }
+ return ttl;
+}
+
+void UdpSocket::SetMulticastLoop(bool x)
+{
+ if (GetSocket() == INVALID_SOCKET)
+ {
+ CreateConnection();
+ }
+#ifdef ENABLE_IPV6
+#ifdef IPPROTO_IPV6
+ if (IsIpv6())
+ {
+ int val = x ? 1 : 0;
+ if (setsockopt(GetSocket(), IPPROTO_IPV6, IPV6_MULTICAST_LOOP, (char *)&val, sizeof(int)) == -1)
+ {
+ Handler().LogError(this, "SetMulticastLoop", Errno, StrError(Errno), LOG_LEVEL_WARNING);
+ }
+ return;
+ }
+#endif
+#endif
+ int val = x ? 1 : 0;
+ if (setsockopt(GetSocket(), SOL_IP, IP_MULTICAST_LOOP, (char *)&val, sizeof(int)) == -1)
+ {
+ Handler().LogError(this, "SetMulticastLoop", Errno, StrError(Errno), LOG_LEVEL_WARNING);
+ }
+}
+
+bool UdpSocket::IsMulticastLoop()
+{
+ if (GetSocket() == INVALID_SOCKET)
+ {
+ CreateConnection();
+ }
+#ifdef ENABLE_IPV6
+#ifdef IPPROTO_IPV6
+ if (IsIpv6())
+ {
+ int is_loop = 0;
+ socklen_t size = sizeof(int);
+ if (getsockopt(GetSocket(), IPPROTO_IPV6, IPV6_MULTICAST_LOOP, (char *)&is_loop, &size) == -1)
+ {
+ Handler().LogError(this, "IsMulticastLoop", Errno, StrError(Errno), LOG_LEVEL_WARNING);
+ }
+ return is_loop ? true : false;
+ }
+#endif
+#endif
+ int is_loop = 0;
+ socklen_t size = sizeof(int);
+ if (getsockopt(GetSocket(), SOL_IP, IP_MULTICAST_LOOP, (char *)&is_loop, &size) == -1)
+ {
+ Handler().LogError(this, "IsMulticastLoop", Errno, StrError(Errno), LOG_LEVEL_WARNING);
+ }
+ return is_loop ? true : false;
+}
+
+void UdpSocket::AddMulticastMembership(const std::string& group, const std::string& local_if, int if_index)
+{
+ if (GetSocket() == INVALID_SOCKET)
+ {
+ CreateConnection();
+ }
+#ifdef ENABLE_IPV6
+#ifdef IPPROTO_IPV6
+ if (IsIpv6())
+ {
+ struct ipv6_mreq x;
+ struct in6_addr addr;
+ if (Utility::u2ip( group, addr ))
+ {
+ x.ipv6mr_multiaddr = addr;
+ x.ipv6mr_interface = if_index;
+ if (setsockopt(GetSocket(), IPPROTO_IPV6, IPV6_ADD_MEMBERSHIP, (char *)&x, sizeof(struct ipv6_mreq)) == -1)
+ {
+ Handler().LogError(this, "AddMulticastMembership", Errno, StrError(Errno), LOG_LEVEL_WARNING);
+ }
+ }
+ return;
+ }
+#endif
+#endif
+ struct ip_mreq x; // ip_mreqn
+ ipaddr_t addr;
+ if (Utility::u2ip( group, addr ))
+ {
+ memcpy(&x.imr_multiaddr.s_addr, &addr, sizeof(addr));
+ Utility::u2ip( local_if, addr);
+ memcpy(&x.imr_interface.s_addr, &addr, sizeof(addr));
+// x.imr_ifindex = if_index;
+ if (setsockopt(GetSocket(), SOL_IP, IP_ADD_MEMBERSHIP, (char *)&x, sizeof(struct ip_mreq)) == -1)
+ {
+ Handler().LogError(this, "AddMulticastMembership", Errno, StrError(Errno), LOG_LEVEL_WARNING);
+ }
+ }
+}
+
+void UdpSocket::DropMulticastMembership(const std::string& group, const std::string& local_if, int if_index)
+{
+ if (GetSocket() == INVALID_SOCKET)
+ {
+ CreateConnection();
+ }
+#ifdef ENABLE_IPV6
+#ifdef IPPROTO_IPV6
+ if (IsIpv6())
+ {
+ struct ipv6_mreq x;
+ struct in6_addr addr;
+ if (Utility::u2ip( group, addr ))
+ {
+ x.ipv6mr_multiaddr = addr;
+ x.ipv6mr_interface = if_index;
+ if (setsockopt(GetSocket(), IPPROTO_IPV6, IPV6_DROP_MEMBERSHIP, (char *)&x, sizeof(struct ipv6_mreq)) == -1)
+ {
+ Handler().LogError(this, "DropMulticastMembership", Errno, StrError(Errno), LOG_LEVEL_WARNING);
+ }
+ }
+ return;
+ }
+#endif
+#endif
+ struct ip_mreq x; // ip_mreqn
+ ipaddr_t addr;
+ if (Utility::u2ip( group, addr ))
+ {
+ memcpy(&x.imr_multiaddr.s_addr, &addr, sizeof(addr));
+ Utility::u2ip( local_if, addr);
+ memcpy(&x.imr_interface.s_addr, &addr, sizeof(addr));
+// x.imr_ifindex = if_index;
+ if (setsockopt(GetSocket(), SOL_IP, IP_DROP_MEMBERSHIP, (char *)&x, sizeof(struct ip_mreq)) == -1)
+ {
+ Handler().LogError(this, "DropMulticastMembership", Errno, StrError(Errno), LOG_LEVEL_WARNING);
+ }
+ }
+}
+
+#ifdef ENABLE_IPV6
+#ifdef IPPROTO_IPV6
+void UdpSocket::SetMulticastHops(int hops)
+{
+ if (GetSocket() == INVALID_SOCKET)
+ {
+ CreateConnection();
+ }
+ if (!IsIpv6())
+ {
+ Handler().LogError(this, "SetMulticastHops", 0, "Ipv6 only", LOG_LEVEL_ERROR);
+ return;
+ }
+ if (setsockopt(GetSocket(), IPPROTO_IPV6, IPV6_MULTICAST_HOPS, (char *)&hops, sizeof(int)) == -1)
+ {
+ Handler().LogError(this, "SetMulticastHops", Errno, StrError(Errno), LOG_LEVEL_WARNING);
+ }
+}
+
+int UdpSocket::GetMulticastHops()
+{
+ if (GetSocket() == INVALID_SOCKET)
+ {
+ CreateConnection();
+ }
+ if (!IsIpv6())
+ {
+ Handler().LogError(this, "SetMulticastHops", 0, "Ipv6 only", LOG_LEVEL_ERROR);
+ return -1;
+ }
+ int hops = 0;
+ socklen_t size = sizeof(int);
+ if (getsockopt(GetSocket(), IPPROTO_IPV6, IPV6_MULTICAST_HOPS, (char *)&hops, &size) == -1)
+ {
+ Handler().LogError(this, "GetMulticastHops", Errno, StrError(Errno), LOG_LEVEL_WARNING);
+ }
+ return hops;
+}
+#endif // IPPROTO_IPV6
+#endif
+
+bool UdpSocket::IsBound()
+{
+ return m_bind_ok;
+}
+
+void UdpSocket::OnRawData(const char *buf, size_t len, struct sockaddr *sa, socklen_t sa_len)
+{
+}
+
+void UdpSocket::OnRawData(const char *buf, size_t len, struct sockaddr *sa, socklen_t sa_len, struct timeval *ts)
+{
+}
+
+port_t UdpSocket::GetPort()
+{
+ return m_port;
+}
+
+int UdpSocket::GetLastSizeWritten()
+{
+ return m_last_size_written;
+}
+
+void UdpSocket::SetTimestamp(bool x)
+{
+ m_b_read_ts = x;
+}
+
+#ifdef SOCKETS_NAMESPACE
+}
+#endif
+
+
diff --git a/externals/sockets/Utility.cpp b/externals/sockets/Utility.cpp
new file mode 100644
index 00000000000..7c093fc0832
--- /dev/null
+++ b/externals/sockets/Utility.cpp
@@ -0,0 +1,960 @@
+/** \file Utility.cpp
+ ** \date 2004-02-13
+ ** \author grymse@alhem.net
+**/
+/*
+Copyright (C) 2004-2007 Anders Hedstrom
+
+This library is made available under the terms of the GNU GPL.
+
+If you would like to use this library in a closed-source application,
+a separate license agreement is available. For information about
+the closed-source license agreement for the C++ sockets library,
+please visit http://www.alhem.net/Sockets/license.html and/or
+email license@alhem.net.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+#include "Utility.h"
+#include "Parse.h"
+#include "Ipv4Address.h"
+#include "Ipv6Address.h"
+#include "Base64.h"
+#include <vector>
+#ifdef _WIN32
+#include <time.h>
+#else
+#include <netdb.h>
+#include <pthread.h>
+#endif
+#include <map>
+
+#ifdef SOCKETS_NAMESPACE
+namespace SOCKETS_NAMESPACE {
+#endif
+
+// defines for the random number generator
+#define TWIST_IA 397
+#define TWIST_IB (TWIST_LEN - TWIST_IA)
+#define UMASK 0x80000000
+#define LMASK 0x7FFFFFFF
+#define MATRIX_A 0x9908B0DF
+#define TWIST(b,i,j) ((b)[i] & UMASK) | ((b)[j] & LMASK)
+#define MAGIC_TWIST(s) (((s) & 1) * MATRIX_A)
+
+// statics
+std::string Utility::m_host;
+bool Utility::m_local_resolved = false;
+ipaddr_t Utility::m_ip = 0;
+std::string Utility::m_addr;
+#ifdef ENABLE_IPV6
+#ifdef IPPROTO_IPV6
+struct in6_addr Utility::m_local_ip6;
+std::string Utility::m_local_addr6;
+#endif
+#endif
+
+std::string Utility::base64(const std::string& str_in)
+{
+ std::string str;
+ Base64 m_b;
+ m_b.encode(str_in, str, false); // , false == do not add cr/lf
+ return str;
+}
+
+std::string Utility::base64d(const std::string& str_in)
+{
+ std::string str;
+ Base64 m_b;
+ m_b.decode(str_in, str);
+ return str;
+}
+
+std::string Utility::l2string(long l)
+{
+ std::string str;
+ char tmp[100];
+ sprintf(tmp,"%ld",l);
+ str = tmp;
+ return str;
+}
+
+std::string Utility::bigint2string(uint64_t l)
+{
+ std::string str;
+ uint64_t tmp = l;
+ while (tmp)
+ {
+ uint64_t a = tmp % 10;
+ str = (char)(a + 48) + str;
+ tmp /= 10;
+ }
+ if (str.empty())
+ {
+ str = "0";
+ }
+ return str;
+}
+
+uint64_t Utility::atoi64(const std::string& str)
+{
+ uint64_t l = 0;
+ for (size_t i = 0; i < str.size(); i++)
+ {
+ l = l * 10 + str[i] - 48;
+ }
+ return l;
+}
+
+unsigned int Utility::hex2unsigned(const std::string& str)
+{
+ unsigned int r = 0;
+ for (size_t i = 0; i < str.size(); i++)
+ {
+ r = r * 16 + str[i] - 48 - ((str[i] >= 'A') ? 7 : 0) - ((str[i] >= 'a') ? 32 : 0);
+ }
+ return r;
+}
+
+/*
+* Encode string per RFC1738 URL encoding rules
+* tnx rstaveley
+*/
+std::string Utility::rfc1738_encode(const std::string& src)
+{
+static char hex[] = "0123456789ABCDEF";
+ std::string dst;
+ for (size_t i = 0; i < src.size(); i++)
+ {
+ if (isalnum(src[i]))
+ {
+ dst += src[i];
+ }
+ else
+ if (src[i] == ' ')
+ {
+ dst += '+';
+ }
+ else
+ {
+ unsigned char c = static_cast<unsigned char>(src[i]);
+ dst += '%';
+ dst += hex[c / 16];
+ dst += hex[c % 16];
+ }
+ }
+ return dst;
+} // rfc1738_encode
+
+/*
+* Decode string per RFC1738 URL encoding rules
+* tnx rstaveley
+*/
+std::string Utility::rfc1738_decode(const std::string& src)
+{
+ std::string dst;
+ for (size_t i = 0; i < src.size(); i++)
+ {
+ if (src[i] == '%' && isxdigit(src[i + 1]) && isxdigit(src[i + 2]))
+ {
+ char c1 = src[++i];
+ char c2 = src[++i];
+ c1 = c1 - 48 - ((c1 >= 'A') ? 7 : 0) - ((c1 >= 'a') ? 32 : 0);
+ c2 = c2 - 48 - ((c2 >= 'A') ? 7 : 0) - ((c2 >= 'a') ? 32 : 0);
+ dst += (char)(c1 * 16 + c2);
+ }
+ else
+ if (src[i] == '+')
+ {
+ dst += ' ';
+ }
+ else
+ {
+ dst += src[i];
+ }
+ }
+ return dst;
+} // rfc1738_decode
+
+bool Utility::isipv4(const std::string& str)
+{
+ int dots = 0;
+ // %! ignore :port?
+ for (size_t i = 0; i < str.size(); i++)
+ {
+ if (str[i] == '.')
+ dots++;
+ else
+ if (!isdigit(str[i]))
+ return false;
+ }
+ if (dots != 3)
+ return false;
+ return true;
+}
+
+bool Utility::isipv6(const std::string& str)
+{
+ size_t qc = 0;
+ size_t qd = 0;
+ for (size_t i = 0; i < str.size(); i++)
+ {
+ qc += (str[i] == ':') ? 1 : 0;
+ qd += (str[i] == '.') ? 1 : 0;
+ }
+ if (qc > 7)
+ {
+ return false;
+ }
+ if (qd && qd != 3)
+ {
+ return false;
+ }
+ Parse pa(str,":.");
+ std::string tmp = pa.getword();
+ while (!tmp.empty())
+ {
+ if (tmp.size() > 4)
+ {
+ return false;
+ }
+ for (size_t i = 0; i < tmp.size(); i++)
+ {
+ if (tmp[i] < '0' || (tmp[i] > '9' && tmp[i] < 'A') ||
+ (tmp[i] > 'F' && tmp[i] < 'a') || tmp[i] > 'f')
+ {
+ return false;
+ }
+ }
+ //
+ tmp = pa.getword();
+ }
+ return true;
+}
+
+bool Utility::u2ip(const std::string& str, ipaddr_t& l)
+{
+ struct sockaddr_in sa;
+ bool r = Utility::u2ip(str, sa);
+ memcpy(&l, &sa.sin_addr, sizeof(l));
+ return r;
+}
+
+#ifdef ENABLE_IPV6
+#ifdef IPPROTO_IPV6
+bool Utility::u2ip(const std::string& str, struct in6_addr& l)
+{
+ struct sockaddr_in6 sa;
+ bool r = Utility::u2ip(str, sa);
+ l = sa.sin6_addr;
+ return r;
+}
+#endif
+#endif
+
+void Utility::l2ip(const ipaddr_t ip, std::string& str)
+{
+ struct sockaddr_in sa;
+ memset(&sa, 0, sizeof(sa));
+ sa.sin_family = AF_INET;
+ memcpy(&sa.sin_addr, &ip, sizeof(sa.sin_addr));
+ Utility::reverse( (struct sockaddr *)&sa, sizeof(sa), str, NI_NUMERICHOST);
+}
+
+void Utility::l2ip(const in_addr& ip, std::string& str)
+{
+ struct sockaddr_in sa;
+ memset(&sa, 0, sizeof(sa));
+ sa.sin_family = AF_INET;
+ sa.sin_addr = ip;
+ Utility::reverse( (struct sockaddr *)&sa, sizeof(sa), str, NI_NUMERICHOST);
+}
+
+#ifdef ENABLE_IPV6
+#ifdef IPPROTO_IPV6
+void Utility::l2ip(const struct in6_addr& ip, std::string& str,bool mixed)
+{
+ char slask[100]; // l2ip temporary
+ *slask = 0;
+ unsigned int prev = 0;
+ bool skipped = false;
+ bool ok_to_skip = true;
+ if (mixed)
+ {
+ unsigned short x;
+ unsigned short addr16[8];
+ memcpy(addr16, &ip, sizeof(addr16));
+ for (size_t i = 0; i < 6; i++)
+ {
+ x = ntohs(addr16[i]);
+ if (*slask && (x || !ok_to_skip || prev))
+ strcat(slask,":");
+ if (x || !ok_to_skip)
+ {
+ sprintf(slask + strlen(slask),"%x", x);
+ if (x && skipped)
+ ok_to_skip = false;
+ }
+ else
+ {
+ skipped = true;
+ }
+ prev = x;
+ }
+ x = ntohs(addr16[6]);
+ sprintf(slask + strlen(slask),":%u.%u",x / 256,x & 255);
+ x = ntohs(addr16[7]);
+ sprintf(slask + strlen(slask),".%u.%u",x / 256,x & 255);
+ }
+ else
+ {
+ struct sockaddr_in6 sa;
+ memset(&sa, 0, sizeof(sa));
+ sa.sin6_family = AF_INET6;
+ sa.sin6_addr = ip;
+ Utility::reverse( (struct sockaddr *)&sa, sizeof(sa), str, NI_NUMERICHOST);
+ return;
+ }
+ str = slask;
+}
+
+int Utility::in6_addr_compare(in6_addr a,in6_addr b)
+{
+ for (size_t i = 0; i < 16; i++)
+ {
+ if (a.s6_addr[i] < b.s6_addr[i])
+ return -1;
+ if (a.s6_addr[i] > b.s6_addr[i])
+ return 1;
+ }
+ return 0;
+}
+#endif
+#endif
+
+void Utility::ResolveLocal()
+{
+ char h[256];
+
+ // get local hostname and translate into ip-address
+ *h = 0;
+ gethostname(h,255);
+ {
+ if (Utility::u2ip(h, m_ip))
+ {
+ Utility::l2ip(m_ip, m_addr);
+ }
+ }
+#ifdef ENABLE_IPV6
+#ifdef IPPROTO_IPV6
+ memset(&m_local_ip6, 0, sizeof(m_local_ip6));
+ {
+ if (Utility::u2ip(h, m_local_ip6))
+ {
+ Utility::l2ip(m_local_ip6, m_local_addr6);
+ }
+ }
+#endif
+#endif
+ m_host = h;
+ m_local_resolved = true;
+}
+
+const std::string& Utility::GetLocalHostname()
+{
+ if (!m_local_resolved)
+ {
+ ResolveLocal();
+ }
+ return m_host;
+}
+
+ipaddr_t Utility::GetLocalIP()
+{
+ if (!m_local_resolved)
+ {
+ ResolveLocal();
+ }
+ return m_ip;
+}
+
+const std::string& Utility::GetLocalAddress()
+{
+ if (!m_local_resolved)
+ {
+ ResolveLocal();
+ }
+ return m_addr;
+}
+
+#ifdef ENABLE_IPV6
+#ifdef IPPROTO_IPV6
+const struct in6_addr& Utility::GetLocalIP6()
+{
+ if (!m_local_resolved)
+ {
+ ResolveLocal();
+ }
+ return m_local_ip6;
+}
+
+const std::string& Utility::GetLocalAddress6()
+{
+ if (!m_local_resolved)
+ {
+ ResolveLocal();
+ }
+ return m_local_addr6;
+}
+#endif
+#endif
+
+void Utility::SetEnv(const std::string& var,const std::string& value)
+{
+#if (defined(SOLARIS8) || defined(SOLARIS))
+ {
+ static std::map<std::string, char *> vmap;
+ if (vmap.find(var) != vmap.end())
+ {
+ delete[] vmap[var];
+ }
+ vmap[var] = new char[var.size() + 1 + value.size() + 1];
+ sprintf(vmap[var], "%s=%s", var.c_str(), value.c_str());
+ putenv( vmap[var] );
+ }
+#elif defined _WIN32
+ {
+ std::string slask = var + "=" + value;
+ _putenv( (char *)slask.c_str());
+ }
+#else
+ setenv(var.c_str(), value.c_str(), 1);
+#endif
+}
+
+std::string Utility::Sa2String(struct sockaddr *sa)
+{
+#ifdef ENABLE_IPV6
+#ifdef IPPROTO_IPV6
+ if (sa -> sa_family == AF_INET6)
+ {
+ struct sockaddr_in6 *sa6 = (struct sockaddr_in6 *)sa;
+ std::string tmp;
+ Utility::l2ip(sa6 -> sin6_addr, tmp);
+ return tmp + ":" + Utility::l2string(ntohs(sa6 -> sin6_port));
+ }
+#endif
+#endif
+ if (sa -> sa_family == AF_INET)
+ {
+ struct sockaddr_in *sa4 = (struct sockaddr_in *)sa;
+ ipaddr_t a;
+ memcpy(&a, &sa4 -> sin_addr, 4);
+ std::string tmp;
+ Utility::l2ip(a, tmp);
+ return tmp + ":" + Utility::l2string(ntohs(sa4 -> sin_port));
+ }
+ return "";
+}
+
+void Utility::GetTime(struct timeval *p)
+{
+#ifdef _WIN32
+ FILETIME ft; // Contains a 64-bit value representing the number of 100-nanosecond intervals since January 1, 1601 (UTC).
+ GetSystemTimeAsFileTime(&ft);
+ uint64_t tt;
+ memcpy(&tt, &ft, sizeof(tt));
+ tt /= 10; // make it usecs
+ p->tv_sec = (long)tt / 1000000;
+ p->tv_usec = (long)tt % 1000000;
+#else
+ gettimeofday(p, NULL);
+#endif
+}
+
+std::auto_ptr<SocketAddress> Utility::CreateAddress(struct sockaddr *sa,socklen_t sa_len)
+{
+ switch (sa -> sa_family)
+ {
+ case AF_INET:
+ if (sa_len == sizeof(struct sockaddr_in))
+ {
+ struct sockaddr_in *p = (struct sockaddr_in *)sa;
+ return std::auto_ptr<SocketAddress>(new Ipv4Address(*p));
+ }
+ break;
+#ifdef ENABLE_IPV6
+#ifdef IPPROTO_IPV6
+ case AF_INET6:
+ if (sa_len == sizeof(struct sockaddr_in6))
+ {
+ struct sockaddr_in6 *p = (struct sockaddr_in6 *)sa;
+ return std::auto_ptr<SocketAddress>(new Ipv6Address(*p));
+ }
+ break;
+#endif
+#endif
+ }
+ return std::auto_ptr<SocketAddress>(NULL);
+}
+
+bool Utility::u2ip(const std::string& host, struct sockaddr_in& sa, int ai_flags)
+{
+ memset(&sa, 0, sizeof(sa));
+ sa.sin_family = AF_INET;
+#ifdef NO_GETADDRINFO
+ if ((ai_flags & AI_NUMERICHOST) != 0 || isipv4(host))
+ {
+ Parse pa((char *)host.c_str(), ".");
+ union {
+ struct {
+ unsigned char b1;
+ unsigned char b2;
+ unsigned char b3;
+ unsigned char b4;
+ } a;
+ ipaddr_t l;
+ } u;
+ u.a.b1 = static_cast<unsigned char>(pa.getvalue());
+ u.a.b2 = static_cast<unsigned char>(pa.getvalue());
+ u.a.b3 = static_cast<unsigned char>(pa.getvalue());
+ u.a.b4 = static_cast<unsigned char>(pa.getvalue());
+ memcpy(&sa.sin_addr, &u.l, sizeof(sa.sin_addr));
+ return true;
+ }
+#ifndef LINUX
+ struct hostent *he = gethostbyname( host.c_str() );
+ if (!he)
+ {
+ return false;
+ }
+ memcpy(&sa.sin_addr, he -> h_addr, sizeof(sa.sin_addr));
+#else
+ struct hostent he;
+ struct hostent *result = NULL;
+ int myerrno = 0;
+ char buf[2000];
+ int n = gethostbyname_r(host.c_str(), &he, buf, sizeof(buf), &result, &myerrno);
+ if (n || !result)
+ {
+ return false;
+ }
+ if (he.h_addr_list && he.h_addr_list[0])
+ memcpy(&sa.sin_addr, he.h_addr, 4);
+ else
+ return false;
+#endif
+ return true;
+#else
+ struct addrinfo hints;
+ memset(&hints, 0, sizeof(hints));
+ // AI_NUMERICHOST
+ // AI_CANONNAME
+ // AI_PASSIVE - server
+ // AI_ADDRCONFIG
+ // AI_V4MAPPED
+ // AI_ALL
+ // AI_NUMERICSERV
+ hints.ai_flags = ai_flags;
+ hints.ai_family = AF_INET;
+ hints.ai_socktype = 0;
+ hints.ai_protocol = 0;
+ struct addrinfo *res;
+ if (Utility::isipv4(host))
+ hints.ai_flags |= AI_NUMERICHOST;
+ int n = getaddrinfo(host.c_str(), NULL, &hints, &res);
+ if (!n)
+ {
+ std::vector<struct addrinfo *> vec;
+ struct addrinfo *ai = res;
+ while (ai)
+ {
+ if (ai -> ai_addrlen == sizeof(sa))
+ vec.push_back( ai );
+ ai = ai -> ai_next;
+ }
+ if (vec.empty())
+ return false;
+ ai = vec[Utility::Rnd() % vec.size()];
+ {
+ memcpy(&sa, ai -> ai_addr, ai -> ai_addrlen);
+ }
+ freeaddrinfo(res);
+ return true;
+ }
+ std::string error = "Error: ";
+#ifndef __CYGWIN__
+ error += gai_strerror(n);
+#endif
+ return false;
+#endif // NO_GETADDRINFO
+}
+
+#ifdef ENABLE_IPV6
+#ifdef IPPROTO_IPV6
+bool Utility::u2ip(const std::string& host, struct sockaddr_in6& sa, int ai_flags)
+{
+ memset(&sa, 0, sizeof(sa));
+ sa.sin6_family = AF_INET6;
+#ifdef NO_GETADDRINFO
+ if ((ai_flags & AI_NUMERICHOST) != 0 || isipv6(host))
+ {
+ std::list<std::string> vec;
+ size_t x = 0;
+ for (size_t i = 0; i <= host.size(); i++)
+ {
+ if (i == host.size() || host[i] == ':')
+ {
+ std::string s = host.substr(x, i - x);
+ //
+ if (strstr(s.c_str(),".")) // x.x.x.x
+ {
+ Parse pa(s,".");
+ char slask[100]; // u2ip temporary hex2string conversion
+ unsigned long b0 = static_cast<unsigned long>(pa.getvalue());
+ unsigned long b1 = static_cast<unsigned long>(pa.getvalue());
+ unsigned long b2 = static_cast<unsigned long>(pa.getvalue());
+ unsigned long b3 = static_cast<unsigned long>(pa.getvalue());
+ sprintf(slask,"%lx",b0 * 256 + b1);
+ vec.push_back(slask);
+ sprintf(slask,"%lx",b2 * 256 + b3);
+ vec.push_back(slask);
+ }
+ else
+ {
+ vec.push_back(s);
+ }
+ //
+ x = i + 1;
+ }
+ }
+ size_t sz = vec.size(); // number of byte pairs
+ size_t i = 0; // index in in6_addr.in6_u.u6_addr16[] ( 0 .. 7 )
+ unsigned short addr16[8];
+ for (std::list<std::string>::iterator it = vec.begin(); it != vec.end(); it++)
+ {
+ std::string bytepair = *it;
+ if (!bytepair.empty())
+ {
+ addr16[i++] = htons(Utility::hex2unsigned(bytepair));
+ }
+ else
+ {
+ addr16[i++] = 0;
+ while (sz++ < 8)
+ {
+ addr16[i++] = 0;
+ }
+ }
+ }
+ memcpy(&sa.sin6_addr, addr16, sizeof(addr16));
+ return true;
+ }
+#ifdef SOLARIS
+ int errnum = 0;
+ struct hostent *he = getipnodebyname( host.c_str(), AF_INET6, 0, &errnum );
+#else
+ struct hostent *he = gethostbyname2( host.c_str(), AF_INET6 );
+#endif
+ if (!he)
+ {
+ return false;
+ }
+ memcpy(&sa.sin6_addr,he -> h_addr_list[0],he -> h_length);
+#ifdef SOLARIS
+ free(he);
+#endif
+ return true;
+#else
+ struct addrinfo hints;
+ memset(&hints, 0, sizeof(hints));
+ hints.ai_flags = ai_flags;
+ hints.ai_family = AF_INET6;
+ hints.ai_socktype = 0;
+ hints.ai_protocol = 0;
+ struct addrinfo *res;
+ if (Utility::isipv6(host))
+ hints.ai_flags |= AI_NUMERICHOST;
+ int n = getaddrinfo(host.c_str(), NULL, &hints, &res);
+ if (!n)
+ {
+ std::vector<struct addrinfo *> vec;
+ struct addrinfo *ai = res;
+ while (ai)
+ {
+ if (ai -> ai_addrlen == sizeof(sa))
+ vec.push_back( ai );
+ ai = ai -> ai_next;
+ }
+ if (vec.empty())
+ return false;
+ ai = vec[Utility::Rnd() % vec.size()];
+ {
+ memcpy(&sa, ai -> ai_addr, ai -> ai_addrlen);
+ }
+ freeaddrinfo(res);
+ return true;
+ }
+ std::string error = "Error: ";
+#ifndef __CYGWIN__
+ error += gai_strerror(n);
+#endif
+ return false;
+#endif // NO_GETADDRINFO
+}
+#endif // IPPROTO_IPV6
+#endif // ENABLE_IPV6
+
+bool Utility::reverse(struct sockaddr *sa, socklen_t sa_len, std::string& hostname, int flags)
+{
+ std::string service;
+ return Utility::reverse(sa, sa_len, hostname, service, flags);
+}
+
+bool Utility::reverse(struct sockaddr *sa, socklen_t sa_len, std::string& hostname, std::string& service, int flags)
+{
+ hostname = "";
+ service = "";
+#ifdef NO_GETADDRINFO
+ switch (sa -> sa_family)
+ {
+ case AF_INET:
+ if (flags & NI_NUMERICHOST)
+ {
+ union {
+ struct {
+ unsigned char b1;
+ unsigned char b2;
+ unsigned char b3;
+ unsigned char b4;
+ } a;
+ ipaddr_t l;
+ } u;
+ struct sockaddr_in *sa_in = (struct sockaddr_in *)sa;
+ memcpy(&u.l, &sa_in -> sin_addr, sizeof(u.l));
+ char tmp[100];
+ sprintf(tmp, "%u.%u.%u.%u", u.a.b1, u.a.b2, u.a.b3, u.a.b4);
+ hostname = tmp;
+ return true;
+ }
+ else
+ {
+ struct sockaddr_in *sa_in = (struct sockaddr_in *)sa;
+ struct hostent *h = gethostbyaddr( (const char *)&sa_in -> sin_addr, sizeof(sa_in -> sin_addr), AF_INET);
+ if (h)
+ {
+ hostname = h -> h_name;
+ return true;
+ }
+ }
+ break;
+#ifdef ENABLE_IPV6
+ case AF_INET6:
+ if (flags & NI_NUMERICHOST)
+ {
+ char slask[100]; // l2ip temporary
+ *slask = 0;
+ unsigned int prev = 0;
+ bool skipped = false;
+ bool ok_to_skip = true;
+ {
+ unsigned short addr16[8];
+ struct sockaddr_in6 *sa_in6 = (struct sockaddr_in6 *)sa;
+ memcpy(addr16, &sa_in6 -> sin6_addr, sizeof(addr16));
+ for (size_t i = 0; i < 8; i++)
+ {
+ unsigned short x = ntohs(addr16[i]);
+ if (*slask && (x || !ok_to_skip || prev))
+ strcat(slask,":");
+ if (x || !ok_to_skip)
+ {
+ sprintf(slask + strlen(slask),"%x", x);
+ if (x && skipped)
+ ok_to_skip = false;
+ }
+ else
+ {
+ skipped = true;
+ }
+ prev = x;
+ }
+ }
+ if (!*slask)
+ strcpy(slask, "::");
+ hostname = slask;
+ return true;
+ }
+ else
+ {
+ // %! TODO: ipv6 reverse lookup
+ struct sockaddr_in6 *sa_in = (struct sockaddr_in6 *)sa;
+ struct hostent *h = gethostbyaddr( (const char *)&sa_in -> sin6_addr, sizeof(sa_in -> sin6_addr), AF_INET6);
+ if (h)
+ {
+ hostname = h -> h_name;
+ return true;
+ }
+ }
+ break;
+#endif
+ }
+ return false;
+#else
+ char host[NI_MAXHOST];
+ char serv[NI_MAXSERV];
+ // NI_NOFQDN
+ // NI_NUMERICHOST
+ // NI_NAMEREQD
+ // NI_NUMERICSERV
+ // NI_DGRAM
+ int n = getnameinfo(sa, sa_len, host, sizeof(host), serv, sizeof(serv), flags);
+ if (n)
+ {
+ // EAI_AGAIN
+ // EAI_BADFLAGS
+ // EAI_FAIL
+ // EAI_FAMILY
+ // EAI_MEMORY
+ // EAI_NONAME
+ // EAI_OVERFLOW
+ // EAI_SYSTEM
+ return false;
+ }
+ hostname = host;
+ service = serv;
+ return true;
+#endif // NO_GETADDRINFO
+}
+
+bool Utility::u2service(const std::string& name, int& service, int ai_flags)
+{
+#ifdef NO_GETADDRINFO
+ // %!
+ return false;
+#else
+ struct addrinfo hints;
+ service = 0;
+ memset(&hints, 0, sizeof(hints));
+ // AI_NUMERICHOST
+ // AI_CANONNAME
+ // AI_PASSIVE - server
+ // AI_ADDRCONFIG
+ // AI_V4MAPPED
+ // AI_ALL
+ // AI_NUMERICSERV
+ hints.ai_flags = ai_flags;
+ hints.ai_family = AF_UNSPEC;
+ hints.ai_socktype = 0;
+ hints.ai_protocol = 0;
+ struct addrinfo *res;
+ int n = getaddrinfo(NULL, name.c_str(), &hints, &res);
+ if (!n)
+ {
+ service = res -> ai_protocol;
+ freeaddrinfo(res);
+ return true;
+ }
+ return false;
+#endif // NO_GETADDRINFO
+}
+
+unsigned long Utility::ThreadID()
+{
+#ifdef _WIN32
+ return GetCurrentThreadId();
+#else
+ return (unsigned long)pthread_self();
+#endif
+}
+
+std::string Utility::ToLower(const std::string& str)
+{
+ std::string r;
+ for (size_t i = 0; i < str.size(); i++)
+ {
+ if (str[i] >= 'A' && str[i] <= 'Z')
+ r += str[i] | 32;
+ else
+ r += str[i];
+ }
+ return r;
+}
+
+std::string Utility::ToUpper(const std::string& str)
+{
+ std::string r;
+ for (size_t i = 0; i < str.size(); i++)
+ {
+ if (str[i] >= 'a' && str[i] <= 'z')
+ r += (char)(str[i] - 32);
+ else
+ r += str[i];
+ }
+ return r;
+}
+
+std::string Utility::ToString(double d)
+{
+ char tmp[100];
+ sprintf(tmp, "%f", d);
+ return tmp;
+}
+
+unsigned long Utility::Rnd()
+{
+static Utility::Rng generator( (unsigned long)time(NULL) );
+ return generator.Get();
+}
+
+Utility::Rng::Rng(unsigned long seed) : m_value( 0 )
+{
+ m_tmp[0]= seed & 0xffffffffUL;
+ for (int i = 1; i < TWIST_LEN; i++)
+ {
+ m_tmp[i] = (1812433253UL * (m_tmp[i - 1] ^ (m_tmp[i - 1] >> 30)) + i);
+ }
+}
+
+unsigned long Utility::Rng::Get()
+{
+ unsigned long val = m_tmp[m_value];
+ ++m_value;
+ if (m_value == TWIST_LEN)
+ {
+ for (int i = 0; i < TWIST_IB; ++i)
+ {
+ unsigned long s = TWIST(m_tmp, i, i + 1);
+ m_tmp[i] = m_tmp[i + TWIST_IA] ^ (s >> 1) ^ MAGIC_TWIST(s);
+ }
+ {
+ for (int i = 0; i < TWIST_LEN - 1; ++i)
+ {
+ unsigned long s = TWIST(m_tmp, i, i + 1);
+ m_tmp[i] = m_tmp[i - TWIST_IB] ^ (s >> 1) ^ MAGIC_TWIST(s);
+ }
+ }
+ unsigned long s = TWIST(m_tmp, TWIST_LEN - 1, 0);
+ m_tmp[TWIST_LEN - 1] = m_tmp[TWIST_IA - 1] ^ (s >> 1) ^ MAGIC_TWIST(s);
+
+ m_value = 0;
+ }
+ return val;
+}
+
+#ifdef SOCKETS_NAMESPACE
+}
+#endif
+
+
diff --git a/externals/sockets/delme b/externals/sockets/delme
deleted file mode 100644
index e69de29bb2d..00000000000
--- a/externals/sockets/delme
+++ /dev/null
diff --git a/externals/sockets/include/Base64.h b/externals/sockets/include/Base64.h
new file mode 100644
index 00000000000..d4323aaa019
--- /dev/null
+++ b/externals/sockets/include/Base64.h
@@ -0,0 +1,77 @@
+/** \file Base64.h
+ ** \date 2004-02-13
+ ** \author grymse@alhem.net
+**/
+/*
+Copyright (C) 2004-2007 Anders Hedstrom
+
+This library is made available under the terms of the GNU GPL.
+
+If you would like to use this library in a closed-source application,
+a separate license agreement is available. For information about
+the closed-source license agreement for the C++ sockets library,
+please visit http://www.alhem.net/Sockets/license.html and/or
+email license@alhem.net.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+#ifndef _SOCKETS_Base64_H
+#define _SOCKETS_Base64_H
+
+#include "sockets-config.h"
+#ifdef _MSC_VER
+#pragma warning(disable:4514)
+#endif
+
+#include <stdio.h>
+#include <string>
+
+#ifdef SOCKETS_NAMESPACE
+namespace SOCKETS_NAMESPACE {
+#endif
+
+/** \defgroup util Utilities */
+
+/** Base64 encode/decode.
+ \ingroup util */
+class Base64
+{
+public:
+ Base64();
+
+ void encode(FILE *, std::string& , bool add_crlf = true);
+ void encode(const std::string&, std::string& , bool add_crlf = true);
+ void encode(const char *, size_t, std::string& , bool add_crlf = true);
+ void encode(const unsigned char *, size_t, std::string& , bool add_crlf = true);
+
+ void decode(const std::string&, std::string& );
+ void decode(const std::string&, unsigned char *, size_t&);
+
+ size_t decode_length(const std::string& );
+
+private:
+ Base64(const Base64& ) {}
+ Base64& operator=(const Base64& ) { return *this; }
+static const char *bstr;
+static const char rstr[128];
+};
+
+#ifdef SOCKETS_NAMESPACE
+}
+#endif
+
+#endif // _SOCKETS_Base64_H
+
+
diff --git a/externals/sockets/include/Exception.h b/externals/sockets/include/Exception.h
new file mode 100644
index 00000000000..bb881b2d74f
--- /dev/null
+++ b/externals/sockets/include/Exception.h
@@ -0,0 +1,55 @@
+/**
+ ** \file Exception.h
+ ** \date 2007-09-28
+ ** \author grymse@alhem.net
+**/
+/*
+Copyright (C) 2007 Anders Hedstrom
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+#ifndef _Sockets_Exception_H
+#define _Sockets_Exception_H
+
+#include <string>
+
+#ifdef SOCKETS_NAMESPACE
+namespace SOCKETS_NAMESPACE {
+#endif
+
+class Exception
+{
+public:
+ Exception(const std::string& description);
+ virtual ~Exception() {}
+
+ virtual const std::string ToString() const;
+
+ Exception(const Exception& ) {} // copy constructor
+
+ Exception& operator=(const Exception& ) { return *this; } // assignment operator
+
+private:
+ std::string m_description;
+
+};
+
+#ifdef SOCKETS_NAMESPACE
+} // namespace SOCKETS_NAMESPACE {
+#endif
+
+#endif // _Sockets_Exception_H
+
+
diff --git a/externals/sockets/include/File.h b/externals/sockets/include/File.h
new file mode 100644
index 00000000000..ed322efa2d8
--- /dev/null
+++ b/externals/sockets/include/File.h
@@ -0,0 +1,82 @@
+/** \file File.h
+ ** \date 2005-04-25
+ ** \author grymse@alhem.net
+**/
+/*
+Copyright (C) 2004-2007 Anders Hedstrom
+
+This library is made available under the terms of the GNU GPL.
+
+If you would like to use this library in a closed-source application,
+a separate license agreement is available. For information about
+the closed-source license agreement for the C++ sockets library,
+please visit http://www.alhem.net/Sockets/license.html and/or
+email license@alhem.net.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+#ifndef _SOCKETS_File_H
+#define _SOCKETS_File_H
+
+#include "sockets-config.h"
+#include "IFile.h"
+#include <stdio.h>
+
+#ifdef SOCKETS_NAMESPACE
+namespace SOCKETS_NAMESPACE {
+#endif
+
+/** IFile implementation of a disk file.
+ \ingroup file */
+class File : public IFile
+{
+public:
+ File();
+ ~File();
+
+ bool fopen(const std::string&, const std::string&);
+ void fclose();
+
+ size_t fread(char *, size_t, size_t) const;
+ size_t fwrite(const char *, size_t, size_t);
+
+ char *fgets(char *, int) const;
+ void fprintf(const char *format, ...);
+
+ off_t size() const;
+ bool eof() const;
+
+ void reset_read() const;
+ void reset_write();
+
+private:
+ File(const File& ) {} // copy constructor
+ File& operator=(const File& ) { return *this; } // assignment operator
+
+ std::string m_path;
+ std::string m_mode;
+ FILE *m_fil;
+ mutable long m_rptr;
+ long m_wptr;
+};
+
+
+#ifdef SOCKETS_NAMESPACE
+}
+#endif
+
+#endif // _SOCKETS_File_H
+
+
diff --git a/externals/sockets/include/IFile.h b/externals/sockets/include/IFile.h
new file mode 100644
index 00000000000..657c8a4b1d9
--- /dev/null
+++ b/externals/sockets/include/IFile.h
@@ -0,0 +1,71 @@
+/** \file IFile.h
+ ** \date 2005-04-25
+ ** \author grymse@alhem.net
+**/
+/*
+Copyright (C) 2004-2007 Anders Hedstrom
+
+This library is made available under the terms of the GNU GPL.
+
+If you would like to use this library in a closed-source application,
+a separate license agreement is available. For information about
+the closed-source license agreement for the C++ sockets library,
+please visit http://www.alhem.net/Sockets/license.html and/or
+email license@alhem.net.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+#ifndef _SOCKETS_IFile_H
+#define _SOCKETS_IFile_H
+
+#include "sockets-config.h"
+#include <string>
+
+#ifdef SOCKETS_NAMESPACE
+namespace SOCKETS_NAMESPACE {
+#endif
+
+/** \defgroup file File handling */
+/** Pure virtual file I/O interface.
+ \ingroup file */
+class IFile
+{
+public:
+ virtual ~IFile() {}
+
+ virtual bool fopen(const std::string&, const std::string&) = 0;
+ virtual void fclose() = 0;
+
+ virtual size_t fread(char *, size_t, size_t) const = 0;
+ virtual size_t fwrite(const char *, size_t, size_t) = 0;
+
+ virtual char *fgets(char *, int) const = 0;
+ virtual void fprintf(const char *format, ...) = 0;
+
+ virtual off_t size() const = 0;
+ virtual bool eof() const = 0;
+
+ virtual void reset_read() const = 0;
+ virtual void reset_write() = 0;
+
+};
+
+#ifdef SOCKETS_NAMESPACE
+}
+#endif
+
+#endif // _SOCKETS_IFile_H
+
+
diff --git a/externals/sockets/include/ISocketHandler.h b/externals/sockets/include/ISocketHandler.h
new file mode 100644
index 00000000000..940783c104b
--- /dev/null
+++ b/externals/sockets/include/ISocketHandler.h
@@ -0,0 +1,231 @@
+/** \file ISocketHandler.h
+ ** \date 2004-02-13
+ ** \author grymse@alhem.net
+**/
+/*
+Copyright (C) 2004-2007 Anders Hedstrom
+
+This library is made available under the terms of the GNU GPL.
+
+If you would like to use this library in a closed-source application,
+a separate license agreement is available. For information about
+the closed-source license agreement for the C++ sockets library,
+please visit http://www.alhem.net/Sockets/license.html and/or
+email license@alhem.net.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+#ifndef _SOCKETS_ISocketHandler_H
+#define _SOCKETS_ISocketHandler_H
+#include "sockets-config.h"
+
+#include <list>
+
+#include "socket_include.h"
+#include "Socket.h"
+#include "StdLog.h"
+
+#ifdef SOCKETS_NAMESPACE
+namespace SOCKETS_NAMESPACE {
+#endif
+
+typedef enum {
+ LIST_CALLONCONNECT = 0,
+#ifdef ENABLE_DETACH
+ LIST_DETACH,
+#endif
+ LIST_TIMEOUT,
+ LIST_RETRY,
+ LIST_CLOSE
+} list_t;
+
+class SocketAddress;
+class Mutex;
+
+/** Socket container class, event generator.
+ \ingroup basic */
+class ISocketHandler
+{
+ friend class Socket;
+
+public:
+ /** Connection pool class for internal use by the ISocketHandler.
+ \ingroup internal */
+#ifdef ENABLE_POOL
+ class PoolSocket : public Socket
+ {
+ public:
+ PoolSocket(ISocketHandler& h,Socket *src) : Socket(h) {
+ CopyConnection( src );
+ SetIsClient();
+ }
+
+ void OnRead() {
+ Handler().LogError(this, "OnRead", 0, "data on hibernating socket", LOG_LEVEL_FATAL);
+ SetCloseAndDelete();
+ }
+ void OnOptions(int,int,int,SOCKET) {}
+
+ };
+#endif
+
+public:
+ virtual ~ISocketHandler() {}
+
+ /** Get mutex reference for threadsafe operations. */
+ virtual Mutex& GetMutex() const = 0;
+
+ /** Register StdLog object for error callback.
+ \param log Pointer to log class */
+ virtual void RegStdLog(StdLog *log) = 0;
+
+ /** Log error to log class for print out / storage. */
+ virtual void LogError(Socket *p,const std::string& user_text,int err,const std::string& sys_err,loglevel_t t = LOG_LEVEL_WARNING) = 0;
+
+ // -------------------------------------------------------------------------
+ // Socket stuff
+ // -------------------------------------------------------------------------
+ /** Add socket instance to socket map. Removal is always automatic. */
+ virtual void Add(Socket *) = 0;
+private:
+ /** Remove socket from socket map, used by Socket class. */
+ virtual void Remove(Socket *) = 0;
+public:
+ /** Get status of read/write/exception file descriptor set for a socket. */
+ virtual void Get(SOCKET s,bool& r,bool& w,bool& e) = 0;
+ /** Set read/write/exception file descriptor sets (fd_set). */
+ virtual void Set(SOCKET s,bool bRead,bool bWrite,bool bException = true) = 0;
+
+ /** Wait for events, generate callbacks. */
+ virtual int Select(long sec,long usec) = 0;
+ /** This method will not return until an event has been detected. */
+ virtual int Select() = 0;
+ /** Wait for events, generate callbacks. */
+ virtual int Select(struct timeval *tsel) = 0;
+
+ /** Check that a socket really is handled by this socket handler. */
+ virtual bool Valid(Socket *) = 0;
+ /** Return number of sockets handled by this handler. */
+ virtual size_t GetCount() = 0;
+
+ /** Override and return false to deny all incoming connections.
+ \param p ListenSocket class pointer (use GetPort to identify which one) */
+ virtual bool OkToAccept(Socket *p) = 0;
+
+ /** Called by Socket when a socket changes state. */
+ virtual void AddList(SOCKET s,list_t which_one,bool add) = 0;
+
+ // -------------------------------------------------------------------------
+ // Connection pool
+ // -------------------------------------------------------------------------
+#ifdef ENABLE_POOL
+ /** Find available open connection (used by connection pool). */
+ virtual ISocketHandler::PoolSocket *FindConnection(int type,const std::string& protocol,SocketAddress&) = 0;
+ /** Enable connection pool (by default disabled). */
+ virtual void EnablePool(bool = true) = 0;
+ /** Check pool status.
+ \return true if connection pool is enabled */
+ virtual bool PoolEnabled() = 0;
+#endif // ENABLE_POOL
+
+ // -------------------------------------------------------------------------
+ // Socks4
+ // -------------------------------------------------------------------------
+#ifdef ENABLE_SOCKS4
+ /** Set socks4 server ip that all new tcp sockets should use. */
+ virtual void SetSocks4Host(ipaddr_t) = 0;
+ /** Set socks4 server hostname that all new tcp sockets should use. */
+ virtual void SetSocks4Host(const std::string& ) = 0;
+ /** Set socks4 server port number that all new tcp sockets should use. */
+ virtual void SetSocks4Port(port_t) = 0;
+ /** Set optional socks4 userid. */
+ virtual void SetSocks4Userid(const std::string& ) = 0;
+ /** If connection to socks4 server fails, immediately try direct connection to final host. */
+ virtual void SetSocks4TryDirect(bool = true) = 0;
+ /** Get socks4 server ip.
+ \return socks4 server ip */
+ virtual ipaddr_t GetSocks4Host() = 0;
+ /** Get socks4 port number.
+ \return socks4 port number */
+ virtual port_t GetSocks4Port() = 0;
+ /** Get socks4 userid (optional).
+ \return socks4 userid */
+ virtual const std::string& GetSocks4Userid() = 0;
+ /** Check status of socks4 try direct flag.
+ \return true if direct connection should be tried if connection to socks4 server fails */
+ virtual bool Socks4TryDirect() = 0;
+#endif // ENABLE_SOCKS4
+
+ // -------------------------------------------------------------------------
+ // DNS resolve server
+ // -------------------------------------------------------------------------
+#ifdef ENABLE_RESOLVER
+ /** Enable asynchronous DNS.
+ \param port Listen port of asynchronous dns server */
+ virtual void EnableResolver(port_t = 16667) = 0;
+ /** Check resolver status.
+ \return true if resolver is enabled */
+ virtual bool ResolverEnabled() = 0;
+ /** Queue a dns request.
+ \param host Hostname to be resolved
+ \param port Port number will be echoed in Socket::OnResolved callback */
+ virtual int Resolve(Socket *,const std::string& host,port_t port) = 0;
+#ifdef ENABLE_IPV6
+ virtual int Resolve6(Socket *,const std::string& host,port_t port) = 0;
+#endif
+ /** Do a reverse dns lookup. */
+ virtual int Resolve(Socket *,ipaddr_t a) = 0;
+#ifdef ENABLE_IPV6
+ virtual int Resolve(Socket *,in6_addr& a) = 0;
+#endif
+ /** Get listen port of asynchronous dns server. */
+ virtual port_t GetResolverPort() = 0;
+ /** Resolver thread ready for queries. */
+ virtual bool ResolverReady() = 0;
+ /** Returns true if socket waiting for a resolve event. */
+ virtual bool Resolving(Socket *) = 0;
+#endif // ENABLE_RESOLVER
+
+#ifdef ENABLE_TRIGGERS
+ /** Fetch unique trigger id. */
+ virtual int TriggerID(Socket *src) = 0;
+ /** Subscribe socket to trigger id. */
+ virtual bool Subscribe(int id, Socket *dst) = 0;
+ /** Unsubscribe socket from trigger id. */
+ virtual bool Unsubscribe(int id, Socket *dst) = 0;
+ /** Execute OnTrigger for subscribed sockets.
+ \param id Trigger ID
+ \param data Data passed from source to destination
+ \param erase Empty trigger id source and destination maps if 'true',
+ Leave them in place if 'false' - if a trigger should be called many times */
+ virtual void Trigger(int id, Socket::TriggerData& data, bool erase = true) = 0;
+#endif // ENABLE_TRIGGERS
+
+#ifdef ENABLE_DETACH
+ /** Indicates that the handler runs under SocketThread. */
+ virtual void SetSlave(bool x = true) = 0;
+ /** Indicates that the handler runs under SocketThread. */
+ virtual bool IsSlave() = 0;
+#endif // ENABLE_DETACH
+
+};
+
+#ifdef SOCKETS_NAMESPACE
+}
+#endif
+
+#endif // _SOCKETS_ISocketHandler_H
+
+
diff --git a/externals/sockets/include/Ipv4Address.h b/externals/sockets/include/Ipv4Address.h
new file mode 100644
index 00000000000..71d925254e9
--- /dev/null
+++ b/externals/sockets/include/Ipv4Address.h
@@ -0,0 +1,95 @@
+/**
+ ** \file Ipv4Address.h
+ ** \date 2006-09-21
+ ** \author grymse@alhem.net
+**/
+/*
+Copyright (C) 2007 Anders Hedstrom
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+#ifndef _SOCKETS_Ipv4Address_H
+#define _SOCKETS_Ipv4Address_H
+
+#include "sockets-config.h"
+#include "SocketAddress.h"
+
+#ifdef SOCKETS_NAMESPACE
+namespace SOCKETS_NAMESPACE {
+#endif
+
+/* Ipv4 address implementation.
+ \ingroup basic */
+class Ipv4Address : public SocketAddress
+{
+public:
+ /** Create empty Ipv4 address structure.
+ \param port Port number */
+ Ipv4Address(port_t port = 0);
+ /** Create Ipv4 address structure.
+ \param a Socket address in network byte order (as returned by Utility::u2ip)
+ \param port Port number in host byte order */
+ Ipv4Address(ipaddr_t a,port_t port);
+ /** Create Ipv4 address structure.
+ \param a Socket address in network byte order
+ \param port Port number in host byte order */
+ Ipv4Address(struct in_addr& a,port_t port);
+ /** Create Ipv4 address structure.
+ \param host Hostname to be resolved
+ \param port Port number in host byte order */
+ Ipv4Address(const std::string& host,port_t port);
+ Ipv4Address(struct sockaddr_in&);
+ ~Ipv4Address();
+
+ // SocketAddress implementation
+
+ operator struct sockaddr *();
+ operator socklen_t();
+ bool operator==(SocketAddress&);
+
+ void SetPort(port_t port);
+ port_t GetPort();
+
+ void SetAddress(struct sockaddr *sa);
+ int GetFamily();
+
+ bool IsValid();
+ std::auto_ptr<SocketAddress> GetCopy();
+
+ /** Convert address struct to text. */
+ std::string Convert(bool include_port = false);
+ std::string Reverse();
+
+ /** Resolve hostname. */
+static bool Resolve(const std::string& hostname,struct in_addr& a);
+ /** Reverse resolve (IP to hostname). */
+static bool Reverse(struct in_addr& a,std::string& name);
+ /** Convert address struct to text. */
+static std::string Convert(struct in_addr& a);
+
+private:
+ Ipv4Address(const Ipv4Address& ) {} // copy constructor
+ Ipv4Address& operator=(const Ipv4Address& ) { return *this; } // assignment operator
+ struct sockaddr_in m_addr;
+ bool m_valid;
+};
+
+
+#ifdef SOCKETS_NAMESPACE
+} // namespace SOCKETS_NAMESPACE {
+#endif
+#endif // _SOCKETS_Ipv4Address_H
+
+
diff --git a/externals/sockets/include/Ipv6Address.h b/externals/sockets/include/Ipv6Address.h
new file mode 100644
index 00000000000..20c68d8c92d
--- /dev/null
+++ b/externals/sockets/include/Ipv6Address.h
@@ -0,0 +1,105 @@
+/**
+ ** \file Ipv6Address.h
+ ** \date 2006-09-21
+ ** \author grymse@alhem.net
+**/
+/*
+Copyright (C) 2007 Anders Hedstrom
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+#ifndef _SOCKETS_Ipv6Address_H
+#define _SOCKETS_Ipv6Address_H
+#include "sockets-config.h"
+#ifdef ENABLE_IPV6
+
+#include "SocketAddress.h"
+#ifdef IPPROTO_IPV6
+#if defined( _WIN32) && !defined(__CYGWIN__)
+typedef unsigned __int32 uint32_t;
+#endif
+
+#ifdef SOCKETS_NAMESPACE
+namespace SOCKETS_NAMESPACE {
+#endif
+
+/** Ipv6 address implementation.
+ \ingroup basic */
+class Ipv6Address : public SocketAddress
+{
+public:
+ /** Create empty Ipv6 address structure.
+ \param port Port number */
+ Ipv6Address(port_t port = 0);
+ /** Create Ipv6 address structure.
+ \param a Socket address in network byte order
+ \param port Port number in host byte order */
+ Ipv6Address(struct in6_addr& a,port_t port);
+ /** Create Ipv6 address structure.
+ \param host Hostname to be resolved
+ \param port Port number in host byte order */
+ Ipv6Address(const std::string& host,port_t port);
+ Ipv6Address(struct sockaddr_in6&);
+ ~Ipv6Address();
+
+ // SocketAddress implementation
+
+ operator struct sockaddr *();
+ operator socklen_t();
+ bool operator==(SocketAddress&);
+
+ void SetPort(port_t port);
+ port_t GetPort();
+
+ void SetAddress(struct sockaddr *sa);
+ int GetFamily();
+
+ bool IsValid();
+ std::auto_ptr<SocketAddress> GetCopy();
+
+ /** Convert address struct to text. */
+ std::string Convert(bool include_port = false);
+ std::string Reverse();
+
+ /** Resolve hostname. */
+static bool Resolve(const std::string& hostname,struct in6_addr& a);
+ /** Reverse resolve (IP to hostname). */
+static bool Reverse(struct in6_addr& a,std::string& name);
+ /** Convert address struct to text. */
+static std::string Convert(struct in6_addr& a,bool mixed = false);
+
+ void SetFlowinfo(uint32_t);
+ uint32_t GetFlowinfo();
+#ifndef _WIN32
+ void SetScopeId(uint32_t);
+ uint32_t GetScopeId();
+#endif
+
+private:
+ Ipv6Address(const Ipv6Address& ) {} // copy constructor
+ Ipv6Address& operator=(const Ipv6Address& ) { return *this; } // assignment operator
+ struct sockaddr_in6 m_addr;
+ bool m_valid;
+};
+
+
+#ifdef SOCKETS_NAMESPACE
+} // namespace SOCKETS_NAMESPACE {
+#endif
+#endif // IPPROTO_IPV6
+#endif // ENABLE_IPV6
+#endif // _SOCKETS_Ipv6Address_H
+
+
diff --git a/externals/sockets/include/ListenSocket.h b/externals/sockets/include/ListenSocket.h
new file mode 100644
index 00000000000..8934a809d0e
--- /dev/null
+++ b/externals/sockets/include/ListenSocket.h
@@ -0,0 +1,418 @@
+/** \file ListenSocket.h
+ ** \date 2004-02-13
+ ** \author grymse@alhem.net
+**/
+/*
+Copyright (C) 2004-2007 Anders Hedstrom
+
+This library is made available under the terms of the GNU GPL.
+
+If you would like to use this library in a closed-source application,
+a separate license agreement is available. For information about
+the closed-source license agreement for the C++ sockets library,
+please visit http://www.alhem.net/Sockets/license.html and/or
+email license@alhem.net.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+#ifndef _SOCKETS_ListenSocket_H
+#define _SOCKETS_ListenSocket_H
+#include "sockets-config.h"
+
+#ifdef _WIN32
+#include <stdlib.h>
+#else
+#include <errno.h>
+#endif
+
+#include "ISocketHandler.h"
+#include "Socket.h"
+#include "Utility.h"
+#include "SctpSocket.h"
+#include "Ipv4Address.h"
+#include "Ipv6Address.h"
+#ifdef ENABLE_EXCEPTIONS
+#include "Exception.h"
+#endif
+
+#ifdef SOCKETS_NAMESPACE
+namespace SOCKETS_NAMESPACE {
+#endif
+
+/** Binds incoming port number to new Socket class X.
+ \ingroup basic */
+template <class X>
+class ListenSocket : public Socket
+{
+public:
+ /** Constructor.
+ \param h ISocketHandler reference
+ \param use_creator Optional use of creator (default true) */
+ ListenSocket(ISocketHandler& h,bool use_creator = true) : Socket(h), m_depth(0), m_creator(NULL)
+ ,m_bHasCreate(false)
+ {
+ if (use_creator)
+ {
+ m_creator = new X(h);
+ Socket *tmp = m_creator -> Create();
+ if (tmp && dynamic_cast<X *>(tmp))
+ {
+ m_bHasCreate = true;
+ }
+ if (tmp)
+ {
+ delete tmp;
+ }
+ }
+ }
+ ~ListenSocket() {
+ if (m_creator)
+ {
+ delete m_creator;
+ }
+ }
+
+ /** Close file descriptor. */
+ int Close() {
+ if (GetSocket() != INVALID_SOCKET)
+ {
+ closesocket(GetSocket());
+ }
+ return 0;
+ }
+
+ /** Bind and listen to any interface.
+ \param port Port (0 is random)
+ \param depth Listen queue depth */
+ int Bind(port_t port,int depth = 20) {
+#ifdef ENABLE_IPV6
+#ifdef IPPROTO_IPV6
+ if (IsIpv6())
+ {
+ Ipv6Address ad(port);
+ return Bind(ad, depth);
+ }
+ else
+#endif
+#endif
+ {
+ Ipv4Address ad(port);
+ return Bind(ad, depth);
+ }
+ }
+
+ int Bind(SocketAddress& ad,int depth) {
+#ifdef USE_SCTP
+ if (dynamic_cast<SctpSocket *>(m_creator))
+ {
+ return Bind(ad, "sctp", depth);
+ }
+#endif
+ return Bind(ad, "tcp", depth);
+ }
+
+ /** Bind and listen to any interface, with optional protocol.
+ \param port Port (0 is random)
+ \param protocol Network protocol
+ \param depth Listen queue depth */
+ int Bind(port_t port,const std::string& protocol,int depth = 20) {
+#ifdef ENABLE_IPV6
+#ifdef IPPROTO_IPV6
+ if (IsIpv6())
+ {
+ Ipv6Address ad(port);
+ return Bind(ad, protocol, depth);
+ }
+ else
+#endif
+#endif
+ {
+ Ipv4Address ad(port);
+ return Bind(ad, protocol, depth);
+ }
+ }
+
+ /** Bind and listen to specific interface.
+ \param intf Interface hostname
+ \param port Port (0 is random)
+ \param depth Listen queue depth */
+ int Bind(const std::string& intf,port_t port,int depth = 20) {
+#ifdef ENABLE_IPV6
+#ifdef IPPROTO_IPV6
+ if (IsIpv6())
+ {
+ Ipv6Address ad(intf, port);
+ if (ad.IsValid())
+ {
+ return Bind(ad, depth);
+ }
+ Handler().LogError(this, "Bind", 0, "name resolution of interface name failed", LOG_LEVEL_FATAL);
+ return -1;
+ }
+ else
+#endif
+#endif
+ {
+ Ipv4Address ad(intf, port);
+ if (ad.IsValid())
+ {
+ return Bind(ad, depth);
+ }
+ Handler().LogError(this, "Bind", 0, "name resolution of interface name failed", LOG_LEVEL_FATAL);
+ return -1;
+ }
+ }
+
+ /** Bind and listen to specific interface.
+ \param intf Interface hostname
+ \param port Port (0 is random)
+ \param protocol Network protocol
+ \param depth Listen queue depth */
+ int Bind(const std::string& intf,port_t port,const std::string& protocol,int depth = 20) {
+#ifdef ENABLE_IPV6
+#ifdef IPPROTO_IPV6
+ if (IsIpv6())
+ {
+ Ipv6Address ad(intf, port);
+ if (ad.IsValid())
+ {
+ return Bind(ad, protocol, depth);
+ }
+ Handler().LogError(this, "Bind", 0, "name resolution of interface name failed", LOG_LEVEL_FATAL);
+ return -1;
+ }
+ else
+#endif
+#endif
+ {
+ Ipv4Address ad(intf, port);
+ if (ad.IsValid())
+ {
+ return Bind(ad, protocol, depth);
+ }
+ Handler().LogError(this, "Bind", 0, "name resolution of interface name failed", LOG_LEVEL_FATAL);
+ return -1;
+ }
+ }
+
+ /** Bind and listen to ipv4 interface.
+ \param a Ipv4 interface address
+ \param port Port (0 is random)
+ \param depth Listen queue depth */
+ int Bind(ipaddr_t a,port_t port,int depth = 20) {
+ Ipv4Address ad(a, port);
+#ifdef USE_SCTP
+ if (dynamic_cast<SctpSocket *>(m_creator))
+ {
+ return Bind(ad, "sctp", depth);
+ }
+#endif
+ return Bind(ad, "tcp", depth);
+ }
+ /** Bind and listen to ipv4 interface.
+ \param a Ipv4 interface address
+ \param port Port (0 is random)
+ \param protocol Network protocol
+ \param depth Listen queue depth */
+ int Bind(ipaddr_t a,port_t port,const std::string& protocol,int depth) {
+ Ipv4Address ad(a, port);
+ return Bind(ad, protocol, depth);
+ }
+
+#ifdef ENABLE_IPV6
+#ifdef IPPROTO_IPV6
+ /** Bind and listen to ipv6 interface.
+ \param a Ipv6 interface address
+ \param port Port (0 is random)
+ \param depth Listen queue depth */
+ int Bind(in6_addr a,port_t port,int depth = 20) {
+ Ipv6Address ad(a, port);
+#ifdef USE_SCTP
+ if (dynamic_cast<SctpSocket *>(m_creator))
+ {
+ return Bind(ad, "sctp", depth);
+ }
+#endif
+ return Bind(ad, "tcp", depth);
+ }
+ /** Bind and listen to ipv6 interface.
+ \param a Ipv6 interface address
+ \param port Port (0 is random)
+ \param protocol Network protocol
+ \param depth Listen queue depth */
+ int Bind(in6_addr a,port_t port,const std::string& protocol,int depth) {
+ Ipv6Address ad(a, port);
+ return Bind(ad, protocol, depth);
+ }
+#endif
+#endif
+
+ /** Bind and listen to network interface.
+ \param ad Interface address
+ \param protocol Network protocol
+ \param depth Listen queue depth */
+ int Bind(SocketAddress& ad,const std::string& protocol,int depth) {
+ SOCKET s;
+ if ( (s = CreateSocket(ad.GetFamily(), SOCK_STREAM, protocol)) == INVALID_SOCKET)
+ {
+ return -1;
+ }
+ if (bind(s, ad, ad) == -1)
+ {
+ Handler().LogError(this, "bind", Errno, StrError(Errno), LOG_LEVEL_FATAL);
+ closesocket(s);
+#ifdef ENABLE_EXCEPTIONS
+ throw Exception("bind() failed for port " + Utility::l2string(ad.GetPort()) + ": " + StrError(Errno));
+#endif
+ return -1;
+ }
+ if (listen(s, depth) == -1)
+ {
+ Handler().LogError(this, "listen", Errno, StrError(Errno), LOG_LEVEL_FATAL);
+ closesocket(s);
+#ifdef ENABLE_EXCEPTIONS
+ throw Exception("listen() failed for port " + Utility::l2string(ad.GetPort()) + ": " + StrError(Errno));
+#endif
+ return -1;
+ }
+ m_depth = depth;
+ Attach(s);
+ return 0;
+ }
+
+ /** Return assigned port number. */
+ port_t GetPort()
+ {
+ return GetSockPort();
+ }
+
+ /** Return listen queue depth. */
+ int GetDepth()
+ {
+ return m_depth;
+ }
+
+ /** OnRead on a ListenSocket receives an incoming connection. */
+ void OnRead()
+ {
+ struct sockaddr sa;
+ socklen_t sa_len = sizeof(struct sockaddr);
+ SOCKET a_s = accept(GetSocket(), &sa, &sa_len);
+
+ if (a_s == INVALID_SOCKET)
+ {
+ Handler().LogError(this, "accept", Errno, StrError(Errno), LOG_LEVEL_ERROR);
+ return;
+ }
+ if (!Handler().OkToAccept(this))
+ {
+ Handler().LogError(this, "accept", -1, "Not OK to accept", LOG_LEVEL_WARNING);
+ closesocket(a_s);
+ return;
+ }
+ if (Handler().GetCount() >= FD_SETSIZE)
+ {
+ Handler().LogError(this, "accept", (int)Handler().GetCount(), "ISocketHandler fd_set limit reached", LOG_LEVEL_FATAL);
+ closesocket(a_s);
+ return;
+ }
+ Socket *tmp = m_bHasCreate ? m_creator -> Create() : new X(Handler());
+#ifdef ENABLE_IPV6
+ tmp -> SetIpv6( IsIpv6() );
+#endif
+ tmp -> SetParent(this);
+ tmp -> Attach(a_s);
+ tmp -> SetNonblocking(true);
+ {
+#ifdef ENABLE_IPV6
+#ifdef IPPROTO_IPV6
+ if (sa_len == sizeof(struct sockaddr_in6))
+ {
+ struct sockaddr_in6 *p = (struct sockaddr_in6 *)&sa;
+ if (p -> sin6_family == AF_INET6)
+ {
+ Ipv6Address ad(p -> sin6_addr,ntohs(p -> sin6_port));
+ ad.SetFlowinfo(p -> sin6_flowinfo);
+#ifndef _WIN32
+ ad.SetScopeId(p -> sin6_scope_id);
+#endif
+ tmp -> SetRemoteAddress(ad);
+ }
+ }
+#endif
+#endif
+ if (sa_len == sizeof(struct sockaddr_in))
+ {
+ struct sockaddr_in *p = (struct sockaddr_in *)&sa;
+ if (p -> sin_family == AF_INET)
+ {
+ Ipv4Address ad(p -> sin_addr,ntohs(p -> sin_port));
+ tmp -> SetRemoteAddress(ad);
+ }
+ }
+ }
+ tmp -> SetConnected(true);
+ tmp -> Init();
+ tmp -> SetDeleteByHandler(true);
+ Handler().Add(tmp);
+#ifdef HAVE_OPENSSL
+ if (tmp -> IsSSL()) // SSL Enabled socket
+ {
+ // %! OnSSLAccept calls SSLNegotiate that can finish in this one call.
+ // %! If that happens and negotiation fails, the 'tmp' instance is
+ // %! still added to the list of active sockets in the sockethandler.
+ // %! See bugfix for this in SocketHandler::Select - don't Set rwx
+ // %! flags if CloseAndDelete() flag is true.
+ // %! An even better fugbix (see TcpSocket::OnSSLAccept) now avoids
+ // %! the Add problem altogether, so ignore the above.
+ // %! (OnSSLAccept does no longer call SSLNegotiate().)
+ tmp -> OnSSLAccept();
+ }
+ else
+#endif
+ {
+ tmp -> OnAccept();
+ }
+ }
+
+ /** Please don't use this method.
+ "accept()" is handled automatically in the OnRead() method. */
+ virtual SOCKET Accept(SOCKET socket, struct sockaddr *saptr, socklen_t *lenptr)
+ {
+ return accept(socket, saptr, lenptr);
+ }
+
+ bool HasCreator() { return m_bHasCreate; }
+
+ void OnOptions(int,int,int,SOCKET) {
+ SetSoReuseaddr(true);
+ }
+
+protected:
+ ListenSocket(const ListenSocket& s) : Socket(s) {}
+private:
+ ListenSocket& operator=(const ListenSocket& ) { return *this; }
+ int m_depth;
+ X *m_creator;
+ bool m_bHasCreate;
+};
+
+#ifdef SOCKETS_NAMESPACE
+}
+#endif
+
+#endif // _SOCKETS_ListenSocket_H
+
+
diff --git a/externals/sockets/include/Lock.h b/externals/sockets/include/Lock.h
new file mode 100644
index 00000000000..f3bb9273920
--- /dev/null
+++ b/externals/sockets/include/Lock.h
@@ -0,0 +1,58 @@
+/** \file Lock.h
+ ** \date 2005-08-22
+ ** \author grymse@alhem.net
+**/
+/*
+Copyright (C) 2005,2007 Anders Hedstrom
+
+This library is made available under the terms of the GNU GPL.
+
+If you would like to use this library in a closed-source application,
+a separate license agreement is available. For information about
+the closed-source license agreement for the C++ sockets library,
+please visit http://www.alhem.net/Sockets/license.html and/or
+email license@alhem.net.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+#ifndef _SOCKETS_Lock_H
+#define _SOCKETS_Lock_H
+
+#include "sockets-config.h"
+#ifdef SOCKETS_NAMESPACE
+namespace SOCKETS_NAMESPACE {
+#endif
+
+class Mutex;
+
+/** Mutex encapsulation class.
+ \ingroup threading */
+class Lock
+{
+public:
+ Lock(Mutex&);
+ ~Lock();
+
+private:
+ Mutex& m_mutex;
+};
+
+
+#ifdef SOCKETS_NAMESPACE
+}
+#endif
+#endif // _SOCKETS_Lock_H
+
+
diff --git a/externals/sockets/include/Mutex.h b/externals/sockets/include/Mutex.h
new file mode 100644
index 00000000000..e42a57c3262
--- /dev/null
+++ b/externals/sockets/include/Mutex.h
@@ -0,0 +1,68 @@
+/** \file Mutex.h
+ ** \date 2004-10-30
+ ** \author grymse@alhem.net
+**/
+/*
+Copyright (C) 2004-2007 Anders Hedstrom
+
+This library is made available under the terms of the GNU GPL.
+
+If you would like to use this library in a closed-source application,
+a separate license agreement is available. For information about
+the closed-source license agreement for the C++ sockets library,
+please visit http://www.alhem.net/Sockets/license.html and/or
+email license@alhem.net.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+#ifndef _SOCKETS_Mutex_H
+#define _SOCKETS_Mutex_H
+
+#include "sockets-config.h"
+#ifndef _WIN32
+#include <pthread.h>
+#else
+#include <windows.h>
+#endif
+
+#ifdef SOCKETS_NAMESPACE
+namespace SOCKETS_NAMESPACE {
+#endif
+
+/** Mutex container class, used by Lock.
+ \ingroup threading */
+class Mutex
+{
+ friend class Lock;
+public:
+ Mutex();
+ ~Mutex();
+
+ void Lock();
+ void Unlock();
+private:
+#ifdef _WIN32
+ HANDLE m_mutex;
+#else
+ pthread_mutex_t m_mutex;
+#endif
+};
+
+#ifdef SOCKETS_NAMESPACE
+}
+#endif
+#endif // _SOCKETS_Mutex_H
+
+
diff --git a/externals/sockets/include/Parse.h b/externals/sockets/include/Parse.h
new file mode 100644
index 00000000000..52bd9327e28
--- /dev/null
+++ b/externals/sockets/include/Parse.h
@@ -0,0 +1,100 @@
+/** \file Parse.h - parse a string
+ **
+ ** Written: 1999-Feb-10 grymse@alhem.net
+ **/
+
+/*
+Copyright (C) 1999-2007 Anders Hedstrom
+
+This library is made available under the terms of the GNU GPL.
+
+If you would like to use this library in a closed-source application,
+a separate license agreement is available. For information about
+the closed-source license agreement for the C++ sockets library,
+please visit http://www.alhem.net/Sockets/license.html and/or
+email license@alhem.net.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+#ifndef _SOCKETS_Parse_H
+#define _SOCKETS_Parse_H
+
+#include "sockets-config.h"
+#ifdef _MSC_VER
+#pragma warning(disable:4514)
+#endif
+
+#include <string>
+
+#ifdef SOCKETS_NAMESPACE
+namespace SOCKETS_NAMESPACE {
+#endif
+
+/***************************************************/
+/* interface of class Parse */
+
+/** Splits a string whatever way you want.
+ \ingroup util */
+class Parse
+{
+public:
+ Parse();
+ Parse(const std::string&);
+ Parse(const std::string&,const std::string&);
+ Parse(const std::string&,const std::string&,short);
+ ~Parse();
+ short issplit(const char);
+ void getsplit();
+ void getsplit(std::string&);
+ std::string getword();
+ void getword(std::string&);
+ void getword(std::string&,std::string&,int);
+ std::string getrest();
+ void getrest(std::string&);
+ long getvalue();
+ void setbreak(const char);
+ int getwordlen();
+ int getrestlen();
+ void enablebreak(const char c) {
+ pa_enable = c;
+ }
+ void disablebreak(const char c) {
+ pa_disable = c;
+ }
+ void getline();
+ void getline(std::string&);
+ size_t getptr() { return pa_the_ptr; }
+ void EnableQuote(bool b) { pa_quote = b; }
+
+private:
+ std::string pa_the_str;
+ std::string pa_splits;
+ std::string pa_ord;
+ size_t pa_the_ptr;
+ char pa_breakchar;
+ char pa_enable;
+ char pa_disable;
+ short pa_nospace;
+ bool pa_quote;
+};
+
+#ifdef SOCKETS_NAMESPACE
+}
+#endif
+
+#endif // _SOCKETS_Parse_H
+
+
diff --git a/externals/sockets/include/ResolvServer.h b/externals/sockets/include/ResolvServer.h
new file mode 100644
index 00000000000..409c9b7a619
--- /dev/null
+++ b/externals/sockets/include/ResolvServer.h
@@ -0,0 +1,72 @@
+/** \file ResolvServer.h
+ ** \date 2005-03-24
+ ** \author grymse@alhem.net
+**/
+/*
+Copyright (C) 2004-2007 Anders Hedstrom
+
+This library is made available under the terms of the GNU GPL.
+
+If you would like to use this library in a closed-source application,
+a separate license agreement is available. For information about
+the closed-source license agreement for the C++ sockets library,
+please visit http://www.alhem.net/Sockets/license.html and/or
+email license@alhem.net.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+#ifndef _SOCKETS_ResolvServer_H
+#define _SOCKETS_ResolvServer_H
+#include "sockets-config.h"
+#ifdef ENABLE_RESOLVER
+#include "socket_include.h"
+#include "Thread.h"
+
+#ifdef SOCKETS_NAMESPACE
+namespace SOCKETS_NAMESPACE {
+#endif
+
+/** \defgroup async Asynchronous DNS */
+/** Async DNS resolver thread.
+ \ingroup async */
+class ResolvServer : public Thread
+{
+public:
+ ResolvServer(port_t);
+ ~ResolvServer();
+
+ void Run();
+ void Quit();
+
+ bool Ready();
+
+private:
+ ResolvServer(const ResolvServer& ) {} // copy constructor
+ ResolvServer& operator=(const ResolvServer& ) { return *this; } // assignment operator
+
+ bool m_quit;
+ port_t m_port;
+ bool m_ready;
+};
+
+
+#ifdef SOCKETS_NAMESPACE
+}
+#endif
+
+#endif // ENABLE_RESOLVER
+#endif // _SOCKETS_ResolvServer_H
+
+
diff --git a/externals/sockets/include/ResolvSocket.h b/externals/sockets/include/ResolvSocket.h
new file mode 100644
index 00000000000..60743736e08
--- /dev/null
+++ b/externals/sockets/include/ResolvSocket.h
@@ -0,0 +1,105 @@
+/** \file ResolvSocket.h
+ ** \date 2005-03-24
+ ** \author grymse@alhem.net
+**/
+/*
+Copyright (C) 2004-2007 Anders Hedstrom
+
+This library is made available under the terms of the GNU GPL.
+
+If you would like to use this library in a closed-source application,
+a separate license agreement is available. For information about
+the closed-source license agreement for the C++ sockets library,
+please visit http://www.alhem.net/Sockets/license.html and/or
+email license@alhem.net.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+#ifndef _SOCKETS_ResolvSocket_H
+#define _SOCKETS_ResolvSocket_H
+#include "sockets-config.h"
+#ifdef ENABLE_RESOLVER
+#include "TcpSocket.h"
+#include <map>
+
+#ifdef SOCKETS_NAMESPACE
+namespace SOCKETS_NAMESPACE {
+#endif
+
+class Mutex;
+
+/** Async DNS resolver socket.
+ \ingroup async */
+class ResolvSocket : public TcpSocket
+{
+ typedef std::map<std::string, /* type */
+ std::map<std::string, std::string> > cache_t; /* host, result */
+ typedef std::map<std::string, /* type */
+ std::map<std::string, time_t> > timeout_t; /* host, time */
+
+public:
+ ResolvSocket(ISocketHandler&);
+ ResolvSocket(ISocketHandler&, Socket *parent, const std::string& host, port_t port, bool ipv6 = false);
+ ResolvSocket(ISocketHandler&, Socket *parent, ipaddr_t);
+#ifdef ENABLE_IPV6
+ ResolvSocket(ISocketHandler&, Socket *parent, in6_addr&);
+#endif
+ ~ResolvSocket();
+
+ void OnAccept() { m_bServer = true; }
+ void OnLine(const std::string& line);
+ void OnDetached();
+ void OnDelete();
+
+ void SetId(int x) { m_resolv_id = x; }
+ int GetId() { return m_resolv_id; }
+
+ void OnConnect();
+
+#ifdef ENABLE_IPV6
+ void SetResolveIpv6(bool x = true) { m_resolve_ipv6 = x; }
+#endif
+
+private:
+ ResolvSocket(const ResolvSocket& s) : TcpSocket(s) {} // copy constructor
+ ResolvSocket& operator=(const ResolvSocket& ) { return *this; } // assignment operator
+
+ std::string m_query;
+ std::string m_data;
+ bool m_bServer;
+ Socket *m_parent;
+ int m_resolv_id;
+ std::string m_resolv_host;
+ port_t m_resolv_port;
+ ipaddr_t m_resolv_address;
+#ifdef ENABLE_IPV6
+ bool m_resolve_ipv6;
+ in6_addr m_resolv_address6;
+#endif
+ static cache_t m_cache;
+ static timeout_t m_cache_to;
+ static Mutex m_cache_mutex;
+ bool m_cached;
+};
+
+
+#ifdef SOCKETS_NAMESPACE
+}
+#endif
+
+#endif // ENABLE_RESOLVER
+#endif // _SOCKETS_ResolvSocket_H
+
+
diff --git a/externals/sockets/include/SctpSocket.h b/externals/sockets/include/SctpSocket.h
new file mode 100644
index 00000000000..ed507fb1880
--- /dev/null
+++ b/externals/sockets/include/SctpSocket.h
@@ -0,0 +1,108 @@
+/**
+ ** \file SctpSocket.h
+ ** \date 2006-09-04
+ ** \author grymse@alhem.net
+**/
+/*
+Copyright (C) 2007 Anders Hedstrom
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+#ifndef _SOCKETS_SctpSocket_H
+#define _SOCKETS_SctpSocket_H
+#include "sockets-config.h"
+
+#include "StreamSocket.h"
+#ifdef USE_SCTP
+#include <netinet/sctp.h>
+
+#ifdef SOCKETS_NAMESPACE
+namespace SOCKETS_NAMESPACE {
+#endif
+
+#define SCTP_BUFSIZE_READ 16400
+
+class SocketAddress;
+
+class SctpSocket : public StreamSocket
+{
+public:
+ /** SctpSocket constructor.
+ \param h Owner
+ \param type SCTP_STREAM or SCTP_SEQPACKET */
+ SctpSocket(ISocketHandler& h,int type);
+ ~SctpSocket();
+
+ /** bind() */
+ int Bind(const std::string&,port_t);
+ int Bind(SocketAddress&);
+ /** sctp_bindx() */
+ int AddAddress(const std::string&,port_t);
+ int AddAddress(SocketAddress&);
+ /** sctp_bindx() */
+ int RemoveAddress(const std::string&,port_t);
+ int RemoveAddress(SocketAddress&);
+
+ /** connect() */
+ int Open(const std::string&,port_t);
+ int Open(SocketAddress&);
+
+ /** Connect timeout callback. */
+ void OnConnectTimeout();
+#ifdef _WIN32
+ /** Connection failed reported as exception on win32 */
+ void OnException();
+#endif
+
+#ifndef SOLARIS
+ /** sctp_connectx() */
+ int AddConnection(const std::string&,port_t);
+ int AddConnection(SocketAddress&);
+#endif
+
+ /** Get peer addresses of an association. */
+ int getpaddrs(sctp_assoc_t id,std::list<std::string>&);
+ /** Get all bound addresses of an association. */
+ int getladdrs(sctp_assoc_t id,std::list<std::string>&);
+
+ /** sctp_peeloff */
+ int PeelOff(sctp_assoc_t id);
+
+ /** recvmsg callback */
+ virtual void OnReceiveMessage(const char *buf,size_t sz,struct sockaddr *sa,socklen_t sa_len,struct sctp_sndrcvinfo *sinfo,int msg_flags) = 0;
+
+ void OnOptions(int,int,int,SOCKET) {}
+
+ virtual int Protocol();
+
+protected:
+ SctpSocket(const SctpSocket& s) : StreamSocket(s) {}
+ void OnRead();
+ void OnWrite();
+
+private:
+ SctpSocket& operator=(const SctpSocket& s) { return *this; }
+ int m_type; ///< SCTP_STREAM or SCTP_SEQPACKET
+ char *m_buf; ///< Temporary receive buffer
+};
+
+#ifdef SOCKETS_NAMESPACE
+} // namespace SOCKETS_NAMESPACE
+#endif
+
+#endif // USE_SCTP
+#endif // _SOCKETS_SctpSocket_H
+
+
diff --git a/externals/sockets/include/Socket.h b/externals/sockets/include/Socket.h
new file mode 100644
index 00000000000..23a806b5ea1
--- /dev/null
+++ b/externals/sockets/include/Socket.h
@@ -0,0 +1,735 @@
+/** \file Socket.h
+ ** \date 2004-02-13
+ ** \author grymse@alhem.net
+**/
+/*
+Copyright (C) 2004-2007 Anders Hedstrom
+
+This software is made available under the terms of the GNU GPL.
+
+If you would like to use this library in a closed-source application,
+a separate license agreement is available. For information about
+the closed-source license agreement for the C++ sockets library,
+please visit http://www.alhem.net/Sockets/license.html and/or
+email license@alhem.net.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+#ifndef _SOCKETS_Socket_H
+#define _SOCKETS_Socket_H
+#include "sockets-config.h"
+
+#include <string>
+#include <vector>
+#include <list>
+#ifdef HAVE_OPENSSL
+#include <openssl/ssl.h>
+#endif
+
+#include "socket_include.h"
+#include <time.h>
+#include "SocketAddress.h"
+#include "Thread.h"
+
+#ifdef SOCKETS_NAMESPACE
+namespace SOCKETS_NAMESPACE {
+#endif
+
+class ISocketHandler;
+class SocketAddress;
+class IFile;
+
+/** \defgroup basic Basic sockets */
+/** Socket base class.
+ \ingroup basic */
+class Socket
+{
+ friend class ISocketHandler;
+#ifdef ENABLE_DETACH
+ /** Detached socket run thread.
+ \ingroup internal */
+ class SocketThread : public Thread
+ {
+ public:
+ SocketThread(Socket *p);
+ ~SocketThread();
+
+ void Run();
+
+ private:
+ Socket *GetSocket() const { return m_socket; }
+ SocketThread(const SocketThread& s) : m_socket(s.GetSocket()) {}
+ SocketThread& operator=(const SocketThread& ) { return *this; }
+ Socket *m_socket;
+ };
+#endif // ENABLE_DETACH
+
+#ifdef ENABLE_TRIGGERS
+public:
+ /** Data pass class from source to destination. */
+ class TriggerData
+ {
+ public:
+ TriggerData() : m_src(NULL) {}
+ virtual ~TriggerData() {}
+
+ Socket *GetSource() const { return m_src; }
+ void SetSource(Socket *x) { m_src = x; }
+
+ private:
+ Socket *m_src;
+ };
+#endif // ENABLE_TRIGGERS
+
+ /** Socket mode flags. */
+/*
+ enum {
+ // Socket
+ SOCK_DEL = 0x01, ///< Delete by handler flag
+ SOCK_CLOSE = 0x02, ///< Close and delete flag
+ SOCK_DISABLE_READ = 0x04, ///< Disable checking for read events
+ SOCK_CONNECTED = 0x08, ///< Socket is connected (tcp/udp)
+
+ SOCK_ERASED_BY_HANDLER = 0x10, ///< Set by handler before delete
+ // HAVE_OPENSSL
+ SOCK_ENABLE_SSL = 0x20, ///< Enable SSL for this TcpSocket
+ SOCK_SSL = 0x40, ///< ssl negotiation mode (TcpSocket)
+ SOCK_SSL_SERVER = 0x80, ///< True if this is an incoming ssl TcpSocket connection
+
+ // ENABLE_IPV6
+ SOCK_IPV6 = 0x0100, ///< This is an ipv6 socket if this one is true
+ // ENABLE_POOL
+ SOCK_CLIENT = 0x0200, ///< only client connections are pooled
+ SOCK_RETAIN = 0x0400, ///< keep connection on close
+ SOCK_LOST = 0x0800, ///< connection lost
+
+ // ENABLE_SOCKS4
+ SOCK_SOCKS4 = 0x1000, ///< socks4 negotiation mode (TcpSocket)
+ // ENABLE_DETACH
+ SOCK_DETACH = 0x2000, ///< Socket ordered to detach flag
+ SOCK_DETACHED = 0x4000, ///< Socket has been detached
+ // StreamSocket
+ STREAMSOCK_CONNECTING = 0x8000, ///< Flag indicating connection in progress
+
+ STREAMSOCK_FLUSH_BEFORE_CLOSE = 0x010000L, ///< Send all data before closing (default true)
+ STREAMSOCK_CALL_ON_CONNECT = 0x020000L, ///< OnConnect will be called next ISocketHandler cycle if true
+ STREAMSOCK_RETRY_CONNECT = 0x040000L, ///< Try another connection attempt next ISocketHandler cycle
+ STREAMSOCK_LINE_PROTOCOL = 0x080000L, ///< Line protocol mode flag
+
+ };
+*/
+
+public:
+ /** "Default" constructor */
+ Socket(ISocketHandler&);
+
+ virtual ~Socket();
+
+ /** Socket class instantiation method. Used when a "non-standard" constructor
+ * needs to be used for the socket class. Note: the socket class still needs
+ * the "default" constructor with one ISocketHandler& as input parameter.
+ */
+ virtual Socket *Create() { return NULL; }
+
+ /** Returns reference to sockethandler that owns the socket.
+ If the socket is detached, this is a reference to the slave sockethandler.
+ */
+ ISocketHandler& Handler() const;
+
+ /** Returns reference to sockethandler that owns the socket.
+ This one always returns the reference to the original sockethandler,
+ even if the socket is detached.
+ */
+ ISocketHandler& MasterHandler() const;
+
+ /** Called by ListenSocket after accept but before socket is added to handler.
+ * CTcpSocket uses this to create its ICrypt member variable.
+ * The ICrypt member variable is created by a virtual method, therefore
+ * it can't be called directly from the CTcpSocket constructor.
+ * Also used to determine if incoming HTTP connection is normal (port 80)
+ * or ssl (port 443).
+ */
+ virtual void Init();
+
+ /** Create a socket file descriptor.
+ \param af Address family AF_INET / AF_INET6 / ...
+ \param type SOCK_STREAM / SOCK_DGRAM / ...
+ \param protocol "tcp" / "udp" / ... */
+ SOCKET CreateSocket(int af,int type,const std::string& protocol = "");
+
+ /** Assign this socket a file descriptor created
+ by a call to socket() or otherwise. */
+ void Attach(SOCKET s);
+
+ /** Return file descriptor assigned to this socket. */
+ SOCKET GetSocket();
+
+ /** Close connection immediately - internal use.
+ \sa SetCloseAndDelete */
+ virtual int Close();
+
+ /** Add file descriptor to sockethandler fd_set's. */
+ void Set(bool bRead,bool bWrite,bool bException = true);
+
+ /** Returns true when socket file descriptor is valid
+ and socket is not about to be closed. */
+ virtual bool Ready();
+
+ /** Returns pointer to ListenSocket that created this instance
+ * on an incoming connection. */
+ Socket *GetParent();
+
+ /** Used by ListenSocket to set parent pointer of newly created
+ * socket instance. */
+ void SetParent(Socket *);
+
+ /** Get listening port from ListenSocket<>. */
+ virtual port_t GetPort();
+
+ /** Set socket non-block operation. */
+ bool SetNonblocking(bool);
+
+ /** Set socket non-block operation. */
+ bool SetNonblocking(bool, SOCKET);
+
+ /** Total lifetime of instance. */
+ time_t Uptime();
+
+ /** Set address/port of last connect() call. */
+ void SetClientRemoteAddress(SocketAddress&);
+
+ /** Get address/port of last connect() call. */
+ std::auto_ptr<SocketAddress> GetClientRemoteAddress();
+
+ /** Common interface for SendBuf used by Tcp and Udp sockets. */
+ virtual void SendBuf(const char *,size_t,int = 0);
+
+ /** Common interface for Send used by Tcp and Udp sockets. */
+ virtual void Send(const std::string&,int = 0);
+
+ /** Outgoing traffic counter. */
+ virtual uint64_t GetBytesSent(bool clear = false);
+
+ /** Incoming traffic counter. */
+ virtual uint64_t GetBytesReceived(bool clear = false);
+
+ // LIST_TIMEOUT
+
+ /** Enable timeout control. 0=disable timeout check. */
+ void SetTimeout(time_t secs);
+
+ /** Check timeout. \return true if time limit reached */
+ bool Timeout(time_t tnow);
+
+ /** Used by ListenSocket. ipv4 and ipv6 */
+ void SetRemoteAddress(SocketAddress&);
+
+ /** \name Event callbacks */
+ //@{
+
+ /** Called when there is something to be read from the file descriptor. */
+ virtual void OnRead();
+ /** Called when there is room for another write on the file descriptor. */
+ virtual void OnWrite();
+ /** Called on socket exception. */
+ virtual void OnException();
+ /** Called before a socket class is deleted by the ISocketHandler. */
+ virtual void OnDelete();
+ /** Called when a connection has completed. */
+ virtual void OnConnect();
+ /** Called when an incoming connection has been completed. */
+ virtual void OnAccept();
+ /** Called when a complete line has been read and the socket is in
+ * line protocol mode. */
+ virtual void OnLine(const std::string& );
+ /** Called on connect timeout (5s). */
+ virtual void OnConnectFailed();
+ /** Called when a client socket is created, to set socket options.
+ \param family AF_INET, AF_INET6, etc
+ \param type SOCK_STREAM, SOCK_DGRAM, etc
+ \param protocol Protocol number (tcp, udp, sctp, etc)
+ \param s Socket file descriptor
+ */
+ virtual void OnOptions(int family,int type,int protocol,SOCKET s) = 0;
+ /** Connection retry callback - return false to abort connection attempts */
+ virtual bool OnConnectRetry();
+#ifdef ENABLE_RECONNECT
+ /** a reconnect has been made */
+ virtual void OnReconnect();
+#endif
+ /** TcpSocket: When a disconnect has been detected (recv/SSL_read returns 0 bytes). */
+ virtual void OnDisconnect();
+ /** Timeout callback. */
+ virtual void OnTimeout();
+ /** Connection timeout. */
+ virtual void OnConnectTimeout();
+ //@}
+
+ /** \name Socket mode flags, set/reset */
+ //@{
+ /** Set delete by handler true when you want the sockethandler to
+ delete the socket instance after use. */
+ void SetDeleteByHandler(bool = true);
+ /** Check delete by handler flag.
+ \return true if this instance should be deleted by the sockethandler */
+ bool DeleteByHandler();
+
+ // LIST_CLOSE - conditional event queue
+
+ /** Set close and delete to terminate the connection. */
+ void SetCloseAndDelete(bool = true);
+ /** Check close and delete flag.
+ \return true if this socket should be closed and the instance removed */
+ bool CloseAndDelete();
+
+ /** Return number of seconds since socket was ordered to close. \sa SetCloseAndDelete */
+ time_t TimeSinceClose();
+
+ /** Ignore read events for an output only socket. */
+ void DisableRead(bool x = true);
+ /** Check ignore read events flag.
+ \return true if read events should be ignored */
+ bool IsDisableRead();
+
+ /** Set connected status. */
+ void SetConnected(bool = true);
+ /** Check connected status.
+ \return true if connected */
+ bool IsConnected();
+
+ /** Connection lost - error while reading/writing from a socket - TcpSocket only. */
+ void SetLost();
+ /** Check connection lost status flag, used by TcpSocket only.
+ \return true if there was an error while r/w causing the socket to close */
+ bool Lost();
+
+ /** Set flag indicating the socket is being actively deleted by the sockethandler. */
+ void SetErasedByHandler(bool x = true);
+ /** Get value of flag indicating socket is deleted by sockethandler. */
+ bool ErasedByHandler();
+
+ //@}
+
+ /** \name Information about remote connection */
+ //@{
+ /** Returns address of remote end. */
+ std::auto_ptr<SocketAddress> GetRemoteSocketAddress();
+ /** Returns address of remote end: ipv4. */
+ ipaddr_t GetRemoteIP4();
+#ifdef ENABLE_IPV6
+ /** Returns address of remote end: ipv6. */
+#ifdef IPPROTO_IPV6
+ struct in6_addr GetRemoteIP6();
+#endif
+#endif
+ /** Returns remote port number: ipv4 and ipv6. */
+ port_t GetRemotePort();
+ /** Returns remote ip as string? ipv4 and ipv6. */
+ std::string GetRemoteAddress();
+ /** ipv4 and ipv6(not implemented) */
+ std::string GetRemoteHostname();
+ //@}
+
+ /** Returns local port number for bound socket file descriptor. */
+ port_t GetSockPort();
+ /** Returns local ipv4 address for bound socket file descriptor. */
+ ipaddr_t GetSockIP4();
+ /** Returns local ipv4 address as text for bound socket file descriptor. */
+ std::string GetSockAddress();
+#ifdef ENABLE_IPV6
+#ifdef IPPROTO_IPV6
+ /** Returns local ipv6 address for bound socket file descriptor. */
+ struct in6_addr GetSockIP6();
+ /** Returns local ipv6 address as text for bound socket file descriptor. */
+ std::string GetSockAddress6();
+#endif
+#endif
+ // --------------------------------------------------------------------------
+ /** @name IP options
+ When an ip or socket option is available on all of the operating systems
+ I'm testing on (linux 2.4.x, _win32, macosx, solaris9 intel) they are not
+ checked with an #ifdef below.
+ This might cause a compile error on other operating systems. */
+ // --------------------------------------------------------------------------
+
+ // IP options
+ //@{
+
+ bool SetIpOptions(const void *p, socklen_t len);
+ bool SetIpTOS(unsigned char tos);
+ unsigned char IpTOS();
+ bool SetIpTTL(int ttl);
+ int IpTTL();
+ bool SetIpHdrincl(bool x = true);
+ bool SetIpMulticastTTL(int);
+ int IpMulticastTTL();
+ bool SetMulticastLoop(bool x = true);
+ bool IpAddMembership(struct ip_mreq&);
+ bool IpDropMembership(struct ip_mreq&);
+
+#ifdef IP_PKTINFO
+ bool SetIpPktinfo(bool x = true);
+#endif
+#ifdef IP_RECVTOS
+ bool SetIpRecvTOS(bool x = true);
+#endif
+#ifdef IP_RECVTTL
+ bool SetIpRecvTTL(bool x = true);
+#endif
+#ifdef IP_RECVOPTS
+ bool SetIpRecvopts(bool x = true);
+#endif
+#ifdef IP_RETOPTS
+ bool SetIpRetopts(bool x = true);
+#endif
+#ifdef IP_RECVERR
+ bool SetIpRecverr(bool x = true);
+#endif
+#ifdef IP_MTU_DISCOVER
+ bool SetIpMtudiscover(bool x = true);
+#endif
+#ifdef IP_MTU
+ int IpMtu();
+#endif
+#ifdef IP_ROUTER_ALERT
+ bool SetIpRouterAlert(bool x = true);
+#endif
+#ifdef LINUX
+ bool IpAddMembership(struct ip_mreqn&);
+#endif
+#ifdef LINUX
+ bool IpDropMembership(struct ip_mreqn&);
+#endif
+ //@}
+
+ // SOCKET options
+ /** @name Socket Options */
+ //@{
+
+ bool SoAcceptconn();
+ bool SetSoBroadcast(bool x = true);
+ bool SetSoDebug(bool x = true);
+ int SoError();
+ bool SetSoDontroute(bool x = true);
+ bool SetSoLinger(int onoff, int linger);
+ bool SetSoOobinline(bool x = true);
+ bool SetSoRcvlowat(int);
+ bool SetSoSndlowat(int);
+ bool SetSoRcvtimeo(struct timeval&);
+ bool SetSoSndtimeo(struct timeval&);
+ bool SetSoRcvbuf(int);
+ int SoRcvbuf();
+ bool SetSoSndbuf(int);
+ int SoSndbuf();
+ int SoType();
+ bool SetSoReuseaddr(bool x = true);
+ bool SetSoKeepalive(bool x = true);
+
+#ifdef SO_BSDCOMPAT
+ bool SetSoBsdcompat(bool x = true);
+#endif
+#ifdef SO_BINDTODEVICE
+ bool SetSoBindtodevice(const std::string& intf);
+#endif
+#ifdef SO_PASSCRED
+ bool SetSoPasscred(bool x = true);
+#endif
+#ifdef SO_PEERCRED
+ bool SoPeercred(struct ucred& );
+#endif
+#ifdef SO_PRIORITY
+ bool SetSoPriority(int);
+#endif
+#ifdef SO_RCVBUFFORCE
+ bool SetSoRcvbufforce(int);
+#endif
+#ifdef SO_SNDBUFFORCE
+ bool SetSoSndbufforce(int);
+#endif
+#ifdef SO_TIMESTAMP
+ bool SetSoTimestamp(bool x = true);
+#endif
+#ifdef SO_NOSIGPIPE
+ bool SetSoNosigpipe(bool x = true);
+#endif
+ //@}
+
+ // TCP options in TcpSocket.h/TcpSocket.cpp
+
+#ifdef HAVE_OPENSSL
+ /** @name SSL Support */
+ //@{
+ /** SSL client/server support - internal use. \sa TcpSocket */
+ virtual void OnSSLConnect();
+ /** SSL client/server support - internal use. \sa TcpSocket */
+ virtual void OnSSLAccept();
+ /** SSL negotiation failed for client connect. */
+ virtual void OnSSLConnectFailed();
+ /** SSL negotiation failed for server accept. */
+ virtual void OnSSLAcceptFailed();
+ /** new SSL support */
+ virtual bool SSLNegotiate();
+ /** Check if SSL is Enabled for this TcpSocket.
+ \return true if this is a TcpSocket with SSL enabled */
+ bool IsSSL();
+ /** Enable SSL operation for a TcpSocket. */
+ void EnableSSL(bool x = true);
+ /** Still negotiating ssl connection.
+ \return true if ssl negotiating is still in progress */
+ bool IsSSLNegotiate();
+ /** Set flag indicating ssl handshaking still in progress. */
+ void SetSSLNegotiate(bool x = true);
+ /** OnAccept called with SSL Enabled.
+ \return true if this is a TcpSocket with an incoming SSL connection */
+ bool IsSSLServer();
+ /** Set flag indicating that this is a TcpSocket with incoming SSL connection. */
+ void SetSSLServer(bool x = true);
+ /** SSL; Get pointer to ssl context structure. */
+ virtual SSL_CTX *GetSslContext() { return NULL; }
+ /** SSL; Get pointer to ssl structure. */
+ virtual SSL *GetSsl() { return NULL; }
+ //@}
+#endif // HAVE_OPENSSL
+
+#ifdef ENABLE_IPV6
+ /** Enable ipv6 for this socket. */
+ void SetIpv6(bool x = true);
+ /** Check ipv6 socket.
+ \return true if this is an ipv6 socket */
+ bool IsIpv6();
+#endif
+
+#ifdef ENABLE_POOL
+ /** @name Connection Pool */
+ //@{
+ /** Client = connecting TcpSocket. */
+ void SetIsClient();
+ /** Socket type from socket() call. */
+ void SetSocketType(int x);
+ /** Socket type from socket() call. */
+ int GetSocketType();
+ /** Protocol type from socket() call. */
+ void SetSocketProtocol(const std::string& x);
+ /** Protocol type from socket() call. */
+ const std::string& GetSocketProtocol();
+ /** Instruct a client socket to stay open in the connection pool after use.
+ If you have connected to a server using tcp, you can call SetRetain
+ to leave the connection open after your socket instance has been deleted.
+ The next connection you make to the same server will reuse the already
+ opened connection, if it is still available.
+ */
+ void SetRetain();
+ /** Check retain flag.
+ \return true if the socket should be moved to connection pool after use */
+ bool Retain();
+ /** Copy connection parameters from sock. */
+ void CopyConnection(Socket *sock);
+ //@}
+#endif // ENABLE_POOL
+
+#ifdef ENABLE_SOCKS4
+ /** \name Socks4 support */
+ //@{
+ /** Socks4 client support internal use. \sa TcpSocket */
+ virtual void OnSocks4Connect();
+ /** Socks4 client support internal use. \sa TcpSocket */
+ virtual void OnSocks4ConnectFailed();
+ /** Socks4 client support internal use. \sa TcpSocket */
+ virtual bool OnSocks4Read();
+ /** Called when the last write caused the tcp output buffer to
+ * become empty. */
+ /** socket still in socks4 negotiation mode */
+ bool Socks4();
+ /** Set flag indicating Socks4 handshaking in progress */
+ void SetSocks4(bool x = true);
+
+ /** Set socks4 server host address to use */
+ void SetSocks4Host(ipaddr_t a);
+ /** Set socks4 server hostname to use. */
+ void SetSocks4Host(const std::string& );
+ /** Socks4 server port to use. */
+ void SetSocks4Port(port_t p);
+ /** Provide a socks4 userid if required by the socks4 server. */
+ void SetSocks4Userid(const std::string& x);
+ /** Get the ip address of socks4 server to use.
+ \return socks4 server host address */
+ ipaddr_t GetSocks4Host();
+ /** Get the socks4 server port to use.
+ \return socks4 server port */
+ port_t GetSocks4Port();
+ /** Get socks4 userid.
+ \return Socks4 userid */
+ const std::string& GetSocks4Userid();
+ //@}
+#endif // ENABLE_SOCKS4
+
+#ifdef ENABLE_RESOLVER
+ /** \name Asynchronous Resolver */
+ //@{
+ /** Request an asynchronous dns resolution.
+ \param host hostname to be resolved
+ \param port port number passed along for the ride
+ \return Resolve ID */
+ int Resolve(const std::string& host,port_t port = 0);
+#ifdef ENABLE_IPV6
+ int Resolve6(const std::string& host, port_t port = 0);
+#endif
+ /** Callback returning a resolved address.
+ \param id Resolve ID from Resolve call
+ \param a resolved ip address
+ \param port port number passed to Resolve */
+ virtual void OnResolved(int id,ipaddr_t a,port_t port);
+#ifdef ENABLE_IPV6
+ virtual void OnResolved(int id,in6_addr& a,port_t port);
+#endif
+ /** Request asynchronous reverse dns lookup.
+ \param a in_addr to be translated */
+ int Resolve(ipaddr_t a);
+#ifdef ENABLE_IPV6
+ int Resolve(in6_addr& a);
+#endif
+ /** Callback returning reverse resolve results.
+ \param id Resolve ID
+ \param name Resolved hostname */
+ virtual void OnReverseResolved(int id,const std::string& name);
+ /** Callback indicating failed dns lookup.
+ \param id Resolve ID */
+ virtual void OnResolveFailed(int id);
+ //@}
+#endif // ENABLE_RESOLVER
+
+#ifdef ENABLE_DETACH
+ /** \name Thread Support */
+ //@{
+ /** Callback fires when a new socket thread has started and this
+ socket is ready for operation again.
+ \sa ResolvSocket */
+ virtual void OnDetached();
+
+ // LIST_DETACH
+
+ /** Internal use. */
+ void SetDetach(bool x = true);
+ /** Check detach flag.
+ \return true if the socket should detach to its own thread */
+ bool IsDetach();
+
+ /** Internal use. */
+ void SetDetached(bool x = true);
+ /** Check detached flag.
+ \return true if the socket runs in its own thread. */
+ const bool IsDetached() const;
+ /** Order this socket to start its own thread and call OnDetached
+ when ready for operation. */
+ bool Detach();
+ /** Store the slave sockethandler pointer. */
+ void SetSlaveHandler(ISocketHandler *);
+ /** Create new thread for this socket to run detached in. */
+ void DetachSocket();
+ //@}
+#endif // ENABLE_DETACH
+
+ /** Write traffic to an IFile. Socket will not delete this object. */
+ void SetTrafficMonitor(IFile *p) { m_traffic_monitor = p; }
+
+#ifdef ENABLE_TRIGGERS
+ /** \name Triggers */
+ //@{
+ /** Subscribe to trigger id. */
+ void Subscribe(int id);
+ /** Unsubscribe from trigger id. */
+ void Unsubscribe(int id);
+ /** Trigger callback, with data passed from source to destination. */
+ virtual void OnTrigger(int id, const TriggerData& data);
+ /** Trigger cancelled because source has been deleted (as in delete). */
+ virtual void OnCancelled(int id);
+ //@}
+#endif
+
+protected:
+ /** default constructor not available */
+ Socket() : m_handler(m_handler) {}
+ /** copy constructor not available */
+ Socket(const Socket& s) : m_handler(s.m_handler) {}
+
+ /** assignment operator not available. */
+ Socket& operator=(const Socket& ) { return *this; }
+
+ /** All traffic will be written to this IFile, if set. */
+ IFile *GetTrafficMonitor() { return m_traffic_monitor; }
+
+// unsigned long m_flags; ///< boolean flags, replacing old 'bool' members
+
+private:
+ ISocketHandler& m_handler; ///< Reference of ISocketHandler in control of this socket
+ SOCKET m_socket; ///< File descriptor
+ bool m_bDel; ///< Delete by handler flag
+ bool m_bClose; ///< Close and delete flag
+ time_t m_tCreate; ///< Time in seconds when this socket was created
+ Socket *m_parent; ///< Pointer to ListenSocket class, valid for incoming sockets
+ bool m_b_disable_read; ///< Disable checking for read events
+ bool m_connected; ///< Socket is connected (tcp/udp)
+ bool m_b_erased_by_handler; ///< Set by handler before delete
+ time_t m_tClose; ///< Time in seconds when ordered to close
+ std::auto_ptr<SocketAddress> m_client_remote_address; ///< Address of last connect()
+ std::auto_ptr<SocketAddress> m_remote_address; ///< Remote end address
+ IFile *m_traffic_monitor;
+ time_t m_timeout_start; ///< Set by SetTimeout
+ time_t m_timeout_limit; ///< Defined by SetTimeout
+ bool m_bLost; ///< connection lost
+
+#ifdef _WIN32
+static WSAInitializer m_winsock_init; ///< Winsock initialization singleton class
+#endif
+
+#ifdef HAVE_OPENSSL
+ bool m_b_enable_ssl; ///< Enable SSL for this TcpSocket
+ bool m_b_ssl; ///< ssl negotiation mode (TcpSocket)
+ bool m_b_ssl_server; ///< True if this is an incoming ssl TcpSocket connection
+#endif
+
+#ifdef ENABLE_IPV6
+ bool m_ipv6; ///< This is an ipv6 socket if this one is true
+#endif
+
+#ifdef ENABLE_POOL
+ int m_socket_type; ///< Type of socket, from socket() call
+ std::string m_socket_protocol; ///< Protocol, from socket() call
+ bool m_bClient; ///< only client connections are pooled
+ bool m_bRetain; ///< keep connection on close
+#endif
+
+#ifdef ENABLE_SOCKS4
+ bool m_bSocks4; ///< socks4 negotiation mode (TcpSocket)
+ ipaddr_t m_socks4_host; ///< socks4 server address
+ port_t m_socks4_port; ///< socks4 server port number
+ std::string m_socks4_userid; ///< socks4 server usedid
+#endif
+
+#ifdef ENABLE_DETACH
+ bool m_detach; ///< Socket ordered to detach flag
+ bool m_detached; ///< Socket has been detached
+ SocketThread *m_pThread; ///< Detach socket thread class pointer
+ ISocketHandler *m_slave_handler; ///< Actual sockethandler while detached
+#endif
+};
+
+#ifdef SOCKETS_NAMESPACE
+}
+#endif
+
+#endif // _SOCKETS_Socket_H
+
+
diff --git a/externals/sockets/include/SocketAddress.h b/externals/sockets/include/SocketAddress.h
new file mode 100644
index 00000000000..abdbbfd2cf6
--- /dev/null
+++ b/externals/sockets/include/SocketAddress.h
@@ -0,0 +1,93 @@
+/**
+ ** \file SocketAddress.h
+ ** \date 2006-09-21
+ ** \author grymse@alhem.net
+**/
+/*
+Copyright (C) 2007 Anders Hedstrom
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+#ifndef _SOCKETS_SocketAddress_H
+#define _SOCKETS_SocketAddress_H
+
+#include "sockets-config.h"
+#include <string>
+#include <memory>
+#include "socket_include.h"
+
+#ifdef SOCKETS_NAMESPACE
+namespace SOCKETS_NAMESPACE {
+#endif
+
+/**
+ This class and its subclasses is intended to be used as replacement
+ for the internal data type 'ipaddr_t' and various implementations of
+ IPv6 addressing found throughout the library.
+ 'ipaddr_t' is an IPv4 address in network byte order.
+ 'port_t' is the portnumber in host byte order.
+ 'struct in6_addr' is an IPv6 address.
+ 'struct in_addr' is an IPv4 address.
+ \ingroup basic
+*/
+class SocketAddress
+{
+public:
+ virtual ~SocketAddress() {}
+
+ /** Get a pointer to the address struct. */
+ virtual operator struct sockaddr *() = 0;
+
+ /** Get length of address struct. */
+ virtual operator socklen_t() = 0;
+
+ /** Compare two addresses. */
+ virtual bool operator==(SocketAddress&) = 0;
+
+ /** Set port number.
+ \param port Port number in host byte order */
+ virtual void SetPort(port_t port) = 0;
+
+ /** Get port number.
+ \return Port number in host byte order. */
+ virtual port_t GetPort() = 0;
+
+ /** Set socket address.
+ \param sa Pointer to either 'struct sockaddr_in' or 'struct sockaddr_in6'. */
+ virtual void SetAddress(struct sockaddr *sa) = 0;
+
+ /** Convert address to text. */
+ virtual std::string Convert(bool include_port) = 0;
+
+ /** Reverse lookup of address. */
+ virtual std::string Reverse() = 0;
+
+ /** Get address family. */
+ virtual int GetFamily() = 0;
+
+ /** Address structure is valid. */
+ virtual bool IsValid() = 0;
+
+ /** Get a copy of this SocketAddress object. */
+ virtual std::auto_ptr<SocketAddress> GetCopy() = 0;
+};
+
+
+#ifdef SOCKETS_NAMESPACE
+} // namespace SOCKETS_NAMESPACE {
+#endif
+#endif // _SOCKETS_SocketAddress_H
+
+
diff --git a/externals/sockets/include/SocketHandler.h b/externals/sockets/include/SocketHandler.h
new file mode 100644
index 00000000000..5598ec4249b
--- /dev/null
+++ b/externals/sockets/include/SocketHandler.h
@@ -0,0 +1,265 @@
+/** \file SocketHandler.h
+ ** \date 2004-02-13
+ ** \author grymse@alhem.net
+**/
+/*
+Copyright (C) 2004-2007 Anders Hedstrom
+
+This library is made available under the terms of the GNU GPL.
+
+If you would like to use this library in a closed-source application,
+a separate license agreement is available. For information about
+the closed-source license agreement for the C++ sockets library,
+please visit http://www.alhem.net/Sockets/license.html and/or
+email license@alhem.net.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+#ifndef _SOCKETS_SocketHandler_H
+#define _SOCKETS_SocketHandler_H
+
+#include "sockets-config.h"
+#include <map>
+#include <list>
+
+#include "socket_include.h"
+#include "ISocketHandler.h"
+
+#ifdef SOCKETS_NAMESPACE
+namespace SOCKETS_NAMESPACE {
+#endif
+
+class Socket;
+#ifdef ENABLE_RESOLVER
+class ResolvServer;
+#endif
+class Mutex;
+
+/** Socket container class, event generator.
+ \ingroup basic */
+class SocketHandler : public ISocketHandler
+{
+protected:
+ /** Map type for holding file descriptors/socket object pointers. */
+ typedef std::map<SOCKET,Socket *> socket_m;
+
+public:
+ /** SocketHandler constructor.
+ \param log Optional log class pointer */
+ SocketHandler(StdLog *log = NULL);
+
+ /** SocketHandler threadsafe constructor.
+ \param mutex Externally declared mutex variable
+ \param log Optional log class pointer */
+ SocketHandler(Mutex& mutex,StdLog *log = NULL);
+
+ ~SocketHandler();
+
+ /** Get mutex reference for threadsafe operations. */
+ Mutex& GetMutex() const;
+
+ /** Register StdLog object for error callback.
+ \param log Pointer to log class */
+ void RegStdLog(StdLog *log);
+
+ /** Log error to log class for print out / storage. */
+ void LogError(Socket *p,const std::string& user_text,int err,const std::string& sys_err,loglevel_t t = LOG_LEVEL_WARNING);
+
+ /** Add socket instance to socket map. Removal is always automatic. */
+ void Add(Socket *);
+
+ /** Get status of read/write/exception file descriptor set for a socket. */
+ void Get(SOCKET s,bool& r,bool& w,bool& e);
+
+ /** Set read/write/exception file descriptor sets (fd_set). */
+ void Set(SOCKET s,bool bRead,bool bWrite,bool bException = true);
+
+ /** Wait for events, generate callbacks. */
+ int Select(long sec,long usec);
+
+ /** This method will not return until an event has been detected. */
+ int Select();
+
+ /** Wait for events, generate callbacks. */
+ int Select(struct timeval *tsel);
+
+ /** Check that a socket really is handled by this socket handler. */
+ bool Valid(Socket *);
+
+ /** Return number of sockets handled by this handler. */
+ size_t GetCount();
+
+ /** Override and return false to deny all incoming connections.
+ \param p ListenSocket class pointer (use GetPort to identify which one) */
+ bool OkToAccept(Socket *p);
+
+ /** Called by Socket when a socket changes state. */
+ void AddList(SOCKET s,list_t which_one,bool add);
+
+ // Connection pool
+#ifdef ENABLE_POOL
+ /** Find available open connection (used by connection pool). */
+ ISocketHandler::PoolSocket *FindConnection(int type,const std::string& protocol,SocketAddress&);
+ /** Enable connection pool (by default disabled). */
+ void EnablePool(bool x = true);
+ /** Check pool status.
+ \return true if connection pool is enabled */
+ bool PoolEnabled();
+#endif // ENABLE_POOL
+
+ // Socks4
+#ifdef ENABLE_SOCKS4
+ /** Set socks4 server ip that all new tcp sockets should use. */
+ void SetSocks4Host(ipaddr_t);
+ /** Set socks4 server hostname that all new tcp sockets should use. */
+ void SetSocks4Host(const std::string& );
+ /** Set socks4 server port number that all new tcp sockets should use. */
+ void SetSocks4Port(port_t);
+ /** Set optional socks4 userid. */
+ void SetSocks4Userid(const std::string& );
+ /** If connection to socks4 server fails, immediately try direct connection to final host. */
+ void SetSocks4TryDirect(bool x = true);
+ /** Get socks4 server ip.
+ \return socks4 server ip */
+ ipaddr_t GetSocks4Host();
+ /** Get socks4 port number.
+ \return socks4 port number */
+ port_t GetSocks4Port();
+ /** Get socks4 userid (optional).
+ \return socks4 userid */
+ const std::string& GetSocks4Userid();
+ /** Check status of socks4 try direct flag.
+ \return true if direct connection should be tried if connection to socks4 server fails */
+ bool Socks4TryDirect();
+#endif // ENABLE_SOCKS4
+
+ // DNS resolve server
+#ifdef ENABLE_RESOLVER
+ /** Enable asynchronous DNS.
+ \param port Listen port of asynchronous dns server */
+ void EnableResolver(port_t port = 16667);
+ /** Check resolver status.
+ \return true if resolver is enabled */
+ bool ResolverEnabled();
+ /** Queue a dns request.
+ \param host Hostname to be resolved
+ \param port Port number will be echoed in Socket::OnResolved callback */
+ int Resolve(Socket *,const std::string& host,port_t port);
+#ifdef ENABLE_IPV6
+ int Resolve6(Socket *,const std::string& host,port_t port);
+#endif
+ /** Do a reverse dns lookup. */
+ int Resolve(Socket *,ipaddr_t a);
+#ifdef ENABLE_IPV6
+ int Resolve(Socket *,in6_addr& a);
+#endif
+ /** Get listen port of asynchronous dns server. */
+ port_t GetResolverPort();
+ /** Resolver thread ready for queries. */
+ bool ResolverReady();
+ /** Returns true if the socket is waiting for a resolve event. */
+ bool Resolving(Socket *);
+#endif // ENABLE_RESOLVER
+
+#ifdef ENABLE_TRIGGERS
+ /** Fetch unique trigger id. */
+ int TriggerID(Socket *src);
+ /** Subscribe socket to trigger id. */
+ bool Subscribe(int id, Socket *dst);
+ /** Unsubscribe socket from trigger id. */
+ bool Unsubscribe(int id, Socket *dst);
+ /** Execute OnTrigger for subscribed sockets.
+ \param id Trigger ID
+ \param data Data passed from source to destination
+ \param erase Empty trigger id source and destination maps if 'true',
+ Leave them in place if 'false' - if a trigger should be called many times */
+ void Trigger(int id, Socket::TriggerData& data, bool erase = true);
+#endif // ENABLE_TRIGGERS
+
+#ifdef ENABLE_DETACH
+ /** Indicates that the handler runs under SocketThread. */
+ void SetSlave(bool x = true);
+ /** Indicates that the handler runs under SocketThread. */
+ bool IsSlave();
+#endif
+
+ /** Sanity check of those accursed lists. */
+ void CheckSanity();
+
+protected:
+ socket_m m_sockets; ///< Active sockets map
+ socket_m m_add; ///< Sockets to be added to sockets map
+ std::list<Socket *> m_delete; ///< Sockets to be deleted (failed when Add)
+
+protected:
+ StdLog *m_stdlog; ///< Registered log class, or NULL
+ Mutex& m_mutex; ///< Thread safety mutex
+ bool m_b_use_mutex; ///< Mutex correctly initialized
+
+private:
+ void CheckList(socket_v&,const std::string&); ///< Used by CheckSanity
+ /** Remove socket from socket map, used by Socket class. */
+ void Remove(Socket *);
+ SOCKET m_maxsock; ///< Highest file descriptor + 1 in active sockets list
+ fd_set m_rfds; ///< file descriptor set monitored for read events
+ fd_set m_wfds; ///< file descriptor set monitored for write events
+ fd_set m_efds; ///< file descriptor set monitored for exceptions
+ int m_preverror; ///< debug select() error
+ int m_errcnt; ///< debug select() error
+ time_t m_tlast; ///< timeout control
+
+ // state lists
+ socket_v m_fds; ///< Active file descriptor list
+ socket_v m_fds_erase; ///< File descriptors that are to be erased from m_sockets
+ socket_v m_fds_callonconnect; ///< checklist CallOnConnect
+#ifdef ENABLE_DETACH
+ socket_v m_fds_detach; ///< checklist Detach
+#endif
+ socket_v m_fds_timeout; ///< checklist timeout
+ socket_v m_fds_retry; ///< checklist retry client connect
+ socket_v m_fds_close; ///< checklist close and delete
+
+#ifdef ENABLE_SOCKS4
+ ipaddr_t m_socks4_host; ///< Socks4 server host ip
+ port_t m_socks4_port; ///< Socks4 server port number
+ std::string m_socks4_userid; ///< Socks4 userid
+ bool m_bTryDirect; ///< Try direct connection if socks4 server fails
+#endif
+#ifdef ENABLE_RESOLVER
+ int m_resolv_id; ///< Resolver id counter
+ ResolvServer *m_resolver; ///< Resolver thread pointer
+ port_t m_resolver_port; ///< Resolver listen port
+ std::map<Socket *, bool> m_resolve_q; ///< resolve queue
+#endif
+#ifdef ENABLE_POOL
+ bool m_b_enable_pool; ///< Connection pool enabled if true
+#endif
+#ifdef ENABLE_TRIGGERS
+ int m_next_trigger_id; ///< Unique trigger id counter
+ std::map<int, Socket *> m_trigger_src; ///< mapping trigger id to source socket
+ std::map<int, std::map<Socket *, bool> > m_trigger_dst; ///< mapping trigger id to destination sockets
+#endif
+#ifdef ENABLE_DETACH
+ bool m_slave; ///< Indicates that this is a ISocketHandler run in SocketThread
+#endif
+};
+
+#ifdef SOCKETS_NAMESPACE
+}
+#endif
+
+#endif // _SOCKETS_SocketHandler_H
+
+
diff --git a/externals/sockets/include/StdLog.h b/externals/sockets/include/StdLog.h
new file mode 100644
index 00000000000..3ff68d6e9ea
--- /dev/null
+++ b/externals/sockets/include/StdLog.h
@@ -0,0 +1,73 @@
+/** \file StdLog.h
+ ** \date 2004-06-01
+ ** \author grymse@alhem.net
+**/
+/*
+Copyright (C) 2004-2007 Anders Hedstrom
+
+This library is made available under the terms of the GNU GPL.
+
+If you would like to use this library in a closed-source application,
+a separate license agreement is available. For information about
+the closed-source license agreement for the C++ sockets library,
+please visit http://www.alhem.net/Sockets/license.html and/or
+email license@alhem.net.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+#ifndef _SOCKETS_StdLog_H
+#define _SOCKETS_StdLog_H
+
+#include "sockets-config.h"
+#include <string>
+
+#ifdef SOCKETS_NAMESPACE
+namespace SOCKETS_NAMESPACE {
+#endif
+
+/** error level enum. */
+typedef enum
+{
+ LOG_LEVEL_WARNING = 0,
+ LOG_LEVEL_ERROR,
+ LOG_LEVEL_FATAL,
+ LOG_LEVEL_INFO
+} loglevel_t;
+
+class ISocketHandler;
+class Socket;
+
+/** \defgroup logging Log help classes */
+/** Log class interface.
+ \ingroup logging */
+class StdLog
+{
+public:
+ virtual ~StdLog() {}
+
+ virtual void error(ISocketHandler *,Socket *,
+ const std::string& user_text,
+ int err,
+ const std::string& sys_err,
+ loglevel_t = LOG_LEVEL_WARNING) = 0;
+};
+
+#ifdef SOCKETS_NAMESPACE
+}
+#endif
+
+#endif // _SOCKETS_StdLog_H
+
+
diff --git a/externals/sockets/include/StdoutLog.h b/externals/sockets/include/StdoutLog.h
new file mode 100644
index 00000000000..aeb25b3e6e6
--- /dev/null
+++ b/externals/sockets/include/StdoutLog.h
@@ -0,0 +1,55 @@
+/** \file StdoutLog.h
+ ** \date 2004-06-01
+ ** \author grymse@alhem.net
+**/
+/*
+Copyright (C) 2004-2007 Anders Hedstrom
+
+This library is made available under the terms of the GNU GPL.
+
+If you would like to use this library in a closed-source application,
+a separate license agreement is available. For information about
+the closed-source license agreement for the C++ sockets library,
+please visit http://www.alhem.net/Sockets/license.html and/or
+email license@alhem.net.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+#ifndef _SOCKETS_StdoutLog_H
+#define _SOCKETS_StdoutLog_H
+
+#include "sockets-config.h"
+#include "StdLog.h"
+
+#ifdef SOCKETS_NAMESPACE
+namespace SOCKETS_NAMESPACE {
+#endif
+
+/** StdLog implementation, logs to stdout.
+ \ingroup logging */
+class StdoutLog : public StdLog
+{
+public:
+ void error(ISocketHandler *,Socket *,const std::string& call,int err,const std::string& sys_err,loglevel_t);
+};
+
+
+#ifdef SOCKETS_NAMESPACE
+}
+#endif
+
+#endif // _SOCKETS_StdoutLog_H
+
+
diff --git a/externals/sockets/include/StreamSocket.h b/externals/sockets/include/StreamSocket.h
new file mode 100644
index 00000000000..bcce10ffbc5
--- /dev/null
+++ b/externals/sockets/include/StreamSocket.h
@@ -0,0 +1,124 @@
+#ifndef _StreamSocket_H
+#define _StreamSocket_H
+
+#include "Socket.h"
+
+#ifdef SOCKETS_NAMESPACE
+namespace SOCKETS_NAMESPACE {
+#endif
+
+/** SOCK_STREAM Socket base class.
+ \ingroup basic */
+class StreamSocket : public Socket
+{
+public:
+ StreamSocket(ISocketHandler& );
+ ~StreamSocket();
+
+ /** Socket should Check Connect on next write event from select(). */
+ void SetConnecting(bool = true);
+
+ /** Check connecting flag.
+ \return true if the socket is still trying to connect */
+ bool Connecting();
+
+ /** Returns true when socket file descriptor is valid,
+ socket connection is established, and socket is not about to
+ be closed. */
+ bool Ready();
+
+ /** Set timeout to use for connection attempt.
+ \param x Timeout in seconds */
+ void SetConnectTimeout(int x);
+
+ /** Return number of seconds to wait for a connection.
+ \return Connection timeout (seconds) */
+ int GetConnectTimeout();
+
+ /** Set flush before close to make a tcp socket completely empty its
+ output buffer before closing the connection. */
+ void SetFlushBeforeClose(bool = true);
+
+ /** Check flush before status.
+ \return true if the socket should send all data before closing */
+ bool GetFlushBeforeClose();
+
+ /** Define number of connection retries (tcp only).
+ n = 0 - no retry
+ n > 0 - number of retries
+ n = -1 - unlimited retries */
+ void SetConnectionRetry(int n);
+
+ /** Get number of maximum connection retries (tcp only). */
+ int GetConnectionRetry();
+
+ /** Increase number of actual connection retries (tcp only). */
+ void IncreaseConnectionRetries();
+
+ /** Get number of actual connection retries (tcp only). */
+ int GetConnectionRetries();
+
+ /** Reset actual connection retries (tcp only). */
+ void ResetConnectionRetries();
+
+ // LIST_CALLONCONNECT
+
+ /** Instruct socket to call OnConnect callback next sockethandler cycle. */
+ void SetCallOnConnect(bool x = true);
+
+ /** Check call on connect flag.
+ \return true if OnConnect() should be called a.s.a.p */
+ bool CallOnConnect();
+
+ // LIST_RETRY
+
+ /** Set flag to initiate a connection attempt after a connection timeout. */
+ void SetRetryClientConnect(bool x = true);
+
+ /** Check if a connection attempt should be made.
+ \return true when another attempt should be made */
+ bool RetryClientConnect();
+
+ /** Called after OnRead if socket is in line protocol mode.
+ \sa SetLineProtocol */
+ /** Enable the OnLine callback. Do not create your own OnRead
+ * callback when using this. */
+ virtual void SetLineProtocol(bool = true);
+
+ /** Check line protocol mode.
+ \return true if socket is in line protocol mode */
+ bool LineProtocol();
+
+ /** Set shutdown status. */
+ void SetShutdown(int);
+
+ /** Get shutdown status. */
+ int GetShutdown();
+
+ /** Returns IPPROTO_TCP or IPPROTO_SCTP */
+ virtual int Protocol() = 0;
+
+protected:
+ StreamSocket(const StreamSocket& ) {} // copy constructor
+
+private:
+ StreamSocket& operator=(const StreamSocket& ) { return *this; } // assignment operator
+
+ bool m_bConnecting; ///< Flag indicating connection in progress
+ int m_connect_timeout; ///< Connection timeout (seconds)
+ bool m_flush_before_close; ///< Send all data before closing (default true)
+ int m_connection_retry; ///< Maximum connection retries (tcp)
+ int m_retries; ///< Actual number of connection retries (tcp)
+ bool m_call_on_connect; ///< OnConnect will be called next ISocketHandler cycle if true
+ bool m_b_retry_connect; ///< Try another connection attempt next ISocketHandler cycle
+ bool m_line_protocol; ///< Line protocol mode flag
+ int m_shutdown; ///< Shutdown status
+};
+
+#ifdef SOCKETS_NAMESPACE
+} // namespace SOCKETS_NAMESPACE {
+#endif
+
+#endif // _StreamSocket_H
+
+
diff --git a/externals/sockets/include/TcpSocket.h b/externals/sockets/include/TcpSocket.h
new file mode 100644
index 00000000000..de1be8bd8b9
--- /dev/null
+++ b/externals/sockets/include/TcpSocket.h
@@ -0,0 +1,356 @@
+/** \file TcpSocket.h
+ ** \date 2004-02-13
+ ** \author grymse@alhem.net
+**/
+/*
+Copyright (C) 2004-2007 Anders Hedstrom
+
+This library is made available under the terms of the GNU GPL.
+
+If you would like to use this library in a closed-source application,
+a separate license agreement is available. For information about
+the closed-source license agreement for the C++ sockets library,
+please visit http://www.alhem.net/Sockets/license.html and/or
+email license@alhem.net.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+#ifndef _SOCKETS_TcpSocket_H
+#define _SOCKETS_TcpSocket_H
+#include "sockets-config.h"
+#include "StreamSocket.h"
+#ifdef HAVE_OPENSSL
+#include <openssl/ssl.h>
+#include "SSLInitializer.h"
+#endif
+
+#include <string.h>
+
+#define TCP_BUFSIZE_READ 16400
+#define TCP_OUTPUT_CAPACITY 1024000
+
+#ifdef SOCKETS_NAMESPACE
+namespace SOCKETS_NAMESPACE {
+#endif
+
+class SocketAddress;
+
+/** Socket implementation for TCP.
+ \ingroup basic */
+class TcpSocket : public StreamSocket
+{
+ /** \defgroup internal Internal utility */
+protected:
+ /** Buffer class containing one read/write circular buffer.
+ \ingroup internal */
+ class CircularBuffer
+ {
+ public:
+ CircularBuffer(size_t size);
+ ~CircularBuffer();
+
+ /** append l bytes from p to buffer */
+ bool Write(const char *p,size_t l);
+ /** copy l bytes from buffer to dest */
+ bool Read(char *dest,size_t l);
+ /** copy l bytes from buffer to dest, dont touch buffer pointers */
+ bool SoftRead(char *dest, size_t l);
+ /** skip l bytes from buffer */
+ bool Remove(size_t l);
+ /** read l bytes from buffer, returns as string. */
+ std::string ReadString(size_t l);
+
+ /** total buffer length */
+ size_t GetLength();
+ /** pointer to circular buffer beginning */
+ const char *GetStart();
+ /** return number of bytes from circular buffer beginning to buffer physical end */
+ size_t GetL();
+ /** return free space in buffer, number of bytes until buffer overrun */
+ size_t Space();
+
+ /** return total number of bytes written to this buffer, ever */
+ unsigned long ByteCounter(bool clear = false);
+
+ private:
+ CircularBuffer(const CircularBuffer& /*s*/) {}
+ CircularBuffer& operator=(const CircularBuffer& ) { return *this; }
+ char *buf;
+ size_t m_max;
+ size_t m_q;
+ size_t m_b;
+ size_t m_t;
+ unsigned long m_count;
+ };
+ /** Output buffer struct.
+ \ingroup internal */
+ struct OUTPUT {
+ OUTPUT() : _b(0), _t(0), _q(0) {}
+ OUTPUT(const char *buf, size_t len) : _b(0), _t(len), _q(len) {
+ memcpy(_buf, buf, len);
+ }
+ size_t Space() {
+ return TCP_OUTPUT_CAPACITY - _t;
+ }
+ void Add(const char *buf, size_t len) {
+ memcpy(_buf + _t, buf, len);
+ _t += len;
+ _q += len;
+ }
+ size_t Remove(size_t len) {
+ _b += len;
+ _q -= len;
+ return _q;
+ }
+ const char *Buf() {
+ return _buf + _b;
+ }
+ size_t Len() {
+ return _q;
+ }
+ size_t _b;
+ size_t _t;
+ size_t _q;
+ char _buf[TCP_OUTPUT_CAPACITY];
+ };
+ typedef std::list<OUTPUT *> output_l;
+
+public:
+ /** Constructor with standard values on input/output buffers. */
+ TcpSocket(ISocketHandler& );
+ /** Constructor with custom values for i/o buffer.
+ \param h ISocketHandler reference
+ \param isize Input buffer size
+ \param osize Output buffer size */
+ TcpSocket(ISocketHandler& h,size_t isize,size_t osize);
+ ~TcpSocket();
+
+ /** Open a connection to a remote server.
+ If you want your socket to connect to a server,
+ always call Open before Add'ing a socket to the sockethandler.
+ If not, the connection attempt will not be monitored by the
+ socket handler...
+ \param ip IP address
+ \param port Port number
+ \param skip_socks Do not use socks4 even if configured */
+ bool Open(ipaddr_t ip,port_t port,bool skip_socks = false);
+#ifdef ENABLE_IPV6
+#ifdef IPPROTO_IPV6
+ /** Open connection.
+ \param ip Ipv6 address
+ \param port Port number
+ \param skip_socks Do not use socks4 even if configured */
+ bool Open(in6_addr ip,port_t port,bool skip_socks = false);
+#endif
+#endif
+ bool Open(SocketAddress&,bool skip_socks = false);
+ bool Open(SocketAddress&,SocketAddress& bind_address,bool skip_socks = false);
+ /** Open connection.
+ \param host Hostname
+ \param port Port number */
+ bool Open(const std::string &host,port_t port);
+
+ /** Connect timeout callback. */
+ void OnConnectTimeout();
+#ifdef _WIN32
+ /** Connection failed reported as exception on win32 */
+ void OnException();
+#endif
+
+ /** Close file descriptor - internal use only.
+ \sa SetCloseAndDelete */
+ int Close();
+
+ /** Send a string.
+ \param s String to send
+ \param f Dummy flags -- not used */
+ void Send(const std::string &s,int f = 0);
+ /** Send string using printf formatting. */
+ void Sendf(const char *format, ...);
+ /** Send buffer of bytes.
+ \param buf Buffer pointer
+ \param len Length of data
+ \param f Dummy flags -- not used */
+ void SendBuf(const char *buf,size_t len,int f = 0);
+ /** This callback is executed after a successful read from the socket.
+ \param buf Pointer to the data
+ \param len Length of the data */
+ virtual void OnRawData(const char *buf,size_t len);
+
+ /** Called when output buffer has been sent.
+ Note: Will only be called IF the output buffer has been used.
+ Send's that was successful without needing the output buffer
+ will not generate a call to this method. */
+ virtual void OnWriteComplete();
+ /** Number of bytes in input buffer. */
+ size_t GetInputLength();
+ /** Number of bytes in output buffer. */
+ size_t GetOutputLength();
+
+ /** Callback fires when a socket in line protocol has read one full line.
+ \param line Line read */
+ void OnLine(const std::string& line);
+ /** Get counter of number of bytes received. */
+ uint64_t GetBytesReceived(bool clear = false);
+ /** Get counter of number of bytes sent. */
+ uint64_t GetBytesSent(bool clear = false);
+
+ /** Socks4 specific callback. */
+ void OnSocks4Connect();
+ /** Socks4 specific callback. */
+ void OnSocks4ConnectFailed();
+ /** Socks4 specific callback.
+ \return 'need_more' */
+ bool OnSocks4Read();
+
+#ifdef ENABLE_RESOLVER
+ /** Callback executed when resolver thread has finished a resolve request. */
+ void OnResolved(int id,ipaddr_t a,port_t port);
+#ifdef ENABLE_IPV6
+ void OnResolved(int id,in6_addr& a,port_t port);
+#endif
+#endif
+#ifdef HAVE_OPENSSL
+ /** Callback for 'New' ssl support - replaces SSLSocket. Internal use. */
+ void OnSSLConnect();
+ /** Callback for 'New' ssl support - replaces SSLSocket. Internal use. */
+ void OnSSLAccept();
+ /** This method must be implemented to initialize
+ the ssl context for an outgoing connection. */
+ virtual void InitSSLClient();
+ /** This method must be implemented to initialize
+ the ssl context for an incoming connection. */
+ virtual void InitSSLServer();
+#endif
+
+#ifdef ENABLE_RECONNECT
+ /** Flag that says a broken connection will try to reconnect. */
+ void SetReconnect(bool = true);
+ /** Check reconnect on lost connection flag status. */
+ bool Reconnect();
+ /** Flag to determine if a reconnect is in progress. */
+ void SetIsReconnect(bool x = true);
+ /** Socket is reconnecting. */
+ bool IsReconnect();
+#endif
+
+ void DisableInputBuffer(bool = true);
+
+ void OnOptions(int,int,int,SOCKET);
+
+ void SetLineProtocol(bool = true);
+
+ // TCP options
+ bool SetTcpNodelay(bool = true);
+
+ virtual int Protocol();
+
+ /** Trigger limit for callback OnTransferLimit. */
+ void SetTransferLimit(size_t sz);
+ /** This callback fires when the output buffer drops below the value
+ set by SetTransferLimit. Default: 0 (disabled). */
+ virtual void OnTransferLimit();
+
+protected:
+ TcpSocket(const TcpSocket& );
+ void OnRead();
+ void OnRead( char *buf, size_t n );
+ void OnWrite();
+#ifdef HAVE_OPENSSL
+ /** SSL; Initialize ssl context for a client socket.
+ \param meth_in SSL method */
+ void InitializeContext(const std::string& context, SSL_METHOD *meth_in = NULL);
+ /** SSL; Initialize ssl context for a server socket.
+ \param keyfile Combined private key/certificate file
+ \param password Password for private key
+ \param meth_in SSL method */
+ void InitializeContext(const std::string& context, const std::string& keyfile, const std::string& password, SSL_METHOD *meth_in = NULL);
+ /** SSL; Initialize ssl context for a server socket.
+ \param certfile Separate certificate file
+ \param keyfile Combined private key/certificate file
+ \param password Password for private key
+ \param meth_in SSL method */
+ void InitializeContext(const std::string& context, const std::string& certfile, const std::string& keyfile, const std::string& password, SSL_METHOD *meth_in = NULL);
+ /** SSL; Password callback method. */
+static int SSL_password_cb(char *buf,int num,int rwflag,void *userdata);
+ /** SSL; Get pointer to ssl context structure. */
+ virtual SSL_CTX *GetSslContext();
+ /** SSL; Get pointer to ssl structure. */
+ virtual SSL *GetSsl();
+ /** ssl; still negotiating connection. */
+ bool SSLNegotiate();
+ /** SSL; Get ssl password. */
+ const std::string& GetPassword();
+#endif
+
+ CircularBuffer ibuf; ///< Circular input buffer
+
+private:
+ TcpSocket& operator=(const TcpSocket& ) { return *this; }
+
+ /** the actual send() */
+ int TryWrite(const char *buf, size_t len);
+ /** add data to output buffer top */
+ void Buffer(const char *buf, size_t len);
+
+ //
+ bool m_b_input_buffer_disabled;
+ uint64_t m_bytes_sent;
+ uint64_t m_bytes_received;
+ bool m_skip_c; ///< Skip second char of CRLF or LFCR sequence in OnRead
+ char m_c; ///< First char in CRLF or LFCR sequence
+ std::string m_line; ///< Current line in line protocol mode
+#ifdef SOCKETS_DYNAMIC_TEMP
+ char *m_buf; ///< temporary read buffer
+#endif
+ output_l m_obuf; ///< output buffer
+ OUTPUT *m_obuf_top; ///< output buffer on top
+ size_t m_transfer_limit;
+ size_t m_output_length;
+
+#ifdef HAVE_OPENSSL
+static SSLInitializer m_ssl_init;
+ SSL_CTX *m_ssl_ctx; ///< ssl context
+ SSL *m_ssl; ///< ssl 'socket'
+ BIO *m_sbio; ///< ssl bio
+ std::string m_password; ///< ssl password
+#endif
+
+#ifdef ENABLE_SOCKS4
+ int m_socks4_state; ///< socks4 support
+ char m_socks4_vn; ///< socks4 support, temporary variable
+ char m_socks4_cd; ///< socks4 support, temporary variable
+ unsigned short m_socks4_dstport; ///< socks4 support
+ unsigned long m_socks4_dstip; ///< socks4 support
+#endif
+
+#ifdef ENABLE_RESOLVER
+ int m_resolver_id; ///< Resolver id (if any) for current Open call
+#endif
+
+#ifdef ENABLE_RECONNECT
+ bool m_b_reconnect; ///< Reconnect on lost connection flag
+ bool m_b_is_reconnect; ///< Trying to reconnect
+#endif
+
+};
+
+#ifdef SOCKETS_NAMESPACE
+}
+#endif
+
+#endif // _SOCKETS_TcpSocket_H
+
+
diff --git a/externals/sockets/include/Thread.h b/externals/sockets/include/Thread.h
new file mode 100644
index 00000000000..efb766e9ee6
--- /dev/null
+++ b/externals/sockets/include/Thread.h
@@ -0,0 +1,100 @@
+/** \file Thread.h
+ ** \date 2004-10-30
+ ** \author grymse@alhem.net
+**/
+/*
+Copyright (C) 2004-2007 Anders Hedstrom
+
+This library is made available under the terms of the GNU GPL.
+
+If you would like to use this library in a closed-source application,
+a separate license agreement is available. For information about
+the closed-source license agreement for the C++ sockets library,
+please visit http://www.alhem.net/Sockets/license.html and/or
+email license@alhem.net.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+#ifndef _SOCKETS_Thread_H
+#define _SOCKETS_Thread_H
+
+#include "sockets-config.h"
+#ifdef SOCKETS_NAMESPACE
+namespace SOCKETS_NAMESPACE {
+#endif
+
+#ifdef _WIN32
+// to be
+//typedef DWORD threadfunc_t;
+//typedef LPVOID threadparam_t;
+//#define STDPREFIX WINAPI
+typedef unsigned threadfunc_t;
+typedef void * threadparam_t;
+#define STDPREFIX __stdcall
+#else
+#include <pthread.h>
+
+typedef void * threadfunc_t;
+typedef void * threadparam_t;
+#define STDPREFIX
+#endif
+
+/** \defgroup threading Threading */
+/** Thread base class.
+The Thread class is used by the resolver (ResolvServer) and running a detached socket (SocketThread).
+When you know some processing will take a long time and will freeze up a socket, there is always the
+possibility to call Detach() on that socket before starting the processing.
+When the OnDetached() callback is later called the processing can continue, now in its own thread.
+ \ingroup threading */
+class Thread
+{
+public:
+ Thread(bool release = true);
+ virtual ~Thread();
+
+ static threadfunc_t STDPREFIX StartThread(threadparam_t);
+
+ virtual void Run() = 0;
+
+ bool IsRunning();
+ void SetRunning(bool x);
+ bool IsReleased();
+ void SetRelease(bool x);
+ bool DeleteOnExit();
+ void SetDeleteOnExit(bool x = true);
+ bool IsDestructor();
+
+private:
+ Thread(const Thread& ) {}
+ Thread& operator=(const Thread& ) { return *this; }
+#ifdef _WIN32
+ HANDLE m_thread;
+ unsigned m_dwThreadId;
+#else
+ pthread_t m_thread;
+#endif
+ bool m_running;
+ bool m_release;
+ bool m_b_delete_on_exit;
+ bool m_b_destructor;
+};
+
+#ifdef SOCKETS_NAMESPACE
+}
+#endif
+
+#endif // _SOCKETS_Thread_H
+
+
diff --git a/externals/sockets/include/UdpSocket.h b/externals/sockets/include/UdpSocket.h
new file mode 100644
index 00000000000..3b06c6955bd
--- /dev/null
+++ b/externals/sockets/include/UdpSocket.h
@@ -0,0 +1,215 @@
+/** \file UdpSocket.h
+ ** \date 2004-02-13
+ ** \author grymse@alhem.net
+**/
+/*
+Copyright (C) 2004-2007 Anders Hedstrom
+
+This library is made available under the terms of the GNU GPL.
+
+If you would like to use this library in a closed-source application,
+a separate license agreement is available. For information about
+the closed-source license agreement for the C++ sockets library,
+please visit http://www.alhem.net/Sockets/license.html and/or
+email license@alhem.net.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+#ifndef _SOCKETS_UdpSocket_H
+#define _SOCKETS_UdpSocket_H
+
+#include "sockets-config.h"
+#include "Socket.h"
+
+#ifdef SOCKETS_NAMESPACE
+namespace SOCKETS_NAMESPACE {
+#endif
+
+/** Socket implementation for UDP.
+ \ingroup basic */
+class UdpSocket : public Socket
+{
+public:
+ /** Constructor.
+ \param h ISocketHandler reference
+ \param ibufsz Maximum size of receive message (extra bytes will be truncated)
+ \param ipv6 'true' if this is an ipv6 socket */
+ UdpSocket(ISocketHandler& h,int ibufsz = 16384,bool ipv6 = false, int retries = 0);
+ ~UdpSocket();
+
+ /** Called when incoming data has been received.
+ \param buf Pointer to data
+ \param len Length of data
+ \param sa Pointer to sockaddr struct of sender
+ \param sa_len Length of sockaddr struct */
+ virtual void OnRawData(const char *buf,size_t len,struct sockaddr *sa,socklen_t sa_len);
+
+ /** Called when incoming data has been received and read timestamp is enabled.
+ \param buf Pointer to data
+ \param len Length of data
+ \param sa Pointer to sockaddr struct of sender
+ \param sa_len Length of sockaddr struct
+ \param ts Timestamp from message */
+ virtual void OnRawData(const char *buf,size_t len,struct sockaddr *sa,socklen_t sa_len,struct timeval *ts);
+
+ /** To receive incoming data, call Bind to setup an incoming port.
+ \param port Incoming port number
+ \param range Port range to try if ports already in use
+ \return 0 if bind succeeded */
+ int Bind(port_t& port,int range = 1);
+ /** To receive data on a specific interface:port, use this.
+ \param intf Interface ip/hostname
+ \param port Port number
+ \param range Port range
+ \return 0 if bind succeeded */
+ int Bind(const std::string& intf,port_t& port,int range = 1);
+ /** To receive data on a specific interface:port, use this.
+ \param a Ip address
+ \param port Port number
+ \param range Port range
+ \return 0 if bind succeeded */
+ int Bind(ipaddr_t a,port_t& port,int range = 1);
+#ifdef ENABLE_IPV6
+#ifdef IPPROTO_IPV6
+ /** To receive data on a specific interface:port, use this.
+ \param a Ipv6 address
+ \param port Port number
+ \param range Port range
+ \return 0 if bind succeeded */
+ int Bind(in6_addr a,port_t& port,int range = 1);
+#endif
+#endif
+ /** To receive data on a specific interface:port, use this.
+ \param ad Socket address
+ \param range Port range
+ \return 0 if bind succeeded */
+ int Bind(SocketAddress& ad,int range = 1);
+
+ /** Define remote host.
+ \param l Address of remote host
+ \param port Port of remote host
+ \return true if successful */
+ bool Open(ipaddr_t l,port_t port);
+ /** Define remote host.
+ \param host Hostname
+ \param port Port number
+ \return true if successful */
+ bool Open(const std::string& host,port_t port);
+#ifdef ENABLE_IPV6
+#ifdef IPPROTO_IPV6
+ /** Define remote host.
+ \param a Address of remote host, ipv6
+ \param port Port of remote host
+ \return true if successful */
+ bool Open(struct in6_addr& a,port_t port);
+#endif
+#endif
+ /** Define remote host.
+ \param ad Socket address
+ \return true if successful */
+ bool Open(SocketAddress& ad);
+
+ /** Send to specified host */
+ void SendToBuf(const std::string& ,port_t,const char *data,int len,int flags = 0);
+ /** Send to specified address */
+ void SendToBuf(ipaddr_t,port_t,const char *data,int len,int flags = 0);
+#ifdef ENABLE_IPV6
+#ifdef IPPROTO_IPV6
+ /** Send to specified ipv6 address */
+ void SendToBuf(in6_addr,port_t,const char *data,int len,int flags = 0);
+#endif
+#endif
+ /** Send to specified socket address */
+ void SendToBuf(SocketAddress& ad,const char *data,int len,int flags = 0);
+
+ /** Send string to specified host */
+ void SendTo(const std::string&,port_t,const std::string&,int flags = 0);
+ /** Send string to specified address */
+ void SendTo(ipaddr_t,port_t,const std::string&,int flags = 0);
+#ifdef ENABLE_IPV6
+#ifdef IPPROTO_IPV6
+ /** Send string to specified ipv6 address */
+ void SendTo(in6_addr,port_t,const std::string&,int flags = 0);
+#endif
+#endif
+ /** Send string to specified socket address */
+ void SendTo(SocketAddress& ad,const std::string&,int flags = 0);
+
+ /** Send to connected address */
+ void SendBuf(const char *data,size_t,int flags = 0);
+ /** Send string to connected address. */
+ void Send(const std::string& ,int flags = 0);
+
+ /** Set broadcast */
+ void SetBroadcast(bool b = true);
+ /** Check broadcast flag.
+ \return true broadcast is enabled. */
+ bool IsBroadcast();
+
+ /** multicast */
+ void SetMulticastTTL(int ttl = 1);
+ int GetMulticastTTL();
+ void SetMulticastLoop(bool = true);
+ bool IsMulticastLoop();
+ void AddMulticastMembership(const std::string& group,const std::string& intf = "0.0.0.0",int if_index = 0);
+ void DropMulticastMembership(const std::string& group,const std::string& intf = "0.0.0.0",int if_index = 0);
+#ifdef ENABLE_IPV6
+#ifdef IPPROTO_IPV6
+ /** multicast, ipv6 only */
+ void SetMulticastHops(int = -1);
+ /** multicast, ipv6 only */
+ int GetMulticastHops();
+#endif
+#endif
+ /** Returns true if Bind succeeded. */
+ bool IsBound();
+ /** Return Bind port number */
+ port_t GetPort();
+
+ void OnOptions(int,int,int,SOCKET) {}
+
+ int GetLastSizeWritten();
+
+ /** Also read timestamp information from incoming message */
+ void SetTimestamp(bool = true);
+
+protected:
+ UdpSocket(const UdpSocket& s) : Socket(s) {}
+ void OnRead();
+#if defined(LINUX) || defined(MACOSX)
+ /** This method emulates socket recvfrom, but uses messages so we can get the timestamp */
+ int ReadTS(char *ioBuf, int inBufSize, struct sockaddr *from, socklen_t fromlen, struct timeval *ts);
+#endif
+
+private:
+ UdpSocket& operator=(const UdpSocket& ) { return *this; }
+ /** create before using sendto methods */
+ void CreateConnection();
+ char *m_ibuf; ///< Input buffer
+ int m_ibufsz; ///< Size of input buffer
+ bool m_bind_ok; ///< Bind completed successfully
+ port_t m_port; ///< Bind port number
+ int m_last_size_written;
+ int m_retries;
+ bool m_b_read_ts;
+};
+
+#ifdef SOCKETS_NAMESPACE
+}
+#endif
+
+#endif // _SOCKETS_UdpSocket_H
+
+
diff --git a/externals/sockets/include/Utility.h b/externals/sockets/include/Utility.h
new file mode 100644
index 00000000000..724a94e4b32
--- /dev/null
+++ b/externals/sockets/include/Utility.h
@@ -0,0 +1,186 @@
+/** \file Utility.h
+ ** \date 2004-02-13
+ ** \author grymse@alhem.net
+**/
+/*
+Copyright (C) 2004-2007 Anders Hedstrom
+
+This library is made available under the terms of the GNU GPL.
+
+If you would like to use this library in a closed-source application,
+a separate license agreement is available. For information about
+the closed-source license agreement for the C++ sockets library,
+please visit http://www.alhem.net/Sockets/license.html and/or
+email license@alhem.net.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+#ifndef _SOCKETS_Utility_H
+#define _SOCKETS_Utility_H
+
+#include "sockets-config.h"
+#include <ctype.h>
+#include <string.h>
+#include <memory>
+#include "socket_include.h"
+#include <map>
+#include <string>
+
+#ifdef SOCKETS_NAMESPACE
+namespace SOCKETS_NAMESPACE {
+#endif
+
+#define TWIST_LEN 624
+
+class SocketAddress;
+
+/** Conversion utilities.
+ \ingroup util */
+class Utility
+{
+ /**
+ The Mersenne Twister
+ http://www.math.keio.ac.jp/~matumoto/emt.html
+ */
+ class Rng {
+ public:
+ Rng(unsigned long seed);
+
+ unsigned long Get();
+
+ private:
+ int m_value;
+ unsigned long m_tmp[TWIST_LEN];
+ };
+ class ncmap_compare {
+ public:
+ bool operator()(const std::string& x, const std::string& y) const {
+ return strcasecmp(x.c_str(), y.c_str()) < 0;
+ }
+ };
+public:
+ template<typename Y> class ncmap : public std::map<std::string, Y, ncmap_compare> {
+ public:
+ ncmap() {}
+ };
+public:
+ static std::string base64(const std::string& str_in);
+ static std::string base64d(const std::string& str_in);
+ static std::string l2string(long l);
+ static std::string bigint2string(uint64_t l);
+ static uint64_t atoi64(const std::string& str);
+ static unsigned int hex2unsigned(const std::string& str);
+ static std::string rfc1738_encode(const std::string& src);
+ static std::string rfc1738_decode(const std::string& src);
+
+ /** Checks whether a string is a valid ipv4/ipv6 ip number. */
+ static bool isipv4(const std::string&);
+ /** Checks whether a string is a valid ipv4/ipv6 ip number. */
+ static bool isipv6(const std::string&);
+
+ /** Hostname to ip resolution ipv4, not asynchronous. */
+ static bool u2ip(const std::string&, ipaddr_t&);
+ static bool u2ip(const std::string&, struct sockaddr_in& sa, int ai_flags = 0);
+
+#ifdef ENABLE_IPV6
+#ifdef IPPROTO_IPV6
+ /** Hostname to ip resolution ipv6, not asynchronous. */
+ static bool u2ip(const std::string&, struct in6_addr&);
+ static bool u2ip(const std::string&, struct sockaddr_in6& sa, int ai_flags = 0);
+#endif
+#endif
+
+ /** Reverse lookup of address to hostname */
+ static bool reverse(struct sockaddr *sa, socklen_t sa_len, std::string&, int flags = 0);
+ static bool reverse(struct sockaddr *sa, socklen_t sa_len, std::string& hostname, std::string& service, int flags = 0);
+
+ static bool u2service(const std::string& name, int& service, int ai_flags = 0);
+
+ /** Convert binary ip address to string: ipv4. */
+ static void l2ip(const ipaddr_t,std::string& );
+ static void l2ip(const in_addr&,std::string& );
+#ifdef ENABLE_IPV6
+#ifdef IPPROTO_IPV6
+ /** Convert binary ip address to string: ipv6. */
+ static void l2ip(const struct in6_addr&,std::string& ,bool mixed = false);
+
+ /** ipv6 address compare. */
+ static int in6_addr_compare(in6_addr,in6_addr);
+#endif
+#endif
+ /** ResolveLocal (hostname) - call once before calling any GetLocal method. */
+ static void ResolveLocal();
+ /** Returns local hostname, ResolveLocal must be called once before using.
+ \sa ResolveLocal */
+ static const std::string& GetLocalHostname();
+ /** Returns local ip, ResolveLocal must be called once before using.
+ \sa ResolveLocal */
+ static ipaddr_t GetLocalIP();
+ /** Returns local ip number as string.
+ \sa ResolveLocal */
+ static const std::string& GetLocalAddress();
+#ifdef ENABLE_IPV6
+#ifdef IPPROTO_IPV6
+ /** Returns local ipv6 ip.
+ \sa ResolveLocal */
+ static const struct in6_addr& GetLocalIP6();
+ /** Returns local ipv6 address.
+ \sa ResolveLocal */
+ static const std::string& GetLocalAddress6();
+#endif
+#endif
+ /** Set environment variable.
+ \param var Name of variable to set
+ \param value Value */
+ static void SetEnv(const std::string& var,const std::string& value);
+ /** Convert sockaddr struct to human readable string.
+ \param sa Ptr to sockaddr struct */
+ static std::string Sa2String(struct sockaddr *sa);
+
+ /** Get current time in sec/microseconds. */
+ static void GetTime(struct timeval *);
+
+ static std::auto_ptr<SocketAddress> CreateAddress(struct sockaddr *,socklen_t);
+
+ static unsigned long ThreadID();
+
+ static std::string ToLower(const std::string& str);
+ static std::string ToUpper(const std::string& str);
+
+ static std::string ToString(double d);
+
+ /** Returns a random 32-bit integer */
+ static unsigned long Rnd();
+
+private:
+ static std::string m_host; ///< local hostname
+ static ipaddr_t m_ip; ///< local ip address
+ static std::string m_addr; ///< local ip address in string format
+#ifdef ENABLE_IPV6
+#ifdef IPPROTO_IPV6
+ static struct in6_addr m_local_ip6; ///< local ipv6 address
+#endif
+ static std::string m_local_addr6; ///< local ipv6 address in string format
+#endif
+ static bool m_local_resolved; ///< ResolveLocal has been called if true
+};
+
+#ifdef SOCKETS_NAMESPACE
+}
+#endif
+
+#endif // _SOCKETS_Utility_H
+
+
diff --git a/externals/sockets/include/socket_include.h b/externals/sockets/include/socket_include.h
new file mode 100644
index 00000000000..89855a54108
--- /dev/null
+++ b/externals/sockets/include/socket_include.h
@@ -0,0 +1,290 @@
+/** \file socket_include.h
+ ** \date 2005-04-12
+ ** \author grymse@alhem.net
+**/
+/*
+Copyright (C) 2004-2007 Anders Hedstrom
+
+This library is made available under the terms of the GNU GPL.
+
+If you would like to use this library in a closed-source application,
+a separate license agreement is available. For information about
+the closed-source license agreement for the C++ sockets library,
+please visit http://www.alhem.net/Sockets/license.html and/or
+email license@alhem.net.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+#ifndef _SOCKETS_socket_include_H
+#define _SOCKETS_socket_include_H
+#include "sockets-config.h"
+
+#ifdef _MSC_VER
+#pragma warning(disable:4514)
+#endif
+
+// common defines affecting library and applications using library
+
+/* Define SOCKETS_DYNAMIC_TEMP to use dynamically allocated buffers
+ in read operations - helps on ECOS */
+#define SOCKETS_DYNAMIC_TEMP
+
+// platform specific stuff
+#if (defined(__unix__) || defined(unix)) && !defined(USG)
+#include <sys/param.h>
+#endif
+#include <list>
+
+// int64
+#ifdef _WIN32
+typedef unsigned __int64 uint64_t;
+#else
+#include <stdlib.h>
+#ifdef SOLARIS
+# include <sys/types.h>
+#else
+# include <stdint.h>
+#endif
+#endif
+
+#ifndef _WIN32
+// ----------------------------------------
+// common unix includes / defines
+#include <unistd.h>
+#include <sys/time.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+//#include <netdb.h>
+
+// all typedefs in this file will be declared outside the sockets namespace,
+// because some os's will already have one or more of the type defined.
+typedef int SOCKET;
+#define Errno errno
+#define StrError strerror
+
+#ifdef SOCKETS_NAMESPACE
+namespace SOCKETS_NAMESPACE {
+#endif
+
+// WIN32 adapt
+#define closesocket close
+#define INVALID_SOCKET -1
+#define SOCKET_ERROR -1
+
+#ifndef INADDR_NONE
+#define INADDR_NONE ((unsigned long) -1)
+#endif // INADDR_NONE
+
+#ifdef SOCKETS_NAMESPACE
+}
+#endif
+
+#endif // !_WIN32
+
+// ----------------------------------------
+// Generic
+#ifndef SOL_IP
+#define SOL_IP IPPROTO_IP
+#endif
+
+// ----------------------------------------
+// OS specific adaptions
+
+#ifdef SOLARIS
+// ----------------------------------------
+// Solaris
+typedef unsigned short port_t;
+#ifdef SOCKETS_NAMESPACE
+namespace SOCKETS_NAMESPACE {
+#endif
+// no defs
+
+#ifdef SOCKETS_NAMESPACE
+}
+#endif
+
+#define s6_addr16 _S6_un._S6_u8
+#define MSG_NOSIGNAL 0
+
+#elif defined __FreeBSD__
+// ----------------------------------------
+// FreeBSD
+# if __FreeBSD_version >= 400014
+# define s6_addr16 __u6_addr.__u6_addr16
+# if !defined(MSG_NOSIGNAL)
+# define MSG_NOSIGNAL 0
+# endif
+# include <netinet/in.h>
+typedef in_addr_t ipaddr_t;
+typedef in_port_t port_t;
+#ifdef SOCKETS_NAMESPACE
+namespace SOCKETS_NAMESPACE {
+#endif
+// no defs
+
+#ifdef SOCKETS_NAMESPACE
+}
+#endif
+
+# define IPV6_ADD_MEMBERSHIP IPV6_JOIN_GROUP
+# define IPV6_DROP_MEMBERSHIP IPV6_LEAVE_GROUP
+# else
+# error FreeBSD versions prior to 400014 does not support ipv6
+# endif
+
+#elif defined (__NetBSD__) || defined (__OpenBSD__)
+# if !defined(MSG_NOSIGNAL)
+# define MSG_NOSIGNAL 0
+# endif
+# include <netinet/in.h>
+typedef in_addr_t ipaddr_t;
+typedef in_port_t port_t;
+#elif defined MACOSX
+// ----------------------------------------
+// Mac OS X
+#include <string.h>
+#ifdef __DARWIN_UNIX03
+typedef unsigned short port_t;
+#else
+#include <mach/port.h>
+#endif // __DARWIN_UNIX03
+typedef unsigned long ipaddr_t;
+#ifdef SOCKETS_NAMESPACE
+namespace SOCKETS_NAMESPACE {
+#endif
+// no defs
+
+#ifdef SOCKETS_NAMESPACE
+}
+#endif
+
+#define s6_addr16 __u6_addr.__u6_addr16
+#define MSG_NOSIGNAL 0 // oops - thanks Derek
+#define IPV6_ADD_MEMBERSHIP IPV6_JOIN_GROUP
+#define IPV6_DROP_MEMBERSHIP IPV6_LEAVE_GROUP
+
+#elif defined _WIN32
+// ----------------------------------------
+// Win32
+#ifdef _MSC_VER
+#pragma comment(lib, "wsock32.lib")
+#endif
+#define strcasecmp _stricmp
+
+typedef unsigned long ipaddr_t;
+typedef unsigned short port_t;
+typedef int socklen_t;
+#ifdef SOCKETS_NAMESPACE
+namespace SOCKETS_NAMESPACE {
+#endif
+// no defs
+
+#ifdef SOCKETS_NAMESPACE
+}
+#endif
+
+// 1.8.6: define FD_SETSIZE to something bigger than 64 if there are a lot of
+// simultaneous connections (must be done before including winsock.h)
+#define FD_SETSIZE 1024
+
+// windows 2000 with ipv6 preview installed:
+// http://msdn.microsoft.com/downloads/sdks/platform/tpipv6.asp
+// see the FAQ on how to install
+#define WIN32_LEAN_AND_MEAN
+#include <winsock2.h>
+#include <ws2tcpip.h>
+#if _MSC_VER < 1200
+#ifndef __CYGWIN__
+#ifdef ENABLE_IPV6
+#include <tpipv6.h> // For IPv6 Tech Preview.
+#endif
+#endif
+#endif // _MSC_VER < 1200
+
+#define MSG_NOSIGNAL 0
+//#define SHUT_RDWR 2
+#define SHUT_WR 1
+
+#define Errno WSAGetLastError()
+const char *StrError(int x);
+
+#ifdef SOCKETS_NAMESPACE
+namespace SOCKETS_NAMESPACE {
+#endif
+
+// class WSAInitializer is a part of the Socket class (on win32)
+// as a static instance - so whenever an application uses a Socket,
+// winsock is initialized
+class WSAInitializer // Winsock Initializer
+{
+public:
+ WSAInitializer() {
+ if (WSAStartup(0x101,&m_wsadata))
+ {
+ exit(-1);
+ }
+ }
+ ~WSAInitializer() {
+ WSACleanup();
+ }
+private:
+ WSADATA m_wsadata;
+};
+
+#ifdef SOCKETS_NAMESPACE
+}
+#endif
+
+#else
+// ----------------------------------------
+// LINUX
+typedef unsigned long ipaddr_t;
+typedef unsigned short port_t;
+#ifdef SOCKETS_NAMESPACE
+namespace SOCKETS_NAMESPACE {
+#endif
+// no defs
+
+#ifdef SOCKETS_NAMESPACE
+}
+#endif
+
+#endif
+
+#ifdef SOCKETS_NAMESPACE
+namespace SOCKETS_NAMESPACE {
+#endif
+ /** List type containing file descriptors. */
+ typedef std::list<SOCKET> socket_v;
+
+#ifdef SOCKETS_NAMESPACE
+}
+#endif
+
+// getaddrinfo / getnameinfo replacements
+#ifdef NO_GETADDRINFO
+#ifndef AI_NUMERICHOST
+#define AI_NUMERICHOST 1
+#endif
+#ifndef NI_NUMERICHOST
+#define NI_NUMERICHOST 1
+#endif
+#endif
+
+#endif // _SOCKETS_socket_include_H
+
+
diff --git a/externals/sockets/include/sockets-config.h b/externals/sockets/include/sockets-config.h
new file mode 100644
index 00000000000..1c8dc439092
--- /dev/null
+++ b/externals/sockets/include/sockets-config.h
@@ -0,0 +1,90 @@
+/**
+ ** \file sockets-config.h
+ ** \date 2007-04-14
+ ** \author grymse@alhem.net
+**/
+/*
+Copyright (C) 2007 Anders Hedstrom
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+#ifndef _SOCKETS_CONFIG_H
+#define _SOCKETS_CONFIG_H
+
+#ifndef _RUN_DP
+/* First undefine symbols if already defined. */
+#undef HAVE_OPENSSL
+#undef ENABLE_IPV6
+#undef USE_SCTP
+#undef NO_GETADDRINFO
+#undef ENABLE_POOL
+#undef ENABLE_SOCKS4
+#undef ENABLE_RESOLVER
+#undef ENABLE_RECONNECT
+#undef ENABLE_DETACH
+#undef ENABLE_TRIGGERS
+#undef ENABLE_EXCEPTIONS
+#endif // _RUN_DP
+
+// define MACOSX for internal socket library checks
+#if defined(__APPLE__) && defined(__MACH__) && !defined(MACOSX)
+#define MACOSX
+#endif
+
+/* OpenSSL support. */
+//#define HAVE_OPENSSL
+
+/* Ipv6 support. */
+//#define ENABLE_IPV6
+
+/* SCTP support. */
+//#define USE_SCTP
+
+/* Define NO_GETADDRINFO if your operating system does not support
+ the "getaddrinfo" and "getnameinfo" function calls. */
+#define NO_GETADDRINFO
+
+/* Connection pool support. */
+#define ENABLE_POOL
+
+/* Socks4 client support. */
+//#define ENABLE_SOCKS4
+
+/* Asynchronous resolver. */
+#define ENABLE_RESOLVER
+
+/* Enable TCP reconnect on lost connection.
+ Socket::OnReconnect
+ Socket::OnDisconnect
+*/
+#define ENABLE_RECONNECT
+
+/* Enable socket thread detach functionality. */
+#define ENABLE_DETACH
+
+/* Enable socket to socket triggers. Not yet in use. */
+//#define ENABLE_TRIGGERS
+
+/* Enabled exceptions. */
+//#define ENABLE_EXCEPTIONS
+
+/* Resolver uses the detach function so either enable both or disable both. */
+#ifndef ENABLE_DETACH
+#undef ENABLE_RESOLVER
+#endif
+
+#endif // _SOCKETS_CONFIG_H
+
+
diff --git a/externals/sockets/network_kist.txt b/externals/sockets/network_kist.txt
new file mode 100644
index 00000000000..f6597bf9c77
--- /dev/null
+++ b/externals/sockets/network_kist.txt
@@ -0,0 +1,20 @@
+The following are the only .cpp files used from the new network library (v2.2.8) This file is just for future reference.
+
+Base64.cpp
+Exception.cpp
+Ipv4Address.cpp
+Ipv6Address.cpp
+Lock.cpp
+Mutex.cpp
+Parse.cpp
+ResolvServer.cpp
+ResolvSocket.cpp
+Socket.cpp
+SocketHandler.cpp
+socket_include.cpp
+StdoutLog.cpp
+StreamSocket.cpp
+TcpSocket.cpp
+Thread.cpp
+UdpSocket.cpp
+Utility.cpp
diff --git a/externals/sockets/socket_include.cpp b/externals/sockets/socket_include.cpp
new file mode 100644
index 00000000000..290602c1b52
--- /dev/null
+++ b/externals/sockets/socket_include.cpp
@@ -0,0 +1,89 @@
+/** \file socket_include.cpp
+ ** \date 2004-11-28
+ ** \author grymse@alhem.net
+**/
+/*
+Copyright (C) 2004-2007 Anders Hedstrom
+
+This library is made available under the terms of the GNU GPL.
+
+If you would like to use this library in a closed-source application,
+a separate license agreement is available. For information about
+the closed-source license agreement for the C++ sockets library,
+please visit http://www.alhem.net/Sockets/license.html and/or
+email license@alhem.net.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+#include <stdio.h>
+
+// only to be included in win32 projects
+const char *StrError(int x)
+{
+static char tmp[100];
+ switch (x)
+ {
+ case 10004: return "Interrupted function call.";
+ case 10013: return "Permission denied.";
+ case 10014: return "Bad address.";
+ case 10022: return "Invalid argument.";
+ case 10024: return "Too many open files.";
+ case 10035: return "Resource temporarily unavailable.";
+ case 10036: return "Operation now in progress.";
+ case 10037: return "Operation already in progress.";
+ case 10038: return "Socket operation on nonsocket.";
+ case 10039: return "Destination address required.";
+ case 10040: return "Message too long.";
+ case 10041: return "Protocol wrong type for socket.";
+ case 10042: return "Bad protocol option.";
+ case 10043: return "Protocol not supported.";
+ case 10044: return "Socket type not supported.";
+ case 10045: return "Operation not supported.";
+ case 10046: return "Protocol family not supported.";
+ case 10047: return "Address family not supported by protocol family.";
+ case 10048: return "Address already in use.";
+ case 10049: return "Cannot assign requested address.";
+ case 10050: return "Network is down.";
+ case 10051: return "Network is unreachable.";
+ case 10052: return "Network dropped connection on reset.";
+ case 10053: return "Software caused connection abort.";
+ case 10054: return "Connection reset by peer.";
+ case 10055: return "No buffer space available.";
+ case 10056: return "Socket is already connected.";
+ case 10057: return "Socket is not connected.";
+ case 10058: return "Cannot send after socket shutdown.";
+ case 10060: return "Connection timed out.";
+ case 10061: return "Connection refused.";
+ case 10064: return "Host is down.";
+ case 10065: return "No route to host.";
+ case 10067: return "Too many processes.";
+ case 10091: return "Network subsystem is unavailable.";
+ case 10092: return "Winsock.dll version out of range.";
+ case 10093: return "Successful WSAStartup not yet performed.";
+ case 10101: return "Graceful shutdown in progress.";
+ case 10109: return "Class type not found.";
+ case 11001: return "Host not found.";
+ case 11002: return "Nonauthoritative host not found.";
+ case 11003: return "This is a nonrecoverable error.";
+ case 11004: return "Valid name, no data record of requested type.";
+
+ default:
+ break;
+ }
+ sprintf(tmp, "Winsock error code: %d", x);
+ return tmp;
+}
+
+
diff --git a/externals/utf8cpp/delme b/externals/utf8cpp/delme
deleted file mode 100644
index e69de29bb2d..00000000000
--- a/externals/utf8cpp/delme
+++ /dev/null
diff --git a/externals/utf8cpp/include/doc/ReleaseNotes b/externals/utf8cpp/include/doc/ReleaseNotes
new file mode 100644
index 00000000000..8541c7a6031
--- /dev/null
+++ b/externals/utf8cpp/include/doc/ReleaseNotes
@@ -0,0 +1,9 @@
+utf8 cpp library
+Release 2.1
+
+This is a minor feature release - added the function peek_next.
+
+Changes from version 2.o
+- Implemented feature request [ 1770746 ] "Provide a const version of next() (some sort of a peek() )
+
+Files included in the release: utf8.h, core.h, checked.h, unchecked.h, utf8cpp.html, ReleaseNotes
diff --git a/externals/utf8cpp/include/doc/utf8cpp.html b/externals/utf8cpp/include/doc/utf8cpp.html
new file mode 100644
index 00000000000..4ad7e1002a9
--- /dev/null
+++ b/externals/utf8cpp/include/doc/utf8cpp.html
@@ -0,0 +1,1574 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
+<html>
+ <head>
+ <meta name="generator" content=
+ "HTML Tidy for Linux/x86 (vers 1st November 2002), see www.w3.org">
+ <meta name="description" content=
+ "A simple, portable and lightweigt C++ library for easy handling of UTF-8 encoded strings">
+ <meta name="keywords" content="UTF-8 C++ portable utf8 unicode generic templates">
+ <meta name="author" content="Nemanja Trifunovic">
+ <title>
+ UTF8-CPP: UTF-8 with C++ in a Portable Way
+ </title>
+ <style type="text/css">
+ <!--
+ span.return_value {
+ color: brown;
+ }
+ span.keyword {
+ color: blue;
+ }
+ span.preprocessor {
+ color: navy;
+ }
+ span.literal {
+ color: olive;
+ }
+ span.comment {
+ color: green;
+ }
+ code {
+ font-weight: bold;
+ }
+ ul.toc {
+ list-style-type: none;
+ }
+ p.version {
+ font-size: small;
+ font-style: italic;
+ }
+ -->
+ </style>
+ </head>
+ <body>
+ <h1>
+ UTF8-CPP: UTF-8 with C++ in a Portable Way
+ </h1>
+ <p>
+ <a href="https://sourceforge.net/projects/utfcpp">The Sourceforge project page</a>
+ </p>
+ <div id="toc">
+ <h2>
+ Table of Contents
+ </h2>
+ <ul class="toc">
+ <li>
+ <a href="#introduction">Introduction</a>
+ </li>
+ <li>
+ <a href="#examples">Examples of Use</a>
+ </li>
+ <li>
+ <a href="#reference">Reference</a>
+ <ul class="toc">
+ <li>
+ <a href="#funutf8">Functions From utf8 Namespace </a>
+ </li>
+ <li>
+ <a href="#typesutf8">Types From utf8 Namespace </a>
+ </li>
+ <li>
+ <a href="#fununchecked">Functions From utf8::unchecked Namespace </a>
+ </li>
+ <li>
+ <a href="#typesunchecked">Types From utf8::unchecked Namespace </a>
+ </li>
+ </ul>
+ </li>
+ <li>
+ <a href="#points">Points of Interest</a>
+ </li>
+ <li>
+ <a href="#conclusion">Conclusion</a>
+ </li>
+ <li>
+ <a href="#links">Links</a>
+ </li>
+ </ul>
+ </div>
+ <h2 id="introduction">
+ Introduction
+ </h2>
+ <p>
+ Many C++ developers miss an easy and portable way of handling Unicode encoded
+ strings. C++ Standard is currently Unicode agnostic, and while some work is being
+ done to introduce Unicode to the next incarnation called C++0x, for the moment
+ nothing of the sort is available. In the meantime, developers use 3rd party
+ libraries like ICU, OS specific capabilities, or simply roll out their own
+ solutions.
+ </p>
+ <p>
+ In order to easily handle UTF-8 encoded Unicode strings, I have come up with a small
+ generic library. For anybody used to work with STL algorithms and iterators, it should be
+ easy and natural to use. The code is freely available for any purpose - check out
+ the license at the beginning of the utf8.h file. If you run into
+ bugs or performance issues, please let me know and I'll do my best to address them.
+ </p>
+ <p>
+ The purpose of this article is not to offer an introduction to Unicode in general,
+ and UTF-8 in particular. If you are not familiar with Unicode, be sure to check out
+ <a href="http://www.unicode.org/">Unicode Home Page</a> or some other source of
+ information for Unicode. Also, it is not my aim to advocate the use of UTF-8
+ encoded strings in C++ programs; if you want to handle UTF-8 encoded strings from
+ C++, I am sure you have good reasons for it.
+ </p>
+ <h2 id="examples">
+ Examples of use
+ </h2>
+ <p>
+ To illustrate the use of this utf8 library, we shall open a file containing UTF-8
+ encoded text, check whether it starts with a byte order mark, read each line into a
+ <code>std::string</code>, check it for validity, convert the text to UTF-16, and
+ back to UTF-8:
+ </p>
+<pre>
+<span class="preprocessor">#include &lt;fstream&gt;</span>
+<span class="preprocessor">#include &lt;iostream&gt;</span>
+<span class="preprocessor">#include &lt;string&gt;</span>
+<span class="preprocessor">#include &lt;vector&gt;</span>
+<span class="preprocessor">#include "utf8.h"</span>
+<span class="keyword">using namespace</span> std;
+<span class="keyword">int</span> main()
+{
+ <span class="keyword">if</span> (argc != <span class="literal">2</span>) {
+ cout &lt;&lt; <span class="literal">"\nUsage: docsample filename\n"</span>;
+ <span class="keyword">return</span> <span class="literal">0</span>;
+ }
+ <span class="keyword">const char</span>* test_file_path = argv[1];
+ <span class="comment">// Open the test file (must be UTF-8 encoded)</span>
+ ifstream fs8(test_file_path);
+ <span class="keyword">if</span> (!fs8.is_open()) {
+ cout &lt;&lt; <span class=
+"literal">"Could not open "</span> &lt;&lt; test_file_path &lt;&lt; endl;
+ <span class="keyword">return</span> <span class="literal">0</span>;
+ }
+ <span class="comment">// Read the first line of the file</span>
+ <span class="keyword">unsigned</span> line_count = <span class="literal">1</span>;
+ string line;
+ <span class="keyword">if</span> (!getline(fs8, line))
+ <span class="keyword">return</span> <span class="literal">0</span>;
+ <span class="comment">// Look for utf-8 byte-order mark at the beginning</span>
+ <span class="keyword">if</span> (line.size() &gt; <span class="literal">2</span>) {
+ <span class="keyword">if</span> (utf8::is_bom(line.c_str()))
+ cout &lt;&lt; <span class=
+"literal">"There is a byte order mark at the beginning of the file\n"</span>;
+ }
+ <span class="comment">// Play with all the lines in the file</span>
+ <span class="keyword">do</span> {
+ <span class="comment">// check for invalid utf-8 (for a simple yes/no check, there is also utf8::is_valid function)</span>
+ string::iterator end_it = utf8::find_invalid(line.begin(), line.end());
+ <span class="keyword">if</span> (end_it != line.end()) {
+ cout &lt;&lt; <span class=
+"literal">"Invalid UTF-8 encoding detected at line "</span> &lt;&lt; line_count &lt;&lt; <span
+ class="literal">"\n"</span>;
+ cout &lt;&lt; <span class=
+"literal">"This part is fine: "</span> &lt;&lt; string(line.begin(), end_it) &lt;&lt; <span
+ class="literal">"\n"</span>;
+ }
+ <span class="comment">// Get the line length (at least for the valid part)</span>
+ <span class="keyword">int</span> length = utf8::distance(line.begin(), end_it);
+ cout &lt;&lt; <span class=
+"literal">"Length of line "</span> &lt;&lt; line_count &lt;&lt; <span class=
+"literal">" is "</span> &lt;&lt; length &lt;&lt; <span class="literal">"\n"</span>;
+ <span class="comment">// Convert it to utf-16</span>
+ vector&lt;unsigned short&gt; utf16line;
+ utf8::utf8to16(line.begin(), end_it, back_inserter(utf16line));
+ <span class="comment">// And back to utf-8</span>
+ string utf8line;
+ utf8::utf16to8(utf16line.begin(), utf16line.end(), back_inserter(utf8line));
+ <span class="comment">// Confirm that the conversion went OK:</span>
+ <span class="keyword">if</span> (utf8line != string(line.begin(), end_it))
+ cout &lt;&lt; <span class=
+"literal">"Error in UTF-16 conversion at line: "</span> &lt;&lt; line_count &lt;&lt; <span
+ class="literal">"\n"</span>;
+ getline(fs8, line);
+ line_count++;
+ } <span class="keyword">while</span> (!fs8.eof());
+ <span class="keyword">return</span> <span class="literal">0</span>;
+}
+</pre>
+ <p>
+ In the previous code sample, we have seen the use of the following functions from
+ <code>utf8</code> namespace: first we used <code>is_bom</code> function to detect
+ UTF-8 byte order mark at the beginning of the file; then for each line we performed
+ a detection of invalid UTF-8 sequences with <code>find_invalid</code>; the number
+ of characters (more precisely - the number of Unicode code points) in each line was
+ determined with a use of <code>utf8::distance</code>; finally, we have converted
+ each line to UTF-16 encoding with <code>utf8to16</code> and back to UTF-8 with
+ <code>utf16to8</code>.
+ </p>
+ <h2 id="reference">
+ Reference
+ </h2>
+ <h3 id="funutf8">
+ Functions From utf8 Namespace
+ </h3>
+ <h4>
+ utf8::append
+ </h4>
+ <p class="version">
+ Available in version 1.0 and later.
+ </p>
+ <p>
+ Encodes a 32 bit code point as a UTF-8 sequence of octets and appends the sequence
+ to a UTF-8 string.
+ </p>
+<pre>
+<span class="keyword">template</span> &lt;<span class=
+"keyword">typename</span> octet_iterator&gt;
+octet_iterator append(uint32_t cp, octet_iterator result);
+
+</pre>
+ <p>
+ <code>cp</code>: A 32 bit integer representing a code point to append to the
+ sequence.<br>
+ <code>result</code>: An output iterator to the place in the sequence where to
+ append the code point.<br>
+ <span class="return_value">Return value</span>: An iterator pointing to the place
+ after the newly appended sequence.
+ </p>
+ <p>
+ Example of use:
+ </p>
+<pre>
+<span class="keyword">unsigned char</span> u[<span class="literal">5</span>] = {<span
+class="literal">0</span>,<span class="literal">0</span>,<span class=
+"literal">0</span>,<span class="literal">0</span>,<span class="literal">0</span>};
+<span class="keyword">unsigned char</span>* end = append(<span class=
+"literal">0x0448</span>, u);
+assert (u[<span class="literal">0</span>] == <span class=
+"literal">0xd1</span> &amp;&amp; u[<span class="literal">1</span>] == <span class=
+"literal">0x88</span> &amp;&amp; u[<span class="literal">2</span>] == <span class=
+"literal">0</span> &amp;&amp; u[<span class="literal">3</span>] == <span class=
+"literal">0</span> &amp;&amp; u[<span class="literal">4</span>] == <span class=
+"literal">0</span>);
+</pre>
+ <p>
+ Note that <code>append</code> does not allocate any memory - it is the burden of
+ the caller to make sure there is enough memory allocated for the operation. To make
+ things more interesting, <code>append</code> can add anywhere between 1 and 4
+ octets to the sequence. In practice, you would most often want to use
+ <code>std::back_inserter</code> to ensure that the necessary memory is allocated.
+ </p>
+ <p>
+ In case of an invalid code point, a <code>utf8::invalid_code_point</code> exception
+ is thrown.
+ </p>
+ <h4>
+ utf8::next
+ </h4>
+ <p class="version">
+ Available in version 1.0 and later.
+ </p>
+ <p>
+ Given the iterator to the beginning of the UTF-8 sequence, it returns the code
+ point and moves the iterator to the next position.
+ </p>
+<pre>
+<span class="keyword">template</span> &lt;<span class=
+"keyword">typename</span> octet_iterator&gt;
+uint32_t next(octet_iterator&amp; it, octet_iterator end);
+
+</pre>
+ <p>
+ <code>it</code>: a reference to an iterator pointing to the beginning of an UTF-8
+ encoded code point. After the function returns, it is incremented to point to the
+ beginning of the next code point.<br>
+ <code>end</code>: end of the UTF-8 sequence to be processed. If <code>it</code>
+ gets equal to <code>end</code> during the extraction of a code point, an
+ <code>utf8::not_enough_room</code> exception is thrown.<br>
+ <span class="return_value">Return value</span>: the 32 bit representation of the
+ processed UTF-8 code point.
+ </p>
+ <p>
+ Example of use:
+ </p>
+<pre>
+<span class="keyword">char</span>* twochars = <span class=
+"literal">"\xe6\x97\xa5\xd1\x88"</span>;
+<span class="keyword">char</span>* w = twochars;
+<span class="keyword">int</span> cp = next(w, twochars + <span class="literal">6</span>);
+assert (cp == <span class="literal">0x65e5</span>);
+assert (w == twochars + <span class="literal">3</span>);
+</pre>
+ <p>
+ This function is typically used to iterate through a UTF-8 encoded string.
+ </p>
+ <p>
+ In case of an invalid UTF-8 seqence, a <code>utf8::invalid_utf8</code> exception is
+ thrown.
+ </p>
+ <h4>
+ utf8::peek_next
+ </h4>
+ <p class="version">
+ Available in version 2.1 and later.
+ </p>
+ <p>
+ Given the iterator to the beginning of the UTF-8 sequence, it returns the code
+ point for the following sequence without changing the value of the iterator.
+ </p>
+<pre>
+<span class="keyword">template</span> &lt;<span class=
+"keyword">typename</span> octet_iterator&gt;
+uint32_t peek_next(octet_iterator it, octet_iterator end);
+
+</pre>
+ <p>
+ <code>it</code>: an iterator pointing to the beginning of an UTF-8
+ encoded code point.<br>
+ <code>end</code>: end of the UTF-8 sequence to be processed. If <code>it</code>
+ gets equal to <code>end</code> during the extraction of a code point, an
+ <code>utf8::not_enough_room</code> exception is thrown.<br>
+ <span class="return_value">Return value</span>: the 32 bit representation of the
+ processed UTF-8 code point.
+ </p>
+ <p>
+ Example of use:
+ </p>
+<pre>
+<span class="keyword">char</span>* twochars = <span class=
+"literal">"\xe6\x97\xa5\xd1\x88"</span>;
+<span class="keyword">char</span>* w = twochars;
+<span class="keyword">int</span> cp = peek_next(w, twochars + <span class="literal">6</span>);
+assert (cp == <span class="literal">0x65e5</span>);
+assert (w == twochars);
+</pre>
+ <p>
+ In case of an invalid UTF-8 seqence, a <code>utf8::invalid_utf8</code> exception is
+ thrown.
+ </p>
+ <h4>
+ utf8::prior
+ </h4>
+ <p class="version">
+ Available in version 1.02 and later.
+ </p>
+ <p>
+ Given a reference to an iterator pointing to an octet in a UTF-8 seqence, it
+ decreases the iterator until it hits the beginning of the previous UTF-8 encoded
+ code point and returns the 32 bits representation of the code point.
+ </p>
+<pre>
+<span class="keyword">template</span> &lt;<span class=
+"keyword">typename</span> octet_iterator&gt;
+uint32_t prior(octet_iterator&amp; it, octet_iterator start);
+
+</pre>
+ <p>
+ <code>it</code>: a reference pointing to an octet within a UTF-8 encoded string.
+ After the function returns, it is decremented to point to the beginning of the
+ previous code point.<br>
+ <code>start</code>: an iterator to the beginning of the sequence where the search
+ for the beginning of a code point is performed. It is a
+ safety measure to prevent passing the beginning of the string in the search for a
+ UTF-8 lead octet.<br>
+ <span class="return_value">Return value</span>: the 32 bit representation of the
+ previous code point.
+ </p>
+ <p>
+ Example of use:
+ </p>
+<pre>
+<span class="keyword">char</span>* twochars = <span class=
+"literal">"\xe6\x97\xa5\xd1\x88"</span>;
+<span class="keyword">unsigned char</span>* w = twochars + <span class=
+"literal">3</span>;
+<span class="keyword">int</span> cp = prior (w, twochars);
+assert (cp == <span class="literal">0x65e5</span>);
+assert (w == twochars);
+</pre>
+ <p>
+ This function has two purposes: one is two iterate backwards through a UTF-8
+ encoded string. Note that it is usually a better idea to iterate forward instead,
+ since <code>utf8::next</code> is faster. The second purpose is to find a beginning
+ of a UTF-8 sequence if we have a random position within a string.
+ </p>
+ <p>
+ <code>it</code> will typically point to the beginning of
+ a code point, and <code>start</code> will point to the
+ beginning of the string to ensure we don't go backwards too far. <code>it</code> is
+ decreased until it points to a lead UTF-8 octet, and then the UTF-8 sequence
+ beginning with that octet is decoded to a 32 bit representation and returned.
+ </p>
+ <p>
+ In case <code>pass_end</code> is reached before a UTF-8 lead octet is hit, or if an
+ invalid UTF-8 sequence is started by the lead octet, an <code>invalid_utf8</code>
+ exception is thrown.
+ </p>
+ <h4>
+ utf8::previous
+ </h4>
+ <p class="version">
+ Deprecated in version 1.02 and later.
+ </p>
+ <p>
+ Given a reference to an iterator pointing to an octet in a UTF-8 seqence, it
+ decreases the iterator until it hits the beginning of the previous UTF-8 encoded
+ code point and returns the 32 bits representation of the code point.
+ </p>
+<pre>
+<span class="keyword">template</span> &lt;<span class=
+"keyword">typename</span> octet_iterator&gt;
+uint32_t previous(octet_iterator&amp; it, octet_iterator pass_start);
+
+</pre>
+ <p>
+ <code>it</code>: a reference pointing to an octet within a UTF-8 encoded string.
+ After the function returns, it is decremented to point to the beginning of the
+ previous code point.<br>
+ <code>pass_start</code>: an iterator to the point in the sequence where the search
+ for the beginning of a code point is aborted if no result was reached. It is a
+ safety measure to prevent passing the beginning of the string in the search for a
+ UTF-8 lead octet.<br>
+ <span class="return_value">Return value</span>: the 32 bit representation of the
+ previous code point.
+ </p>
+ <p>
+ Example of use:
+ </p>
+<pre>
+<span class="keyword">char</span>* twochars = <span class=
+"literal">"\xe6\x97\xa5\xd1\x88"</span>;
+<span class="keyword">unsigned char</span>* w = twochars + <span class=
+"literal">3</span>;
+<span class="keyword">int</span> cp = previous (w, twochars - <span class=
+"literal">1</span>);
+assert (cp == <span class="literal">0x65e5</span>);
+assert (w == twochars);
+</pre>
+ <p>
+ <code>utf8::previous</code> is deprecated, and <code>utf8::prior</code> should
+ be used instead, although the existing code can continue using this function.
+ The problem is the parameter <code>pass_start</code> that points to the position
+ just before the beginning of the sequence. Standard containers don't have the
+ concept of "pass start" and the function can not be used with their iterators.
+ </p>
+ <p>
+ <code>it</code> will typically point to the beginning of
+ a code point, and <code>pass_start</code> will point to the octet just before the
+ beginning of the string to ensure we don't go backwards too far. <code>it</code> is
+ decreased until it points to a lead UTF-8 octet, and then the UTF-8 sequence
+ beginning with that octet is decoded to a 32 bit representation and returned.
+ </p>
+ <p>
+ In case <code>pass_end</code> is reached before a UTF-8 lead octet is hit, or if an
+ invalid UTF-8 sequence is started by the lead octet, an <code>invalid_utf8</code>
+ exception is thrown
+ </p>
+ <h4>
+ utf8::advance
+ </h4>
+ <p class="version">
+ Available in version 1.0 and later.
+ </p>
+ <p>
+ Advances an iterator by the specified number of code points within an UTF-8
+ sequence.
+ </p>
+<pre>
+<span class="keyword">template</span> &lt;<span class=
+"keyword">typename</span> octet_iterator, typename distance_type&gt;
+<span class=
+"keyword">void</span> advance (octet_iterator&amp; it, distance_type n, octet_iterator end);
+
+</pre>
+ <p>
+ <code>it</code>: a reference to an iterator pointing to the beginning of an UTF-8
+ encoded code point. After the function returns, it is incremented to point to the
+ nth following code point.<br>
+ <code>n</code>: a positive integer that shows how many code points we want to
+ advance.<br>
+ <code>end</code>: end of the UTF-8 sequence to be processed. If <code>it</code>
+ gets equal to <code>end</code> during the extraction of a code point, an
+ <code>utf8::not_enough_room</code> exception is thrown.<br>
+ </p>
+ <p>
+ Example of use:
+ </p>
+<pre>
+<span class="keyword">char</span>* twochars = <span class=
+"literal">"\xe6\x97\xa5\xd1\x88"</span>;
+<span class="keyword">unsigned char</span>* w = twochars;
+advance (w, <span class="literal">2</span>, twochars + <span class="literal">6</span>);
+assert (w == twochars + <span class="literal">5</span>);
+</pre>
+ <p>
+ This function works only "forward". In case of a negative <code>n</code>, there is
+ no effect.
+ </p>
+ <p>
+ In case of an invalid code point, a <code>utf8::invalid_code_point</code> exception
+ is thrown.
+ </p>
+ <h4>
+ utf8::distance
+ </h4>
+ <p class="version">
+ Available in version 1.0 and later.
+ </p>
+ <p>
+ Given the iterators to two UTF-8 encoded code points in a seqence, returns the
+ number of code points between them.
+ </p>
+<pre>
+<span class="keyword">template</span> &lt;<span class=
+"keyword">typename</span> octet_iterator&gt;
+<span class=
+"keyword">typename</span> std::iterator_traits&lt;octet_iterator&gt;::difference_type distance (octet_iterator first, octet_iterator last);
+
+</pre>
+ <p>
+ <code>first</code>: an iterator to a beginning of a UTF-8 encoded code point.<br>
+ <code>last</code>: an iterator to a "post-end" of the last UTF-8 encoded code
+ point in the sequence we are trying to determine the length. It can be the
+ beginning of a new code point, or not.<br>
+ <span class="return_value">Return value</span> the distance between the iterators,
+ in code points.
+ </p>
+ <p>
+ Example of use:
+ </p>
+<pre>
+<span class="keyword">char</span>* twochars = <span class=
+"literal">"\xe6\x97\xa5\xd1\x88"</span>;
+size_t dist = utf8::distance(twochars, twochars + <span class="literal">5</span>);
+assert (dist == <span class="literal">2</span>);
+</pre>
+ <p>
+ This function is used to find the length (in code points) of a UTF-8 encoded
+ string. The reason it is called <em>distance</em>, rather than, say,
+ <em>length</em> is mainly because developers are used that <em>length</em> is an
+ O(1) function. Computing the length of an UTF-8 string is a linear operation, and
+ it looked better to model it after <code>std::distance</code> algorithm.
+ </p>
+ <p>
+ In case of an invalid UTF-8 seqence, a <code>utf8::invalid_utf8</code> exception is
+ thrown. If <code>last</code> does not point to the past-of-end of a UTF-8 seqence,
+ a <code>utf8::not_enough_room</code> exception is thrown.
+ </p>
+ <h4>
+ utf8::utf16to8
+ </h4>
+ <p class="version">
+ Available in version 1.0 and later.
+ </p>
+ <p>
+ Converts a UTF-16 encoded string to UTF-8.
+ </p>
+<pre>
+<span class="keyword">template</span> &lt;<span class=
+"keyword">typename</span> u16bit_iterator, <span class=
+"keyword">typename</span> octet_iterator&gt;
+octet_iterator utf16to8 (u16bit_iterator start, u16bit_iterator end, octet_iterator result);
+
+</pre>
+ <p>
+ <code>start</code>: an iterator pointing to the beginning of the UTF-16 encoded
+ string to convert.<br>
+ <code>end</code>: an iterator pointing to pass-the-end of the UTF-16 encoded
+ string to convert.<br>
+ <code>result</code>: an output iterator to the place in the UTF-8 string where to
+ append the result of conversion.<br>
+ <span class="return_value">Return value</span>: An iterator pointing to the place
+ after the appended UTF-8 string.
+ </p>
+ <p>
+ Example of use:
+ </p>
+<pre>
+<span class="keyword">unsigned short</span> utf16string[] = {<span class=
+"literal">0x41</span>, <span class="literal">0x0448</span>, <span class=
+"literal">0x65e5</span>, <span class="literal">0xd834</span>, <span class=
+"literal">0xdd1e</span>};
+vector&lt;<span class="keyword">unsigned char</span>&gt; utf8result;
+utf16to8(utf16string, utf16string + <span class=
+"literal">5</span>, back_inserter(utf8result));
+assert (utf8result.size() == <span class="literal">10</span>);
+</pre>
+ <p>
+ In case of invalid UTF-16 sequence, a <code>utf8::invalid_utf16</code> exception is
+ thrown.
+ </p>
+ <h4>
+ utf8::utf8to16
+ </h4>
+ <p class="version">
+ Available in version 1.0 and later.
+ </p>
+ <p>
+ Converts an UTF-8 encoded string to UTF-16
+ </p>
+<pre>
+<span class="keyword">template</span> &lt;<span class=
+"keyword">typename</span> u16bit_iterator, typename octet_iterator&gt;
+u16bit_iterator utf8to16 (octet_iterator start, octet_iterator end, u16bit_iterator result);
+
+</pre>
+ <p>
+ <code>start</code>: an iterator pointing to the beginning of the UTF-8 encoded
+ string to convert. &lt; br /&gt; <code>end</code>: an iterator pointing to
+ pass-the-end of the UTF-8 encoded string to convert.<br>
+ <code>result</code>: an output iterator to the place in the UTF-16 string where to
+ append the result of conversion.<br>
+ <span class="return_value">Return value</span>: An iterator pointing to the place
+ after the appended UTF-16 string.
+ </p>
+ <p>
+ Example of use:
+ </p>
+<pre>
+<span class="keyword">char</span> utf8_with_surrogates[] = <span class=
+"literal">"\xe6\x97\xa5\xd1\x88\xf0\x9d\x84\x9e"</span>;
+vector &lt;<span class="keyword">unsigned short</span>&gt; utf16result;
+utf8to16(utf8_with_surrogates, utf8_with_surrogates + <span class=
+"literal">9</span>, back_inserter(utf16result));
+assert (utf16result.size() == <span class="literal">4</span>);
+assert (utf16result[<span class="literal">2</span>] == <span class=
+"literal">0xd834</span>);
+assert (utf16result[<span class="literal">3</span>] == <span class=
+"literal">0xdd1e</span>);
+</pre>
+ <p>
+ In case of an invalid UTF-8 seqence, a <code>utf8::invalid_utf8</code> exception is
+ thrown. If <code>end</code> does not point to the past-of-end of a UTF-8 seqence, a
+ <code>utf8::not_enough_room</code> exception is thrown.
+ </p>
+ <h4>
+ utf8::utf32to8
+ </h4>
+ <p class="version">
+ Available in version 1.0 and later.
+ </p>
+ <p>
+ Converts a UTF-32 encoded string to UTF-8.
+ </p>
+<pre>
+<span class="keyword">template</span> &lt;<span class=
+"keyword">typename</span> octet_iterator, typename u32bit_iterator&gt;
+octet_iterator utf32to8 (u32bit_iterator start, u32bit_iterator end, octet_iterator result);
+
+</pre>
+ <p>
+ <code>start</code>: an iterator pointing to the beginning of the UTF-32 encoded
+ string to convert.<br>
+ <code>end</code>: an iterator pointing to pass-the-end of the UTF-32 encoded
+ string to convert.<br>
+ <code>result</code>: an output iterator to the place in the UTF-8 string where to
+ append the result of conversion.<br>
+ <span class="return_value">Return value</span>: An iterator pointing to the place
+ after the appended UTF-8 string.
+ </p>
+ <p>
+ Example of use:
+ </p>
+<pre>
+<span class="keyword">int</span> utf32string[] = {<span class=
+"literal">0x448</span>, <span class="literal">0x65E5</span>, <span class=
+"literal">0x10346</span>, <span class="literal">0</span>};
+vector&lt;<span class="keyword">unsigned char</span>&gt; utf8result;
+utf32to8(utf32string, utf32string + <span class=
+"literal">3</span>, back_inserter(utf8result));
+assert (utf8result.size() == <span class="literal">9</span>);
+</pre>
+ <p>
+ In case of invalid UTF-32 string, a <code>utf8::invalid_code_point</code> exception
+ is thrown.
+ </p>
+ <h4>
+ utf8::utf8to32
+ </h4>
+ <p class="version">
+ Available in version 1.0 and later.
+ </p>
+ <p>
+ Converts a UTF-8 encoded string to UTF-32.
+ </p>
+<pre>
+<span class="keyword">template</span> &lt;<span class=
+"keyword">typename</span> octet_iterator, <span class=
+"keyword">typename</span> u32bit_iterator&gt;
+u32bit_iterator utf8to32 (octet_iterator start, octet_iterator end, u32bit_iterator result);
+
+</pre>
+ <p>
+ <code>start</code>: an iterator pointing to the beginning of the UTF-8 encoded
+ string to convert.<br>
+ <code>end</code>: an iterator pointing to pass-the-end of the UTF-8 encoded string
+ to convert.<br>
+ <code>result</code>: an output iterator to the place in the UTF-32 string where to
+ append the result of conversion.<br>
+ <span class="return_value">Return value</span>: An iterator pointing to the place
+ after the appended UTF-32 string.
+ </p>
+ <p>
+ Example of use:
+ </p>
+<pre>
+<span class="keyword">char</span>* twochars = <span class=
+"literal">"\xe6\x97\xa5\xd1\x88"</span>;
+vector&lt;<span class="keyword">int</span>&gt; utf32result;
+utf8to32(twochars, twochars + <span class=
+"literal">5</span>, back_inserter(utf32result));
+assert (utf32result.size() == <span class="literal">2</span>);
+</pre>
+ <p>
+ In case of an invalid UTF-8 seqence, a <code>utf8::invalid_utf8</code> exception is
+ thrown. If <code>end</code> does not point to the past-of-end of a UTF-8 seqence, a
+ <code>utf8::not_enough_room</code> exception is thrown.
+ </p>
+ <h4>
+ utf8::find_invalid
+ </h4>
+ <p class="version">
+ Available in version 1.0 and later.
+ </p>
+ <p>
+ Detects an invalid sequence within a UTF-8 string.
+ </p>
+<pre>
+<span class="keyword">template</span> &lt;<span class=
+"keyword">typename</span> octet_iterator&gt;
+octet_iterator find_invalid(octet_iterator start, octet_iterator end);
+</pre>
+ <p>
+ <code>start</code>: an iterator pointing to the beginning of the UTF-8 string to
+ test for validity.<br>
+ <code>end</code>: an iterator pointing to pass-the-end of the UTF-8 string to test
+ for validity.<br>
+ <span class="return_value">Return value</span>: an iterator pointing to the first
+ invalid octet in the UTF-8 string. In case none were found, equals
+ <code>end</code>.
+ </p>
+ <p>
+ Example of use:
+ </p>
+<pre>
+<span class="keyword">char</span> utf_invalid[] = <span class=
+"literal">"\xe6\x97\xa5\xd1\x88\xfa"</span>;
+<span class=
+"keyword">char</span>* invalid = find_invalid(utf_invalid, utf_invalid + <span class=
+"literal">6</span>);
+assert (invalid == utf_invalid + <span class="literal">5</span>);
+</pre>
+ <p>
+ This function is typically used to make sure a UTF-8 string is valid before
+ processing it with other functions. It is especially important to call it if before
+ doing any of the <em>unchecked</em> operations on it.
+ </p>
+ <h4>
+ utf8::is_valid
+ </h4>
+ <p class="version">
+ Available in version 1.0 and later.
+ </p>
+ <p>
+ Checks whether a sequence of octets is a valid UTF-8 string.
+ </p>
+<pre>
+<span class="keyword">template</span> &lt;<span class=
+"keyword">typename</span> octet_iterator&gt;
+<span class="keyword">bool</span> is_valid(octet_iterator start, octet_iterator end);
+
+</pre>
+ <p>
+ <code>start</code>: an iterator pointing to the beginning of the UTF-8 string to
+ test for validity.<br>
+ <code>end</code>: an iterator pointing to pass-the-end of the UTF-8 string to test
+ for validity.<br>
+ <span class="return_value">Return value</span>: <code>true</code> if the sequence
+ is a valid UTF-8 string; <code>false</code> if not.
+ </p>
+ Example of use:
+<pre>
+<span class="keyword">char</span> utf_invalid[] = <span class=
+"literal">"\xe6\x97\xa5\xd1\x88\xfa"</span>;
+<span class="keyword">bool</span> bvalid = is_valid(utf_invalid, utf_invalid + <span
+class="literal">6</span>);
+assert (bvalid == false);
+</pre>
+ <p>
+ <code>is_valid</code> is a shorthand for <code>find_invalid(start, end) ==
+ end;</code>. You may want to use it to make sure that a byte seqence is a valid
+ UTF-8 string without the need to know where it fails if it is not valid.
+ </p>
+ <h4>
+ utf8::replace_invalid
+ </h4>
+ <p class="version">
+ Available in version 2.0 and later.
+ </p>
+ <p>
+ Replaces all invalid UTF-8 sequences within a string with a replacement marker.
+ </p>
+<pre>
+<span class="keyword">template</span> &lt;<span class=
+"keyword">typename</span> octet_iterator, <span class=
+"keyword">typename</span> output_iterator&gt;
+output_iterator replace_invalid(octet_iterator start, octet_iterator end, output_iterator out, uint32_t replacement);
+<span class="keyword">template</span> &lt;<span class=
+"keyword">typename</span> octet_iterator, <span class=
+"keyword">typename</span> output_iterator&gt;
+output_iterator replace_invalid(octet_iterator start, octet_iterator end, output_iterator out);
+
+</pre>
+ <p>
+ <code>start</code>: an iterator pointing to the beginning of the UTF-8 string to
+ look for invalid UTF-8 sequences.<br>
+ <code>end</code>: an iterator pointing to pass-the-end of the UTF-8 string to look
+ for invalid UTF-8 sequences.<br>
+ <code>out</code>: An output iterator to the range where the result of replacement
+ is stored.<br>
+ <code>replacement</code>: A Unicode code point for the replacement marker. The
+ version without this parameter assumes the value <code>0xfffd</code><br>
+ <span class="return_value">Return value</span>: An iterator pointing to the place
+ after the UTF-8 string with replaced invalid sequences.
+ </p>
+ <p>
+ Example of use:
+ </p>
+<pre>
+<span class="keyword">char</span> invalid_sequence[] = <span class=
+"literal">"a\x80\xe0\xa0\xc0\xaf\xed\xa0\x80z"</span>;
+vector&lt;<span class="keyword">char</span>&gt; replace_invalid_result;
+replace_invalid (invalid_sequence, invalid_sequence + sizeof(invalid_sequence), back_inserter(replace_invalid_result), <span
+ class="literal">'?'</span>);
+bvalid = is_valid(replace_invalid_result.begin(), replace_invalid_result.end());
+assert (bvalid);
+<span class="keyword">char</span>* fixed_invalid_sequence = <span class=
+"literal">"a????z"</span>;
+assert (std::equal(replace_invalid_result.begin(), replace_invalid_result.end(), fixed_invalid_sequence));
+</pre>
+ <p>
+ <code>replace_invalid</code> does not perform in-place replacement of invalid
+ sequences. Rather, it produces a copy of the original string with the invalid
+ sequences replaced with a replacement marker. Therefore, <code>out</code> must not
+ be in the <code>[start, end]</code> range.
+ </p>
+ <p>
+ If <code>end</code> does not point to the past-of-end of a UTF-8 sequence, a
+ <code>utf8::not_enough_room</code> exception is thrown.
+ </p>
+ <h4>
+ utf8::is_bom
+ </h4>
+ <p class="version">
+ Available in version 1.0 and later.
+ </p>
+ <p>
+ Checks whether a sequence of three octets is a UTF-8 byte order mark (BOM)
+ </p>
+<pre>
+<span class="keyword">template</span> &lt;<span class=
+"keyword">typename</span> octet_iterator&gt;
+<span class="keyword">bool</span> is_bom (octet_iterator it);
+</pre>
+ <p>
+ <code>it</code>: beginning of the 3-octet sequence to check<br>
+ <span class="return_value">Return value</span>: <code>true</code> if the sequence
+ is UTF-8 byte order mark; <code>false</code> if not.
+ </p>
+ <p>
+ Example of use:
+ </p>
+<pre>
+<span class="keyword">unsigned char</span> byte_order_mark[] = {<span class=
+"literal">0xef</span>, <span class="literal">0xbb</span>, <span class=
+"literal">0xbf</span>};
+<span class="keyword">bool</span> bbom = is_bom(byte_order_mark);
+assert (bbom == <span class="literal">true</span>);
+</pre>
+ <p>
+ The typical use of this function is to check the first three bytes of a file. If
+ they form the UTF-8 BOM, we want to skip them before processing the actual UTF-8
+ encoded text.
+ </p>
+ <h3 id="typesutf8">
+ Types From utf8 Namespace
+ </h3>
+ <h4>
+ utf8::iterator
+ </h4>
+ <p class="version">
+ Available in version 2.0 and later.
+ </p>
+ <p>
+ Adapts the underlying octet iterator to iterate over the sequence of code points,
+ rather than raw octets.
+ </p>
+<pre>
+<span class="keyword">template</span> &lt;<span class="keyword">typename</span> octet_iterator&gt;
+<span class="keyword">class</span> iterator;
+</pre>
+
+ <h5>Member functions</h5>
+ <dl>
+ <dt><code>iterator();</code> <dd> the deafult constructor; the underlying <code>octet_iterator</code> is
+ constructed with its default constructor.
+ <dt><code><span class="keyword">explicit</span> iterator (const octet_iterator&amp; octet_it,
+ const octet_iterator&amp; range_start,
+ const octet_iterator&amp; range_end);</code> <dd> a constructor
+ that initializes the underlying <code>octet_iterator</code> with <code>octet_it</code>
+ and sets the range in which the iterator is considered valid.
+ <dt><code>octet_iterator base () <span class="keyword">const</span>;</code> <dd> returns the
+ underlying <code>octet_iterator</code>.
+ <dt><code>uint32_t operator * () <span class="keyword">const</span>;</code> <dd> decodes the utf-8 sequence
+ the underlying <code>octet_iterator</code> is pointing to and returns the code point.
+ <dt><code><span class="keyword">bool operator</span> == (const iterator&amp; rhs)
+ <span class="keyword">const</span>;</code> <dd> returns <span class="keyword">true</span>
+ if the two underlaying iterators are equal.
+ <dt><code><span class="keyword">bool operator</span> != (const iterator&amp; rhs)
+ <span class="keyword">const</span>;</code> <dd> returns <span class="keyword">true</span>
+ if the two underlaying iterators are not equal.
+ <dt><code>iterator&amp; <span class="keyword">operator</span> ++ (); </code> <dd> the prefix increment - moves
+ the iterator to the next UTF-8 encoded code point.
+ <dt><code>iterator <span class="keyword">operator</span> ++ (<span class="keyword">int</span>); </code> <dd>
+ the postfix increment - moves the iterator to the next UTF-8 encoded code point and returns the current one.
+ <dt><code>iterator&amp; <span class="keyword">operator</span> -- (); </code> <dd> the prefix decrement - moves
+ the iterator to the previous UTF-8 encoded code point.
+ <dt><code>iterator <span class="keyword">operator</span> -- (<span class="keyword">int</span>); </code> <dd>
+ the postfix decrement - moves the iterator to the previous UTF-8 encoded code point and returns the current one.
+ </dl>
+ <p>
+ Example of use:
+ </p>
+<pre>
+<span class="keyword">char</span>* threechars = <span class="literal">"\xf0\x90\x8d\x86\xe6\x97\xa5\xd1\x88"</span>;
+utf8::iterator&lt;<span class="keyword">char</span>*&gt; it(threechars, threechars, threechars + <span class="literal">9</span>);
+utf8::iterator&lt;<span class="keyword">char</span>*&gt; it2 = it;
+assert (it2 == it);
+assert (*it == <span class="literal">0x10346</span>);
+assert (*(++it) == <span class="literal">0x65e5</span>);
+assert ((*it++) == <span class="literal">0x65e5</span>);
+assert (*it == <span class="literal">0x0448</span>);
+assert (it != it2);
+utf8::iterator&lt;<span class="keyword">char</span>*&gt; endit (threechars + <span class="literal">9</span>, threechars, threechars + <span class="literal">9</span>);
+assert (++it == endit);
+assert (*(--it) == <span class="literal">0x0448</span>);
+assert ((*it--) == <span class="literal">0x0448</span>);
+assert (*it == <span class="literal">0x65e5</span>);
+assert (--it == utf8::iterator&lt;<span class="keyword">char</span>*&gt;(threechars, threechars, threechars + <span class="literal">9</span>));
+assert (*it == <span class="literal">0x10346</span>);
+</pre>
+ <p>
+ The purpose of <code>utf8::iterator</code> adapter is to enable easy iteration as well as the use of STL
+ algorithms with UTF-8 encoded strings. Increment and decrement operators are implemented in terms of
+ <code>utf8::next()</code> and <code>utf8::prior()</code> functions.
+ </p>
+ <p>
+ Note that <code>utf8::iterator</code> adapter is a checked iterator. It operates on the range specified in
+ the constructor; any attempt to go out of that range will result in an exception. Even the comparison operators
+ require both iterator object to be constructed against the same range - otherwise an exception is thrown. Typically,
+ the range will be determined by sequence container functions <code>begin</code> and <code>end</code>, i.e.:
+ </p>
+<pre>
+std::string s = <span class="literal">"example"</span>;
+utf8::iterator i (s.begin(), s.begin(), s.end());
+</pre>
+ <h3 id="fununchecked">
+ Functions From utf8::unchecked Namespace
+ </h3>
+ <h4>
+ utf8::unchecked::append
+ </h4>
+ <p class="version">
+ Available in version 1.0 and later.
+ </p>
+ <p>
+ Encodes a 32 bit code point as a UTF-8 sequence of octets and appends the sequence
+ to a UTF-8 string.
+ </p>
+<pre>
+<span class="keyword">template</span> &lt;<span class=
+"keyword">typename</span> octet_iterator&gt;
+octet_iterator append(uint32_t cp, octet_iterator result);
+
+</pre>
+ <p>
+ <code>cp</code>: A 32 bit integer representing a code point to append to the
+ sequence.<br>
+ <code>result</code>: An output iterator to the place in the sequence where to
+ append the code point.<br>
+ <span class="return_value">Return value</span>: An iterator pointing to the place
+ after the newly appended sequence.
+ </p>
+ <p>
+ Example of use:
+ </p>
+<pre>
+<span class="keyword">unsigned char</span> u[<span class="literal">5</span>] = {<span
+class="literal">0</span>,<span class="literal">0</span>,<span class=
+"literal">0</span>,<span class="literal">0</span>,<span class="literal">0</span>};
+<span class="keyword">unsigned char</span>* end = unchecked::append(<span class=
+"literal">0x0448</span>, u);
+assert (u[<span class="literal">0</span>] == <span class=
+"literal">0xd1</span> &amp;&amp; u[<span class="literal">1</span>] == <span class=
+"literal">0x88</span> &amp;&amp; u[<span class="literal">2</span>] == <span class=
+"literal">0</span> &amp;&amp; u[<span class="literal">3</span>] == <span class=
+"literal">0</span> &amp;&amp; u[<span class="literal">4</span>] == <span class=
+"literal">0</span>);
+</pre>
+ <p>
+ This is a faster but less safe version of <code>utf8::append</code>. It does not
+ check for validity of the supplied code point, and may produce an invalid UTF-8
+ sequence.
+ </p>
+ <h4>
+ utf8::unchecked::next
+ </h4>
+ <p class="version">
+ Available in version 1.0 and later.
+ </p>
+ <p>
+ Given the iterator to the beginning of a UTF-8 sequence, it returns the code point
+ and moves the iterator to the next position.
+ </p>
+<pre>
+<span class="keyword">template</span> &lt;<span class=
+"keyword">typename</span> octet_iterator&gt;
+uint32_t next(octet_iterator&amp; it);
+
+</pre>
+ <p>
+ <code>it</code>: a reference to an iterator pointing to the beginning of an UTF-8
+ encoded code point. After the function returns, it is incremented to point to the
+ beginning of the next code point.<br>
+ <span class="return_value">Return value</span>: the 32 bit representation of the
+ processed UTF-8 code point.
+ </p>
+ <p>
+ Example of use:
+ </p>
+<pre>
+<span class="keyword">char</span>* twochars = <span class=
+"literal">"\xe6\x97\xa5\xd1\x88"</span>;
+<span class="keyword">char</span>* w = twochars;
+<span class="keyword">int</span> cp = unchecked::next(w);
+assert (cp == <span class="literal">0x65e5</span>);
+assert (w == twochars + <span class="literal">3</span>);
+</pre>
+ <p>
+ This is a faster but less safe version of <code>utf8::next</code>. It does not
+ check for validity of the supplied UTF-8 sequence.
+ </p>
+ <h4>
+ utf8::unchecked::peek_next
+ </h4>
+ <p class="version">
+ Available in version 2.1 and later.
+ </p>
+ <p>
+ Given the iterator to the beginning of a UTF-8 sequence, it returns the code point.
+ </p>
+<pre>
+<span class="keyword">template</span> &lt;<span class=
+"keyword">typename</span> octet_iterator&gt;
+uint32_t peek_next(octet_iterator it);
+
+</pre>
+ <p>
+ <code>it</code>: an iterator pointing to the beginning of an UTF-8
+ encoded code point.<br>
+ <span class="return_value">Return value</span>: the 32 bit representation of the
+ processed UTF-8 code point.
+ </p>
+ <p>
+ Example of use:
+ </p>
+<pre>
+<span class="keyword">char</span>* twochars = <span class=
+"literal">"\xe6\x97\xa5\xd1\x88"</span>;
+<span class="keyword">char</span>* w = twochars;
+<span class="keyword">int</span> cp = unchecked::peek_next(w);
+assert (cp == <span class="literal">0x65e5</span>);
+assert (w == twochars);
+</pre>
+ <p>
+ This is a faster but less safe version of <code>utf8::peek_next</code>. It does not
+ check for validity of the supplied UTF-8 sequence.
+ </p>
+ <h4>
+ utf8::unchecked::prior
+ </h4>
+ <p class="version">
+ Available in version 1.02 and later.
+ </p>
+ <p>
+ Given a reference to an iterator pointing to an octet in a UTF-8 seqence, it
+ decreases the iterator until it hits the beginning of the previous UTF-8 encoded
+ code point and returns the 32 bits representation of the code point.
+ </p>
+<pre>
+<span class="keyword">template</span> &lt;<span class=
+"keyword">typename</span> octet_iterator&gt;
+uint32_t prior(octet_iterator&amp; it);
+
+</pre>
+ <p>
+ <code>it</code>: a reference pointing to an octet within a UTF-8 encoded string.
+ After the function returns, it is decremented to point to the beginning of the
+ previous code point.<br>
+ <span class="return_value">Return value</span>: the 32 bit representation of the
+ previous code point.
+ </p>
+ <p>
+ Example of use:
+ </p>
+<pre>
+<span class="keyword">char</span>* twochars = <span class=
+"literal">"\xe6\x97\xa5\xd1\x88"</span>;
+<span class="keyword">char</span>* w = twochars + <span class="literal">3</span>;
+<span class="keyword">int</span> cp = unchecked::prior (w);
+assert (cp == <span class="literal">0x65e5</span>);
+assert (w == twochars);
+</pre>
+ <p>
+ This is a faster but less safe version of <code>utf8::prior</code>. It does not
+ check for validity of the supplied UTF-8 sequence and offers no boundary checking.
+ </p>
+ <h4>
+ utf8::unchecked::previous (deprecated, see utf8::unchecked::prior)
+ </h4>
+ <p class="version">
+ Deprecated in version 1.02 and later.
+ </p>
+ <p>
+ Given a reference to an iterator pointing to an octet in a UTF-8 seqence, it
+ decreases the iterator until it hits the beginning of the previous UTF-8 encoded
+ code point and returns the 32 bits representation of the code point.
+ </p>
+<pre>
+<span class="keyword">template</span> &lt;<span class=
+"keyword">typename</span> octet_iterator&gt;
+uint32_t previous(octet_iterator&amp; it);
+
+</pre>
+ <p>
+ <code>it</code>: a reference pointing to an octet within a UTF-8 encoded string.
+ After the function returns, it is decremented to point to the beginning of the
+ previous code point.<br>
+ <span class="return_value">Return value</span>: the 32 bit representation of the
+ previous code point.
+ </p>
+ <p>
+ Example of use:
+ </p>
+<pre>
+<span class="keyword">char</span>* twochars = <span class=
+"literal">"\xe6\x97\xa5\xd1\x88"</span>;
+<span class="keyword">char</span>* w = twochars + <span class="literal">3</span>;
+<span class="keyword">int</span> cp = unchecked::previous (w);
+assert (cp == <span class="literal">0x65e5</span>);
+assert (w == twochars);
+</pre>
+ <p>
+ The reason this function is deprecated is just the consistency with the "checked"
+ versions, where <code>prior</code> should be used instead of <code>previous</code>.
+ In fact, <code>unchecked::previous</code> behaves exactly the same as <code>
+ unchecked::prior</code>
+ </p>
+ <p>
+ This is a faster but less safe version of <code>utf8::previous</code>. It does not
+ check for validity of the supplied UTF-8 sequence and offers no boundary checking.
+ </p>
+ <h4>
+ utf8::unchecked::advance
+ </h4>
+ <p class="version">
+ Available in version 1.0 and later.
+ </p>
+ <p>
+ Advances an iterator by the specified number of code points within an UTF-8
+ sequence.
+ </p>
+<pre>
+<span class="keyword">template</span> &lt;<span class=
+"keyword">typename</span> octet_iterator, typename distance_type&gt;
+<span class="keyword">void</span> advance (octet_iterator&amp; it, distance_type n);
+
+</pre>
+ <p>
+ <code>it</code>: a reference to an iterator pointing to the beginning of an UTF-8
+ encoded code point. After the function returns, it is incremented to point to the
+ nth following code point.<br>
+ <code>n</code>: a positive integer that shows how many code points we want to
+ advance.<br>
+ </p>
+ <p>
+ Example of use:
+ </p>
+<pre>
+<span class="keyword">char</span>* twochars = <span class=
+"literal">"\xe6\x97\xa5\xd1\x88"</span>;
+<span class="keyword">char</span>* w = twochars;
+unchecked::advance (w, <span class="literal">2</span>);
+assert (w == twochars + <span class="literal">5</span>);
+</pre>
+ <p>
+ This function works only "forward". In case of a negative <code>n</code>, there is
+ no effect.
+ </p>
+ <p>
+ This is a faster but less safe version of <code>utf8::advance</code>. It does not
+ check for validity of the supplied UTF-8 sequence and offers no boundary checking.
+ </p>
+ <h4>
+ utf8::unchecked::distance
+ </h4>
+ <p class="version">
+ Available in version 1.0 and later.
+ </p>
+ <p>
+ Given the iterators to two UTF-8 encoded code points in a seqence, returns the
+ number of code points between them.
+ </p>
+<pre>
+<span class="keyword">template</span> &lt;<span class=
+"keyword">typename</span> octet_iterator&gt;
+<span class=
+"keyword">typename</span> std::iterator_traits&lt;octet_iterator&gt;::difference_type distance (octet_iterator first, octet_iterator last);
+</pre>
+ <p>
+ <code>first</code>: an iterator to a beginning of a UTF-8 encoded code point.<br>
+ <code>last</code>: an iterator to a "post-end" of the last UTF-8 encoded code
+ point in the sequence we are trying to determine the length. It can be the
+ beginning of a new code point, or not.<br>
+ <span class="return_value">Return value</span> the distance between the iterators,
+ in code points.
+ </p>
+ <p>
+ Example of use:
+ </p>
+<pre>
+<span class="keyword">char</span>* twochars = <span class=
+"literal">"\xe6\x97\xa5\xd1\x88"</span>;
+size_t dist = utf8::unchecked::distance(twochars, twochars + <span class=
+"literal">5</span>);
+assert (dist == <span class="literal">2</span>);
+</pre>
+ <p>
+ This is a faster but less safe version of <code>utf8::distance</code>. It does not
+ check for validity of the supplied UTF-8 sequence.
+ </p>
+ <h4>
+ utf8::unchecked::utf16to8
+ </h4>
+ <p class="version">
+ Available in version 1.0 and later.
+ </p>
+ <p>
+ Converts a UTF-16 encoded string to UTF-8.
+ </p>
+<pre>
+<span class="keyword">template</span> &lt;<span class=
+"keyword">typename</span> u16bit_iterator, <span class=
+"keyword">typename</span> octet_iterator&gt;
+octet_iterator utf16to8 (u16bit_iterator start, u16bit_iterator end, octet_iterator result);
+
+</pre>
+ <p>
+ <code>start</code>: an iterator pointing to the beginning of the UTF-16 encoded
+ string to convert.<br>
+ <code>end</code>: an iterator pointing to pass-the-end of the UTF-16 encoded
+ string to convert.<br>
+ <code>result</code>: an output iterator to the place in the UTF-8 string where to
+ append the result of conversion.<br>
+ <span class="return_value">Return value</span>: An iterator pointing to the place
+ after the appended UTF-8 string.
+ </p>
+ <p>
+ Example of use:
+ </p>
+<pre>
+<span class="keyword">unsigned short</span> utf16string[] = {<span class=
+"literal">0x41</span>, <span class="literal">0x0448</span>, <span class=
+"literal">0x65e5</span>, <span class="literal">0xd834</span>, <span class=
+"literal">0xdd1e</span>};
+vector&lt;<span class="keyword">unsigned char</span>&gt; utf8result;
+unchecked::utf16to8(utf16string, utf16string + <span class=
+"literal">5</span>, back_inserter(utf8result));
+assert (utf8result.size() == <span class="literal">10</span>);
+</pre>
+ <p>
+ This is a faster but less safe version of <code>utf8::utf16to8</code>. It does not
+ check for validity of the supplied UTF-16 sequence.
+ </p>
+ <h4>
+ utf8::unchecked::utf8to16
+ </h4>
+ <p class="version">
+ Available in version 1.0 and later.
+ </p>
+ <p>
+ Converts an UTF-8 encoded string to UTF-16
+ </p>
+<pre>
+<span class="keyword">template</span> &lt;<span class=
+"keyword">typename</span> u16bit_iterator, typename octet_iterator&gt;
+u16bit_iterator utf8to16 (octet_iterator start, octet_iterator end, u16bit_iterator result);
+
+</pre>
+ <p>
+ <code>start</code>: an iterator pointing to the beginning of the UTF-8 encoded
+ string to convert. &lt; br /&gt; <code>end</code>: an iterator pointing to
+ pass-the-end of the UTF-8 encoded string to convert.<br>
+ <code>result</code>: an output iterator to the place in the UTF-16 string where to
+ append the result of conversion.<br>
+ <span class="return_value">Return value</span>: An iterator pointing to the place
+ after the appended UTF-16 string.
+ </p>
+ <p>
+ Example of use:
+ </p>
+<pre>
+<span class="keyword">char</span> utf8_with_surrogates[] = <span class=
+"literal">"\xe6\x97\xa5\xd1\x88\xf0\x9d\x84\x9e"</span>;
+vector &lt;<span class="keyword">unsigned short</span>&gt; utf16result;
+unchecked::utf8to16(utf8_with_surrogates, utf8_with_surrogates + <span class=
+"literal">9</span>, back_inserter(utf16result));
+assert (utf16result.size() == <span class="literal">4</span>);
+assert (utf16result[<span class="literal">2</span>] == <span class=
+"literal">0xd834</span>);
+assert (utf16result[<span class="literal">3</span>] == <span class=
+"literal">0xdd1e</span>);
+</pre>
+ <p>
+ This is a faster but less safe version of <code>utf8::utf8to16</code>. It does not
+ check for validity of the supplied UTF-8 sequence.
+ </p>
+ <h4>
+ utf8::unchecked::utf32to8
+ </h4>
+ <p class="version">
+ Available in version 1.0 and later.
+ </p>
+ <p>
+ Converts a UTF-32 encoded string to UTF-8.
+ </p>
+<pre>
+<span class="keyword">template</span> &lt;<span class=
+"keyword">typename</span> octet_iterator, <span class=
+"keyword">typename</span> u32bit_iterator&gt;
+octet_iterator utf32to8 (u32bit_iterator start, u32bit_iterator end, octet_iterator result);
+
+</pre>
+ <p>
+ <code>start</code>: an iterator pointing to the beginning of the UTF-32 encoded
+ string to convert.<br>
+ <code>end</code>: an iterator pointing to pass-the-end of the UTF-32 encoded
+ string to convert.<br>
+ <code>result</code>: an output iterator to the place in the UTF-8 string where to
+ append the result of conversion.<br>
+ <span class="return_value">Return value</span>: An iterator pointing to the place
+ after the appended UTF-8 string.
+ </p>
+ <p>
+ Example of use:
+ </p>
+<pre>
+<span class="keyword">int</span> utf32string[] = {<span class=
+"literal">0x448</span>, <span class="literal">0x65e5</span>, <span class=
+"literal">0x10346</span>, <span class="literal">0</span>};
+vector&lt;<span class="keyword">unsigned char</span>&gt; utf8result;
+utf32to8(utf32string, utf32string + <span class=
+"literal">3</span>, back_inserter(utf8result));
+assert (utf8result.size() == <span class="literal">9</span>);
+</pre>
+ <p>
+ This is a faster but less safe version of <code>utf8::utf32to8</code>. It does not
+ check for validity of the supplied UTF-32 sequence.
+ </p>
+ <h4>
+ utf8::unchecked::utf8to32
+ </h4>
+ <p class="version">
+ Available in version 1.0 and later.
+ </p>
+ <p>
+ Converts a UTF-8 encoded string to UTF-32.
+ </p>
+<pre>
+<span class="keyword">template</span> &lt;<span class=
+"keyword">typename</span> octet_iterator, typename u32bit_iterator&gt;
+u32bit_iterator utf8to32 (octet_iterator start, octet_iterator end, u32bit_iterator result);
+
+</pre>
+ <p>
+ <code>start</code>: an iterator pointing to the beginning of the UTF-8 encoded
+ string to convert.<br>
+ <code>end</code>: an iterator pointing to pass-the-end of the UTF-8 encoded string
+ to convert.<br>
+ <code>result</code>: an output iterator to the place in the UTF-32 string where to
+ append the result of conversion.<br>
+ <span class="return_value">Return value</span>: An iterator pointing to the place
+ after the appended UTF-32 string.
+ </p>
+ <p>
+ Example of use:
+ </p>
+<pre>
+<span class="keyword">char</span>* twochars = <span class=
+"literal">"\xe6\x97\xa5\xd1\x88"</span>;
+vector&lt;<span class="keyword">int</span>&gt; utf32result;
+unchecked::utf8to32(twochars, twochars + <span class=
+"literal">5</span>, back_inserter(utf32result));
+assert (utf32result.size() == <span class="literal">2</span>);
+</pre>
+ <p>
+ This is a faster but less safe version of <code>utf8::utf8to32</code>. It does not
+ check for validity of the supplied UTF-8 sequence.
+ </p>
+ <h3 id="typesunchecked">
+ Types From utf8::unchecked Namespace
+ </h3>
+ <h4>
+ utf8::iterator
+ </h4>
+ <p class="version">
+ Available in version 2.0 and later.
+ </p>
+ <p>
+ Adapts the underlying octet iterator to iterate over the sequence of code points,
+ rather than raw octets.
+ </p>
+<pre>
+<span class="keyword">template</span> &lt;<span class="keyword">typename</span> octet_iterator&gt;
+<span class="keyword">class</span> iterator;
+</pre>
+
+ <h5>Member functions</h5>
+ <dl>
+ <dt><code>iterator();</code> <dd> the deafult constructor; the underlying <code>octet_iterator</code> is
+ constructed with its default constructor.
+ <dt><code><span class="keyword">explicit</span> iterator (const octet_iterator&amp; octet_it);
+ </code> <dd> a constructor
+ that initializes the underlying <code>octet_iterator</code> with <code>octet_it</code>
+ <dt><code>octet_iterator base () <span class="keyword">const</span>;</code> <dd> returns the
+ underlying <code>octet_iterator</code>.
+ <dt><code>uint32_t operator * () <span class="keyword">const</span>;</code> <dd> decodes the utf-8 sequence
+ the underlying <code>octet_iterator</code> is pointing to and returns the code point.
+ <dt><code><span class="keyword">bool operator</span> == (const iterator&amp; rhs)
+ <span class="keyword">const</span>;</code> <dd> returns <span class="keyword">true</span>
+ if the two underlaying iterators are equal.
+ <dt><code><span class="keyword">bool operator</span> != (const iterator&amp; rhs)
+ <span class="keyword">const</span>;</code> <dd> returns <span class="keyword">true</span>
+ if the two underlaying iterators are not equal.
+ <dt><code>iterator&amp; <span class="keyword">operator</span> ++ (); </code> <dd> the prefix increment - moves
+ the iterator to the next UTF-8 encoded code point.
+ <dt><code>iterator <span class="keyword">operator</span> ++ (<span class="keyword">int</span>); </code> <dd>
+ the postfix increment - moves the iterator to the next UTF-8 encoded code point and returns the current one.
+ <dt><code>iterator&amp; <span class="keyword">operator</span> -- (); </code> <dd> the prefix decrement - moves
+ the iterator to the previous UTF-8 encoded code point.
+ <dt><code>iterator <span class="keyword">operator</span> -- (<span class="keyword">int</span>); </code> <dd>
+ the postfix decrement - moves the iterator to the previous UTF-8 encoded code point and returns the current one.
+ </dl>
+ <p>
+ Example of use:
+ </p>
+<pre>
+<span class="keyword">char</span>* threechars = <span class="literal">"\xf0\x90\x8d\x86\xe6\x97\xa5\xd1\x88"</span>;
+utf8::unchecked::iterator&lt;<span class="keyword">char</span>*&gt; un_it(threechars);
+utf8::unchecked::iterator&lt;<span class="keyword">char</span>*&gt; un_it2 = un_it;
+assert (un_it2 == un_it);
+assert (*un_it == <span class="literal">0x10346</span>);
+assert (*(++un_it) == <span class="literal">0x65e5</span>);
+assert ((*un_it++) == <span class="literal">0x65e5</span>);
+assert (*un_it == <span class="literal">0x0448</span>);
+assert (un_it != un_it2);
+utf8::::unchecked::iterator&lt;<span class="keyword">char</span>*&gt; un_endit (threechars + <span class="literal">9</span>);
+assert (++un_it == un_endit);
+assert (*(--un_it) == <span class="literal">0x0448</span>);
+assert ((*un_it--) == <span class="literal">0x0448</span>);
+assert (*un_it == <span class="literal">0x65e5</span>);
+assert (--un_it == utf8::unchecked::iterator&lt;<span class="keyword">char</span>*&gt;(threechars));
+assert (*un_it == <span class="literal">0x10346</span>);
+</pre>
+ <p>
+ This is an unchecked version of <code>utf8::iterator</code>. It is faster in many cases, but offers
+ no validity or range checks.
+ </p>
+ <h2 id="points">
+ Points of interest
+ </h2>
+ <h4>
+ Design goals and decisions
+ </h4>
+ <p>
+ The library was designed to be:
+ </p>
+ <ol>
+ <li>
+ Generic: for better or worse, there are many C++ string classes out there, and
+ the library should work with as many of them as possible.
+ </li>
+ <li>
+ Portable: the library should be portable both accross different platforms and
+ compilers. The only non-portable code is a small section that declares unsigned
+ integers of different sizes: three typedefs. They can be changed by the users of
+ the library if they don't match their platform. The default setting should work
+ for Windows (both 32 and 64 bit), and most 32 bit and 64 bit Unix derivatives.
+ </li>
+ <li>
+ Lightweight: follow the "pay only for what you use" guidline.
+ </li>
+ <li>
+ Unintrusive: avoid forcing any particular design or even programming style on the
+ user. This is a library, not a framework.
+ </li>
+ </ol>
+ <h4>
+ Alternatives
+ </h4>
+ <p>
+ In case you want to look into other means of working with UTF-8 strings from C++,
+ here is the list of solutions I am aware of:
+ </p>
+ <ol>
+ <li>
+ <a href="http://icu.sourceforge.net/">ICU Library</a>. It is very powerful,
+ complete, feature-rich, mature, and widely used. Also big, intrusive,
+ non-generic, and doesn't play well with the Standard Library. I definitelly
+ recommend looking at ICU even if you don't plan to use it.
+ </li>
+ <li>
+ <a href=
+ "http://www.gtkmm.org/gtkmm2/docs/tutorial/html/ch03s04.html">Glib::ustring</a>.
+ A class specifically made to work with UTF-8 strings, and also feel like
+ <code>std::string</code>. If you prefer to have yet another string class in your
+ code, it may be worth a look. Be aware of the licensing issues, though.
+ </li>
+ <li>
+ Platform dependent solutions: Windows and POSIX have functions to convert strings
+ from one encoding to another. That is only a subset of what my library offers,
+ but if that is all you need it may be good enough, especially given the fact that
+ these functions are mature and tested in production.
+ </li>
+ </ol>
+ <h2 id="conclusion">
+ Conclusion
+ </h2>
+ <p>
+ Until Unicode becomes officially recognized by the C++ Standard Library, we need to
+ use other means to work with UTF-8 strings. Template functions I describe in this
+ article may be a good step in this direction.
+ </p>
+ <h2 id="links">
+ Links
+ </h2>
+ <ol>
+ <li>
+ <a href="http://www.unicode.org/">The Unicode Consortium</a>.
+ </li>
+ <li>
+ <a href="http://icu.sourceforge.net/">ICU Library</a>.
+ </li>
+ <li>
+ <a href="http://en.wikipedia.org/wiki/UTF-8">UTF-8 at Wikipedia</a>
+ </li>
+ <li>
+ <a href="http://www.cl.cam.ac.uk/~mgk25/unicode.html">UTF-8 and Unicode FAQ for
+ Unix/Linux</a>
+ </li>
+ </ol>
+ </body>
+</html>
diff --git a/externals/utf8cpp/include/utf8.h b/externals/utf8cpp/include/utf8.h
new file mode 100644
index 00000000000..cc463cb82d5
--- /dev/null
+++ b/externals/utf8cpp/include/utf8.h
@@ -0,0 +1,35 @@
+// Copyright 2006 Nemanja Trifunovic
+
+/*
+Permission is hereby granted, free of charge, to any person or organization
+obtaining a copy of the software and accompanying documentation covered by
+this license (the "Software") to use, reproduce, display, distribute,
+execute, and transmit the Software, and to prepare derivative works of the
+Software, and to permit third-parties to whom the Software is furnished to
+do so, all subject to the following:
+
+The copyright notices in the Software and this entire statement, including
+the above license grant, this restriction and the following disclaimer,
+must be included in all copies of the Software, in whole or in part, and
+all derivative works of the Software, unless such copies or derivative
+works are solely in the form of machine-executable object code generated by
+a source language processor.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
+SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
+FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
+ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+DEALINGS IN THE SOFTWARE.
+*/
+
+
+#ifndef UTF8_FOR_CPP_2675DCD0_9480_4c0c_B92A_CC14C027B731
+#define UTF8_FOR_CPP_2675DCD0_9480_4c0c_B92A_CC14C027B731
+
+#include "utf8/checked.h"
+#include "utf8/unchecked.h"
+
+#endif // header guard
+
diff --git a/externals/utf8cpp/include/utf8/checked.h b/externals/utf8cpp/include/utf8/checked.h
new file mode 100644
index 00000000000..86204eae3ea
--- /dev/null
+++ b/externals/utf8cpp/include/utf8/checked.h
@@ -0,0 +1,319 @@
+// Copyright 2006 Nemanja Trifunovic
+
+/*
+Permission is hereby granted, free of charge, to any person or organization
+obtaining a copy of the software and accompanying documentation covered by
+this license (the "Software") to use, reproduce, display, distribute,
+execute, and transmit the Software, and to prepare derivative works of the
+Software, and to permit third-parties to whom the Software is furnished to
+do so, all subject to the following:
+
+The copyright notices in the Software and this entire statement, including
+the above license grant, this restriction and the following disclaimer,
+must be included in all copies of the Software, in whole or in part, and
+all derivative works of the Software, unless such copies or derivative
+works are solely in the form of machine-executable object code generated by
+a source language processor.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
+SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
+FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
+ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+DEALINGS IN THE SOFTWARE.
+*/
+
+
+#ifndef UTF8_FOR_CPP_CHECKED_H_2675DCD0_9480_4c0c_B92A_CC14C027B731
+#define UTF8_FOR_CPP_CHECKED_H_2675DCD0_9480_4c0c_B92A_CC14C027B731
+
+#include "core.h"
+#include <stdexcept>
+
+namespace utf8
+{
+ // Exceptions that may be thrown from the library functions.
+ class invalid_code_point : public std::exception {
+ uint32_t cp;
+ public:
+ invalid_code_point(uint32_t cp) : cp(cp) {}
+ virtual const char* what() const throw() { return "Invalid code point"; }
+ uint32_t code_point() const {return cp;}
+ };
+
+ class invalid_utf8 : public std::exception {
+ uint8_t u8;
+ public:
+ invalid_utf8 (uint8_t u) : u8(u) {}
+ virtual const char* what() const throw() { return "Invalid UTF-8"; }
+ uint8_t utf8_octet() const {return u8;}
+ };
+
+ class invalid_utf16 : public std::exception {
+ uint16_t u16;
+ public:
+ invalid_utf16 (uint16_t u) : u16(u) {}
+ virtual const char* what() const throw() { return "Invalid UTF-16"; }
+ uint16_t utf16_word() const {return u16;}
+ };
+
+ class not_enough_room : public std::exception {
+ public:
+ virtual const char* what() const throw() { return "Not enough space"; }
+ };
+
+ /// The library API - functions intended to be called by the users
+
+ template <typename octet_iterator, typename output_iterator>
+ output_iterator replace_invalid(octet_iterator start, octet_iterator end, output_iterator out, uint32_t replacement)
+ {
+ while (start != end) {
+ octet_iterator sequence_start = start;
+ internal::utf_error err_code = internal::validate_next(start, end);
+ switch (err_code) {
+ case internal::OK :
+ for (octet_iterator it = sequence_start; it != start; ++it)
+ *out++ = *it;
+ break;
+ case internal::NOT_ENOUGH_ROOM:
+ throw not_enough_room();
+ case internal::INVALID_LEAD:
+ append (replacement, out);
+ ++start;
+ break;
+ case internal::INCOMPLETE_SEQUENCE:
+ case internal::OVERLONG_SEQUENCE:
+ case internal::INVALID_CODE_POINT:
+ append (replacement, out);
+ ++start;
+ // just one replacement mark for the sequence
+ while (internal::is_trail(*start) && start != end)
+ ++start;
+ break;
+ }
+ }
+ return out;
+ }
+
+ template <typename octet_iterator, typename output_iterator>
+ inline output_iterator replace_invalid(octet_iterator start, octet_iterator end, output_iterator out)
+ {
+ static const uint32_t replacement_marker = internal::mask16(0xfffd);
+ return replace_invalid(start, end, out, replacement_marker);
+ }
+
+ template <typename octet_iterator>
+ octet_iterator append(uint32_t cp, octet_iterator result)
+ {
+ if (!internal::is_code_point_valid(cp))
+ throw invalid_code_point(cp);
+
+ if (cp < 0x80) // one octet
+ *(result++) = static_cast<uint8_t>(cp);
+ else if (cp < 0x800) { // two octets
+ *(result++) = static_cast<uint8_t>((cp >> 6) | 0xc0);
+ *(result++) = static_cast<uint8_t>((cp & 0x3f) | 0x80);
+ }
+ else if (cp < 0x10000) { // three octets
+ *(result++) = static_cast<uint8_t>((cp >> 12) | 0xe0);
+ *(result++) = static_cast<uint8_t>(((cp >> 6) & 0x3f) | 0x80);
+ *(result++) = static_cast<uint8_t>((cp & 0x3f) | 0x80);
+ }
+ else if (cp <= internal::CODE_POINT_MAX) { // four octets
+ *(result++) = static_cast<uint8_t>((cp >> 18) | 0xf0);
+ *(result++) = static_cast<uint8_t>(((cp >> 12)& 0x3f) | 0x80);
+ *(result++) = static_cast<uint8_t>(((cp >> 6) & 0x3f) | 0x80);
+ *(result++) = static_cast<uint8_t>((cp & 0x3f) | 0x80);
+ }
+ else
+ throw invalid_code_point(cp);
+
+ return result;
+ }
+
+ template <typename octet_iterator>
+ uint32_t next(octet_iterator& it, octet_iterator end)
+ {
+ uint32_t cp = 0;
+ internal::utf_error err_code = internal::validate_next(it, end, &cp);
+ switch (err_code) {
+ case internal::OK :
+ break;
+ case internal::NOT_ENOUGH_ROOM :
+ throw not_enough_room();
+ case internal::INVALID_LEAD :
+ case internal::INCOMPLETE_SEQUENCE :
+ case internal::OVERLONG_SEQUENCE :
+ throw invalid_utf8(*it);
+ case internal::INVALID_CODE_POINT :
+ throw invalid_code_point(cp);
+ }
+ return cp;
+ }
+
+ template <typename octet_iterator>
+ uint32_t peek_next(octet_iterator it, octet_iterator end)
+ {
+ return next(it, end);
+ }
+
+ template <typename octet_iterator>
+ uint32_t prior(octet_iterator& it, octet_iterator start)
+ {
+ octet_iterator end = it;
+ while (internal::is_trail(*(--it)))
+ if (it < start)
+ throw invalid_utf8(*it); // error - no lead byte in the sequence
+ octet_iterator temp = it;
+ return next(temp, end);
+ }
+
+ /// Deprecated in versions that include "prior"
+ template <typename octet_iterator>
+ uint32_t previous(octet_iterator& it, octet_iterator pass_start)
+ {
+ octet_iterator end = it;
+ while (internal::is_trail(*(--it)))
+ if (it == pass_start)
+ throw invalid_utf8(*it); // error - no lead byte in the sequence
+ octet_iterator temp = it;
+ return next(temp, end);
+ }
+
+ template <typename octet_iterator, typename distance_type>
+ void advance (octet_iterator& it, distance_type n, octet_iterator end)
+ {
+ for (distance_type i = 0; i < n; ++i)
+ next(it, end);
+ }
+
+ template <typename octet_iterator>
+ typename std::iterator_traits<octet_iterator>::difference_type
+ distance (octet_iterator first, octet_iterator last)
+ {
+ typename std::iterator_traits<octet_iterator>::difference_type dist;
+ for (dist = 0; first < last; ++dist)
+ next(first, last);
+ return dist;
+ }
+
+ template <typename u16bit_iterator, typename octet_iterator>
+ octet_iterator utf16to8 (u16bit_iterator start, u16bit_iterator end, octet_iterator result)
+ {
+ while (start != end) {
+ uint32_t cp = internal::mask16(*start++);
+ // Take care of surrogate pairs first
+ if (internal::is_surrogate(cp)) {
+ if (start != end) {
+ uint32_t trail_surrogate = internal::mask16(*start++);
+ if (trail_surrogate >= internal::TRAIL_SURROGATE_MIN && trail_surrogate <= internal::TRAIL_SURROGATE_MAX)
+ cp = (cp << 10) + trail_surrogate + internal::SURROGATE_OFFSET;
+ else
+ throw invalid_utf16(static_cast<uint16_t>(trail_surrogate));
+ }
+ else
+ throw invalid_utf16(static_cast<uint16_t>(*start));
+
+ }
+ result = append(cp, result);
+ }
+ return result;
+ }
+
+ template <typename u16bit_iterator, typename octet_iterator>
+ u16bit_iterator utf8to16 (octet_iterator start, octet_iterator end, u16bit_iterator result)
+ {
+ while (start != end) {
+ uint32_t cp = next(start, end);
+ if (cp > 0xffff) { //make a surrogate pair
+ *result++ = static_cast<uint16_t>((cp >> 10) + internal::LEAD_OFFSET);
+ *result++ = static_cast<uint16_t>((cp & 0x3ff) + internal::TRAIL_SURROGATE_MIN);
+ }
+ else
+ *result++ = static_cast<uint16_t>(cp);
+ }
+ return result;
+ }
+
+ template <typename octet_iterator, typename u32bit_iterator>
+ octet_iterator utf32to8 (u32bit_iterator start, u32bit_iterator end, octet_iterator result)
+ {
+ while (start != end)
+ result = append(*(start++), result);
+
+ return result;
+ }
+
+ template <typename octet_iterator, typename u32bit_iterator>
+ u32bit_iterator utf8to32 (octet_iterator start, octet_iterator end, u32bit_iterator result)
+ {
+ while (start < end)
+ (*result++) = next(start, end);
+
+ return result;
+ }
+
+ // The iterator class
+ template <typename octet_iterator>
+ class iterator : public std::iterator <std::bidirectional_iterator_tag, uint32_t> {
+ octet_iterator it;
+ octet_iterator range_start;
+ octet_iterator range_end;
+ public:
+ iterator () {};
+ explicit iterator (const octet_iterator& octet_it,
+ const octet_iterator& range_start,
+ const octet_iterator& range_end) :
+ it(octet_it), range_start(range_start), range_end(range_end)
+ {
+ if (it < range_start || it > range_end)
+ throw std::out_of_range("Invalid utf-8 iterator position");
+ }
+ // the default "big three" are OK
+ octet_iterator base () const { return it; }
+ uint32_t operator * () const
+ {
+ octet_iterator temp = it;
+ return next(temp, range_end);
+ }
+ bool operator == (const iterator& rhs) const
+ {
+ if (range_start != rhs.range_start || range_end != rhs.range_end)
+ throw std::logic_error("Comparing utf-8 iterators defined with different ranges");
+ return (it == rhs.it);
+ }
+ bool operator != (const iterator& rhs) const
+ {
+ return !(operator == (rhs));
+ }
+ iterator& operator ++ ()
+ {
+ next(it, range_end);
+ return *this;
+ }
+ iterator operator ++ (int)
+ {
+ iterator temp = *this;
+ next(it, range_end);
+ return temp;
+ }
+ iterator& operator -- ()
+ {
+ prior(it, range_start);
+ return *this;
+ }
+ iterator operator -- (int)
+ {
+ iterator temp = *this;
+ prior(it, range_start);
+ return temp;
+ }
+ }; // class iterator
+
+} // namespace utf8
+
+#endif //header guard
+
+
+
diff --git a/externals/utf8cpp/include/utf8/core.h b/externals/utf8cpp/include/utf8/core.h
new file mode 100644
index 00000000000..389dd3e8ca5
--- /dev/null
+++ b/externals/utf8cpp/include/utf8/core.h
@@ -0,0 +1,269 @@
+// Copyright 2006 Nemanja Trifunovic
+
+/*
+Permission is hereby granted, free of charge, to any person or organization
+obtaining a copy of the software and accompanying documentation covered by
+this license (the "Software") to use, reproduce, display, distribute,
+execute, and transmit the Software, and to prepare derivative works of the
+Software, and to permit third-parties to whom the Software is furnished to
+do so, all subject to the following:
+
+The copyright notices in the Software and this entire statement, including
+the above license grant, this restriction and the following disclaimer,
+must be included in all copies of the Software, in whole or in part, and
+all derivative works of the Software, unless such copies or derivative
+works are solely in the form of machine-executable object code generated by
+a source language processor.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
+SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
+FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
+ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+DEALINGS IN THE SOFTWARE.
+*/
+
+
+#ifndef UTF8_FOR_CPP_CORE_H_2675DCD0_9480_4c0c_B92A_CC14C027B731
+#define UTF8_FOR_CPP_CORE_H_2675DCD0_9480_4c0c_B92A_CC14C027B731
+
+#include <iterator>
+
+// use Trinity core types
+#include "Platform/Define.h"
+
+namespace utf8
+{
+ // The typedefs for 8-bit, 16-bit and 32-bit unsigned integers
+ // You may need to change them to match your system.
+ // These typedefs have the same names as ones from cstdint, or boost/cstdint
+
+ /* use Trinity alternatives
+ typedef unsigned char uint8_t;
+ typedef unsigned short uint16_t;
+ typedef unsigned int uint32_t;
+ */
+ typedef uint8 uint8_t;
+ typedef uint16 uint16_t;
+ typedef uint32 uint32_t;
+
+// Helper code - not intended to be directly called by the library users. May be changed at any time
+namespace internal
+{
+ // Unicode constants
+ // Leading (high) surrogates: 0xd800 - 0xdbff
+ // Trailing (low) surrogates: 0xdc00 - 0xdfff
+ const uint16_t LEAD_SURROGATE_MIN = 0xd800u;
+ const uint16_t LEAD_SURROGATE_MAX = 0xdbffu;
+ const uint16_t TRAIL_SURROGATE_MIN = 0xdc00u;
+ const uint16_t TRAIL_SURROGATE_MAX = 0xdfffu;
+ const uint16_t LEAD_OFFSET = LEAD_SURROGATE_MIN - (0x10000 >> 10);
+ const uint32_t SURROGATE_OFFSET = 0x10000u - (LEAD_SURROGATE_MIN << 10) - TRAIL_SURROGATE_MIN;
+
+ // Maximum valid value for a Unicode code point
+ const uint32_t CODE_POINT_MAX = 0x0010ffffu;
+
+ template<typename octet_type>
+ inline uint8_t mask8(octet_type oc)
+ {
+ return static_cast<uint8_t>(0xff & oc);
+ }
+ template<typename u16_type>
+ inline uint16_t mask16(u16_type oc)
+ {
+ return static_cast<uint16_t>(0xffff & oc);
+ }
+ template<typename octet_type>
+ inline bool is_trail(octet_type oc)
+ {
+ return ((mask8(oc) >> 6) == 0x2);
+ }
+
+ template <typename u16>
+ inline bool is_surrogate(u16 cp)
+ {
+ return (cp >= LEAD_SURROGATE_MIN && cp <= TRAIL_SURROGATE_MAX);
+ }
+
+ template <typename u32>
+ inline bool is_code_point_valid(u32 cp)
+ {
+ return (cp <= CODE_POINT_MAX && !is_surrogate(cp) && cp != 0xfffe && cp != 0xffff);
+ }
+
+ template <typename octet_iterator>
+ inline typename std::iterator_traits<octet_iterator>::difference_type
+ sequence_length(octet_iterator lead_it)
+ {
+ uint8_t lead = mask8(*lead_it);
+ if (lead < 0x80)
+ return 1;
+ else if ((lead >> 5) == 0x6)
+ return 2;
+ else if ((lead >> 4) == 0xe)
+ return 3;
+ else if ((lead >> 3) == 0x1e)
+ return 4;
+ else
+ return 0;
+ }
+
+ enum utf_error {OK, NOT_ENOUGH_ROOM, INVALID_LEAD, INCOMPLETE_SEQUENCE, OVERLONG_SEQUENCE, INVALID_CODE_POINT};
+
+ template <typename octet_iterator>
+ utf_error validate_next(octet_iterator& it, octet_iterator end, uint32_t* code_point)
+ {
+ uint32_t cp = mask8(*it);
+ // Check the lead octet
+ typedef typename std::iterator_traits<octet_iterator>::difference_type octet_difference_type;
+ octet_difference_type length = sequence_length(it);
+
+ // "Shortcut" for ASCII characters
+ if (length == 1) {
+ if (end - it > 0) {
+ if (code_point)
+ *code_point = cp;
+ ++it;
+ return OK;
+ }
+ else
+ return NOT_ENOUGH_ROOM;
+ }
+
+ // Do we have enough memory?
+ if (std::distance(it, end) < length)
+ return NOT_ENOUGH_ROOM;
+
+ // Check trail octets and calculate the code point
+ switch (length) {
+ case 0:
+ return INVALID_LEAD;
+ break;
+ case 2:
+ if (is_trail(*(++it))) {
+ cp = ((cp << 6) & 0x7ff) + ((*it) & 0x3f);
+ }
+ else {
+ --it;
+ return INCOMPLETE_SEQUENCE;
+ }
+ break;
+ case 3:
+ if (is_trail(*(++it))) {
+ cp = ((cp << 12) & 0xffff) + ((mask8(*it) << 6) & 0xfff);
+ if (is_trail(*(++it))) {
+ cp += (*it) & 0x3f;
+ }
+ else {
+ std::advance(it, -2);
+ return INCOMPLETE_SEQUENCE;
+ }
+ }
+ else {
+ --it;
+ return INCOMPLETE_SEQUENCE;
+ }
+ break;
+ case 4:
+ if (is_trail(*(++it))) {
+ cp = ((cp << 18) & 0x1fffff) + ((mask8(*it) << 12) & 0x3ffff);
+ if (is_trail(*(++it))) {
+ cp += (mask8(*it) << 6) & 0xfff;
+ if (is_trail(*(++it))) {
+ cp += (*it) & 0x3f;
+ }
+ else {
+ std::advance(it, -3);
+ return INCOMPLETE_SEQUENCE;
+ }
+ }
+ else {
+ std::advance(it, -2);
+ return INCOMPLETE_SEQUENCE;
+ }
+ }
+ else {
+ --it;
+ return INCOMPLETE_SEQUENCE;
+ }
+ break;
+ }
+ // Is the code point valid?
+ if (!is_code_point_valid(cp)) {
+ for (octet_difference_type i = 0; i < length - 1; ++i)
+ --it;
+ return INVALID_CODE_POINT;
+ }
+
+ if (code_point)
+ *code_point = cp;
+
+ if (cp < 0x80) {
+ if (length != 1) {
+ std::advance(it, -(length-1));
+ return OVERLONG_SEQUENCE;
+ }
+ }
+ else if (cp < 0x800) {
+ if (length != 2) {
+ std::advance(it, -(length-1));
+ return OVERLONG_SEQUENCE;
+ }
+ }
+ else if (cp < 0x10000) {
+ if (length != 3) {
+ std::advance(it, -(length-1));
+ return OVERLONG_SEQUENCE;
+ }
+ }
+
+ ++it;
+ return OK;
+ }
+
+ template <typename octet_iterator>
+ inline utf_error validate_next(octet_iterator& it, octet_iterator end) {
+ return validate_next(it, end, 0);
+ }
+
+} // namespace internal
+
+ /// The library API - functions intended to be called by the users
+
+ // Byte order mark
+ const uint8_t bom[] = {0xef, 0xbb, 0xbf};
+
+ template <typename octet_iterator>
+ octet_iterator find_invalid(octet_iterator start, octet_iterator end)
+ {
+ octet_iterator result = start;
+ while (result != end) {
+ internal::utf_error err_code = internal::validate_next(result, end);
+ if (err_code != internal::OK)
+ return result;
+ }
+ return result;
+ }
+
+ template <typename octet_iterator>
+ inline bool is_valid(octet_iterator start, octet_iterator end)
+ {
+ return (find_invalid(start, end) == end);
+ }
+
+ template <typename octet_iterator>
+ inline bool is_bom (octet_iterator it)
+ {
+ return (
+ (internal::mask8(*it++)) == bom[0] &&
+ (internal::mask8(*it++)) == bom[1] &&
+ (internal::mask8(*it)) == bom[2]
+ );
+ }
+} // namespace utf8
+
+#endif // header guard
+
+
+
diff --git a/externals/utf8cpp/include/utf8/unchecked.h b/externals/utf8cpp/include/utf8/unchecked.h
new file mode 100644
index 00000000000..fc7267d1b98
--- /dev/null
+++ b/externals/utf8cpp/include/utf8/unchecked.h
@@ -0,0 +1,229 @@
+// Copyright 2006 Nemanja Trifunovic
+
+/*
+Permission is hereby granted, free of charge, to any person or organization
+obtaining a copy of the software and accompanying documentation covered by
+this license (the "Software") to use, reproduce, display, distribute,
+execute, and transmit the Software, and to prepare derivative works of the
+Software, and to permit third-parties to whom the Software is furnished to
+do so, all subject to the following:
+
+The copyright notices in the Software and this entire statement, including
+the above license grant, this restriction and the following disclaimer,
+must be included in all copies of the Software, in whole or in part, and
+all derivative works of the Software, unless such copies or derivative
+works are solely in the form of machine-executable object code generated by
+a source language processor.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
+SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
+FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
+ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+DEALINGS IN THE SOFTWARE.
+*/
+
+
+#ifndef UTF8_FOR_CPP_UNCHECKED_H_2675DCD0_9480_4c0c_B92A_CC14C027B731
+#define UTF8_FOR_CPP_UNCHECKED_H_2675DCD0_9480_4c0c_B92A_CC14C027B731
+
+#include "core.h"
+
+namespace utf8
+{
+ namespace unchecked
+ {
+ template <typename octet_iterator>
+ octet_iterator append(uint32_t cp, octet_iterator result)
+ {
+ if (cp < 0x80) // one octet
+ *(result++) = static_cast<uint8_t>(cp);
+ else if (cp < 0x800) { // two octets
+ *(result++) = static_cast<uint8_t>((cp >> 6) | 0xc0);
+ *(result++) = static_cast<uint8_t>((cp & 0x3f) | 0x80);
+ }
+ else if (cp < 0x10000) { // three octets
+ *(result++) = static_cast<uint8_t>((cp >> 12) | 0xe0);
+ *(result++) = static_cast<uint8_t>((cp >> 6) & 0x3f | 0x80);
+ *(result++) = static_cast<uint8_t>((cp & 0x3f) | 0x80);
+ }
+ else { // four octets
+ *(result++) = static_cast<uint8_t>((cp >> 18) | 0xf0);
+ *(result++) = static_cast<uint8_t>((cp >> 12)& 0x3f | 0x80);
+ *(result++) = static_cast<uint8_t>((cp >> 6) & 0x3f | 0x80);
+ *(result++) = static_cast<uint8_t>((cp & 0x3f) | 0x80);
+ }
+ return result;
+ }
+
+ template <typename octet_iterator>
+ uint32_t next(octet_iterator& it)
+ {
+ uint32_t cp = internal::mask8(*it);
+ typename std::iterator_traits<octet_iterator>::difference_type length = utf8::internal::sequence_length(it);
+ switch (length) {
+ case 1:
+ break;
+ case 2:
+ it++;
+ cp = ((cp << 6) & 0x7ff) + ((*it) & 0x3f);
+ break;
+ case 3:
+ ++it;
+ cp = ((cp << 12) & 0xffff) + ((internal::mask8(*it) << 6) & 0xfff);
+ ++it;
+ cp += (*it) & 0x3f;
+ break;
+ case 4:
+ ++it;
+ cp = ((cp << 18) & 0x1fffff) + ((internal::mask8(*it) << 12) & 0x3ffff);
+ ++it;
+ cp += (internal::mask8(*it) << 6) & 0xfff;
+ ++it;
+ cp += (*it) & 0x3f;
+ break;
+ }
+ ++it;
+ return cp;
+ }
+
+ template <typename octet_iterator>
+ uint32_t peek_next(octet_iterator it)
+ {
+ return next(it);
+ }
+
+ template <typename octet_iterator>
+ uint32_t prior(octet_iterator& it)
+ {
+ while (internal::is_trail(*(--it))) ;
+ octet_iterator temp = it;
+ return next(temp);
+ }
+
+ // Deprecated in versions that include prior, but only for the sake of consistency (see utf8::previous)
+ template <typename octet_iterator>
+ inline uint32_t previous(octet_iterator& it)
+ {
+ return prior(it);
+ }
+
+ template <typename octet_iterator, typename distance_type>
+ void advance (octet_iterator& it, distance_type n)
+ {
+ for (distance_type i = 0; i < n; ++i)
+ next(it);
+ }
+
+ template <typename octet_iterator>
+ typename std::iterator_traits<octet_iterator>::difference_type
+ distance (octet_iterator first, octet_iterator last)
+ {
+ typename std::iterator_traits<octet_iterator>::difference_type dist;
+ for (dist = 0; first < last; ++dist)
+ next(first);
+ return dist;
+ }
+
+ template <typename u16bit_iterator, typename octet_iterator>
+ octet_iterator utf16to8 (u16bit_iterator start, u16bit_iterator end, octet_iterator result)
+ {
+ while (start != end) {
+ uint32_t cp = internal::mask16(*start++);
+ // Take care of surrogate pairs first
+ if (internal::is_surrogate(cp)) {
+ uint32_t trail_surrogate = internal::mask16(*start++);
+ cp = (cp << 10) + trail_surrogate + internal::SURROGATE_OFFSET;
+ }
+ result = append(cp, result);
+ }
+ return result;
+ }
+
+ template <typename u16bit_iterator, typename octet_iterator>
+ u16bit_iterator utf8to16 (octet_iterator start, octet_iterator end, u16bit_iterator result)
+ {
+ while (start != end) {
+ uint32_t cp = next(start);
+ if (cp > 0xffff) { //make a surrogate pair
+ *result++ = static_cast<uint16_t>((cp >> 10) + internal::LEAD_OFFSET);
+ *result++ = static_cast<uint16_t>((cp & 0x3ff) + internal::TRAIL_SURROGATE_MIN);
+ }
+ else
+ *result++ = static_cast<uint16_t>(cp);
+ }
+ return result;
+ }
+
+ template <typename octet_iterator, typename u32bit_iterator>
+ octet_iterator utf32to8 (u32bit_iterator start, u32bit_iterator end, octet_iterator result)
+ {
+ while (start != end)
+ result = append(*(start++), result);
+
+ return result;
+ }
+
+ template <typename octet_iterator, typename u32bit_iterator>
+ u32bit_iterator utf8to32 (octet_iterator start, octet_iterator end, u32bit_iterator result)
+ {
+ while (start < end)
+ (*result++) = next(start);
+
+ return result;
+ }
+
+ // The iterator class
+ template <typename octet_iterator>
+ class iterator : public std::iterator <std::bidirectional_iterator_tag, uint32_t> {
+ octet_iterator it;
+ public:
+ iterator () {};
+ explicit iterator (const octet_iterator& octet_it): it(octet_it) {}
+ // the default "big three" are OK
+ octet_iterator base () const { return it; }
+ uint32_t operator * () const
+ {
+ octet_iterator temp = it;
+ return next(temp);
+ }
+ bool operator == (const iterator& rhs) const
+ {
+ return (it == rhs.it);
+ }
+ bool operator != (const iterator& rhs) const
+ {
+ return !(operator == (rhs));
+ }
+ iterator& operator ++ ()
+ {
+ std::advance(it, internal::sequence_length(it));
+ return *this;
+ }
+ iterator operator ++ (int)
+ {
+ iterator temp = *this;
+ std::advance(it, internal::sequence_length(it));
+ return temp;
+ }
+ iterator& operator -- ()
+ {
+ prior(it);
+ return *this;
+ }
+ iterator operator -- (int)
+ {
+ iterator temp = *this;
+ prior(it);
+ return temp;
+ }
+ }; // class iterator
+
+ } // namespace utf8::unchecked
+} // namespace utf8
+
+
+#endif // header guard
+
+
diff --git a/externals/vld/delme b/externals/vld/delme
deleted file mode 100644
index e69de29bb2d..00000000000
--- a/externals/vld/delme
+++ /dev/null
diff --git a/externals/vld/vld.h b/externals/vld/vld.h
new file mode 100644
index 00000000000..72bebd8c4f6
--- /dev/null
+++ b/externals/vld/vld.h
@@ -0,0 +1,105 @@
+////////////////////////////////////////////////////////////////////////////////
+// $Id: vld.h,v 1.27 2006/11/12 18:09:20 dmouldin Exp $
+//
+// Visual Leak Detector (Version 1.9d) - Import Library Header
+// Copyright (c) 2006 Dan Moulding
+//
+// This library is free software; you can redistribute it and/or
+// modify it under the terms of the GNU Lesser General Public
+// License as published by the Free Software Foundation; either
+// version 2.1 of the License, or (at your option) any later version.
+//
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+// Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public
+// License along with this library; if not, write to the Free Software
+// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+//
+// See COPYING.txt for the full terms of the GNU Lesser General Public License.
+//
+////////////////////////////////////////////////////////////////////////////////
+
+//#pragma once
+#ifndef _VLD_H_
+#define _VLD_H_
+
+#ifdef _DEBUG
+
+#pragma comment(lib, "vld.lib")
+
+// Force a symbolic reference to the global VisualLeakDetector class object from
+// the DLL. This enusres that the DLL is loaded and linked with the program,
+// even if no code otherwise imports any of the DLL's exports.
+#pragma comment(linker, "/include:__imp_?vld@@3VVisualLeakDetector@@A")
+
+////////////////////////////////////////////////////////////////////////////////
+//
+// Visual Leak Detector APIs
+//
+
+#ifdef __cplusplus
+extern "C" {
+#endif // __cplusplus
+
+// VLDDisable - Disables Visual Leak Detector's memory leak detection at
+// runtime. If memory leak detection is already disabled, then calling this
+// function has no effect.
+//
+// Note: In multithreaded programs, this function operates on a per-thread
+// basis. In other words, if you call this function from one thread, then
+// memory leak detection is only disabled for that thread. If memory leak
+// detection is enabled for other threads, then it will remain enabled for
+// those other threads. It was designed to work this way to insulate you,
+// the programmer, from having to ensure thread synchronization when calling
+// VLDEnable() and VLDDisable(). Without this, calling these two functions
+// unsychronized could result in unpredictable and unintended behavior.
+// But this also means that if you want to disable memory leak detection
+// process-wide, then you need to call this function from every thread in
+// the process.
+//
+// Return Value:
+//
+// None.
+//
+
+__declspec(dllimport) void VLDDisable ();
+
+// VLDEnable - Enables Visual Leak Detector's memory leak detection at runtime.
+// If memory leak detection is already enabled, which it is by default, then
+// calling this function has no effect.
+//
+// Note: In multithreaded programs, this function operates on a per-thread
+// basis. In other words, if you call this function from one thread, then
+// memory leak detection is only enabled for that thread. If memory leak
+// detection is disabled for other threads, then it will remain disabled for
+// those other threads. It was designed to work this way to insulate you,
+// the programmer, from having to ensure thread synchronization when calling
+// VLDEnable() and VLDDisable(). Without this, calling these two functions
+// unsychronized could result in unpredictable and unintended behavior.
+// But this also means that if you want to enable memory leak detection
+// process-wide, then you need to call this function from every thread in
+// the process.
+//
+// Return Value:
+//
+// None.
+//
+
+__declspec(dllimport) void VLDEnable ();
+
+#ifdef __cplusplus
+}
+#endif // __cplusplus
+
+#else // !_DEBUG
+
+#define VLDEnable()
+#define VLDDisable()
+
+#endif // _DEBUG
+
+#endif // _VLD_H_
+
diff --git a/externals/zlib/win/VC90/zlib.vcproj b/externals/zlib/win/VC90/zlib.vcproj
index 8e89623915c..1b63254aa23 100644
--- a/externals/zlib/win/VC90/zlib.vcproj
+++ b/externals/zlib/win/VC90/zlib.vcproj
@@ -22,7 +22,7 @@
<Configuration
Name="Debug|Win32"
OutputDirectory="..\..\bin\$(PlatformName)_$(ConfigurationName)"
- IntermediateDirectory=".\bzip2__$(PlatformName)_$(ConfigurationName)"
+ IntermediateDirectory=".\zlib__$(PlatformName)_$(ConfigurationName)"
ConfigurationType="4"
CharacterSet="2"
>
@@ -90,7 +90,7 @@
<Configuration
Name="Debug|x64"
OutputDirectory="..\..\bin\$(PlatformName)_$(ConfigurationName)"
- IntermediateDirectory=".\bzip2__$(PlatformName)_$(ConfigurationName)"
+ IntermediateDirectory=".\zlib__$(PlatformName)_$(ConfigurationName)"
ConfigurationType="4"
CharacterSet="2"
>
@@ -159,7 +159,7 @@
<Configuration
Name="Release|Win32"
OutputDirectory="..\..\bin\$(PlatformName)_$(ConfigurationName)"
- IntermediateDirectory=".\bzip2__$(PlatformName)_$(ConfigurationName)"
+ IntermediateDirectory=".\zlib__$(PlatformName)_$(ConfigurationName)"
ConfigurationType="4"
CharacterSet="2"
>
@@ -225,7 +225,7 @@
<Configuration
Name="Release|x64"
OutputDirectory="..\..\bin\$(PlatformName)_$(ConfigurationName)"
- IntermediateDirectory=".\bzip2__$(PlatformName)_$(ConfigurationName)"
+ IntermediateDirectory=".\zlib__$(PlatformName)_$(ConfigurationName)"
ConfigurationType="4"
CharacterSet="2"
>