aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorclick <none@none>2010-08-27 01:52:05 +0200
committerclick <none@none>2010-08-27 01:52:05 +0200
commit7686d6ee57038e848f75130563fc78c1adebc1a2 (patch)
tree258a7a6db03e3479a0219ace8cfc3ec3ba53a1be
parent1d3deae555f0ec523d77875bd5a307ab9bd19742 (diff)
Core/Dependency: Upgrade G3d-library to v8.0-release (patched version!)
Note: Due to issues with G3D (normally requiring X11 and the ZIP-library), the sourcetree version contains a modified version. The applied patch is commited to the repository for future reference. --HG-- branch : trunk
-rw-r--r--dep/PackageList.txt6
-rw-r--r--dep/g3dlite/CMakeLists.txt46
-rw-r--r--dep/g3dlite/G3D-v8.0.diff440
-rw-r--r--dep/g3dlite/Readme.txt3
-rw-r--r--dep/g3dlite/g3dfnmatch.cpp204
-rw-r--r--dep/g3dlite/include/G3D/AABox.h (renamed from dep/g3dlite/G3D/AABox.h)0
-rw-r--r--dep/g3dlite/include/G3D/Any.h (renamed from dep/g3dlite/G3D/Any.h)105
-rw-r--r--dep/g3dlite/include/G3D/AnyVal.h (renamed from dep/g3dlite/G3D/AnyVal.h)0
-rw-r--r--dep/g3dlite/include/G3D/AreaMemoryManager.h (renamed from dep/g3dlite/G3D/AreaMemoryManager.h)0
-rw-r--r--dep/g3dlite/include/G3D/Array.h (renamed from dep/g3dlite/G3D/Array.h)0
-rw-r--r--dep/g3dlite/include/G3D/AtomicInt32.h (renamed from dep/g3dlite/G3D/AtomicInt32.h)0
-rw-r--r--dep/g3dlite/include/G3D/BinaryFormat.h (renamed from dep/g3dlite/G3D/BinaryFormat.h)0
-rw-r--r--dep/g3dlite/include/G3D/BinaryInput.h (renamed from dep/g3dlite/G3D/BinaryInput.h)9
-rw-r--r--dep/g3dlite/include/G3D/BinaryOutput.h (renamed from dep/g3dlite/G3D/BinaryOutput.h)0
-rw-r--r--dep/g3dlite/include/G3D/BoundsTrait.h (renamed from dep/g3dlite/G3D/BoundsTrait.h)0
-rw-r--r--dep/g3dlite/include/G3D/Box.h (renamed from dep/g3dlite/G3D/Box.h)0
-rw-r--r--dep/g3dlite/include/G3D/Box2D.h (renamed from dep/g3dlite/G3D/Box2D.h)0
-rw-r--r--dep/g3dlite/include/G3D/BumpMapPreprocess.h (renamed from dep/g3dlite/G3D/BumpMapPreprocess.h)0
-rw-r--r--dep/g3dlite/include/G3D/Capsule.h (renamed from dep/g3dlite/G3D/Capsule.h)0
-rw-r--r--dep/g3dlite/include/G3D/CollisionDetection.h (renamed from dep/g3dlite/G3D/CollisionDetection.h)0
-rw-r--r--dep/g3dlite/include/G3D/Color1.h (renamed from dep/g3dlite/G3D/Color1.h)0
-rw-r--r--dep/g3dlite/include/G3D/Color1uint8.h (renamed from dep/g3dlite/G3D/Color1uint8.h)0
-rw-r--r--dep/g3dlite/include/G3D/Color3.h (renamed from dep/g3dlite/G3D/Color3.h)0
-rw-r--r--dep/g3dlite/include/G3D/Color3uint8.h (renamed from dep/g3dlite/G3D/Color3uint8.h)37
-rw-r--r--dep/g3dlite/include/G3D/Color4.h (renamed from dep/g3dlite/G3D/Color4.h)11
-rw-r--r--dep/g3dlite/include/G3D/Color4uint8.h (renamed from dep/g3dlite/G3D/Color4uint8.h)14
-rw-r--r--dep/g3dlite/include/G3D/Cone.h (renamed from dep/g3dlite/G3D/Cone.h)0
-rw-r--r--dep/g3dlite/include/G3D/ConvexPolyhedron.h (renamed from dep/g3dlite/G3D/ConvexPolyhedron.h)0
-rw-r--r--dep/g3dlite/include/G3D/CoordinateFrame.h (renamed from dep/g3dlite/G3D/CoordinateFrame.h)4
-rw-r--r--dep/g3dlite/include/G3D/Crypto.h (renamed from dep/g3dlite/G3D/Crypto.h)0
-rw-r--r--dep/g3dlite/include/G3D/Cylinder.h (renamed from dep/g3dlite/G3D/Cylinder.h)0
-rw-r--r--dep/g3dlite/include/G3D/EqualsTrait.h (renamed from dep/g3dlite/G3D/EqualsTrait.h)0
-rw-r--r--dep/g3dlite/include/G3D/FileSystem.h466
-rw-r--r--dep/g3dlite/include/G3D/G3D.h (renamed from dep/g3dlite/G3D/G3D.h)5
-rw-r--r--dep/g3dlite/include/G3D/G3DAll.h (renamed from dep/g3dlite/G3D/G3DAll.h)0
-rw-r--r--dep/g3dlite/include/G3D/G3DGameUnits.h (renamed from dep/g3dlite/G3D/G3DGameUnits.h)0
-rw-r--r--dep/g3dlite/include/G3D/GCamera.h (renamed from dep/g3dlite/G3D/GCamera.h)19
-rw-r--r--dep/g3dlite/include/G3D/GImage.h (renamed from dep/g3dlite/G3D/GImage.h)8
-rw-r--r--dep/g3dlite/include/G3D/GLight.h (renamed from dep/g3dlite/G3D/GLight.h)7
-rw-r--r--dep/g3dlite/include/G3D/GMutex.h (renamed from dep/g3dlite/G3D/GMutex.h)0
-rw-r--r--dep/g3dlite/include/G3D/GThread.h (renamed from dep/g3dlite/G3D/GThread.h)0
-rw-r--r--dep/g3dlite/include/G3D/GUniqueID.h (renamed from dep/g3dlite/G3D/GUniqueID.h)0
-rw-r--r--dep/g3dlite/include/G3D/HashTrait.h (renamed from dep/g3dlite/G3D/HashTrait.h)2
-rw-r--r--dep/g3dlite/include/G3D/Image1.h (renamed from dep/g3dlite/G3D/Image1.h)0
-rw-r--r--dep/g3dlite/include/G3D/Image1uint8.h (renamed from dep/g3dlite/G3D/Image1uint8.h)0
-rw-r--r--dep/g3dlite/include/G3D/Image3.h (renamed from dep/g3dlite/G3D/Image3.h)0
-rw-r--r--dep/g3dlite/include/G3D/Image3uint8.h (renamed from dep/g3dlite/G3D/Image3uint8.h)0
-rw-r--r--dep/g3dlite/include/G3D/Image4.h (renamed from dep/g3dlite/G3D/Image4.h)0
-rw-r--r--dep/g3dlite/include/G3D/Image4uint8.h (renamed from dep/g3dlite/G3D/Image4uint8.h)0
-rw-r--r--dep/g3dlite/include/G3D/ImageFormat.h (renamed from dep/g3dlite/G3D/ImageFormat.h)28
-rw-r--r--dep/g3dlite/include/G3D/Intersect.h (renamed from dep/g3dlite/G3D/Intersect.h)0
-rw-r--r--dep/g3dlite/include/G3D/KDTree.h (renamed from dep/g3dlite/G3D/KDTree.h)0
-rw-r--r--dep/g3dlite/include/G3D/Line.h (renamed from dep/g3dlite/G3D/Line.h)0
-rw-r--r--dep/g3dlite/include/G3D/LineSegment.h (renamed from dep/g3dlite/G3D/LineSegment.h)0
-rw-r--r--dep/g3dlite/include/G3D/Log.h (renamed from dep/g3dlite/G3D/Log.h)0
-rw-r--r--dep/g3dlite/include/G3D/Map2D.h (renamed from dep/g3dlite/G3D/Map2D.h)0
-rw-r--r--dep/g3dlite/include/G3D/Matrix.h (renamed from dep/g3dlite/G3D/Matrix.h)0
-rw-r--r--dep/g3dlite/include/G3D/Matrix2.h (renamed from dep/g3dlite/G3D/Matrix2.h)33
-rw-r--r--dep/g3dlite/include/G3D/Matrix3.h (renamed from dep/g3dlite/G3D/Matrix3.h)0
-rw-r--r--dep/g3dlite/include/G3D/Matrix4.h (renamed from dep/g3dlite/G3D/Matrix4.h)9
-rw-r--r--dep/g3dlite/include/G3D/MemoryManager.h (renamed from dep/g3dlite/G3D/MemoryManager.h)0
-rw-r--r--dep/g3dlite/include/G3D/MeshAlg.h (renamed from dep/g3dlite/G3D/MeshAlg.h)2
-rw-r--r--dep/g3dlite/include/G3D/MeshBuilder.h (renamed from dep/g3dlite/G3D/MeshBuilder.h)0
-rw-r--r--dep/g3dlite/include/G3D/NetAddress.h (renamed from dep/g3dlite/G3D/NetAddress.h)30
-rw-r--r--dep/g3dlite/include/G3D/NetworkDevice.h (renamed from dep/g3dlite/G3D/NetworkDevice.h)5
-rw-r--r--dep/g3dlite/include/G3D/ParseError.h (renamed from dep/g3dlite/G3D/ParseError.h)0
-rw-r--r--dep/g3dlite/include/G3D/PhysicsFrame.h (renamed from dep/g3dlite/G3D/PhysicsFrame.h)43
-rw-r--r--dep/g3dlite/include/G3D/PhysicsFrameSpline.h37
-rw-r--r--dep/g3dlite/include/G3D/Plane.h (renamed from dep/g3dlite/G3D/Plane.h)0
-rw-r--r--dep/g3dlite/include/G3D/PointHashGrid.h (renamed from dep/g3dlite/G3D/PointHashGrid.h)48
-rw-r--r--dep/g3dlite/include/G3D/PointKDTree.h (renamed from dep/g3dlite/G3D/PointKDTree.h)0
-rw-r--r--dep/g3dlite/include/G3D/Pointer.h (renamed from dep/g3dlite/G3D/Pointer.h)0
-rw-r--r--dep/g3dlite/include/G3D/PositionTrait.h (renamed from dep/g3dlite/G3D/PositionTrait.h)0
-rw-r--r--dep/g3dlite/include/G3D/PrecomputedRandom.h (renamed from dep/g3dlite/G3D/PrecomputedRandom.h)0
-rw-r--r--dep/g3dlite/include/G3D/Quat.h (renamed from dep/g3dlite/G3D/Quat.h)166
-rw-r--r--dep/g3dlite/include/G3D/Quat.inl (renamed from dep/g3dlite/G3D/Quat.inl)0
-rw-r--r--dep/g3dlite/include/G3D/Queue.h (renamed from dep/g3dlite/G3D/Queue.h)0
-rw-r--r--dep/g3dlite/include/G3D/Random.h (renamed from dep/g3dlite/G3D/Random.h)0
-rw-r--r--dep/g3dlite/include/G3D/Ray.h (renamed from dep/g3dlite/G3D/Ray.h)2
-rw-r--r--dep/g3dlite/include/G3D/Rect2D.h (renamed from dep/g3dlite/G3D/Rect2D.h)0
-rw-r--r--dep/g3dlite/include/G3D/ReferenceCount.h (renamed from dep/g3dlite/G3D/ReferenceCount.h)0
-rw-r--r--dep/g3dlite/include/G3D/RegistryUtil.h (renamed from dep/g3dlite/G3D/RegistryUtil.h)0
-rw-r--r--dep/g3dlite/include/G3D/Set.h (renamed from dep/g3dlite/G3D/Set.h)7
-rw-r--r--dep/g3dlite/include/G3D/SmallArray.h (renamed from dep/g3dlite/G3D/SmallArray.h)14
-rw-r--r--dep/g3dlite/include/G3D/Sphere.h (renamed from dep/g3dlite/G3D/Sphere.h)0
-rw-r--r--dep/g3dlite/include/G3D/Spline.h (renamed from dep/g3dlite/G3D/Spline.h)15
-rw-r--r--dep/g3dlite/include/G3D/Stopwatch.h (renamed from dep/g3dlite/G3D/Stopwatch.h)0
-rw-r--r--dep/g3dlite/include/G3D/System.h (renamed from dep/g3dlite/G3D/System.h)60
-rw-r--r--dep/g3dlite/include/G3D/Table.h (renamed from dep/g3dlite/G3D/Table.h)2
-rw-r--r--dep/g3dlite/include/G3D/TextInput.h (renamed from dep/g3dlite/G3D/TextInput.h)61
-rw-r--r--dep/g3dlite/include/G3D/TextOutput.h (renamed from dep/g3dlite/G3D/TextOutput.h)0
-rw-r--r--dep/g3dlite/include/G3D/ThreadSet.h (renamed from dep/g3dlite/G3D/ThreadSet.h)0
-rw-r--r--dep/g3dlite/include/G3D/Triangle.h (renamed from dep/g3dlite/G3D/Triangle.h)0
-rw-r--r--dep/g3dlite/include/G3D/UprightFrame.h (renamed from dep/g3dlite/G3D/UprightFrame.h)0
-rw-r--r--dep/g3dlite/include/G3D/Vector2.h (renamed from dep/g3dlite/G3D/Vector2.h)0
-rw-r--r--dep/g3dlite/include/G3D/Vector2.inl (renamed from dep/g3dlite/G3D/Vector2.inl)0
-rw-r--r--dep/g3dlite/include/G3D/Vector2int16.h (renamed from dep/g3dlite/G3D/Vector2int16.h)0
-rw-r--r--dep/g3dlite/include/G3D/Vector3.h (renamed from dep/g3dlite/G3D/Vector3.h)4
-rw-r--r--dep/g3dlite/include/G3D/Vector3.inl (renamed from dep/g3dlite/G3D/Vector3.inl)0
-rw-r--r--dep/g3dlite/include/G3D/Vector3int16.h (renamed from dep/g3dlite/G3D/Vector3int16.h)2
-rw-r--r--dep/g3dlite/include/G3D/Vector3int32.h (renamed from dep/g3dlite/G3D/Vector3int32.h)0
-rw-r--r--dep/g3dlite/include/G3D/Vector4.h (renamed from dep/g3dlite/G3D/Vector4.h)0
-rw-r--r--dep/g3dlite/include/G3D/Vector4.inl (renamed from dep/g3dlite/G3D/Vector4.inl)0
-rw-r--r--dep/g3dlite/include/G3D/Vector4int8.h (renamed from dep/g3dlite/G3D/Vector4int8.h)0
-rw-r--r--dep/g3dlite/include/G3D/WeakCache.h (renamed from dep/g3dlite/G3D/WeakCache.h)0
-rw-r--r--dep/g3dlite/include/G3D/Welder.h (renamed from dep/g3dlite/G3D/Welder.h)0
-rw-r--r--dep/g3dlite/include/G3D/WrapMode.h (renamed from dep/g3dlite/G3D/WrapMode.h)26
-rw-r--r--dep/g3dlite/include/G3D/XML.h204
-rw-r--r--dep/g3dlite/include/G3D/constants.h (renamed from dep/g3dlite/G3D/constants.h)46
-rw-r--r--dep/g3dlite/include/G3D/debug.h (renamed from dep/g3dlite/G3D/debug.h)0
-rw-r--r--dep/g3dlite/include/G3D/debugAssert.h (renamed from dep/g3dlite/G3D/debugAssert.h)4
-rw-r--r--dep/g3dlite/include/G3D/debugPrintf.h (renamed from dep/g3dlite/G3D/debugPrintf.h)0
-rw-r--r--dep/g3dlite/include/G3D/enumclass.h (renamed from dep/g3dlite/G3D/enumclass.h)110
-rw-r--r--dep/g3dlite/include/G3D/fileutils.h (renamed from dep/g3dlite/G3D/fileutils.h)97
-rw-r--r--dep/g3dlite/include/G3D/filter.h (renamed from dep/g3dlite/G3D/filter.h)0
-rw-r--r--dep/g3dlite/include/G3D/format.h (renamed from dep/g3dlite/G3D/format.h)0
-rw-r--r--dep/g3dlite/include/G3D/g3dfnmatch.h (renamed from dep/g3dlite/G3D/g3dfnmatch.h)0
-rw-r--r--dep/g3dlite/include/G3D/g3dmath.h (renamed from dep/g3dlite/G3D/g3dmath.h)16
-rw-r--r--dep/g3dlite/include/G3D/g3dmath.inl (renamed from dep/g3dlite/G3D/g3dmath.inl)0
-rw-r--r--dep/g3dlite/include/G3D/netheaders.h24
-rw-r--r--dep/g3dlite/include/G3D/networkHelpers.h91
-rw-r--r--dep/g3dlite/include/G3D/platform.h (renamed from dep/g3dlite/G3D/platform.h)28
-rw-r--r--dep/g3dlite/include/G3D/prompt.h (renamed from dep/g3dlite/G3D/prompt.h)0
-rw-r--r--dep/g3dlite/include/G3D/serialize.h (renamed from dep/g3dlite/G3D/serialize.h)0
-rw-r--r--dep/g3dlite/include/G3D/splinefunc.h (renamed from dep/g3dlite/G3D/splinefunc.h)0
-rw-r--r--dep/g3dlite/include/G3D/stringutils.h (renamed from dep/g3dlite/G3D/stringutils.h)33
-rw-r--r--dep/g3dlite/include/G3D/uint128.h (renamed from dep/g3dlite/G3D/uint128.h)0
-rw-r--r--dep/g3dlite/include/G3D/units.h (renamed from dep/g3dlite/G3D/units.h)0
-rw-r--r--dep/g3dlite/include/G3D/vectorMath.h (renamed from dep/g3dlite/G3D/vectorMath.h)0
-rw-r--r--dep/g3dlite/source/AABox.cpp (renamed from dep/g3dlite/AABox.cpp)0
-rw-r--r--dep/g3dlite/source/Any.cpp (renamed from dep/g3dlite/Any.cpp)153
-rw-r--r--dep/g3dlite/source/AnyVal.cpp1379
-rw-r--r--dep/g3dlite/source/AreaMemoryManager.cpp87
-rw-r--r--dep/g3dlite/source/BinaryFormat.cpp (renamed from dep/g3dlite/BinaryFormat.cpp)0
-rw-r--r--dep/g3dlite/source/BinaryInput.cpp (renamed from dep/g3dlite/BinaryInput.cpp)85
-rw-r--r--dep/g3dlite/source/BinaryOutput.cpp (renamed from dep/g3dlite/BinaryOutput.cpp)26
-rw-r--r--dep/g3dlite/source/Box.cpp (renamed from dep/g3dlite/Box.cpp)0
-rw-r--r--dep/g3dlite/source/Box2D.cpp113
-rw-r--r--dep/g3dlite/source/BumpMapPreprocess.cpp43
-rw-r--r--dep/g3dlite/source/Capsule.cpp (renamed from dep/g3dlite/Capsule.cpp)0
-rw-r--r--dep/g3dlite/source/CollisionDetection.cpp (renamed from dep/g3dlite/CollisionDetection.cpp)0
-rw-r--r--dep/g3dlite/source/Color1.cpp58
-rw-r--r--dep/g3dlite/source/Color1uint8.cpp38
-rw-r--r--dep/g3dlite/source/Color3.cpp384
-rw-r--r--dep/g3dlite/source/Color3uint8.cpp45
-rw-r--r--dep/g3dlite/source/Color4.cpp192
-rw-r--r--dep/g3dlite/source/Color4uint8.cpp47
-rw-r--r--dep/g3dlite/source/Cone.cpp79
-rw-r--r--dep/g3dlite/source/ConvexPolyhedron.cpp457
-rw-r--r--dep/g3dlite/source/CoordinateFrame.cpp (renamed from dep/g3dlite/CoordinateFrame.cpp)55
-rw-r--r--dep/g3dlite/source/Crypto.cpp (renamed from dep/g3dlite/Crypto.cpp)0
-rw-r--r--dep/g3dlite/source/Crypto_md5.cpp471
-rw-r--r--dep/g3dlite/source/Cylinder.cpp (renamed from dep/g3dlite/Cylinder.cpp)0
-rw-r--r--dep/g3dlite/source/FileSystem.cpp859
-rw-r--r--dep/g3dlite/source/GCamera.cpp511
-rw-r--r--dep/g3dlite/source/GImage.cpp1166
-rw-r--r--dep/g3dlite/source/GImage_bayer.cpp298
-rw-r--r--dep/g3dlite/source/GImage_bmp.cpp717
-rw-r--r--dep/g3dlite/source/GImage_jpeg.cpp446
-rw-r--r--dep/g3dlite/source/GImage_png.cpp276
-rw-r--r--dep/g3dlite/source/GImage_ppm.cpp217
-rw-r--r--dep/g3dlite/source/GImage_tga.cpp236
-rw-r--r--dep/g3dlite/source/GLight.cpp275
-rw-r--r--dep/g3dlite/source/GThread.cpp229
-rw-r--r--dep/g3dlite/source/GUniqueID.cpp78
-rw-r--r--dep/g3dlite/source/Image1.cpp224
-rw-r--r--dep/g3dlite/source/Image1uint8.cpp212
-rw-r--r--dep/g3dlite/source/Image3.cpp225
-rw-r--r--dep/g3dlite/source/Image3uint8.cpp225
-rw-r--r--dep/g3dlite/source/Image4.cpp226
-rw-r--r--dep/g3dlite/source/Image4uint8.cpp222
-rw-r--r--dep/g3dlite/source/ImageFormat.cpp588
-rw-r--r--dep/g3dlite/source/ImageFormat_convert.cpp1307
-rw-r--r--dep/g3dlite/source/Intersect.cpp844
-rw-r--r--dep/g3dlite/source/Line.cpp (renamed from dep/g3dlite/Line.cpp)0
-rw-r--r--dep/g3dlite/source/LineSegment.cpp (renamed from dep/g3dlite/LineSegment.cpp)0
-rw-r--r--dep/g3dlite/source/Log.cpp (renamed from dep/g3dlite/Log.cpp)7
-rw-r--r--dep/g3dlite/source/Matrix.cpp1802
-rw-r--r--dep/g3dlite/source/Matrix3.cpp (renamed from dep/g3dlite/Matrix3.cpp)14
-rw-r--r--dep/g3dlite/source/Matrix4.cpp (renamed from dep/g3dlite/Matrix4.cpp)7
-rw-r--r--dep/g3dlite/source/MemoryManager.cpp (renamed from dep/g3dlite/MemoryManager.cpp)0
-rw-r--r--dep/g3dlite/source/MeshAlg.cpp637
-rw-r--r--dep/g3dlite/source/MeshAlgAdjacency.cpp745
-rw-r--r--dep/g3dlite/source/MeshAlgWeld.cpp213
-rw-r--r--dep/g3dlite/source/MeshBuilder.cpp113
-rw-r--r--dep/g3dlite/source/NetAddress.cpp164
-rw-r--r--dep/g3dlite/source/NetworkDevice.cpp1274
-rw-r--r--dep/g3dlite/source/PhysicsFrame.cpp110
-rw-r--r--dep/g3dlite/source/PhysicsFrameSpline.cpp80
-rw-r--r--dep/g3dlite/source/Plane.cpp (renamed from dep/g3dlite/Plane.cpp)0
-rw-r--r--dep/g3dlite/source/PrecomputedRandom.cpp125
-rw-r--r--dep/g3dlite/source/Quat.cpp (renamed from dep/g3dlite/Quat.cpp)22
-rw-r--r--dep/g3dlite/source/Random.cpp (renamed from dep/g3dlite/Random.cpp)0
-rw-r--r--dep/g3dlite/source/Ray.cpp (renamed from dep/g3dlite/Ray.cpp)0
-rw-r--r--dep/g3dlite/source/Rect2D.cpp41
-rw-r--r--dep/g3dlite/source/ReferenceCount.cpp (renamed from dep/g3dlite/ReferenceCount.cpp)0
-rw-r--r--dep/g3dlite/source/RegistryUtil.cpp (renamed from dep/g3dlite/RegistryUtil.cpp)0
-rw-r--r--dep/g3dlite/source/Sphere.cpp (renamed from dep/g3dlite/Sphere.cpp)0
-rw-r--r--dep/g3dlite/source/SplineBase.cpp162
-rw-r--r--dep/g3dlite/source/Stopwatch.cpp119
-rw-r--r--dep/g3dlite/source/System.cpp (renamed from dep/g3dlite/System.cpp)110
-rw-r--r--dep/g3dlite/source/TextInput.cpp (renamed from dep/g3dlite/TextInput.cpp)196
-rw-r--r--dep/g3dlite/source/TextOutput.cpp (renamed from dep/g3dlite/TextOutput.cpp)13
-rw-r--r--dep/g3dlite/source/ThreadSet.cpp166
-rw-r--r--dep/g3dlite/source/Triangle.cpp (renamed from dep/g3dlite/Triangle.cpp)0
-rw-r--r--dep/g3dlite/source/UprightFrame.cpp (renamed from dep/g3dlite/UprightFrame.cpp)0
-rw-r--r--dep/g3dlite/source/Vector2.cpp (renamed from dep/g3dlite/Vector2.cpp)0
-rw-r--r--dep/g3dlite/source/Vector2int16.cpp47
-rw-r--r--dep/g3dlite/source/Vector3.cpp (renamed from dep/g3dlite/Vector3.cpp)7
-rw-r--r--dep/g3dlite/source/Vector3int16.cpp49
-rw-r--r--dep/g3dlite/source/Vector3int32.cpp57
-rw-r--r--dep/g3dlite/source/Vector4.cpp (renamed from dep/g3dlite/Vector4.cpp)9
-rw-r--r--dep/g3dlite/source/Vector4int8.cpp58
-rw-r--r--dep/g3dlite/source/Welder.cpp425
-rw-r--r--dep/g3dlite/source/WinMain.cpp159
-rw-r--r--dep/g3dlite/source/XML.cpp216
-rw-r--r--dep/g3dlite/source/constants.cpp16
-rw-r--r--dep/g3dlite/source/debugAssert.cpp (renamed from dep/g3dlite/debugAssert.cpp)4
-rw-r--r--dep/g3dlite/source/fileutils.cpp (renamed from dep/g3dlite/fileutils.cpp)279
-rw-r--r--dep/g3dlite/source/filter.cpp32
-rw-r--r--dep/g3dlite/source/format.cpp (renamed from dep/g3dlite/format.cpp)0
-rw-r--r--dep/g3dlite/source/g3dfnmatch.cpp223
-rw-r--r--dep/g3dlite/source/g3dmath.cpp (renamed from dep/g3dlite/g3dmath.cpp)1
-rw-r--r--dep/g3dlite/source/license.cpp73
-rw-r--r--dep/g3dlite/source/prompt.cpp (renamed from dep/g3dlite/prompt.cpp)11
-rw-r--r--dep/g3dlite/source/stringutils.cpp (renamed from dep/g3dlite/stringutils.cpp)0
-rw-r--r--dep/g3dlite/source/uint128.cpp155
-rw-r--r--src/server/collision/CMakeLists.txt2
-rw-r--r--src/tools/vmap3_assembler/CMakeLists.txt2
229 files changed, 22598 insertions, 1073 deletions
diff --git a/dep/PackageList.txt b/dep/PackageList.txt
index 9bc9763de27..d59381d4cc5 100644
--- a/dep/PackageList.txt
+++ b/dep/PackageList.txt
@@ -10,7 +10,7 @@ bzip2 (a freely available, patent free, high-quality data compressor)
G3D (a commercial-grade C++ 3D engine available as Open Source (BSD License)
http://g3d.sourceforge.net/
- Version: 6.09
+ Version: 8.0-Release
jemalloc (a general-purpose scalable concurrent malloc-implementation)
http://www.canonware.com/jemalloc/
@@ -36,10 +36,6 @@ utf8-cpp (UTF-8 with C++ in a Portable Way)
http://utfcpp.sourceforge.net/
Version: 2.3
-vld (a free open-source memory leak detection system for Visual C++)
- http://sites.google.com/site/dmoulding/vld
- Version: 1.0
-
zlib (A Massively Spiffy Yet Delicately Unobtrusive Compression Library)
http://www.zlib.net/
Version: 1.2.5
diff --git a/dep/g3dlite/CMakeLists.txt b/dep/g3dlite/CMakeLists.txt
index 071fa4ed7ea..af59592e1c4 100644
--- a/dep/g3dlite/CMakeLists.txt
+++ b/dep/g3dlite/CMakeLists.txt
@@ -8,19 +8,57 @@
# WITHOUT ANY WARRANTY, to the extent permitted by law; without even the
# implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
-file(GLOB sources *.cpp)
set(g3dlib_STAT_SRCS
- ${sources}
+ source/AABox.cpp
+ source/Any.cpp
+ source/BinaryFormat.cpp
+ source/BinaryInput.cpp
+ source/BinaryOutput.cpp
+ source/Box.cpp
+ source/Capsule.cpp
+ source/CollisionDetection.cpp
+ source/CoordinateFrame.cpp
+ source/Crypto.cpp
+ source/Cylinder.cpp
+ source/debugAssert.cpp
+ source/fileutils.cpp
+ source/format.cpp
+ source/g3dfnmatch.cpp
+ source/g3dmath.cpp
+ source/Line.cpp
+ source/LineSegment.cpp
+ source/Log.cpp
+ source/Matrix3.cpp
+ source/Matrix4.cpp
+ source/MemoryManager.cpp
+ source/Plane.cpp
+ source/prompt.cpp
+ source/Quat.cpp
+ source/Random.cpp
+ source/Ray.cpp
+ source/ReferenceCount.cpp
+ source/RegistryUtil.cpp
+ source/Sphere.cpp
+ source/stringutils.cpp
+ source/System.cpp
+ source/TextInput.cpp
+ source/TextOutput.cpp
+ source/Triangle.cpp
+ source/UprightFrame.cpp
+ source/Vector2.cpp
+ source/Vector3.cpp
+ source/Vector4.cpp
)
+
if(WIN32)
include_directories(
- ${CMAKE_CURRENT_SOURCE_DIR}
+ ${CMAKE_CURRENT_SOURCE_DIR}/include
${CMAKE_SOURCE_DIR}/dep/zlib
)
else()
include_directories(
- ${CMAKE_CURRENT_SOURCE_DIR}
+ ${CMAKE_CURRENT_SOURCE_DIR}/include
)
endif()
diff --git a/dep/g3dlite/G3D-v8.0.diff b/dep/g3dlite/G3D-v8.0.diff
new file mode 100644
index 00000000000..3a6fbbc2782
--- /dev/null
+++ b/dep/g3dlite/G3D-v8.0.diff
@@ -0,0 +1,440 @@
+diff -urN g3d-beta4/include/G3D/debugAssert.h g3d-mangos/include/G3D/debugAssert.h
+--- g3d-beta4/include/G3D/debugAssert.h 2010-02-07 23:39:20.000000000 +0100
++++ g3d-mangos/include/G3D/debugAssert.h 2010-08-26 21:36:32.000000000 +0200
+@@ -39,10 +39,12 @@
+ #ifdef G3D_LINUX
+ // Needed so we can define a global display
+ // pointer for debugAssert.
++#if 0 /* G3DFIX: Disabled to avoid requirement for X11 libraries */
+ #include <X11/Xlib.h>
+ #include <X11/Xutil.h>
+ #include <X11/Xatom.h>
+ #endif
++#endif
+
+
+ /**
+@@ -178,6 +180,7 @@
+ namespace G3D { namespace _internal {
+
+ #ifdef G3D_LINUX
++#if 0 /* G3DFIX: Disabled to avoid requirement for X11 libraries */
+ /**
+ A pointer to the X11 display. Initially NULL. If set to a
+ non-null value (e.g. by SDLWindow), debugAssert attempts to use
+@@ -194,6 +197,7 @@
+ */
+ extern Window x11Window;
+ #endif
++#endif
+
+ /**
+ Pops up an assertion dialog or prints an assertion
+diff -urN g3d-beta4/include/G3D/g3dmath.h g3d-mangos/include/G3D/g3dmath.h
+--- g3d-beta4/include/G3D/g3dmath.h 2010-02-07 23:39:20.000000000 +0100
++++ g3d-mangos/include/G3D/g3dmath.h 2010-08-26 21:36:32.000000000 +0200
+@@ -65,6 +65,8 @@
+ return ::rand() / double(RAND_MAX);
+ }
+
++#if !defined(_WIN64)
++
+ /**
+ Win32 implementation of the C99 fast rounding routines.
+
+@@ -99,6 +101,19 @@
+
+ return intgr;
+ }
++
++#else
++
++ __inline long int lrint (double flt) {
++ return (long int)floor(flt+0.5f);
++ }
++
++ __inline long int lrintf(float flt) {
++ return (long int)floorf(flt+0.5f);
++ }
++
++#endif
++
+ #endif
+
+
+diff -urN g3d-beta4/include/G3D/platform.h g3d-mangos/include/G3D/platform.h
+--- g3d-beta4/include/G3D/platform.h 2010-02-07 23:39:20.000000000 +0100
++++ g3d-mangos/include/G3D/platform.h 2010-08-26 21:36:32.000000000 +0200
+@@ -56,12 +57,15 @@
+ // pi as a constant, which creates a conflict with G3D
+ #define __FP__
+ #else
+- #error Unknown platform
++ #error Unknown platform
+ #endif
+
+ // Detect 64-bit under various compilers
+ #if (defined(_M_X64) || defined(_WIN64) || defined(__LP64__) || defined(_LP64))
+ # define G3D_64BIT
++ #if defined(WIN32)
++ #include <intrin.h>
++ #endif
+ #else
+ # define G3D_32BIT
+ #endif
+@@ -126,13 +130,11 @@
+ // TODO: remove
+ # pragma warning (disable : 4244)
+
+-# define ZLIB_WINAPI
+-
+ # define restrict
+
+ /** @def G3D_CHECK_PRINTF_METHOD_ARGS()
+ Enables printf parameter validation on gcc. */
+-# define G3D_CHECK_PRINTF_ARGS
++# define G3D_CHECK_PRINTF_ARGS
+
+ /** @def G3D_CHECK_PRINTF_METHOD_ARGS()
+ Enables printf parameter validation on gcc. */
+diff -urN g3d-beta4/include/G3D/System.h g3d-mangos/include/G3D/System.h
+--- g3d-beta4/include/G3D/System.h 2010-02-07 23:39:20.000000000 +0100
++++ g3d-mangos/include/G3D/System.h 2010-08-26 21:36:32.000000000 +0200
+@@ -375,10 +375,10 @@
+ // count now contains the cycle count for the intervening operation.
+ </PRE>
+ */
+- static void beginCycleCount(uint64& cycleCount);
++ /* static void beginCycleCount(uint64& cycleCount);
+ static void endCycleCount(uint64& cycleCount);
+
+- static uint64 getCycleCount();
++ static uint64 getCycleCount(); */
+
+ inline static void setOutOfMemoryCallback(OutOfMemoryCallback c) {
+ instance().m_outOfMemoryCallback = c;
+@@ -438,7 +438,7 @@
+
+ };
+
+-
++/* don't need that for MaNGOS, not portable to Win64...
+ #ifdef _MSC_VER
+ inline uint64 System::getCycleCount() {
+ uint32 timehi, timelo;
+@@ -493,14 +493,14 @@
+ cycleCount = getCycleCount() - cycleCount;
+ #else
+ AbsoluteTime end = UpTime();
+- Nanoseconds diffNS =
++ Nanoseconds diffNS =
+ AbsoluteDeltaToNanoseconds(end, UInt64ToUnsignedWide(cycleCount));
+- cycleCount =
+- (uint64) ((double) (instance().m_OSXCPUSpeed) *
++ cycleCount =
++ (uint64) ((double) (instance().m_OSXCPUSpeed) *
+ (double) UnsignedWideToUInt64(diffNS) * instance().m_secondsPerNS);
+ #endif
+ }
+-
++ */
+
+ } // namespace
+
+diff -urN g3d-beta4/source/debugAssert.cpp g3d-mangos/source/debugAssert.cpp
+--- g3d-beta4/source/debugAssert.cpp 2010-02-07 23:39:20.000000000 +0100
++++ g3d-mangos/source/debugAssert.cpp 2010-08-15 11:37:26.000000000 +0200
+@@ -37,9 +37,11 @@
+ AssertionHook _failureHook = _handleErrorCheck_;
+
+ #ifdef G3D_LINUX
++#if 0 /* G3DFIX: Disabled to avoid requirement for X11 libraries */
+ Display* x11Display = NULL;
+ Window x11Window = 0;
+ #endif
++#endif
+
+
+ #ifdef G3D_WIN32
+@@ -250,6 +252,7 @@
+ ClipCursor(NULL);
+
+ #elif defined(G3D_LINUX)
++#if 0 /* G3DFIX: Disabled to avoid requirement for X11 libraries */
+ if (x11Display != NULL) {
+ XUngrabPointer(x11Display, CurrentTime);
+ XUngrabKeyboard(x11Display, CurrentTime);
+@@ -264,6 +267,7 @@
+ XAllowEvents(x11Display, AsyncPointer, CurrentTime);
+ XFlush(x11Display);
+ }
++#endif
+ #elif defined(G3D_OSX)
+ // TODO: OS X
+ #endif
+diff -urN g3d-beta4/source/fileutils.cpp g3d-mangos/source/fileutils.cpp
+--- g3d-beta4/source/fileutils.cpp 2010-02-07 23:39:20.000000000 +0100
++++ g3d-mangos/source/fileutils.cpp 2010-08-15 11:37:26.000000000 +0200
+@@ -20,7 +20,9 @@
+
+ #include <sys/stat.h>
+ #include <sys/types.h>
+-#include "zip.h"
++#if _HAVE_ZIP /* G3DFIX: Use ZIP-library only if defined */
++ #include "zip.h"
++#endif
+
+ #ifdef G3D_WIN32
+ // Needed for _getcwd
+@@ -144,7 +146,7 @@
+ void*& data,
+ size_t& length) {
+ std::string zip, desiredFile;
+-
++#if _HAVE_ZIP /* G3DFIX: Use ZIP-library only if defined */
+ if (zipfileExists(file, zip, desiredFile)) {
+ struct zip *z = zip_open( zip.c_str(), ZIP_CHECKCONS, NULL );
+ {
+@@ -167,6 +169,9 @@
+ } else {
+ data = NULL;
+ }
++#else
++ data = NULL;
++#endif
+ }
+
+
+@@ -180,6 +185,7 @@
+ int result = _stat(filename.c_str(), &st);
+
+ if (result == -1) {
++#if _HAVE_ZIP /* G3DFIX: Use ZIP-library only if defined */
+ std::string zip, contents;
+ if(zipfileExists(filename, zip, contents)){
+ int64 requiredMem;
+@@ -198,6 +204,9 @@
+ } else {
+ return -1;
+ }
++#else
++ return -1;
++#endif
+ }
+
+ return st.st_size;
+@@ -518,6 +527,7 @@
+
+ ///////////////////////////////////////////////////////////////////////////////
+
++#if _HAVE_ZIP /* G3DFIX: Use ZIP-library only if defined */
+ /* 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){
+@@ -551,12 +561,12 @@
+ }
+ return true;
+ }
+-
++#endif
+
+ // If no zipfile exists, outZipfile and outInternalFile are unchanged
+ bool zipfileExists(const std::string& filename, std::string& outZipfile,
+ std::string& outInternalFile){
+-
++#if _HAVE_ZIP /* G3DFIX: Use ZIP-library only if defined */
+ Array<std::string> path;
+ std::string drive, base, ext, zipfile, infile;
+ parseFilename(filename, drive, path, base, ext);
+@@ -618,7 +628,7 @@
+ }
+
+ }
+-
++#endif
+ // not a valid directory structure ever,
+ // obviously no .zip was found within the path
+ return false;
+@@ -900,7 +910,7 @@
+ # endif
+ }
+
+-
++#if _HAVE_ZIP /* G3DFIX: Use ZIP-library only if defined */
+ /**
+ @param path The zipfile name (no trailing slash)
+ @param prefix Directory inside the zipfile. No leading slash, must have trailing slash if non-empty.
+@@ -951,13 +961,14 @@
+ }
+ }
+ }
+-
++#endif
+
+ static void getFileOrDirListZip(const std::string& path,
+ const std::string& prefix,
+ Array<std::string>& files,
+ bool wantFiles,
+ bool includePath){
++#if _HAVE_ZIP /* G3DFIX: Use ZIP-library only if defined */
+ struct zip *z = zip_open( path.c_str(), ZIP_CHECKCONS, NULL );
+
+ Set<std::string> fileSet;
+@@ -973,6 +984,7 @@
+ zip_close( z );
+
+ fileSet.getMembers(files);
++#endif
+ }
+
+
+diff -urN g3d-beta4/source/prompt.cpp g3d-mangos/source/prompt.cpp
+--- g3d-beta4/source/prompt.cpp 2010-02-07 23:39:20.000000000 +0100
++++ g3d-mangos/source/prompt.cpp 2010-08-15 11:37:26.000000000 +0200
+@@ -21,6 +21,7 @@
+ # define _getch getchar
+ #endif
+
++#if 0 /* G3DFIX: exclude GUI prompt code */
+ #ifdef G3D_OSX
+
+ /*#ifdef __LP64__
+@@ -37,9 +38,11 @@
+ */
+
+ #endif
++#endif /* G3DFIX: exclude GUI prompt code */
+
+ namespace G3D {
+
++#if 0 /* G3DFIX: exclude GUI prompt code */
+ #ifdef G3D_WIN32
+
+ namespace _internal {
+@@ -469,6 +472,7 @@
+ }
+
+ #endif
++#endif /* G3DFIX: exclude GUI prompt code */
+
+
+ /**
+@@ -531,6 +535,8 @@
+ return c;
+ }
+
++#if 0 /* G3DFIX: exclude GUI prompt code */
++
+ #ifdef G3D_OSX
+
+ // See http://developer.apple.com/documentation/Carbon/Reference/Carbon_Event_Manager_Ref/index.html
+@@ -689,13 +695,15 @@
+
+ #endif
+
++#endif /* G3DFIX: exclude GUI prompt code */
++
+ int prompt(
+ const char* windowTitle,
+ const char* prompt,
+ const char** choice,
+ int numChoices,
+ bool useGui) {
+-
++#if 0 /* G3DFIX: exclude GUI prompt code */
+ #ifdef G3D_WIN32
+ if (useGui) {
+ // Build the message box
+@@ -709,6 +717,7 @@
+ return guiPrompt(windowTitle, prompt, choice, numChoices);
+ }
+ #endif
++#endif /* G3DFIX: exclude GUI prompt code */
+ return textPrompt(windowTitle, prompt, choice, numChoices);
+ }
+
+diff -urN g3d-beta4/source/RegistryUtil.cpp g3d-mangos/source/RegistryUtil.cpp
+--- g3d-beta4/source/RegistryUtil.cpp 2010-02-07 23:39:20.000000000 +0100
++++ g3d-mangos/source/RegistryUtil.cpp 2010-08-15 11:37:26.000000000 +0200
+@@ -257,7 +257,7 @@
+
+
+ // static helpers
+-static HKEY getRootKeyFromString(const char* str, uint32 length) {
++static HKEY getRootKeyFromString(const char* str, size_t length) {
+ debugAssert(str);
+
+ if (str) {
+diff -urN g3d-beta4/source/System.cpp g3d-mangos/source/System.cpp
+--- g3d-beta4/source/System.cpp 2010-02-07 23:39:20.000000000 +0100
++++ g3d-mangos/source/System.cpp 2010-08-15 11:37:26.000000000 +0200
+@@ -80,8 +80,9 @@
+ #endif
+
+ // SIMM include
++#ifdef __SSE__
+ #include <xmmintrin.h>
+-
++#endif
+
+ namespace G3D {
+
+@@ -559,7 +560,7 @@
+ #endif
+ }
+
+-#if defined(G3D_WIN32)
++#if defined(G3D_WIN32) && !defined(G3D_64BIT) /* G3DFIX: Don't check if on 64-bit Windows platforms */
+ #pragma message("Port System::memcpy SIMD to all platforms")
+ /** Michael Herf's fast memcpy */
+ void memcpyMMX(void* dst, const void* src, int nbytes) {
+@@ -610,7 +611,7 @@
+ #endif
+
+ void System::memcpy(void* dst, const void* src, size_t numBytes) {
+-#if defined(G3D_WIN32)
++#if defined(G3D_WIN32) && !defined(G3D_64BIT) /* G3DFIX: Don't check if on 64-bit Windows platforms */
+ memcpyMMX(dst, src, numBytes);
+ #else
+ ::memcpy(dst, src, numBytes);
+@@ -620,7 +621,7 @@
+
+ /** Michael Herf's fastest memset. n32 must be filled with the same
+ character repeated. */
+-#if defined(G3D_WIN32)
++#if defined(G3D_WIN32) && !defined(G3D_64BIT) /* G3DFIX: Don't check if on 64-bit Windows platforms */
+ #pragma message("Port System::memfill SIMD to all platforms")
+
+ // On x86 processors, use MMX
+@@ -659,7 +660,7 @@
+
+
+ void System::memset(void* dst, uint8 value, size_t numBytes) {
+-#if defined(G3D_WIN32)
++#if defined(G3D_WIN32) && !defined(G3D_64BIT) /* G3DFIX: Don't check if on 64-bit Windows platforms */
+ uint32 v = value;
+ v = v + (v << 8) + (v << 16) + (v << 24);
+ G3D::memfill(dst, v, numBytes);
+@@ -1676,6 +1677,7 @@
+
+ // VC on Intel
+ void System::cpuid(CPUIDFunction func, uint32& areg, uint32& breg, uint32& creg, uint32& dreg) {
++#if !defined(G3D_64BIT) /* G3DFIX: Don't check if on 64-bit platform */
+ // Can't copy from assembler direct to a function argument (which is on the stack) in VC.
+ uint32 a,b,c,d;
+
+@@ -1693,6 +1695,14 @@
+ breg = b;
+ creg = c;
+ dreg = d;
++#else
++ int CPUInfo[4];
++ __cpuid(CPUInfo, func);
++ memcpy(&areg, &CPUInfo[0], 4);
++ memcpy(&breg, &CPUInfo[1], 4);
++ memcpy(&creg, &CPUInfo[2], 4);
++ memcpy(&dreg, &CPUInfo[3], 4);
++#endif
+ }
+
+ #elif defined(G3D_OSX) && ! defined(G3D_OSX_INTEL)
diff --git a/dep/g3dlite/Readme.txt b/dep/g3dlite/Readme.txt
new file mode 100644
index 00000000000..b91c20dce38
--- /dev/null
+++ b/dep/g3dlite/Readme.txt
@@ -0,0 +1,3 @@
+ Due to issues with G3D (normally requiring X11 and the ZIP-library), the
+sourcetree version contains a modified version. The applied patch is
+commited to the repository for future reference.
diff --git a/dep/g3dlite/g3dfnmatch.cpp b/dep/g3dlite/g3dfnmatch.cpp
deleted file mode 100644
index 39ef7b31914..00000000000
--- a/dep/g3dlite/g3dfnmatch.cpp
+++ /dev/null
@@ -1,204 +0,0 @@
-/*-
-* Copyright (c) 1992, 1993
-*The Regents of the University of California. All rights reserved.
-*
-* Redistribution and use in source and binary forms, with or without
-* modification, are permitted provided that the following conditions
-* are met:
-* 1. Redistributions of source code must retain the above copyright
-* notice, this list of conditions and the following disclaimer.
-* 2. Redistributions in binary form must reproduce the above copyright
-* notice, this list of conditions and the following disclaimer in the
-* documentation and/or other materials provided with the distribution.
-* 3. All advertising materials mentioning features or use of this software
-* must display the following acknowledgement:
-*This product includes software developed by the University of
-*California, Berkeley and its contributors.
-* 4. Neither the name of the University nor the names of its contributors
-* may be used to endorse or promote products derived from this software
-* without specific prior written permission.
-*
-* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
-* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
-* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
-* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
-* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
-* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
-* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
-* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
-* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
-* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
-* SUCH DAMAGE.
-*
-*@(#)fnmatch.h8.1 (Berkeley) 6/2/93
-*
-* From FreeBSD fnmatch.h 1.7
-* $Id: g3dfnmatch.cpp,v 1.2 2010/02/06 10:03:24 corey_taylor Exp $
-*/
-#include "G3D/g3dfnmatch.h"
-
-#ifdef G3D_WIN32
-
-#include <ctype.h>
-#include <string.h>
-#include <stdio.h>
-
-namespace G3D {
-
-#define EOS '\0'
-
-static const char *rangematch(const char *, char, int);
-
-int g3dfnmatch(const char *pattern, const char *string, int flags)
-{
- const char *stringstart;
- char c, test;
-
- for (stringstart = string;;)
- switch (c = *pattern++) {
- case EOS:
- if ((flags & FNM_LEADING_DIR) && *string == '/')
- return (0);
- return (*string == EOS ? 0 : FNM_NOMATCH);
- case '?':
- if (*string == EOS)
- return (FNM_NOMATCH);
- if (*string == '/' && (flags & FNM_PATHNAME))
- return (FNM_NOMATCH);
- if (*string == '.' && (flags & FNM_PERIOD) &&
- (string == stringstart ||
- ((flags & FNM_PATHNAME) && *(string - 1) == '/')))
- return (FNM_NOMATCH);
- ++string;
- break;
- case '*':
- c = *pattern;
- /* Collapse multiple stars. */
- while (c == '*')
- c = *++pattern;
-
- if (*string == '.' && (flags & FNM_PERIOD) &&
- (string == stringstart ||
- ((flags & FNM_PATHNAME) && *(string - 1) == '/')))
- return (FNM_NOMATCH);
-
- /* Optimize for pattern with * at end or before /. */
- if (c == EOS)
- if (flags & FNM_PATHNAME)
- return ((flags & FNM_LEADING_DIR) ||
- strchr(string, '/') == NULL ?
- 0 : FNM_NOMATCH);
- else
- return (0);
- else if (c == '/' && flags & FNM_PATHNAME) {
- if ((string = strchr(string, '/')) == NULL)
- return (FNM_NOMATCH);
- break;
- }
-
- /* General case, use recursion. */
- while ((test = *string) != EOS) {
- if (!rangematch(pattern, *string, flags & ~FNM_PERIOD))
- return (0);
- if (test == '/' && flags & FNM_PATHNAME)
- break;
- ++string;
- }
- return (FNM_NOMATCH);
- case '[':
- if (*string == EOS)
- return (FNM_NOMATCH);
- if (*string == '/' && flags & FNM_PATHNAME)
- return (FNM_NOMATCH);
- if ((pattern =
- rangematch(pattern, *string, flags)) == NULL)
- return (FNM_NOMATCH);
- ++string;
- break;
- case '\\':
- if (!(flags & FNM_NOESCAPE)) {
- if ((c = *pattern++) == EOS) {
- c = '\\';
- --pattern;
- }
- }
- /* FALLTHROUGH */
- default:
- if (c == *string)
- ;
- else if ((flags & FNM_CASEFOLD) &&
- (tolower((unsigned char)c) ==
- tolower((unsigned char)*string)))
- ;
- else if ((flags & FNM_PREFIX_DIRS) && *string == EOS &&
- ((c == '/' && string != stringstart) ||
- (string == stringstart+1 && *stringstart == '/')))
- return (0);
- else
- return (FNM_NOMATCH);
- string++;
- break;
- }
- /* NOTREACHED */
-}
-
-static const char *
-rangematch(const char *pattern, char test, int flags)
-{
- int negate, ok;
- char c, c2;
-
- /*
- * A bracket expression starting with an unquoted circumflex
- * character produces unspecified results (IEEE 1003.2-1992,
- * 3.13.2). This implementation treats it like '!', for
- * consistency with the regular expression syntax.
- * J.T. Conklin (conklin@ngai.kaleida.com)
- */
- if ( (negate = (*pattern == '!' || *pattern == '^')) )
- ++pattern;
-
- if (flags & FNM_CASEFOLD)
- test = tolower((unsigned char)test);
-
- for (ok = 0; (c = *pattern++) != ']';) {
- if (c == '\\' && !(flags & FNM_NOESCAPE))
- c = *pattern++;
- if (c == EOS)
- return (NULL);
-
- if (flags & FNM_CASEFOLD)
- c = tolower((unsigned char)c);
-
- if (*pattern == '-'
- && (c2 = *(pattern+1)) != EOS && c2 != ']') {
- pattern += 2;
- if (c2 == '\\' && !(flags & FNM_NOESCAPE))
- c2 = *pattern++;
- if (c2 == EOS)
- return (NULL);
-
- if (flags & FNM_CASEFOLD)
- c2 = tolower((unsigned char)c2);
-
- if ((unsigned char)c <= (unsigned char)test &&
- (unsigned char)test <= (unsigned char)c2)
- ok = 1;
- } else if (c == test)
- ok = 1;
- }
- return (ok == negate ? NULL : pattern);
-}
-
-}
-
-#else
-
-namespace G3D {
-int g3dfnmatch(const char * a, const char *b, int c) {
- return fnmatch(a, b, c);
-}
-}
-
-#endif
-
diff --git a/dep/g3dlite/G3D/AABox.h b/dep/g3dlite/include/G3D/AABox.h
index 2e8da1f6098..2e8da1f6098 100644
--- a/dep/g3dlite/G3D/AABox.h
+++ b/dep/g3dlite/include/G3D/AABox.h
diff --git a/dep/g3dlite/G3D/Any.h b/dep/g3dlite/include/G3D/Any.h
index 49701202ca9..c267eedd22f 100644
--- a/dep/g3dlite/G3D/Any.h
+++ b/dep/g3dlite/include/G3D/Any.h
@@ -5,7 +5,7 @@
@maintainer Morgan McGuire
@created 2006-06-11
- @edited 2009-12-16
+ @edited 2010-03-16
Copyright 2000-2010, Morgan McGuire.
All rights reserved.
@@ -34,11 +34,14 @@ class TextOutput;
/**
\brief Easy loading and saving of human-readable configuration files.
-Encodes typed, structured data and can serialize it to a human
+Any encodes typed, structured data and can serialize it to a human
readable format that is very similar to the Python language's data
-syntax. Well-suited for quickly creating human-readable file formats,
-especially since deserialization and serialization preserve comments and
-an Any can tell you what file and line it came from.
+syntax. It is well-suited for quickly creating human-readable file
+formats, especially since deserialization and serialization preserve
+comments and an Any can tell you what file and line it came from. The
+syntax allows most C++ editors to properly highlight Any files, and
+makes it easy to design little ad-hoc C-like languages in
+configuration files.
The class is designed so that copying Anys generally is fast, even if
it is a large array or table. This is because data is shared between
@@ -50,15 +53,17 @@ Sample File:
{
shape = "round",
- # in meters
+ // in meters
radius = 3.7,
position = Vector3(1.0, -1.0, 0.0),
- texture = { format = "RGB8", size = (320, 200)}
+ video = { format = "RGB8", size = (320, 200)},
+
+ material = #include("rocks.mat")
}
</pre>
-Sample code using:
+Sample code using Any:
<pre>
Any x;
x.load("ball.txt");
@@ -110,6 +115,21 @@ Vector3::Vector3(const Any& any) {
}
</pre>
+It is often convenient to iterate through the table portion:
+
+<pre>
+ for (Any::AnyTable::Iterator it = any.table().begin(); it.hasMore(); ++it) {
+ const std::string& k = toLower(it->key);
+ if (key == "hello") {
+ ...
+ } else if (key == "goodbye") {
+ ...
+ } else {
+ any.verify(false, "Unsupported key: " + it->key);
+ }
+ }
+</pre>
+
\section BNF
Serialized format BNF:
@@ -119,27 +139,38 @@ identifier-op ::= "::" | "->" | "."
identifier-exp ::= [identifier-op] identifier (identifier-op identifier)*
-comment ::= "#" <any characters> "\n"
+comment ::= C++ single or multi-line comments
separator ::= "," | ";"
number ::= <legal C printf number format>
string ::= <legal C double-quoted string; backslashes must be escaped>
boolean ::= "True" | "False"
none ::= "None"
-array ::= "(" [value ("," value)*] ")"
+array ::= ("(" | "[") [ value (separator value)* [separator] ] (")" | "]")
pair ::= (identifier | string) "=" value
-table ::= "{" [pair (separator pair)*] "}"
-named-array ::= identifier-exp tuple
-named-table ::= identifier-exp dict
+table ::= "{" [ pair (separator pair)* [separator] ] "}"
+named-array ::= identifier-exp array
+named-table ::= identifier-exp table
+include ::= "#" "include" "(" string ")"
-value ::= [comment] (none | number | boolean | string | array | table | named-array | named-table)
+value ::= [comment] (none | number | boolean | string | array | table | named-array | named-table | include)
</pre>
Except for single-line comments, whitespace is not significant.
All parsing is case-insensitive.
+The include expression pastes the contents of the named file in as if
+they appeared in the original source. Note that an include expression
+can only appear in the locations where a value is expected. This means
+that it cannot yield more than one element of an array and cannot serve
+as the pair in a table.
+
The deserializer allows the substitution of [] for () when writing
-tuples and ";" for ",".
+tuples and ";" for ",". These are convenient when mimicing a
+programming language, e.g., <code>"[ printf("hello world."); clearScreen();]"</code>
+parses as an array containing two named arrays within it. The
+deserializer also allows a trailing comma inside any array or table,
+which also convenient when commenting out the last element.
The serializer indents four spaces for each level of nesting.
Tables are written with the keys in alphabetic order.
@@ -407,9 +438,32 @@ public:
const std::string& string() const;
bool boolean() const;
+ /** If a valid string, takes the string value and creates a fully qualified filename.
+ If not found, the returned string is empty.
+
+ The file is searched for the following ways:
+
+ - In the directory from which the Any was loaded.
+ - By calling System::findDataFile as you would with other data files.
+ */
+ std::string resolveStringAsFilename() const;
+
+
/** If this is named ARRAY or TABLE, returns the name. */
const std::string& name() const;
+ /** If this is named ARRAY or TABLE, returns true if the name begins with \a s. The comparision is case insensitive. */
+ bool nameBeginsWith(const std::string& s) const;
+
+ /** If this is named ARRAY or TABLE, returns true if the name begins with \a s. The comparision is case insensitive. */
+ bool nameBeginsWith(const char* s) const;
+
+ /** If this is named ARRAY or TABLE, returns true if the name is \a s. The comparision is case insensitive. */
+ bool nameEquals(const std::string& s) const;
+
+ /** If this is named ARRAY or TABLE, returns true if the name is\a s. The comparision is case insensitive. */
+ bool nameEquals(const char* s) const;
+
/** \brief Set the name used when serializing an ARRAY or TABLE.
Only legal for ARRAY or TABLE. The \a name must begin with a letter
@@ -439,6 +493,14 @@ public:
const Any& operator[](int i) const;
Any& operator[](int i);
+ const Any& last() const {
+ return (*this)[size() - 1];
+ }
+
+ Any& last() {
+ return (*this)[size() - 1];
+ }
+
/** Directly exposes the underlying data structure for an ARRAY. */
const Array<Any>& array() const;
void append(const Any& v0);
@@ -503,6 +565,10 @@ public:
/** for an ARRAY, resizes and returns the last element */
Any& next();
+ /** The parent directory of the location from which this Any was loaded. This is useful for
+ interpreting filenames relative to the Any's source location,
+ which may not match the current directory if the Any was from an included file. */
+ std::string sourceDirectory() const;
/** True if the Anys are exactly equal, ignoring comments. Applies deeply on arrays and tables. */
bool operator==(const Any& x) const;
@@ -542,10 +608,15 @@ public:
*/
void verify(bool value, const std::string& message = "") const;
- /** Verifies that the name begins with identifier \a n. It may contain
- identifier operators after this */
+
+ /** Verifies that the name <i>begins with</i> identifier \a n (case insensitive).
+ It may contain identifier operators after this */
void verifyName(const std::string& n) const;
+ /** Verifies that the name <i>begins with</i> identifier \a n or \a m (case insensitive).
+ It may contain identifier operators after this */
+ void verifyName(const std::string& n, const std::string& m) const;
+
/** Verifies that the type is \a t. */
void verifyType(Type t) const;
diff --git a/dep/g3dlite/G3D/AnyVal.h b/dep/g3dlite/include/G3D/AnyVal.h
index 8c1bc72f206..8c1bc72f206 100644
--- a/dep/g3dlite/G3D/AnyVal.h
+++ b/dep/g3dlite/include/G3D/AnyVal.h
diff --git a/dep/g3dlite/G3D/AreaMemoryManager.h b/dep/g3dlite/include/G3D/AreaMemoryManager.h
index d8d8f710359..d8d8f710359 100644
--- a/dep/g3dlite/G3D/AreaMemoryManager.h
+++ b/dep/g3dlite/include/G3D/AreaMemoryManager.h
diff --git a/dep/g3dlite/G3D/Array.h b/dep/g3dlite/include/G3D/Array.h
index cc9e1d9dd01..cc9e1d9dd01 100644
--- a/dep/g3dlite/G3D/Array.h
+++ b/dep/g3dlite/include/G3D/Array.h
diff --git a/dep/g3dlite/G3D/AtomicInt32.h b/dep/g3dlite/include/G3D/AtomicInt32.h
index 2d63f998355..2d63f998355 100644
--- a/dep/g3dlite/G3D/AtomicInt32.h
+++ b/dep/g3dlite/include/G3D/AtomicInt32.h
diff --git a/dep/g3dlite/G3D/BinaryFormat.h b/dep/g3dlite/include/G3D/BinaryFormat.h
index f6719a1c540..f6719a1c540 100644
--- a/dep/g3dlite/G3D/BinaryFormat.h
+++ b/dep/g3dlite/include/G3D/BinaryFormat.h
diff --git a/dep/g3dlite/G3D/BinaryInput.h b/dep/g3dlite/include/G3D/BinaryInput.h
index 1dac93ea55e..e8a9b88618b 100644
--- a/dep/g3dlite/G3D/BinaryInput.h
+++ b/dep/g3dlite/include/G3D/BinaryInput.h
@@ -1,12 +1,12 @@
/**
@file BinaryInput.h
- @maintainer Morgan McGuire, graphics3d.com
+ @maintainer Morgan McGuire, http://graphics.cs.williams.edu
@created 2001-08-09
- @edited 2006-07-19
+ @edited 2010-03-19
- Copyright 2000-2009, Morgan McGuire.
+ Copyright 2000-2010, Morgan McGuire.
All rights reserved.
*/
@@ -370,6 +370,9 @@ public:
*/
std::string readString();
+ /** Reads until \r, \r\n, \n\r, \n or the end of the file is encountered. Consumes the newline.*/
+ std::string readStringNewline();
+
/**
Reads until NULL or the end of the file is encountered.
If the string has odd length (including NULL), reads
diff --git a/dep/g3dlite/G3D/BinaryOutput.h b/dep/g3dlite/include/G3D/BinaryOutput.h
index d81ec56a67b..d81ec56a67b 100644
--- a/dep/g3dlite/G3D/BinaryOutput.h
+++ b/dep/g3dlite/include/G3D/BinaryOutput.h
diff --git a/dep/g3dlite/G3D/BoundsTrait.h b/dep/g3dlite/include/G3D/BoundsTrait.h
index 15e1418010c..15e1418010c 100644
--- a/dep/g3dlite/G3D/BoundsTrait.h
+++ b/dep/g3dlite/include/G3D/BoundsTrait.h
diff --git a/dep/g3dlite/G3D/Box.h b/dep/g3dlite/include/G3D/Box.h
index 82af9125b05..82af9125b05 100644
--- a/dep/g3dlite/G3D/Box.h
+++ b/dep/g3dlite/include/G3D/Box.h
diff --git a/dep/g3dlite/G3D/Box2D.h b/dep/g3dlite/include/G3D/Box2D.h
index 80accad89dd..80accad89dd 100644
--- a/dep/g3dlite/G3D/Box2D.h
+++ b/dep/g3dlite/include/G3D/Box2D.h
diff --git a/dep/g3dlite/G3D/BumpMapPreprocess.h b/dep/g3dlite/include/G3D/BumpMapPreprocess.h
index 955f99e61b2..955f99e61b2 100644
--- a/dep/g3dlite/G3D/BumpMapPreprocess.h
+++ b/dep/g3dlite/include/G3D/BumpMapPreprocess.h
diff --git a/dep/g3dlite/G3D/Capsule.h b/dep/g3dlite/include/G3D/Capsule.h
index baeea3aa82b..baeea3aa82b 100644
--- a/dep/g3dlite/G3D/Capsule.h
+++ b/dep/g3dlite/include/G3D/Capsule.h
diff --git a/dep/g3dlite/G3D/CollisionDetection.h b/dep/g3dlite/include/G3D/CollisionDetection.h
index c8fcf5534c2..c8fcf5534c2 100644
--- a/dep/g3dlite/G3D/CollisionDetection.h
+++ b/dep/g3dlite/include/G3D/CollisionDetection.h
diff --git a/dep/g3dlite/G3D/Color1.h b/dep/g3dlite/include/G3D/Color1.h
index 0f68c84b363..0f68c84b363 100644
--- a/dep/g3dlite/G3D/Color1.h
+++ b/dep/g3dlite/include/G3D/Color1.h
diff --git a/dep/g3dlite/G3D/Color1uint8.h b/dep/g3dlite/include/G3D/Color1uint8.h
index 092099d0d17..092099d0d17 100644
--- a/dep/g3dlite/G3D/Color1uint8.h
+++ b/dep/g3dlite/include/G3D/Color1uint8.h
diff --git a/dep/g3dlite/G3D/Color3.h b/dep/g3dlite/include/G3D/Color3.h
index bffe434fc27..bffe434fc27 100644
--- a/dep/g3dlite/G3D/Color3.h
+++ b/dep/g3dlite/include/G3D/Color3.h
diff --git a/dep/g3dlite/G3D/Color3uint8.h b/dep/g3dlite/include/G3D/Color3uint8.h
index bd4b00d7fd6..7a1cc79d94d 100644
--- a/dep/g3dlite/G3D/Color3uint8.h
+++ b/dep/g3dlite/include/G3D/Color3uint8.h
@@ -1,20 +1,27 @@
/**
@file Color3uint8.h
- @maintainer Morgan McGuire, graphics3d.com
+ @maintainer Morgan McGuire, http://graphics.cs.williams.edu
@created 2003-04-07
- @edited 2006-06-24
+ @edited 2010-03-24
- Copyright 2000-2006, Morgan McGuire.
+ Copyright 2000-2010, Morgan McGuire.
All rights reserved.
*/
-#ifndef G3D_COLOR3UINT8_H
-#define G3D_COLOR3UINT8_H
+#ifndef G3D_Color3uint8_h
+#define G3D_Color3uint8_h
#include "G3D/platform.h"
#include "G3D/g3dmath.h"
+#ifdef max
+#undef max
+#endif
+
+#ifdef min
+#undef min
+#endif
namespace G3D {
@@ -53,7 +60,7 @@ public:
Color3uint8(class BinaryInput& bi);
- inline static Color3uint8 fromARGB(uint32 i) {
+ static Color3uint8 fromARGB(uint32 i) {
Color3uint8 c;
c.r = (i >> 16) & 0xFF;
c.g = (i >> 8) & 0xFF;
@@ -61,15 +68,23 @@ public:
return c;
}
- inline Color3uint8 bgr() const {
+ Color3uint8 bgr() const {
return Color3uint8(b, g, r);
}
+ Color3uint8 max(const Color3uint8 x) const {
+ return Color3uint8(G3D::max(r, x.r), G3D::max(g, x.g), G3D::max(b, x.b));
+ }
+
+ Color3uint8 min(const Color3uint8 x) const {
+ return Color3uint8(G3D::min(r, x.r), G3D::min(g, x.g), G3D::min(b, x.b));
+ }
+
/**
Returns the color packed into a uint32
(the upper byte is 0xFF)
*/
- inline uint32 asUInt32() const {
+ uint32 asUInt32() const {
return (0xFF << 24) + ((uint32)r << 16) + ((uint32)g << 8) + b;
}
@@ -95,12 +110,12 @@ public:
return (uint8*)this;
}
- bool operator==(const Color3uint8& other) 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);
+ bool operator!=(const Color3uint8 other) const {
+ return ! (*this == other);
}
}
G3D_END_PACKED_CLASS(1)
diff --git a/dep/g3dlite/G3D/Color4.h b/dep/g3dlite/include/G3D/Color4.h
index d8858abbce2..0be0c25879f 100644
--- a/dep/g3dlite/G3D/Color4.h
+++ b/dep/g3dlite/include/G3D/Color4.h
@@ -117,9 +117,18 @@ public:
Color4 operator+ (const Color4& rkVector) const;
Color4 operator- (const Color4& rkVector) const;
Color4 operator* (float fScalar) const;
- inline Color4 operator* (const Color4& k) const {
+ Color4 operator* (const Color4& k) const {
return Color4(r*k.r, g*k.g, b*k.b, a * k.a);
}
+
+ Color4& operator*= (const Color4& c) {
+ r *= c.r;
+ g *= c.g;
+ b *= c.b;
+ a *= c.a;
+ return *this;
+ }
+
Color4 operator/ (float fScalar) const;
Color4 operator- () const;
friend Color4 operator* (double fScalar, const Color4& rkVector);
diff --git a/dep/g3dlite/G3D/Color4uint8.h b/dep/g3dlite/include/G3D/Color4uint8.h
index ab8c0729276..2cd3c662788 100644
--- a/dep/g3dlite/G3D/Color4uint8.h
+++ b/dep/g3dlite/include/G3D/Color4uint8.h
@@ -1,12 +1,12 @@
/**
@file Color4uint8.h
- @maintainer Morgan McGuire, graphics3d.com
+ @maintainer Morgan McGuire, http://graphics.cs.williams.edu
@created 2003-04-07
- @edited 2006-03-24
+ @edited 2010-03-24
- Copyright 2000-2006, Morgan McGuire.
+ Copyright 2000-2010, Morgan McGuire.
All rights reserved.
*/
@@ -50,6 +50,14 @@ public:
Color4uint8(const class Color4& c);
+ Color4uint8 max(const Color4uint8 x) const {
+ return Color4uint8(G3D::max(r, x.r), G3D::max(g, x.g), G3D::max(b, x.b), G3D::max(a, x.a));
+ }
+
+ Color4uint8 min(const Color4uint8 x) const {
+ return Color4uint8(G3D::min(r, x.r), G3D::min(g, x.g), G3D::min(b, x.b), G3D::min(a, x.a));
+ }
+
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) {}
diff --git a/dep/g3dlite/G3D/Cone.h b/dep/g3dlite/include/G3D/Cone.h
index d801a9b348f..d801a9b348f 100644
--- a/dep/g3dlite/G3D/Cone.h
+++ b/dep/g3dlite/include/G3D/Cone.h
diff --git a/dep/g3dlite/G3D/ConvexPolyhedron.h b/dep/g3dlite/include/G3D/ConvexPolyhedron.h
index a6fdd62cf90..a6fdd62cf90 100644
--- a/dep/g3dlite/G3D/ConvexPolyhedron.h
+++ b/dep/g3dlite/include/G3D/ConvexPolyhedron.h
diff --git a/dep/g3dlite/G3D/CoordinateFrame.h b/dep/g3dlite/include/G3D/CoordinateFrame.h
index 7ed4d0acc65..c83754abb77 100644
--- a/dep/g3dlite/G3D/CoordinateFrame.h
+++ b/dep/g3dlite/include/G3D/CoordinateFrame.h
@@ -68,6 +68,8 @@ public:
- CFrame((matrix3 expr), (vector3 expr))
- CFrame::fromXYZYPRDegrees(#, #, #, #, #, #)
- CFrame { rotation = (matrix3 expr), translation = (vector3 expr) }
+ - Vector3( ... )
+ - Matrix3( ... )
*/
CoordinateFrame(const Any& any);
@@ -109,6 +111,8 @@ public:
static CoordinateFrame fromXYZYPRRadians(float x, float y, float z, float yaw = 0.0f, float pitch = 0.0f, float roll = 0.0f);
+ std::string toXYZYPRDegreesString() const;
+
/** 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
diff --git a/dep/g3dlite/G3D/Crypto.h b/dep/g3dlite/include/G3D/Crypto.h
index 56c816a4977..56c816a4977 100644
--- a/dep/g3dlite/G3D/Crypto.h
+++ b/dep/g3dlite/include/G3D/Crypto.h
diff --git a/dep/g3dlite/G3D/Cylinder.h b/dep/g3dlite/include/G3D/Cylinder.h
index 85eba77b794..85eba77b794 100644
--- a/dep/g3dlite/G3D/Cylinder.h
+++ b/dep/g3dlite/include/G3D/Cylinder.h
diff --git a/dep/g3dlite/G3D/EqualsTrait.h b/dep/g3dlite/include/G3D/EqualsTrait.h
index 349cb5088fb..349cb5088fb 100644
--- a/dep/g3dlite/G3D/EqualsTrait.h
+++ b/dep/g3dlite/include/G3D/EqualsTrait.h
diff --git a/dep/g3dlite/include/G3D/FileSystem.h b/dep/g3dlite/include/G3D/FileSystem.h
new file mode 100644
index 00000000000..b2a6e86520c
--- /dev/null
+++ b/dep/g3dlite/include/G3D/FileSystem.h
@@ -0,0 +1,466 @@
+/**
+ @file FileSystem.h
+
+ @author Morgan McGuire, http://graphics.cs.williams.edu
+
+ @author 2002-06-06
+ @edited 2010-02-05
+ */
+#ifndef G3D_FileSystem_h
+#define G3D_FileSystem_h
+
+#include "G3D/platform.h"
+#include "G3D/Array.h"
+#include "G3D/Table.h"
+
+namespace G3D {
+
+/**
+ OS-independent file system layer that optimizes the performance
+ of queries by caching and prefetching.
+
+ This class uses the following definitions:
+ <ul>
+ <li> "file" = document that can be opened for reading or writing
+ <li> "directory" = folder containing files and other directories
+ <li> "node" = file or directory
+ <li> "path" = string identifying a (see the FSPath class)
+ <li> "zipfile" = a compressed file storing an archive of files and directories in the zip format
+ </ul>
+
+ In G3D, Zipfiles are transparently treated as if they were directories, provided:
+ <ul>
+ <li> The zipfile name contains an extension (e.g., map.pk3, files.zip)
+ <li> There are no nested zipfiles
+ </ul>
+
+ The extension requirement allows G3D to quickly identify whether a path could enter a
+ zipfile without forcing it to open all parent directories for reading.
+
+ \sa FilePath
+ TODO: make threadsafe!
+*/
+class FileSystem {
+public:
+
+ class ListSettings {
+ public:
+ /** Defaults to true */
+ bool files;
+
+ /** Defaults to true */
+ bool directories;
+
+ /** Defaults to true on Windows, false on other platforms.*/
+ bool caseSensitive;
+
+ /** Can get recurse into subdirectories? Defaults to true. */
+ bool recursive;
+
+ /** Prefix the full path onto names that are returned. Defaults to true */
+ bool includeParentPath;
+
+ ListSettings() :
+ files(true),
+ directories(true),
+# ifdef G3D_WIN32
+ caseSensitive(true),
+# else
+ caseSensitive(false),
+# endif
+ recursive(false),
+ includeParentPath(true) {}
+ };
+
+private:
+
+ /** Drive letters. Only used on windows, but defined on all platforms to help
+ avoid breaking the Windows build when compiling on another platform. */
+ Array<std::string> m_winDrive;
+
+ float m_cacheLifetime;
+
+ enum Type {
+ /** Not yet checked */
+ UNKNOWN,
+ FILE_TYPE,
+ DIR_TYPE
+ };
+
+ class Entry {
+ public:
+ /** Name, not including parent path */
+ std::string name;
+ Type type;
+ Entry() : type(UNKNOWN) {}
+ Entry(const char* n) : name(n), type(UNKNOWN) {}
+ };
+
+ class Dir {
+ public:
+
+ /** If false, this path did not exist (even inside a zipfile) when last checked, or it is not a directory. */
+ bool exists;
+
+ bool isZipfile;
+ bool inZipfile;
+
+ /** Files and directories */
+ Array<Entry> nodeArray;
+
+ /** When this entry was last updated */
+ double lastChecked;
+
+ /** Case-independent comparison on Windows */
+ bool contains(const std::string& child) const;
+
+ /** Compute the contents of nodeArray from this zipfile. */
+ void computeZipListing(const std::string& zipfile, const std::string& pathInsideZipfile);
+
+ Dir() : exists(false), isZipfile(false), inZipfile(false), lastChecked(0) {}
+ };
+
+ /** Maps path names (without trailing slashes, except for the file system root) to contents.
+ On Windows, all paths are lowercase */
+ Table<std::string, Dir> m_cache;
+
+ /** Update the cache entry for path if it is not already present.
+ \param forceUpdate If true, always override the current cache value.*/
+ Dir& getContents(const std::string& path, bool forceUpdate);
+
+ /** Don't allow public construction. */
+ FileSystem();
+
+ static FileSystem& instance();
+
+# ifdef G3D_WIN32
+ /** On Windows, the drive letters that form the file system roots.*/
+ const Array<std::string>& _drives();
+# endif
+
+ /** Returns true if some sub-path of \a path is a zipfile.
+
+ If the path itself is a zipfile, returns false.
+
+ \param zipfile The part of \a path that was the zipfile */
+ bool _inZipfile(const std::string& path, std::string& zipfile);
+
+ /** Clears old cache entries so that exists() and list() will reflect recent changes to the file system.
+ \param path Clear only \a path and its subdirectories ("" means clear the entire cache) */
+ void _clearCache(const std::string& path);
+
+ bool _inZipfile(const std::string& path) {
+ std::string ignore;
+ return inZipfile(path, ignore);
+ }
+
+ /** Set the cacheLifetime().
+ \param t in seconds */
+ void _setCacheLifetime(float t);
+
+
+ /** A cache is used to optimize repeated calls. A cache entry is considered
+ valid for this many seconds after it has been checked. */
+ float _cacheLifetime() const {
+ return m_cacheLifetime;
+ }
+
+ /** Creates the directory named, including any subdirectories
+ that do not already exist.
+
+ The directory must not be inside a zipfile.
+
+ Flushes the cache.
+ */
+ void _createDirectory(const std::string& path);
+
+ /** Returns true if a node named \a f exists.
+
+ \param f If \a f contains wildcards, the function returns true if any file
+ matches those wildcards. Wildcards may only appear in the base or ext, not the
+ path.
+
+ \param trustCache If true, uses the cache for optimizing repeated calls
+ in the same parent directory.
+ */
+ bool _exists(const std::string& f, bool trustCache = true);
+
+ /** Known bug: does not work inside zipfiles */
+ bool _isDirectory(const std::string& path);
+
+ /** Known bug: does not work inside zipfiles */
+ bool _isFile(const std::string& path) {
+ return ! isDirectory(path);
+ }
+
+ /**
+ \param srcPath Must name a file.
+ \param dstPath Must not contain a zipfile.
+
+ Flushes the cache.
+ */
+ void _copyFile(const std::string& srcPath, const std::string& dstPath);
+
+ /** Fully qualifies a filename.
+
+ The filename may contain wildcards, in which case the wildcards will be preserved in the returned value.
+
+ \param cwd The directory to treat as the "current" directory when resolving a relative path. The default
+ value is the actual current directory. (G3D::Any::sourceDirectory is a common alternative)
+ */
+ std::string _resolve(const std::string& path, const std::string& cwd = currentDirectory());
+
+ /** Returns true if \param dst does not exist or \param src is newer than \param dst,
+ according to their time stamps.
+
+ Known bug: does not work inside zipfiles.
+ */
+ bool _isNewer(const std::string& src, const std::string& dst);
+
+ /** The current working directory (cwd). Only ends in a slash if this is the root of the file system. */
+ std::string _currentDirectory();
+
+ /** Returns the length of the file in bytes, or -1 if the file could not be opened. */
+ int64 _size(const std::string& path);
+
+ /** Called from list() */
+ void listHelper(const std::string& shortSpec, const std::string& parentPath, Array<std::string>& result, const ListSettings& settings);
+
+ /** Appends all nodes matching \a spec to the \a result array.
+
+ Wildcards can only appear to the right of the last slash in \a spec.
+
+ The names will not contain parent paths unless \a includePath == true.
+ These may be relative to the current directory unless \a spec
+ is fully qualified (can be done with resolveFilename).
+
+ */
+ void _list(const std::string& spec, Array<std::string>& result, const ListSettings& listSettings = ListSettings());
+
+ /** Returns true if \a path is a file that is a zipfile. Note that G3D requires zipfiles to have
+ some extension, although it is not required to be "zip" */
+ bool _isZipfile(const std::string& path);
+
+ /** list() files */
+ void _getFiles(const std::string& spec, Array<std::string>& result, bool includeParentPath = false) {
+ ListSettings set;
+ set.includeParentPath = includeParentPath;
+ set.directories = false;
+ set.files = true;
+ return list(spec, result, set);
+ }
+
+ /** list() directories */
+ void _getDirectories(const std::string& spec, Array<std::string>& result, bool includeParentPath = false) {
+ ListSettings set;
+ set.includeParentPath = includeParentPath;
+ set.directories = true;
+ set.files = false;
+ return list(spec, result, set);
+ }
+
+ /** Same as the C standard library fopen, but updates the file cache
+ to acknowledge the new file on a write operation. */
+ FILE* _fopen(const char* filename, const char* mode);
+
+public:
+
+
+ /** Create the common instance. */
+ static void init();
+
+ /** Destroy the common instance. */
+ static void cleanup();
+
+# ifdef G3D_WIN32
+ /** \copydoc _drives */
+ static const Array<std::string>& drives() {
+ return instance()._drives();
+ }
+# endif
+
+ /** \copydoc _inZipfile */
+ static bool inZipfile(const std::string& path, std::string& zipfile) {
+ return instance()._inZipfile(path, zipfile);
+ }
+
+ /** \copydoc _clearCache */
+ static void clearCache(const std::string& path = "") {
+ instance()._clearCache(path);
+ }
+
+ /** \copydoc _fopen */
+ static FILE* fopen(const char* filename, const char* mode) {
+ return instance()._fopen(filename, mode);
+ }
+
+ static void fclose(FILE* f) {
+ ::fclose(f);
+ }
+
+ static bool inZipfile(const std::string& path) {
+ return instance()._inZipfile(path);
+ }
+
+ /** \copydoc isZipfile */
+ static bool isZipfile(const std::string& path) {
+ return instance()._isZipfile(path);
+ }
+
+ /** \copydoc _setCacheLifetime */
+ void setCacheLifetime(float t) {
+ instance()._setCacheLifetime(t);
+ }
+
+ /** \copydoc _cacheLifetime */
+ static float cacheLifetime() {
+ return instance()._cacheLifetime();
+ }
+
+ /** \copydoc _createDirectory */
+ static void createDirectory(const std::string& path) {
+ instance()._createDirectory(path);
+ }
+
+ /** \copydoc _currentDirectory */
+ static std::string currentDirectory() {
+ return instance()._currentDirectory();
+ }
+
+ /** \copydoc _copyFile */
+ static void copyFile(const std::string& srcPath, const std::string& dstPath) {
+ instance()._copyFile(srcPath, dstPath);
+ }
+
+ /** \copydoc _exists */
+ static bool exists(const std::string& f, bool trustCache = true) {
+ return instance()._exists(f, trustCache);
+ }
+
+ /** \copydoc _isDirectory */
+ static bool isDirectory(const std::string& path) {
+ return instance()._isDirectory(path);
+ }
+
+ /** \copydoc _isFile */
+ static bool isFile(const std::string& path) {
+ return instance()._isFile(path);
+ }
+
+ /** \copydoc _resolve */
+ static std::string resolve(const std::string& path, const std::string& cwd = currentDirectory()) {
+ return instance()._resolve(path, cwd);
+ }
+
+ /** \copydoc _isNewer */
+ static bool isNewer(const std::string& src, const std::string& dst) {
+ return instance()._isNewer(src, dst);
+ }
+
+ /** \copydoc _size */
+ static int64 size(const std::string& path) {
+ return instance()._size(path);
+ }
+
+ /** \copydoc _list */
+ static void list(const std::string& spec, Array<std::string>& result,
+ const ListSettings& listSettings = ListSettings()) {
+ return instance()._list(spec, result, listSettings);
+ }
+
+ /** \copydoc _getFiles */
+ static void getFiles(const std::string& spec, Array<std::string>& result, bool includeParentPath = false) {
+ return instance()._getFiles(spec, result, includeParentPath);
+ }
+
+ /** \copydoc getDirectories */
+ static void getDirectories(const std::string& spec, Array<std::string>& result, bool includeParentPath = false) {
+ return instance()._getDirectories(spec, result, includeParentPath);
+ }
+};
+
+
+/** \brief Parsing of file system paths.
+
+ None of these routines touch the disk--they are purely string manipulation.
+
+ In "/a/b/base.ext",
+
+ <ul>
+ <li> base = "base"
+ <li> ext = "ext"
+ <li> parentPath = "/a/b"
+ <li> baseExt = "base.ext"
+ </ul>
+
+*/
+class FilePath {
+public:
+
+ /** Appends file onto dirname, ensuring a / if needed. */
+ static std::string concat(const std::string& a, const std::string& b);
+
+ static bool isRoot(const std::string& f);
+
+ /** Removes the trailing slash unless \a f is a filesystem root */
+ static std::string removeTrailingSlash(const std::string& f);
+
+ /** Returns everything to the right of the last '.' */
+ static std::string ext(const std::string& path);
+
+ /** Returns everything to the right of the last slash (or, on Windows, the last ':') */
+ static std::string baseExt(const std::string& path);
+
+ /** Returns everything between the right-most slash and the following '.' */
+ static std::string base(const std::string& path);
+
+ /** Returns everything to the left of the right-most slash */
+ static std::string parent(const std::string& path);
+
+ /** Returns true if '*' or '?' appear in the filename */
+ static bool containsWildcards(const std::string& p);
+
+ /** Convert all slashes to '/' */
+ static std::string canonicalize(std::string x);
+
+ /**
+ 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"
+
+ */
+ static void parse
+ (const std::string& filename,
+ std::string& drive,
+ Array<std::string>& path,
+ std::string& base,
+ std::string& ext);
+
+
+ /**
+ Returns true if \a path matches \a pattern, with standard filesystem wildcards.
+ */
+ static bool matches(const std::string& path, const std::string& pattern, bool caseSensitive = true);
+};
+
+} // namespace G3D
+#endif
+
diff --git a/dep/g3dlite/G3D/G3D.h b/dep/g3dlite/include/G3D/G3D.h
index 5b56b9c71dc..082d6434842 100644
--- a/dep/g3dlite/G3D/G3D.h
+++ b/dep/g3dlite/include/G3D/G3D.h
@@ -7,7 +7,7 @@
@maintainer Morgan McGuire, http://graphics.cs.williams.edu
@created 2001-08-25
- @edited 2010-01-30
+ @edited 2010-03-20
Copyright 2000-2010, Morgan McGuire.
All rights reserved.
@@ -44,6 +44,7 @@
#include "G3D/Matrix4.h"
#include "G3D/CoordinateFrame.h"
#include "G3D/PhysicsFrame.h"
+#include "G3D/PhysicsFrameSpline.h"
#include "G3D/Plane.h"
#include "G3D/Line.h"
#include "G3D/Ray.h"
@@ -57,6 +58,7 @@
#include "G3D/stringutils.h"
#include "G3D/prompt.h"
#include "G3D/Table.h"
+#include "G3D/FileSystem.h"
#include "G3D/Set.h"
#include "G3D/GUniqueID.h"
#include "G3D/BinaryFormat.h"
@@ -118,6 +120,7 @@ template<class T> struct HashTrait< G3D::ReferenceCountedPointer<T> > {
#include "G3D/ThreadSet.h"
#include "G3D/RegistryUtil.h"
#include "G3D/Any.h"
+#include "G3D/XML.h"
#include "G3D/PointHashGrid.h"
#include "G3D/Map2D.h"
#include "G3D/Image1.h"
diff --git a/dep/g3dlite/G3D/G3DAll.h b/dep/g3dlite/include/G3D/G3DAll.h
index 1176fe742e7..1176fe742e7 100644
--- a/dep/g3dlite/G3D/G3DAll.h
+++ b/dep/g3dlite/include/G3D/G3DAll.h
diff --git a/dep/g3dlite/G3D/G3DGameUnits.h b/dep/g3dlite/include/G3D/G3DGameUnits.h
index e2bc2c811e8..e2bc2c811e8 100644
--- a/dep/g3dlite/G3D/G3DGameUnits.h
+++ b/dep/g3dlite/include/G3D/G3DGameUnits.h
diff --git a/dep/g3dlite/G3D/GCamera.h b/dep/g3dlite/include/G3D/GCamera.h
index 018fbc85d59..1b7b2511b43 100644
--- a/dep/g3dlite/G3D/GCamera.h
+++ b/dep/g3dlite/include/G3D/GCamera.h
@@ -38,6 +38,9 @@ class Any;
All viewport arguments are the pixel bounds of the viewport-- e.g.,
RenderDevice::viewport().
+
+ See http://bittermanandy.wordpress.com/2009/04/10/a-view-to-a-thrill-part-one-camera-concepts/
+ for a nice introduction to camera transformations.
*/
class GCamera {
@@ -64,6 +67,8 @@ private:
/** Horizontal or Vertical */
FOVDirection m_direction;
+ Vector2 m_pixelOffset;
+
public:
/** Must be of the format produced by the Any cast, e.g.,
@@ -117,6 +122,18 @@ public:
return m_cframe;
}
+ /** Displacement from the upper left added in pixels in screen
+ space to the projection matrix. This is useful for shifting
+ the sampled location from the pixel center (OpenGL convention)
+ to other locations, such as the upper-left.*/
+ void setPixelOffset(const Vector2& p) {
+ m_pixelOffset = p;
+ }
+
+ const Vector2& pixelOffset() const {
+ return m_pixelOffset;
+ }
+
/** Sets c to the camera's coordinate frame */
void getCoordinateFrame(CoordinateFrame& c) const;
@@ -154,7 +171,7 @@ public:
This is the full angle, i.e., from the left side of the
viewport to the right side.
*/
- void setFieldOfView(float angle, FOVDirection direction);
+ void setFieldOfView(float edgeToEdgeAngleRadians, FOVDirection direction);
/** Returns the current full field of view angle (from the left side of the
viewport to the right side) and direction */
diff --git a/dep/g3dlite/G3D/GImage.h b/dep/g3dlite/include/G3D/GImage.h
index 8ae11134fc9..9a6851d32b3 100644
--- a/dep/g3dlite/G3D/GImage.h
+++ b/dep/g3dlite/include/G3D/GImage.h
@@ -43,9 +43,9 @@ 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
+ (Uncompressed) TGA 24, (Uncompressed) TGA 32, BMP 1, BMP 4, BMP 8, BMP
24, PPM (P6), and PPM ASCII (P1, P2, P3), which includes PPM, PGM,
- and PBM. 8-bit paletted PCX, 24-bit PCX, and ICO are supported for
+ and PBM. (Compressed) TGA 24, (Compressed) TGA 32, 8-bit paletted PCX, 24-bit PCX, and ICO are supported for
decoding only.
Sample usage:
@@ -78,6 +78,10 @@ class BinaryOutput;
recommended over GImage (we don't include it directly in G3D because their license
is more restrictive than the BSD one).
+ \cite http://tfcduke.developpez.com/tutoriel/format/tga/fichiers/tga_specs.pdf
+
+ \sa Image3, Image3uint8, Image4, Image4uint8, Image1, Image1uint8, Texture, Map2D
+
*/
class GImage {
private:
diff --git a/dep/g3dlite/G3D/GLight.h b/dep/g3dlite/include/G3D/GLight.h
index 3a95f1a8114..48e017d365d 100644
--- a/dep/g3dlite/G3D/GLight.h
+++ b/dep/g3dlite/include/G3D/GLight.h
@@ -90,6 +90,13 @@ public:
const Color3& color, float constAtt = 1, float linAtt = 0, float quadAtt = 0,
bool specular = true, bool diffuse = true);
+ /** Creates a spot light that looks at a specific point (by calling spot() ) */
+ static GLight spotTarget(const Vector3& pos, const Vector3& target, float cutOffAngleDegrees,
+ const Color3& color, float constAtt = 1, float linAtt = 0, float quadAtt = 0,
+ bool specular = true, bool diffuse = true) {
+ return spot(pos, target - pos, cutOffAngleDegrees, color, constAtt, linAtt, quadAtt, specular, diffuse);
+ }
+
/** 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;
diff --git a/dep/g3dlite/G3D/GMutex.h b/dep/g3dlite/include/G3D/GMutex.h
index 3469b812736..3469b812736 100644
--- a/dep/g3dlite/G3D/GMutex.h
+++ b/dep/g3dlite/include/G3D/GMutex.h
diff --git a/dep/g3dlite/G3D/GThread.h b/dep/g3dlite/include/G3D/GThread.h
index 58437efc3fb..58437efc3fb 100644
--- a/dep/g3dlite/G3D/GThread.h
+++ b/dep/g3dlite/include/G3D/GThread.h
diff --git a/dep/g3dlite/G3D/GUniqueID.h b/dep/g3dlite/include/G3D/GUniqueID.h
index c8b775c2e66..c8b775c2e66 100644
--- a/dep/g3dlite/G3D/GUniqueID.h
+++ b/dep/g3dlite/include/G3D/GUniqueID.h
diff --git a/dep/g3dlite/G3D/HashTrait.h b/dep/g3dlite/include/G3D/HashTrait.h
index ca35da48643..72de3da52fd 100644
--- a/dep/g3dlite/G3D/HashTrait.h
+++ b/dep/g3dlite/include/G3D/HashTrait.h
@@ -52,7 +52,7 @@ template <> struct HashTrait <G3D::uint32> {
static size_t hashCode(G3D::uint32 k) { return static_cast<size_t>(k); }
};
-#if 0
+#ifdef G3D_OSX
template <> struct HashTrait <long unsigned int> {
static size_t hashCode(G3D::uint32 k) { return static_cast<size_t>(k); }
};
diff --git a/dep/g3dlite/G3D/Image1.h b/dep/g3dlite/include/G3D/Image1.h
index 711e83f2079..711e83f2079 100644
--- a/dep/g3dlite/G3D/Image1.h
+++ b/dep/g3dlite/include/G3D/Image1.h
diff --git a/dep/g3dlite/G3D/Image1uint8.h b/dep/g3dlite/include/G3D/Image1uint8.h
index f32e022e92a..f32e022e92a 100644
--- a/dep/g3dlite/G3D/Image1uint8.h
+++ b/dep/g3dlite/include/G3D/Image1uint8.h
diff --git a/dep/g3dlite/G3D/Image3.h b/dep/g3dlite/include/G3D/Image3.h
index 13cb8fa7faf..13cb8fa7faf 100644
--- a/dep/g3dlite/G3D/Image3.h
+++ b/dep/g3dlite/include/G3D/Image3.h
diff --git a/dep/g3dlite/G3D/Image3uint8.h b/dep/g3dlite/include/G3D/Image3uint8.h
index d4fdbc169ca..d4fdbc169ca 100644
--- a/dep/g3dlite/G3D/Image3uint8.h
+++ b/dep/g3dlite/include/G3D/Image3uint8.h
diff --git a/dep/g3dlite/G3D/Image4.h b/dep/g3dlite/include/G3D/Image4.h
index 21d7f1e79b1..21d7f1e79b1 100644
--- a/dep/g3dlite/G3D/Image4.h
+++ b/dep/g3dlite/include/G3D/Image4.h
diff --git a/dep/g3dlite/G3D/Image4uint8.h b/dep/g3dlite/include/G3D/Image4uint8.h
index 46df6b490b4..46df6b490b4 100644
--- a/dep/g3dlite/G3D/Image4uint8.h
+++ b/dep/g3dlite/include/G3D/Image4uint8.h
diff --git a/dep/g3dlite/G3D/ImageFormat.h b/dep/g3dlite/include/G3D/ImageFormat.h
index 7f098322d26..15d2564330f 100644
--- a/dep/g3dlite/G3D/ImageFormat.h
+++ b/dep/g3dlite/include/G3D/ImageFormat.h
@@ -4,7 +4,7 @@
@maintainer Morgan McGuire, http://graphics.cs.williams.edu
@created 2003-05-23
- @edited 2010-01-01
+ @edited 2010-05-01
*/
#ifndef GLG3D_ImageFormat_H
@@ -13,6 +13,7 @@
#include "G3D/platform.h"
#include "G3D/Table.h"
#include "G3D/enumclass.h"
+#include "G3D/Any.h"
namespace G3D {
@@ -57,13 +58,19 @@ public:
CODE_RGB8I,
CODE_RGB8UI,
+ CODE_RGBA8UI,
+
CODE_ARGB8,
CODE_BGR8,
+ CODE_R8,
+
CODE_RG8,
CODE_RG8I,
CODE_RG8UI,
+ CODE_RG16F,
+
CODE_RGBA8,
CODE_RGBA16,
CODE_RGBA16F,
@@ -260,10 +267,14 @@ public:
static const ImageFormat* BGR8();
+ static const ImageFormat* R8();
+
static const ImageFormat* RG8();
static const ImageFormat* RG8I();
static const ImageFormat* RG8UI();
+ static const ImageFormat* RG16F();
+
static const ImageFormat* RGB5();
static const ImageFormat* RGB5A1();
@@ -297,6 +308,8 @@ public:
static const ImageFormat* RGB8I();
static const ImageFormat* RGB8UI();
+
+ static const ImageFormat* RGBA8UI();
static const ImageFormat* RGB_DXT1();
@@ -376,9 +389,18 @@ public:
NEAREST,
BILINEAR,
MHC,
- HIGH_QUALITY = MHC
+ BEST = MHC
};
private:
+ static const char* toString(int i, Value& v) {
+ static const char* str[] = {"NEAREST", "BILINEAR", "MHC", "BEST", NULL};
+ static const Value val[] = {NEAREST, BILINEAR, MHC, BEST};
+ const char* s = str[i];
+ if (s) {
+ v = val[i];
+ }
+ return s;
+ }
Value value;
@@ -399,7 +421,7 @@ public:
*/
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);
+ bool invertY = false, BayerAlgorithm bayerAlg = BayerAlgorithm::MHC);
/* 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);
diff --git a/dep/g3dlite/G3D/Intersect.h b/dep/g3dlite/include/G3D/Intersect.h
index 4a3c8fb4540..4a3c8fb4540 100644
--- a/dep/g3dlite/G3D/Intersect.h
+++ b/dep/g3dlite/include/G3D/Intersect.h
diff --git a/dep/g3dlite/G3D/KDTree.h b/dep/g3dlite/include/G3D/KDTree.h
index 4785ef2baea..4785ef2baea 100644
--- a/dep/g3dlite/G3D/KDTree.h
+++ b/dep/g3dlite/include/G3D/KDTree.h
diff --git a/dep/g3dlite/G3D/Line.h b/dep/g3dlite/include/G3D/Line.h
index 3579a6becec..3579a6becec 100644
--- a/dep/g3dlite/G3D/Line.h
+++ b/dep/g3dlite/include/G3D/Line.h
diff --git a/dep/g3dlite/G3D/LineSegment.h b/dep/g3dlite/include/G3D/LineSegment.h
index 70210ec7e00..70210ec7e00 100644
--- a/dep/g3dlite/G3D/LineSegment.h
+++ b/dep/g3dlite/include/G3D/LineSegment.h
diff --git a/dep/g3dlite/G3D/Log.h b/dep/g3dlite/include/G3D/Log.h
index d252d0c1a17..d252d0c1a17 100644
--- a/dep/g3dlite/G3D/Log.h
+++ b/dep/g3dlite/include/G3D/Log.h
diff --git a/dep/g3dlite/G3D/Map2D.h b/dep/g3dlite/include/G3D/Map2D.h
index 9af9f7242c1..9af9f7242c1 100644
--- a/dep/g3dlite/G3D/Map2D.h
+++ b/dep/g3dlite/include/G3D/Map2D.h
diff --git a/dep/g3dlite/G3D/Matrix.h b/dep/g3dlite/include/G3D/Matrix.h
index 3c5394d9a76..3c5394d9a76 100644
--- a/dep/g3dlite/G3D/Matrix.h
+++ b/dep/g3dlite/include/G3D/Matrix.h
diff --git a/dep/g3dlite/G3D/Matrix2.h b/dep/g3dlite/include/G3D/Matrix2.h
index eaf4aefa220..cab70c83e44 100644
--- a/dep/g3dlite/G3D/Matrix2.h
+++ b/dep/g3dlite/include/G3D/Matrix2.h
@@ -1,5 +1,5 @@
-#ifndef G3D_MATRIX2_H
-#define G3D_MATRIX2_H
+#ifndef G3D_Matrix2_h
+#define G3D_Matrix2_h
#include "G3D/platform.h"
#include "G3D/Vector2.h"
@@ -14,54 +14,59 @@ private:
public:
- inline Matrix2() {
+ 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) {
+ 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 {
+ static Matrix2 identity() {
+ return Matrix2(1.0f, 0.0f, 0.0f, 1.0f);
+ }
+
+ 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());
+ Matrix2 inverse() const {
+ return Matrix2(data[1][1], -data[0][1],
+ -data[1][0], data[0][0]) * (1.0f / determinant());
}
- inline Matrix2 transpose() const {
+ Matrix2 transpose() const {
return Matrix2(data[0][0], data[1][0],
data[0][1], data[1][1]);
}
- inline float determinant() const {
+ float determinant() const {
return data[0][0] * data[1][1] - data[0][1] * data[1][0];
}
- inline Matrix2 operator*(float f) const {
+ 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 {
+ 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) {
+ float* operator[](int i) {
debugAssert(i >= 0 && i <= 2);
return data[i];
}
- inline const float* operator[](int i) const {
+ const float* operator[](int i) const {
debugAssert(i >= 0 && i <= 1);
return data[i];
}
+
};
}
diff --git a/dep/g3dlite/G3D/Matrix3.h b/dep/g3dlite/include/G3D/Matrix3.h
index 06ec7e67474..06ec7e67474 100644
--- a/dep/g3dlite/G3D/Matrix3.h
+++ b/dep/g3dlite/include/G3D/Matrix3.h
diff --git a/dep/g3dlite/G3D/Matrix4.h b/dep/g3dlite/include/G3D/Matrix4.h
index 9ce87d875b8..6b810ba77e8 100644
--- a/dep/g3dlite/G3D/Matrix4.h
+++ b/dep/g3dlite/include/G3D/Matrix4.h
@@ -119,6 +119,15 @@ public:
}
Matrix4 operator*(const Matrix4& other) const;
+ Matrix4 operator+(const Matrix4& other) const {
+ Matrix4 result;
+ for (int r = 0; r < 4; ++r) {
+ for (int c = 0; c < 4; ++c) {
+ result.elt[r][c] = elt[r][c] + other.elt[r][c];
+ }
+ }
+ return result;
+ }
class Matrix3 upper3x3() const;
diff --git a/dep/g3dlite/G3D/MemoryManager.h b/dep/g3dlite/include/G3D/MemoryManager.h
index 15bf6d8be43..15bf6d8be43 100644
--- a/dep/g3dlite/G3D/MemoryManager.h
+++ b/dep/g3dlite/include/G3D/MemoryManager.h
diff --git a/dep/g3dlite/G3D/MeshAlg.h b/dep/g3dlite/include/G3D/MeshAlg.h
index 1decea10105..da55d13000a 100644
--- a/dep/g3dlite/G3D/MeshAlg.h
+++ b/dep/g3dlite/include/G3D/MeshAlg.h
@@ -600,7 +600,7 @@ public:
outIndices.resize(N + (inSize - 2) * 3);
bool atEven = false;
- for (IndexType i = 0, outIndex = N; i <= (inSize - 2); ++i, outIndex += 3) {
+ for (IndexType i = 0, outIndex = N; i < (inSize - 2); ++i, outIndex += 3) {
if (atEven) {
outIndices[outIndex] = inIndices[i + 1];
outIndices[outIndex + 1] = inIndices[i];
diff --git a/dep/g3dlite/G3D/MeshBuilder.h b/dep/g3dlite/include/G3D/MeshBuilder.h
index 9920d59d7d3..9920d59d7d3 100644
--- a/dep/g3dlite/G3D/MeshBuilder.h
+++ b/dep/g3dlite/include/G3D/MeshBuilder.h
diff --git a/dep/g3dlite/G3D/NetAddress.h b/dep/g3dlite/include/G3D/NetAddress.h
index 8ed20a06690..fd5a199593b 100644
--- a/dep/g3dlite/G3D/NetAddress.h
+++ b/dep/g3dlite/include/G3D/NetAddress.h
@@ -1,33 +1,9 @@
-#ifndef G3D_NETADDRESS_H
-#define G3D_NETADDRESS_H
+#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/netheaders.h"
#include "G3D/g3dmath.h"
diff --git a/dep/g3dlite/G3D/NetworkDevice.h b/dep/g3dlite/include/G3D/NetworkDevice.h
index ea3290cbc09..7a3f8ed4aca 100644
--- a/dep/g3dlite/G3D/NetworkDevice.h
+++ b/dep/g3dlite/include/G3D/NetworkDevice.h
@@ -258,8 +258,8 @@ public:
~ReliableConduit();
- // The message is actually copied from the socket to an internal buffer during
- // this call. Receive only deserializes.
+ /** The message is actually copied from the socket to an internal buffer during
+ this call. Receive only deserializes.*/
virtual bool messageWaiting();
/**
@@ -348,6 +348,7 @@ public:
messageWaiting();
}
+ /** The address of the other end of the conduit */
NetAddress address() const;
};
diff --git a/dep/g3dlite/G3D/ParseError.h b/dep/g3dlite/include/G3D/ParseError.h
index f02948e3d29..f02948e3d29 100644
--- a/dep/g3dlite/G3D/ParseError.h
+++ b/dep/g3dlite/include/G3D/ParseError.h
diff --git a/dep/g3dlite/G3D/PhysicsFrame.h b/dep/g3dlite/include/G3D/PhysicsFrame.h
index a5a9305b83e..9aee5e5a883 100644
--- a/dep/g3dlite/G3D/PhysicsFrame.h
+++ b/dep/g3dlite/include/G3D/PhysicsFrame.h
@@ -43,19 +43,27 @@ public:
PhysicsFrame();
/**
- Purely translational force
+ Purely translational.
*/
PhysicsFrame(const Vector3& translation) : translation(translation) {}
-
+ PhysicsFrame(const Quat& rot, const Vector3& translation) : rotation(rot), translation(translation) {}
+ PhysicsFrame(const Matrix3& rot, const Vector3& translation) : rotation(rot), translation(translation) {}
+ PhysicsFrame(const Matrix3& rot) : rotation(rot), translation(Vector3::zero()) {}
PhysicsFrame(const CoordinateFrame& coordinateFrame);
+ /**
+ - PhysicsFrame( [quat], [vec3] )
+ - Vector3( ... )
+ - CFrame( ... )
+ - CFrame::from...( ... )
+ */
+ PhysicsFrame(const class Any& any);
+
/** 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).
*/
@@ -67,8 +75,35 @@ public:
void serialize(class BinaryOutput& b) const;
+ operator CFrame() const;
+
+ /** Multiplies both pieces by \a f; note that this will result in a non-unit
+ quaternion that needs to be normalized */
+ PhysicsFrame& operator*=(float f) {
+ rotation *= f;
+ translation *= f;
+ return *this;
+ }
+
+ /** Multiplies both pieces by \a f; note that this will result in a non-unit
+ quaternion that needs to be normalized */
+ PhysicsFrame operator*(float f) const {
+ return PhysicsFrame(rotation * f, translation * f);
+ }
+
+ PhysicsFrame operator+(const PhysicsFrame& f) const {
+ return PhysicsFrame(rotation + f.rotation, translation + f.translation);
+ }
+
+ PhysicsFrame& operator+=(const PhysicsFrame& f) {
+ rotation += f.rotation;
+ translation += f.translation;
+ return *this;
+ }
};
+typedef PhysicsFrame PFrame;
+
} // namespace
#endif
diff --git a/dep/g3dlite/include/G3D/PhysicsFrameSpline.h b/dep/g3dlite/include/G3D/PhysicsFrameSpline.h
new file mode 100644
index 00000000000..4a21503df35
--- /dev/null
+++ b/dep/g3dlite/include/G3D/PhysicsFrameSpline.h
@@ -0,0 +1,37 @@
+/**
+ @file PhysicsFrameSpline.h
+
+ @author Morgan McGuire, http://graphics.cs.williams.edu
+ */
+#ifndef G3D_PhysicsFrameSpline_h
+#define G3D_PhysicsFrameSpline_h
+
+#include "G3D/platform.h"
+#include "G3D/PhysicsFrame.h"
+#include "G3D/Spline.h"
+
+namespace G3D {
+
+/**
+ A subclass of Spline that keeps the rotation field of a
+ PhysicsFrame normalized and rotating the short direction.
+
+ \sa UprightFrameSpline
+ */
+class PhysicsFrameSpline : public Spline<PhysicsFrame> {
+public:
+ PhysicsFrameSpline();
+
+ /** Accepts a table of properties, or any valid PhysicsFrame specification for a single control*/
+ PhysicsFrameSpline(const Any& any);
+
+ /** Clear and then reset all values from the any */
+ PhysicsFrameSpline& operator=(const Any& any);
+
+ virtual void correct(PhysicsFrame& frame) const;
+ virtual void ensureShortestPath(PhysicsFrame* A, int N) const;
+};
+
+}
+
+#endif
diff --git a/dep/g3dlite/G3D/Plane.h b/dep/g3dlite/include/G3D/Plane.h
index 360bcd2bc75..360bcd2bc75 100644
--- a/dep/g3dlite/G3D/Plane.h
+++ b/dep/g3dlite/include/G3D/Plane.h
diff --git a/dep/g3dlite/G3D/PointHashGrid.h b/dep/g3dlite/include/G3D/PointHashGrid.h
index 0db9e677321..5b128d1b5d9 100644
--- a/dep/g3dlite/G3D/PointHashGrid.h
+++ b/dep/g3dlite/include/G3D/PointHashGrid.h
@@ -5,7 +5,7 @@
@created 2008-07-01
@edited 2009-05-28
- Copyright 2000-2009, Morgan McGuire.
+ Copyright 2000-2010, Morgan McGuire.
All rights reserved.
*/
#ifndef G3D_PointHashGrid_h
@@ -30,18 +30,44 @@ namespace G3D {
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.
+ <i>Value</i> must be supported by a G3D::PositionTrait and
+ G3D::EqualsTrait. Overloads are provided for
+ common G3D classes like G3D::Vector3. For example:
+
+ <pre>
+ class EqualsFunc {
+ public:
+ static bool equals(const Data& p, const Data& q) {
+ return p == q;
+ }
+ };
+
+ class PosFunc {
+ public:
+ static void getPosition(const Data& d, Vector3& pos) {
+ pos = d.location;
+ }
+ };
+
+ PointHashGrid<Data, Data::PosFunc, Data::EqualsFunc> grid;
+ </pre>
+
+ If the Value class defines operator==, the Equalsfunc is optional:
+
+ <pre>
+ PointHashGrid<Data, Data::PosFunc> grid;
+ </pre>
+
*/
template<class Value,
class PosFunc = PositionTrait<Value>,
- class EqualsFunc = EqualsTrait<Value>,
- class HashFunc = HashTrait<Vector3int32> >
+ class EqualsFunc = EqualsTrait<Value> >
class PointHashGrid {
private:
-#define ThisType PointHashGrid<Value, PosFunc, EqualsFunc, HashFunc>
+# define expectedCellSize (3)
+
+# define ThisType PointHashGrid<Value, PosFunc, EqualsFunc>
/** A value annotated with precomputed position and hash code.*/
class Entry {
@@ -51,8 +77,8 @@ private:
};
/** One cell of the grid. */
- typedef Array<Entry> Cell;
- typedef Table<Vector3int32, Cell, HashFunc> CellTable;
+ typedef SmallArray<Entry, expectedCellSize> Cell;
+ typedef Table<Vector3int32, Cell > CellTable;
/** The cube of +/-1 along each dimension. Initialized by initOffsetArray.*/
Vector3int32 m_offsetArray[3*3*3];
@@ -182,11 +208,11 @@ public:
Array<Entry> entry(init.size());
for (int i = 0; i < entry.size(); ++i) {
const Value& value = init[i];
- Vector3 pos = m_posFunc(value);
+ Vector3 pos;
entry[i].value = value;
entry[i].hashCode = m_hashFunc(value);
- entry[i].position = pos;
+ PosFunc::getPosition(value, entry[i].position);
lo = lo.min(pos);
hi = hi.max(pos);
diff --git a/dep/g3dlite/G3D/PointKDTree.h b/dep/g3dlite/include/G3D/PointKDTree.h
index 151cbd5f2f3..151cbd5f2f3 100644
--- a/dep/g3dlite/G3D/PointKDTree.h
+++ b/dep/g3dlite/include/G3D/PointKDTree.h
diff --git a/dep/g3dlite/G3D/Pointer.h b/dep/g3dlite/include/G3D/Pointer.h
index 6e35062a746..6e35062a746 100644
--- a/dep/g3dlite/G3D/Pointer.h
+++ b/dep/g3dlite/include/G3D/Pointer.h
diff --git a/dep/g3dlite/G3D/PositionTrait.h b/dep/g3dlite/include/G3D/PositionTrait.h
index 67a4f64138a..67a4f64138a 100644
--- a/dep/g3dlite/G3D/PositionTrait.h
+++ b/dep/g3dlite/include/G3D/PositionTrait.h
diff --git a/dep/g3dlite/G3D/PrecomputedRandom.h b/dep/g3dlite/include/G3D/PrecomputedRandom.h
index 411d128c582..411d128c582 100644
--- a/dep/g3dlite/G3D/PrecomputedRandom.h
+++ b/dep/g3dlite/include/G3D/PrecomputedRandom.h
diff --git a/dep/g3dlite/G3D/Quat.h b/dep/g3dlite/include/G3D/Quat.h
index 9ef3d57b301..d8fdfafeca4 100644
--- a/dep/g3dlite/G3D/Quat.h
+++ b/dep/g3dlite/include/G3D/Quat.h
@@ -21,6 +21,8 @@
namespace G3D {
/**
+ Arbitrary quaternion (not necessarily unit)
+
Unit quaternions are used in computer graphics to represent
rotation about an axis. Any 3x3 rotation matrix can
be stored as a quaternion.
@@ -42,7 +44,7 @@ namespace G3D {
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.
+ \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:
@@ -63,31 +65,100 @@ public:
float x, y, z, w;
/**
- Initializes to a zero degree rotation.
+ Initializes to a zero degree rotation, (0,0,0,1)
*/
- inline Quat() : x(0), y(0), z(0), w(1) {}
+ Quat() : x(0), y(0), z(0), w(1) {}
+
+ /** Expects "Quat(x,y,z,w)" or a Matrix3 constructor. */
+ Quat(const class Any& a);
- Quat(
- const Matrix3& rot);
+ Quat(const Matrix3& rot);
- inline Quat(float _x, float _y, float _z, float _w) :
+ 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) {
+ 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 {
+ const float& real() const {
return w;
}
- inline float& real() {
+ float& real() {
return w;
}
+ Quat operator-() const {
+ return Quat(-x, -y, -z, -w);
+ }
+
+ Quat operator-(const Quat& other) const {
+ return Quat(x - other.x, y - other.y, z - other.z, w - other.w);
+ }
+
+ Quat& operator-=(const Quat& q) {
+ x -= q.x;
+ y -= q.y;
+ z -= q.z;
+ w -= q.w;
+ return *this;
+ }
+
+ Quat operator+(const Quat& q) const {
+ return Quat(x + q.x, y + q.y, z + q.z, w + q.w);
+ }
+
+ Quat& operator+=(const Quat& q) {
+ x += q.x;
+ y += q.y;
+ z += q.z;
+ w += q.w;
+ return *this;
+ }
+
+ /**
+ Negates the imaginary part.
+ */
+ Quat conj() const {
+ return Quat(-x, -y, -z, w);
+ }
+
+ float sum() const {
+ return x + y + z + w;
+ }
+
+ float average() const {
+ return sum() / 4.0f;
+ }
+
+ Quat operator*(float s) const {
+ return Quat(x * s, y * s, z * s, w * s);
+ }
+
+ 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);
+ }
+
+ float dot(const Quat& other) const {
+ return (x * other.x) + (y * other.y) + (z * other.z) + (w * other.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);
@@ -100,18 +171,14 @@ public:
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 {
+ const Vector3& imag() const {
return *(reinterpret_cast<const Vector3*>(this));
}
- inline Vector3& imag() {
+ Vector3& imag() {
return *(reinterpret_cast<Vector3*>(this));
}
@@ -158,43 +225,7 @@ public:
/** 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 */
@@ -202,10 +233,6 @@ public:
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.
@@ -217,18 +244,16 @@ public:
return (*this) * other.inverse();
}
-
/** Is the magnitude nearly 1.0? */
- inline bool isUnit(float tolerance = 1e-5) const {
+ bool isUnit(float tolerance = 1e-5) const {
return abs(dot(*this) - 1.0f) < tolerance;
- }
-
+ }
- inline float magnitude() const {
+ float magnitude() const {
return sqrtf(dot(*this));
}
- inline Quat log() const {
+ Quat log() const {
if ((x == 0) && (y == 0) && (z == 0)) {
if (w > 0) {
return Quat(0, 0, 0, ::logf(w));
@@ -289,18 +314,16 @@ public:
return (log() * x).exp();
}
- inline void unitize() {
- float mag2 = dot(*this);
- if (! G3D::fuzzyEq(mag2, 1.0f)) {
- *this *= rsq(mag2);
- }
+ /** Make unit length in place */
+ void unitize() {
+ *this *= rsq(dot(*this));
}
/**
Returns a unit quaterion obtained by dividing through by
the magnitude.
*/
- inline Quat toUnit() const {
+ Quat toUnit() const {
Quat x = *this;
x.unitize();
return x;
@@ -312,7 +335,7 @@ public:
n(q) value used in Eberly's 1999 paper, which is the square of the
norm.
*/
- inline float norm() const {
+ float norm() const {
return magnitude();
}
@@ -706,13 +729,6 @@ inline const float& Quat::operator[] (int i) const {
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
diff --git a/dep/g3dlite/G3D/Quat.inl b/dep/g3dlite/include/G3D/Quat.inl
index 9e4c861d93b..9e4c861d93b 100644
--- a/dep/g3dlite/G3D/Quat.inl
+++ b/dep/g3dlite/include/G3D/Quat.inl
diff --git a/dep/g3dlite/G3D/Queue.h b/dep/g3dlite/include/G3D/Queue.h
index 36573265d1a..36573265d1a 100644
--- a/dep/g3dlite/G3D/Queue.h
+++ b/dep/g3dlite/include/G3D/Queue.h
diff --git a/dep/g3dlite/G3D/Random.h b/dep/g3dlite/include/G3D/Random.h
index 54491d06f1b..54491d06f1b 100644
--- a/dep/g3dlite/G3D/Random.h
+++ b/dep/g3dlite/include/G3D/Random.h
diff --git a/dep/g3dlite/G3D/Ray.h b/dep/g3dlite/include/G3D/Ray.h
index bfee9243343..80df5828aff 100644
--- a/dep/g3dlite/G3D/Ray.h
+++ b/dep/g3dlite/include/G3D/Ray.h
@@ -36,7 +36,7 @@ private:
// The following are for the "ray slope" optimization from
// "Fast Ray / Axis-Aligned Bounding Box Overlap Tests using Ray Slopes"
- // by Martin Eisemann, Thorsten Grosch, Stefan Müller and Marcus Magnor
+ // by Martin Eisemann, Thorsten Grosch, Stefan Müller and Marcus Magnor
// Computer Graphics Lab, TU Braunschweig, Germany and
// University of Koblenz-Landau, Germany*/
enum Classification {MMM, MMP, MPM, MPP, PMM, PMP, PPM, PPP, POO, MOO, OPO, OMO, OOP, OOM, OMM, OMP, OPM, OPP, MOM, MOP, POM, POP, MMO, MPO, PMO, PPO}; Classification classification;
diff --git a/dep/g3dlite/G3D/Rect2D.h b/dep/g3dlite/include/G3D/Rect2D.h
index 2fb58c50465..2fb58c50465 100644
--- a/dep/g3dlite/G3D/Rect2D.h
+++ b/dep/g3dlite/include/G3D/Rect2D.h
diff --git a/dep/g3dlite/G3D/ReferenceCount.h b/dep/g3dlite/include/G3D/ReferenceCount.h
index 84591c6d8e5..84591c6d8e5 100644
--- a/dep/g3dlite/G3D/ReferenceCount.h
+++ b/dep/g3dlite/include/G3D/ReferenceCount.h
diff --git a/dep/g3dlite/G3D/RegistryUtil.h b/dep/g3dlite/include/G3D/RegistryUtil.h
index 4b47be5f4bd..4b47be5f4bd 100644
--- a/dep/g3dlite/G3D/RegistryUtil.h
+++ b/dep/g3dlite/include/G3D/RegistryUtil.h
diff --git a/dep/g3dlite/G3D/Set.h b/dep/g3dlite/include/G3D/Set.h
index 9a8e1b619bb..0ea96b880cf 100644
--- a/dep/g3dlite/G3D/Set.h
+++ b/dep/g3dlite/include/G3D/Set.h
@@ -57,9 +57,12 @@ public:
/**
Inserts into the table if not already present.
+ Returns true if this is the first time the element was added.
*/
- void insert(const T& member) {
- memberTable.set(member, true);
+ bool insert(const T& member) {
+ bool isNew = false;
+ memberTable.getCreate(member, isNew) = true;
+ return isNew;
}
/**
diff --git a/dep/g3dlite/G3D/SmallArray.h b/dep/g3dlite/include/G3D/SmallArray.h
index 41f9959e264..900d2335ee1 100644
--- a/dep/g3dlite/G3D/SmallArray.h
+++ b/dep/g3dlite/include/G3D/SmallArray.h
@@ -2,9 +2,9 @@
@file SmallArray.h
@created 2009-04-26
- @edited 2009-04-26
+ @edited 2010-02-26
- Copyright 2000-2009, Morgan McGuire, http://graphics.cs.williams.edu
+ Copyright 2000-2010, Morgan McGuire, http://graphics.cs.williams.edu
All rights reserved.
*/
#ifndef G3D_SmallArray_h
@@ -12,6 +12,7 @@
#include "G3D/platform.h"
#include "G3D/Array.h"
+#include "G3D/MemoryManager.h"
namespace G3D {
@@ -46,6 +47,11 @@ public:
resize(0, shrinkIfNecessary);
}
+ void clearAndSetMemoryManager(MemoryManager::Ref& m) {
+ clear();
+ m_rest.clearAndSetMemoryManager(m);
+ }
+
inline T& operator[](int i) {
debugAssert(i < m_size && i >= 0);
if (i < N) {
@@ -77,7 +83,7 @@ public:
push(v);
}
- void fastRemove(int i) {
+ void fastRemove(int i, bool shrinkIfNecessary = false) {
debugAssert(i < m_size && i >= 0);
if (i < N) {
if (m_size <= N) {
@@ -89,7 +95,7 @@ public:
}
} else {
// Removing from the rest array
- m_rest.fastRemove(i - N);
+ m_rest.fastRemove(i - N, shrinkIfNecessary);
}
--m_size;
}
diff --git a/dep/g3dlite/G3D/Sphere.h b/dep/g3dlite/include/G3D/Sphere.h
index 595b61c4bf1..595b61c4bf1 100644
--- a/dep/g3dlite/G3D/Sphere.h
+++ b/dep/g3dlite/include/G3D/Sphere.h
diff --git a/dep/g3dlite/G3D/Spline.h b/dep/g3dlite/include/G3D/Spline.h
index fdd29e69ce9..89f89194f1e 100644
--- a/dep/g3dlite/G3D/Spline.h
+++ b/dep/g3dlite/include/G3D/Spline.h
@@ -100,7 +100,7 @@ public:
See Real Time Rendering, 2nd edition, ch 12 for a general discussion
of splines and their properties.
- @sa G3D::UprightSpline, G3D::QuatSpline
+ \sa G3D::UprightSpline
*/
template<typename Control>
class Spline : public SplineBase {
@@ -270,7 +270,7 @@ protected:
}
/**
- Mutates the array of N control points. It is useful to override this
+ Mutates the array of N control points that begins at \a A. 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.
*/
@@ -333,10 +333,6 @@ public:
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
@@ -346,12 +342,17 @@ public:
float n0 = x / dt0;
float n1 = x / dt1;
float n2 = x / dt2;
+
+ const Control& dp0 = p1 + (p0*-1.0f);
+ const Control& dp1 = p2 + (p1*-1.0f);
+ const Control& dp2 = p3 + (p2*-1.0f);
+
const Control& dp1n1 = dp1 * n1;
const Control& tan1 = dp0 * n0 + dp1n1;
const Control& tan2 = dp1n1 + dp2 * n2;
sum =
- tan1 * weights[0]+
+ tan1 * weights[0] +
p1 * weights[1] +
p2 * weights[2] +
tan2 * weights[3];
diff --git a/dep/g3dlite/G3D/Stopwatch.h b/dep/g3dlite/include/G3D/Stopwatch.h
index 3f2aa9c8d86..3f2aa9c8d86 100644
--- a/dep/g3dlite/G3D/Stopwatch.h
+++ b/dep/g3dlite/include/G3D/Stopwatch.h
diff --git a/dep/g3dlite/G3D/System.h b/dep/g3dlite/include/G3D/System.h
index 56ef9c8e3dc..1c0cf99410c 100644
--- a/dep/g3dlite/G3D/System.h
+++ b/dep/g3dlite/include/G3D/System.h
@@ -1,8 +1,8 @@
-/**
+/**
@file System.h
-
+
@maintainer Morgan McGuire, http://graphics.cs.williams.edu
-
+
@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
@@ -39,7 +39,7 @@ 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.
+ string that must appear in your documentation.
<B>Your program can be commercial, closed-source</B> under
any license you want.
@deprecated Use System::license
@@ -48,19 +48,19 @@ std::string license();
/**
@brief The order in which the bytes of an integer are stored on a
-machine.
+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_BIG_ENDIAN,
G3D_LITTLE_ENDIAN
};
/**
- @brief OS and processor abstraction.
+ @brief OS and processor abstraction.
The first time any method is called the processor will be analyzed.
Future calls are then fast.
@@ -83,7 +83,7 @@ public:
@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
+ 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
@@ -140,7 +140,7 @@ private:
/** @brief Used for the singleton instance only. */
System();
- /** @brief The singleton instance.
+ /** @brief The singleton instance.
Used instead of a global variable to ensure that the order of
intialization is correct, which is critical because other
@@ -155,7 +155,7 @@ private:
CPUID_GET_HIGHEST_FUNCTION = 0x80000000,
CPUID_EXTENDED_FEATURES = 0x80000001};
- /** Helper macro to call cpuid functions and return all values
+ /** Helper macro to call cpuid functions and return all values
See http://software.intel.com/en-us/articles/intel-64-architecture-processor-topology-enumeration/
or http://www.amd.com/us-en/assets/content_type/white_papers_and_tech_docs/25481.pdf
@@ -171,10 +171,10 @@ private:
/** Called from init() */
void initTime();
-
+
public:
- /** Returns the speed of processor 0 in MHz.
+ /** Returns the speed of processor 0 in MHz.
Always returns 0 on linux.*/
inline static int cpuSpeedMHz() {
return instance().m_cpuSpeed;
@@ -229,7 +229,7 @@ public:
inline static const std::string& operatingSystem() {
return instance().m_operatingSystem;
}
-
+
/** e.g., 80686 */
inline static const std::string& cpuArchitecture() {
return instance().m_cpuArch;
@@ -241,24 +241,24 @@ public:
static std::string currentDateString();
/**
- Guarantees that the start of the array is aligned to the
+ 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);
/**
@@ -272,7 +272,7 @@ public:
static std::string mallocPerformance();
static void resetMallocPerformanceCounters();
- /**
+ /**
Returns a string describing the current usage of the buffer pools used for
optimizing System::malloc.
*/
@@ -294,12 +294,12 @@ public:
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.
@@ -331,7 +331,7 @@ public:
/**
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()
+ The sleep will be extremely precise; it uses System::time()
to calibrate the exact yeild time.
*/
static void sleep(RealTime t);
@@ -347,7 +347,7 @@ public:
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
@@ -358,7 +358,7 @@ public:
/**
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().
*/
@@ -392,7 +392,7 @@ public:
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
+ You can use outOfMemoryCallback to free data structures or to
register the failure.
*/
inline static OutOfMemoryCallback outOfMemoryCallback() {
@@ -401,7 +401,7 @@ public:
/** 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);
@@ -426,7 +426,7 @@ public:
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);
/**
@@ -472,11 +472,11 @@ public:
#elif defined(G3D_OSX)
inline uint64 System::getCycleCount() {
- //Note: To put off extra processing until the end, this does not
+ //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();
}
diff --git a/dep/g3dlite/G3D/Table.h b/dep/g3dlite/include/G3D/Table.h
index 287efa94d97..896a5a2a08a 100644
--- a/dep/g3dlite/G3D/Table.h
+++ b/dep/g3dlite/include/G3D/Table.h
@@ -846,7 +846,7 @@ public:
return true;
}
node = node->next;
- } while (node != NULL);
+ }
return false;
}
diff --git a/dep/g3dlite/G3D/TextInput.h b/dep/g3dlite/include/G3D/TextInput.h
index 33eb8c48e53..7aefb50ffa8 100644
--- a/dep/g3dlite/G3D/TextInput.h
+++ b/dep/g3dlite/include/G3D/TextInput.h
@@ -8,9 +8,9 @@
@cite Based on a lexer written by Aaron Orenstein.
@created 2002-11-27
- @edited 2009-11-24
+ @edited 2010-07-03
- Copyright 2000-2009, Morgan McGuire.
+ Copyright 2000-2010, Morgan McGuire.
All rights reserved.
*/
@@ -76,6 +76,7 @@ private:
bool _bool;
int _line;
int _character;
+ uint64 _bytePosition;
Type _type;
ExtendedType _extendedType;
@@ -86,14 +87,15 @@ public:
_bool(false),
_line(0),
_character(0),
+ _bytePosition(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, int L, int c, uint64 byte)
+ : _string(s), _bool(false), _line(L), _character(c), _bytePosition(byte), _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) {}
+ Token(Type t, ExtendedType e, const std::string& s, bool b, int L, int c, uint64 byte)
+ : _string(s), _bool(b), _line(L), _character(c), _bytePosition(byte), _type(t), _extendedType(e) {}
Type type() const {
return _type;
@@ -132,6 +134,12 @@ public:
return _character;
}
+ /** Number of bytes from the beginning of the buffer that this token was parsed from.
+ Begins at 0 */
+ uint64 bytePosition() const {
+ return _bytePosition;
+ }
+
/** Return the numeric value for a number type, or zero if this is
not a number type.
*/
@@ -212,6 +220,12 @@ public:
*/
class TextInput {
public:
+ /** Includes MSVC specials parsing */
+ static double parseNumber(const std::string& _string);
+
+ /** toLower(_string) == "true" */
+ static bool parseBoolean(const std::string& _string);
+
/** Tokenizer configuration options. */
class Settings {
@@ -316,12 +330,13 @@ public:
/**
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.
+ nan(), -1.#INF00 as -G3D::inf(), and 1.#INF00 as G3D::inf().
+
+ Note that the C99 standard specifies that a variety of formats
+ like "nan" are to be used; these are supported by
+ G3D::TextInput::Settings::simpleFloatSpecials.
- An alternative to specifying msvcSpecials is to read numbers as:
+ An alternative to specifying msvcFloatSpecials is to read numbers as:
<pre>
Token x = t.read();
Token y = t.peek();
@@ -339,10 +354,14 @@ public:
special format overrides the comment and will be parsed
instead.
- If signedNumbers is false msvcSpecials will not be parsed.
+ If signedNumbers is false msvcFloatSpecials will not be parsed.
Default is true. */
- bool msvcSpecials;
+ bool msvcFloatSpecials;
+
+ /** Parses "+inf', "-inf", "inf", "nan" as floats instead of symbols.
+ Defaults to true.*/
+ bool simpleFloatSpecials;
/**
Parse the following set of useful proof symbols:
@@ -362,7 +381,7 @@ public:
bool proofSymbols;
/**
- When parsing booleans and msvcSpecials, is case significant?
+ When parsing booleans and msvcFloatSpecials, is case significant?
Default is {true}
*/
bool caseSensitive;
@@ -623,9 +642,17 @@ public:
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.
+
+ \sa readString(), readStringToken(), readUntilNewlineAsString()
*/
void readString(const std::string& s);
+ /** Read from the beginning of the next token until the following newline
+ and return the result as a string, ignoring all parsing in between. The newline
+ is not returned in the string, and the following token read will be a newline or
+ end of file token (if they are enabled for parsing).*/
+ std::string readUntilNewlineAsString();
+
/** Reads a comment token or throws WrongTokenType, and returns the token.
Use this method (rather than readComment) if you want the token's
@@ -733,13 +760,13 @@ public:
/** Read a series of two specific symbols. See readSymbol. */
- inline void readSymbols(const std::string& s1, const std::string& s2) {
+ 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(
+ void readSymbols(
const std::string& s1,
const std::string& s2,
const std::string& s3) {
@@ -749,7 +776,7 @@ public:
}
/** Read a series of four specific symbols. See readSymbol. */
- inline void readSymbols(
+ void readSymbols(
const std::string& s1,
const std::string& s2,
const std::string& s3,
diff --git a/dep/g3dlite/G3D/TextOutput.h b/dep/g3dlite/include/G3D/TextOutput.h
index 4c22b7d5653..4c22b7d5653 100644
--- a/dep/g3dlite/G3D/TextOutput.h
+++ b/dep/g3dlite/include/G3D/TextOutput.h
diff --git a/dep/g3dlite/G3D/ThreadSet.h b/dep/g3dlite/include/G3D/ThreadSet.h
index 121f1415a1d..121f1415a1d 100644
--- a/dep/g3dlite/G3D/ThreadSet.h
+++ b/dep/g3dlite/include/G3D/ThreadSet.h
diff --git a/dep/g3dlite/G3D/Triangle.h b/dep/g3dlite/include/G3D/Triangle.h
index 590dbaad946..590dbaad946 100644
--- a/dep/g3dlite/G3D/Triangle.h
+++ b/dep/g3dlite/include/G3D/Triangle.h
diff --git a/dep/g3dlite/G3D/UprightFrame.h b/dep/g3dlite/include/G3D/UprightFrame.h
index ad5157cb14b..ad5157cb14b 100644
--- a/dep/g3dlite/G3D/UprightFrame.h
+++ b/dep/g3dlite/include/G3D/UprightFrame.h
diff --git a/dep/g3dlite/G3D/Vector2.h b/dep/g3dlite/include/G3D/Vector2.h
index dba7353785e..dba7353785e 100644
--- a/dep/g3dlite/G3D/Vector2.h
+++ b/dep/g3dlite/include/G3D/Vector2.h
diff --git a/dep/g3dlite/G3D/Vector2.inl b/dep/g3dlite/include/G3D/Vector2.inl
index 4f7c55a39cf..4f7c55a39cf 100644
--- a/dep/g3dlite/G3D/Vector2.inl
+++ b/dep/g3dlite/include/G3D/Vector2.inl
diff --git a/dep/g3dlite/G3D/Vector2int16.h b/dep/g3dlite/include/G3D/Vector2int16.h
index ba72266d75a..ba72266d75a 100644
--- a/dep/g3dlite/G3D/Vector2int16.h
+++ b/dep/g3dlite/include/G3D/Vector2int16.h
diff --git a/dep/g3dlite/G3D/Vector3.h b/dep/g3dlite/include/G3D/Vector3.h
index 4825efb9985..b4e684b92e5 100644
--- a/dep/g3dlite/G3D/Vector3.h
+++ b/dep/g3dlite/include/G3D/Vector3.h
@@ -209,9 +209,9 @@ public:
*/
inline Vector3 directionOrZero() const {
float mag = magnitude();
- if (G3D::fuzzyEq(mag, 0.0f)) {
+ if (mag < 0.0000001f) {
return Vector3::zero();
- } else if (G3D::fuzzyEq(mag, 1.0f)) {
+ } else if (mag < 1.00001f && mag > 0.99999f) {
return *this;
} else {
return *this * (1.0f / mag);
diff --git a/dep/g3dlite/G3D/Vector3.inl b/dep/g3dlite/include/G3D/Vector3.inl
index 9211c2a70fd..9211c2a70fd 100644
--- a/dep/g3dlite/G3D/Vector3.inl
+++ b/dep/g3dlite/include/G3D/Vector3.inl
diff --git a/dep/g3dlite/G3D/Vector3int16.h b/dep/g3dlite/include/G3D/Vector3int16.h
index 3197ea49d1a..6043aea9f61 100644
--- a/dep/g3dlite/G3D/Vector3int16.h
+++ b/dep/g3dlite/include/G3D/Vector3int16.h
@@ -80,7 +80,7 @@ public:
inline Vector3int16& operator+=(const Vector3int16& other) {
x += other.x;
y += other.y;
- z += other.y;
+ z += other.z;
return *this;
}
diff --git a/dep/g3dlite/G3D/Vector3int32.h b/dep/g3dlite/include/G3D/Vector3int32.h
index 2f256ea0300..2f256ea0300 100644
--- a/dep/g3dlite/G3D/Vector3int32.h
+++ b/dep/g3dlite/include/G3D/Vector3int32.h
diff --git a/dep/g3dlite/G3D/Vector4.h b/dep/g3dlite/include/G3D/Vector4.h
index 5e511451f86..5e511451f86 100644
--- a/dep/g3dlite/G3D/Vector4.h
+++ b/dep/g3dlite/include/G3D/Vector4.h
diff --git a/dep/g3dlite/G3D/Vector4.inl b/dep/g3dlite/include/G3D/Vector4.inl
index 576cca83b56..576cca83b56 100644
--- a/dep/g3dlite/G3D/Vector4.inl
+++ b/dep/g3dlite/include/G3D/Vector4.inl
diff --git a/dep/g3dlite/G3D/Vector4int8.h b/dep/g3dlite/include/G3D/Vector4int8.h
index 544b693e8b3..544b693e8b3 100644
--- a/dep/g3dlite/G3D/Vector4int8.h
+++ b/dep/g3dlite/include/G3D/Vector4int8.h
diff --git a/dep/g3dlite/G3D/WeakCache.h b/dep/g3dlite/include/G3D/WeakCache.h
index f9fdc4bbd5b..f9fdc4bbd5b 100644
--- a/dep/g3dlite/G3D/WeakCache.h
+++ b/dep/g3dlite/include/G3D/WeakCache.h
diff --git a/dep/g3dlite/G3D/Welder.h b/dep/g3dlite/include/G3D/Welder.h
index 2c2554da7b6..2c2554da7b6 100644
--- a/dep/g3dlite/G3D/Welder.h
+++ b/dep/g3dlite/include/G3D/Welder.h
diff --git a/dep/g3dlite/G3D/WrapMode.h b/dep/g3dlite/include/G3D/WrapMode.h
index 8ef38a77c23..25a2fb47256 100644
--- a/dep/g3dlite/G3D/WrapMode.h
+++ b/dep/g3dlite/include/G3D/WrapMode.h
@@ -4,7 +4,7 @@
@maintainer Morgan McGuire, http://graphics.cs.williams.edu
@created 2007-04-17
- @edited 2007-04-17
+ @edited 2010-04-17
Copyright 2000-2010, Morgan McGuire.
All rights reserved.
@@ -15,6 +15,7 @@
#include "G3D/platform.h"
#include "G3D/enumclass.h"
+#include "G3D/Any.h"
#ifdef IGNORE
# undef IGNORE
@@ -65,25 +66,22 @@ public:
private:
+ static const char* toString(int i, Value& v) {
+ static const char* str[] = {"CLAMP", "TILE", "ZERO", "IGNORE", "ERROR", NULL};
+ static const Value val[] = {CLAMP, TILE, ZERO, IGNORE, ERROR};
+ const char* s = str[i];
+ if (s) {
+ v = val[i];
+ }
+ return s;
+ }
+
Value value;
public:
G3D_DECLARE_ENUM_CLASS_METHODS(WrapMode);
- inline const char* toString() const {
- static const char* s[] = {"CLAMP", "TILE", "ZERO", "IGNORE", "ERROR"};
- return s[value];
- }
-
- inline explicit WrapMode(const std::string& x) : value(ERROR) {
- static const char* s[] = {"CLAMP", "TILE", "ZERO", "IGNORE", "ERROR"};
- for (int i = 0; i < 5; ++i) {
- if (x == s[i]) {
- value = (Value)i;
- }
- }
- }
};
} // namespace G3D
diff --git a/dep/g3dlite/include/G3D/XML.h b/dep/g3dlite/include/G3D/XML.h
new file mode 100644
index 00000000000..9ff98f9d171
--- /dev/null
+++ b/dep/g3dlite/include/G3D/XML.h
@@ -0,0 +1,204 @@
+/**
+ @file XML.h
+
+ @author Morgan McGuire
+ @maintainer Morgan McGuire
+
+ @created 2010-02-11
+ @edited 2010-02-24
+
+ Copyright 2000-2010, Morgan McGuire.
+ All rights reserved.
+ */
+
+#ifndef G3D_XML_h
+#define G3D_XML_h
+
+#include "G3D/platform.h"
+#include "G3D/Table.h"
+#include "G3D/Array.h"
+#include "G3D/format.h"
+#include <string>
+
+namespace G3D {
+
+class TextInput;
+class TextOutput;
+
+/**
+\brief Easy loading and saving of XML and HTML files.
+
+The XML class is intended primarily for interchange with other
+programs. We recommend using G3D::Any to make your own human-readable
+formats because it is a more general syntax, the implementation is
+more efficient, and contains better error handling.
+
+Every XML is either a <i>VALUE</i>, or a <i>TAG</i> that contains both
+a table of its XML attributes and an array of its children. Children
+are nested tags and the strings between the nested tags.
+
+No validation is performed, and the XML must be completely legal. XML
+Entity references (e.g., the ampersand codes for greater than and less
+than) are not automatically converted.
+
+Tags with names that begin with "!" or "?" are ignored. Comment tags must
+end with "-->" e.g.,
+
+<pre>
+ <?xml version="1.0" encoding="ISO-8859-1"?>
+ <!DOCTYPE note SYSTEM "Note.dtd">
+ <!-- a comment -->
+</pre>
+
+\sa G3D::Any, http://www.grinninglizard.com/tinyxml/
+
+<pre>
+<foo key0="value0" key1="value1">
+ child0 ...
+ <child1>...</child1>
+ child2 ...
+</foo>
+</pre>
+*/
+class XML {
+public:
+
+ enum Type {VALUE, TAG};
+
+ typedef Table<std::string, XML> AttributeTable;
+
+private:
+
+ Type m_type;
+ std::string m_name;
+ std::string m_value;
+ AttributeTable m_attribute;
+ Array<XML> m_child;
+
+public:
+
+ XML() : m_type(VALUE) {}
+
+ XML(const std::string& v) : m_type(VALUE), m_value(v) {}
+
+ XML(const double& v) : m_type(VALUE), m_value(format("%f", v)) {}
+
+ XML(float v) : m_type(VALUE), m_value(format("%f", v)) {}
+
+ XML(int v) : m_type(VALUE), m_value(format("%d", v)) {}
+
+ /** \param tagType Must be XML::TAG to dismbiguate from the string constructor */
+ XML(Type tagType, const std::string& name, const AttributeTable& at, const Array<XML>& ch = Array<XML>()) : m_type(TAG), m_name(name), m_attribute(at), m_child(ch) {
+ (void)tagType;
+ debugAssert(tagType == TAG);
+ }
+
+ /** \param tagType Must be XML::TAG to dismbiguate from the string constructor */
+ XML(Type tagType, const std::string& name, const Array<XML>& ch = Array<XML>()) : m_type(TAG), m_name(name), m_child(ch) {
+ (void)tagType;
+ debugAssert(tagType == TAG);
+ }
+
+ XML(TextInput& t);
+
+ void serialize(TextOutput& t) const;
+
+ void deserialize(TextInput& t);
+
+ void load(const std::string& filename);
+
+ void save(const std::string& filename) const;
+
+ void parse(const std::string &s);
+
+ void unparse(std::string& s) const;
+
+ const AttributeTable& attributeTable() const {
+ return m_attribute;
+ }
+
+ const Array<XML> childArray() const {
+ return m_child;
+ }
+
+ /** Array size; zero for a VALUE */
+ int numChildren() const {
+ return m_child.size();
+ }
+
+ /** Attribute table size; zero for a TAG */
+ int numAttributes() const {
+ return m_attribute.size();
+ }
+
+ /** Return child \a i. Children are nested tags and the unquoted
+ strings of characters between tags.*/
+ const XML& operator[](int i) const {
+ return m_child[i];
+ }
+
+ /** Return the attribute with this name. */
+ const XML& operator[](const std::string& k) const {
+ return m_attribute[k];
+ }
+
+ const bool containsAttribute(const std::string& k) const {
+ return m_attribute.containsKey(k);
+ }
+
+ /** Note that the result is always copied, making this inefficient
+ for return values that are not VALUEs. */
+ XML get(const std::string& k, const XML& defaultVal) const {
+ const XML* x = m_attribute.getPointer(k);
+ if (x) {
+ return *x;
+ } else {
+ return defaultVal;
+ }
+ }
+
+ Type type() const {
+ return m_type;
+ }
+
+ /** The name, if this is a TAG. */
+ const std::string name() const {
+ return m_name;
+ }
+
+ /** Returns "" if a TAG. */
+ const std::string& string() const {
+ return m_value;
+ }
+
+ /** Parse as a number. Returns nan() if a TAG or unparseable as a number. */
+ double number() const;
+
+ /** Returns false if a TAG. */
+ bool boolean() const;
+
+ operator std::string() const {
+ return m_value;
+ }
+
+ operator bool() const {
+ return boolean();
+ }
+
+ operator double() const {
+ return number();
+ }
+
+ operator float() const {
+ return float(number());
+ }
+
+ operator int() const {
+ return iRound(number());
+ }
+
+}; // class XML
+
+} // namespace G3D
+
+#endif
diff --git a/dep/g3dlite/G3D/constants.h b/dep/g3dlite/include/G3D/constants.h
index dd5cb3649e5..4121f744e17 100644
--- a/dep/g3dlite/G3D/constants.h
+++ b/dep/g3dlite/include/G3D/constants.h
@@ -3,13 +3,14 @@
@maintainer Morgan McGuire, http://graphics.cs.williams.edu
@created 2009-05-20
- @edited 2009-05-20
+ @edited 2010-05-20
*/
#ifndef G3D_constants_h
#define G3D_constants_h
#include "G3D/platform.h"
#include "G3D/enumclass.h"
+#include "G3D/Any.h"
namespace G3D {
@@ -29,6 +30,16 @@ public:
};
private:
+
+ static const char* toString(int i, Value& v) {
+ static const char* str[] = {"POINTS", "LINES", "LINE_STRIP", "TRIANGLES", "TRIANGLE_FAN", "QUADS", "QUAD_STRIP", NULL};
+ static const Value val[] = {POINTS, LINES, LINE_STRIP, TRIANGLES, TRIANGLE_FAN, QUADS, QUAD_STRIP};
+ const char* s = str[i];
+ if (s) {
+ v = val[i];
+ }
+ return s;
+ }
Value value;
@@ -66,18 +77,21 @@ public:
private:
- /** Used for to/from string conversion. Last is the emtpy string as a sentinel */
- static const std::string str[7];
- static const Value enm[6];
+ static const char* toString(int i, Value& v) {
+ static const char* str[] = {"NONE", "STATIC_ENV", "DYNAMIC_FLAT", "DYNAMIC_FLAT_MULTILAYER", "DYNAMIC_ENV", "BEST", NULL};
+ static const Value val[] = {NONE, STATIC_ENV, DYNAMIC_FLAT, DYNAMIC_FLAT_MULTILAYER, DYNAMIC_ENV, BEST};
+ const char* s = str[i];
+ if (s) {
+ v = val[i];
+ }
+ return s;
+ }
+
Value value;
public:
G3D_DECLARE_ENUM_CLASS_METHODS(RefractionQuality);
- RefractionQuality(const class Any&);
- RefractionQuality& operator=(const Any&);
- operator Any() const;
- const std::string& toString() const;
};
@@ -105,18 +119,20 @@ public:
private:
- /** Used for to/from string conversion. Last is the emtpy string as a sentinel */
- static const std::string str[6];
- static const Value enm[5];
+ static const char* toString(int i, Value& v) {
+ static const char* str[] = {"NONE", "STATIC_ENV", "DYNAMIC_PLANAR", "DYNAMIC_ENV", "BEST", NULL};
+ static const Value val[] = {NONE, STATIC_ENV, DYNAMIC_PLANAR, DYNAMIC_ENV, BEST};
+ const char* s = str[i];
+ if (s) {
+ v = val[i];
+ }
+ return s;
+ }
Value value;
public:
G3D_DECLARE_ENUM_CLASS_METHODS(MirrorQuality);
- MirrorQuality(const class Any&);
- MirrorQuality& operator=(const Any&);
- operator Any() const;
- const std::string& toString() const;
};
} // namespace G3D
diff --git a/dep/g3dlite/G3D/debug.h b/dep/g3dlite/include/G3D/debug.h
index a7697fe9c01..a7697fe9c01 100644
--- a/dep/g3dlite/G3D/debug.h
+++ b/dep/g3dlite/include/G3D/debug.h
diff --git a/dep/g3dlite/G3D/debugAssert.h b/dep/g3dlite/include/G3D/debugAssert.h
index 432e97e679d..edff671061d 100644
--- a/dep/g3dlite/G3D/debugAssert.h
+++ b/dep/g3dlite/include/G3D/debugAssert.h
@@ -39,7 +39,7 @@
#ifdef G3D_LINUX
// Needed so we can define a global display
// pointer for debugAssert.
-#if SOMEONE_MADE_THIS_USEFUL
+#if 0 /* G3DFIX: Disabled to avoid requirement for X11 libraries */
#include <X11/Xlib.h>
#include <X11/Xutil.h>
#include <X11/Xatom.h>
@@ -180,7 +180,7 @@ namespace _internal {
namespace G3D { namespace _internal {
#ifdef G3D_LINUX
-#if SOMEONE_MADE_THIS_USEFUL
+#if 0 /* G3DFIX: Disabled to avoid requirement for X11 libraries */
/**
A pointer to the X11 display. Initially NULL. If set to a
non-null value (e.g. by SDLWindow), debugAssert attempts to use
diff --git a/dep/g3dlite/G3D/debugPrintf.h b/dep/g3dlite/include/G3D/debugPrintf.h
index b42151cae9e..b42151cae9e 100644
--- a/dep/g3dlite/G3D/debugPrintf.h
+++ b/dep/g3dlite/include/G3D/debugPrintf.h
diff --git a/dep/g3dlite/G3D/enumclass.h b/dep/g3dlite/include/G3D/enumclass.h
index c7dfe45f14f..2874a630ad9 100644
--- a/dep/g3dlite/G3D/enumclass.h
+++ b/dep/g3dlite/include/G3D/enumclass.h
@@ -22,112 +22,170 @@
Enum classes are initialized to their zero value by default.
+ You must implement the following method before calling G3D_DECLARE_ENUM_CLASS_METHODS, as either:
+
+ <pre>
+ static const char* toString(int i, Value& v) {
+ static const char* str[] = {"FUEL", "FOOD", "WATER", NULL}; // Whatever your enum values are
+ static const Value val[] = {FUEL, FOOD, WATER}; // Whatever your enum values are
+ const char* s = str[i];
+ if (s) {
+ v = val[i];
+ }
+ return s;
+ }
+ </pre>
+
See GLG3D/GKey.h for an example.
\sa G3D_DECLARE_ENUM_CLASS_HASHCODE
*/
#define G3D_DECLARE_ENUM_CLASS_METHODS(Classname)\
- inline Classname(char v) : value((Value)v) {}\
+private: \
+ void fromString(const std::string& x) {\
+ Value v;\
+ const char* s;\
+ int i = 0;\
+\
+ do {\
+ s = toString(i, v);\
+ if (x == s) {\
+ value = v;\
+ return;\
+ }\
+ ++i;\
+ } while (s);\
+ }\
+\
+public:\
+\
+ const char* toString() const {\
+ const char* s;\
+ int i = 0;\
+ Value v = (Value)0;\
+ while (true) {\
+ s = toString(i, v);\
+ if ((s == NULL) || (v == value)) {\
+ return s;\
+ }\
+ ++i;\
+ }\
+ return NULL;\
+ }\
+\
+ explicit Classname(const std::string& x) : value((Value)0) {\
+ fromString(x);\
+ }\
+\
+ Classname(const Any& a) : value((Value)0) {\
+ fromString(a.string());\
+ }\
+\
+ operator Any() const {\
+ return Any(toString());\
+ }\
+\
+ Classname(char v) : value((Value)v) {}\
\
- inline Classname() : value((Value)0) {}\
+ Classname() : value((Value)0) {}\
\
- inline Classname(const Value v) : value(v) {}\
+ Classname(const Value v) : value(v) {}\
\
- explicit inline Classname(int v) : value((Value)v) {}\
+ explicit 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 {\
+ operator int() const {\
return (int)value;\
}\
\
- inline bool operator== (const Classname other) const {\
+ bool operator== (const Classname other) const {\
return value == other.value;\
}\
\
- inline bool operator== (const Classname::Value other) const {\
+ bool operator== (const Classname::Value other) const {\
return value == other;\
}\
\
- inline bool operator!= (const Classname other) const {\
+ bool operator!= (const Classname other) const {\
return value != other.value;\
}\
\
- inline bool operator!= (const Classname::Value other) const {\
+ bool operator!= (const Classname::Value other) const {\
return value != other;\
}\
\
- inline bool operator< (const Classname other) const {\
+ bool operator< (const Classname other) const {\
return value < other.value;\
}\
\
- inline bool operator> (const Classname other) const {\
+ bool operator> (const Classname other) const {\
return value > other.value;\
}\
\
- inline bool operator>= (const Classname other) const {\
+ bool operator>= (const Classname other) const {\
return value >= other.value;\
}\
\
- inline bool operator<= (const Classname other) const {\
+ bool operator<= (const Classname other) const {\
return value <= other.value;\
}\
\
- inline bool operator< (const Value other) const {\
+ bool operator< (const Value other) const {\
return value < other;\
}\
\
- inline bool operator> (const Value other) const {\
+ bool operator> (const Value other) const {\
return value > other;\
}\
\
- inline bool operator<= (const Value other) const {\
+ bool operator<= (const Value other) const {\
return value <= other;\
}\
\
- inline bool operator>= (const Value other) const {\
+ bool operator>= (const Value other) const {\
return value >= other;\
}\
\
- inline Classname& operator-- () {\
+ Classname& operator-- () {\
value = (Value)((int)value - 1);\
return *this;\
}\
\
- inline Classname& operator++ () {\
+ Classname& operator++ () {\
value = (Value)((int)value + 1);\
return *this;\
}\
\
- inline Classname& operator+= (const int x) {\
+ Classname& operator+= (const int x) {\
value = (Value)((int)value + x);\
return *this;\
}\
\
- inline Classname& operator-= (const int x) {\
+ Classname& operator-= (const int x) {\
value = (Value)((int)value - x);\
return *this;\
}\
\
- inline Classname operator+ (const int x) const {\
+ Classname operator+ (const int x) const {\
return Classname((int)value + x);\
}\
\
- inline Classname operator- (const int x) const {\
+ Classname operator- (const int x) const {\
return Classname((int)value - x);\
}\
\
- inline unsigned int hashCode() const {\
+ unsigned int hashCode() const {\
return (unsigned int)value;\
}\
\
- inline void serialize(BinaryOutput& b) const {\
+ void serialize(BinaryOutput& b) const {\
b.writeInt32(value);\
}\
\
- inline void deserialize(BinaryInput& b) {\
+ void deserialize(BinaryInput& b) {\
value = (Value)b.readInt32();\
}
diff --git a/dep/g3dlite/G3D/fileutils.h b/dep/g3dlite/include/G3D/fileutils.h
index 9e49777d93a..89b41690206 100644
--- a/dep/g3dlite/G3D/fileutils.h
+++ b/dep/g3dlite/include/G3D/fileutils.h
@@ -4,14 +4,14 @@
@maintainer Morgan McGuire, http://graphics.cs.williams.edu
@author 2002-06-06
- @edited 2010-02-06
+ @edited 2010-03-06
Copyright 2000-2010, Morgan McGuire.
All rights reserved.
*/
-#ifndef G3D_fileUtils_h
-#define G3D_fileUtils_h
+#ifndef G3D_fileutils_h
+#define G3D_fileutils_h
#include "G3D/platform.h"
#include <string>
@@ -68,97 +68,11 @@ void writeWholeFile(
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()
- \param trustCache If true and \a lookInZipfiles is true, cache directory and zipfile contents
- so that subsequent calls to the same directory are fast.
-
- \sa G3D::clearFileSystemCache, G3D::zipfileExists
- */
-bool fileExists
-(const std::string& filename,
- bool lookInZipfiles = true,
- bool trustCache = true);
-
-
-/** Clears the cache used by fileExists */
-void clearFileSystemCache();
/**
Returns true if the given file (or directory) exists
@@ -242,10 +156,7 @@ 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. */
+/** Appends file onto dirname, ensuring a / if needed. \deprecated Use FilePath::concat */
std::string pathConcat(const std::string& dirname, const std::string& file);
} // namespace
diff --git a/dep/g3dlite/G3D/filter.h b/dep/g3dlite/include/G3D/filter.h
index 609477b79c9..609477b79c9 100644
--- a/dep/g3dlite/G3D/filter.h
+++ b/dep/g3dlite/include/G3D/filter.h
diff --git a/dep/g3dlite/G3D/format.h b/dep/g3dlite/include/G3D/format.h
index 3c7f0678876..3c7f0678876 100644
--- a/dep/g3dlite/G3D/format.h
+++ b/dep/g3dlite/include/G3D/format.h
diff --git a/dep/g3dlite/G3D/g3dfnmatch.h b/dep/g3dlite/include/G3D/g3dfnmatch.h
index 464b3927eee..464b3927eee 100644
--- a/dep/g3dlite/G3D/g3dfnmatch.h
+++ b/dep/g3dlite/include/G3D/g3dfnmatch.h
diff --git a/dep/g3dlite/G3D/g3dmath.h b/dep/g3dlite/include/G3D/g3dmath.h
index d16214ebb37..b0c98aeaf04 100644
--- a/dep/g3dlite/G3D/g3dmath.h
+++ b/dep/g3dlite/include/G3D/g3dmath.h
@@ -135,6 +135,10 @@ inline double pi() {
return 3.1415926535898;
}
+inline float pif() {
+ return 3.1415926535898f;
+}
+
inline double halfPi() {
return 1.57079633;
}
@@ -608,6 +612,18 @@ inline double aCos (double fValue) {
}
}
+inline float acos (float fValue) {
+ if ( -1.0f < fValue ) {
+ if ( fValue < 1.0f ) {
+ return ::acos(fValue);
+ } else {
+ return 0.0f;
+ }
+ } else {
+ return pif();
+ }
+}
+
//----------------------------------------------------------------------------
inline double aSin (double fValue) {
if ( -1.0 < fValue ) {
diff --git a/dep/g3dlite/G3D/g3dmath.inl b/dep/g3dlite/include/G3D/g3dmath.inl
index 9bf661a7ebc..9bf661a7ebc 100644
--- a/dep/g3dlite/G3D/g3dmath.inl
+++ b/dep/g3dlite/include/G3D/g3dmath.inl
diff --git a/dep/g3dlite/include/G3D/netheaders.h b/dep/g3dlite/include/G3D/netheaders.h
new file mode 100644
index 00000000000..a82d7c36f36
--- /dev/null
+++ b/dep/g3dlite/include/G3D/netheaders.h
@@ -0,0 +1,24 @@
+#ifndef G3D_netheaders_h
+#define G3D_netheaders_h
+
+#include "G3D/platform.h"
+
+#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
+
+#endif
diff --git a/dep/g3dlite/include/G3D/networkHelpers.h b/dep/g3dlite/include/G3D/networkHelpers.h
new file mode 100644
index 00000000000..7a532d71ee6
--- /dev/null
+++ b/dep/g3dlite/include/G3D/networkHelpers.h
@@ -0,0 +1,91 @@
+// Included by NetworkDevice.cpp and TCPConduit.cpp to provide a platform-independent networking base
+
+#include <cstring>
+#include <stdlib.h>
+#include <time.h>
+
+#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
diff --git a/dep/g3dlite/G3D/platform.h b/dep/g3dlite/include/G3D/platform.h
index 11ba0127a16..d399135101b 100644
--- a/dep/g3dlite/G3D/platform.h
+++ b/dep/g3dlite/include/G3D/platform.h
@@ -13,10 +13,10 @@
#define G3D_platform_h
/**
- The version number of G3D in the form: MmmBB ->
+ The version number of G3D in the form: MmmBB ->
version M.mm [beta BB]
*/
-#define G3D_VER 80004
+#define G3D_VER 80000
// fatal error for unsupported architectures
#if defined(__powerpc__)
@@ -37,13 +37,19 @@
# define G3D_DEBUG
#endif
+/** 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
+
#ifndef _MSC_VER
/// Fast call is a register-based optimized calling convention supported only by Visual C++
#define __fastcall
-
#endif
-#ifdef _MSC_VER
+#ifdef _MSC_VER
#define G3D_WIN32
#elif defined(__FreeBSD__) || defined(__OpenBSD__)
#define G3D_FREEBSD
@@ -89,7 +95,7 @@
# ifndef __GNUC__
# error G3D only supports the gcc compiler on OS X.
# endif
-
+
# if defined(__i386__)
# define G3D_OSX_INTEL
# elif defined(__PPC__)
@@ -142,7 +148,7 @@
/** @def G3D_CHECK_PRINTF_METHOD_ARGS()
Enables printf parameter validation on gcc. */
-# define G3D_CHECK_PRINTF_METHOD_ARGS
+# define G3D_CHECK_PRINTF_METHOD_ARGS
/** @def G3D_CHECK_PRINTF_METHOD_ARGS()
Enables printf parameter validation on gcc. */
@@ -151,7 +157,7 @@
// 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.
+ // 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
@@ -285,18 +291,18 @@ int WINAPI WinMain(HINSTANCE hInst, HINSTANCE hPrev, LPSTR szCmdLine, int sw) {\
#endif
-/**
+/**
@def STR(expression)
Creates a string from the expression. Frequently used with G3D::Shader
- to express shading programs inline.
+ to express shading programs inline.
<CODE>STR(this becomes a string)\verbatim<PRE>\endverbatim evaluates the same as \verbatim<CODE>\endverbatim"this becomes a string"</CODE>
*/
#define STR(x) #x
/** @def PRAGMA(expression)
- \#pragma may not appear inside a macro, so this uses the pragma operator
+ \#pragma may not appear inside a macro, so this uses the pragma operator
to create an equivalent statement.*/
#ifdef _MSC_VER
// Microsoft's version http://msdn.microsoft.com/en-us/library/d9x1s805.aspx
@@ -322,7 +328,7 @@ int WINAPI WinMain(HINSTANCE hInst, HINSTANCE hPrev, LPSTR szCmdLine, int sw) {\
# define G3D_END_PACKED_CLASS(byteAlign) ; PRAGMA( pack(pop) )
#elif defined(__GNUC__)
# define G3D_END_PACKED_CLASS(byteAlign) __attribute((aligned(byteAlign))) ;
-#else
+#else
# define G3D_END_PACKED_CLASS(byteAlign) ;
#endif
diff --git a/dep/g3dlite/G3D/prompt.h b/dep/g3dlite/include/G3D/prompt.h
index c6df628099e..c6df628099e 100644
--- a/dep/g3dlite/G3D/prompt.h
+++ b/dep/g3dlite/include/G3D/prompt.h
diff --git a/dep/g3dlite/G3D/serialize.h b/dep/g3dlite/include/G3D/serialize.h
index 2382c0ee0fd..2382c0ee0fd 100644
--- a/dep/g3dlite/G3D/serialize.h
+++ b/dep/g3dlite/include/G3D/serialize.h
diff --git a/dep/g3dlite/G3D/splinefunc.h b/dep/g3dlite/include/G3D/splinefunc.h
index 3f3a018c292..3f3a018c292 100644
--- a/dep/g3dlite/G3D/splinefunc.h
+++ b/dep/g3dlite/include/G3D/splinefunc.h
diff --git a/dep/g3dlite/G3D/stringutils.h b/dep/g3dlite/include/G3D/stringutils.h
index e15a757a7a6..42a4886521a 100644
--- a/dep/g3dlite/G3D/stringutils.h
+++ b/dep/g3dlite/include/G3D/stringutils.h
@@ -4,11 +4,11 @@
@maintainer Morgan McGuire, http://graphics.cs.williams.edu
@author 2000-09-09
- @edited 2008-08-05
+ @edited 2010-03-05
*/
-#ifndef G3D_STRINGUTILS_H
-#define G3D_STRINGUTILS_H
+#ifndef G3D_stringutils_h
+#define G3D_stringutils_h
#include "G3D/platform.h"
#include "G3D/Array.h"
@@ -28,6 +28,33 @@ extern const char* NEWLINE;
*/
void parseCommaSeparated(const std::string s, Array<std::string>& array, bool stripQuotes = true);
+/** Finds the index of the first '\\' or '/' character, starting at index \a start.
+ \sa G3D::findLastSlash, G3D::isSlash
+*/
+inline int findSlash(const std::string& f, int start = 0) {
+ int i = f.find('/', start);
+ int j = f.find('\\', start);
+ if (((i != -1) && (i < j)) || (j == -1)) {
+ return i;
+ } else {
+ return j;
+ }
+}
+
+
+/** Finds the index of the first '\\' or '/' character, starting at index \a start (if \a start is -1, starts at the end of the string).
+ \sa G3D::findSlash, G3D::isSlash
+ */
+inline int findLastSlash(const std::string& f, int start = -1) {
+ if (start == -1) {
+ start = f.length() - 1;
+ }
+
+ int i = f.rfind('/', start);
+ int j = f.rfind('\\', start);
+ return max(i, j);
+}
+
/**
Returns true if the test string begins with the pattern string.
*/
diff --git a/dep/g3dlite/G3D/uint128.h b/dep/g3dlite/include/G3D/uint128.h
index da1af3ec272..da1af3ec272 100644
--- a/dep/g3dlite/G3D/uint128.h
+++ b/dep/g3dlite/include/G3D/uint128.h
diff --git a/dep/g3dlite/G3D/units.h b/dep/g3dlite/include/G3D/units.h
index 2e30304dc62..2e30304dc62 100644
--- a/dep/g3dlite/G3D/units.h
+++ b/dep/g3dlite/include/G3D/units.h
diff --git a/dep/g3dlite/G3D/vectorMath.h b/dep/g3dlite/include/G3D/vectorMath.h
index ac6d2b32e9d..ac6d2b32e9d 100644
--- a/dep/g3dlite/G3D/vectorMath.h
+++ b/dep/g3dlite/include/G3D/vectorMath.h
diff --git a/dep/g3dlite/AABox.cpp b/dep/g3dlite/source/AABox.cpp
index 035497aa3c4..035497aa3c4 100644
--- a/dep/g3dlite/AABox.cpp
+++ b/dep/g3dlite/source/AABox.cpp
diff --git a/dep/g3dlite/Any.cpp b/dep/g3dlite/source/Any.cpp
index de4d32e83ea..92159bb1862 100644
--- a/dep/g3dlite/Any.cpp
+++ b/dep/g3dlite/source/Any.cpp
@@ -5,9 +5,9 @@
@author Shawn Yarbrough
@created 2006-06-11
- @edited 2009-11-15
+ @edited 2010-07-24
- Copyright 2000-2009, Morgan McGuire.
+ Copyright 2000-2010, Morgan McGuire.
All rights reserved.
*/
@@ -15,11 +15,73 @@
#include "G3D/TextOutput.h"
#include "G3D/TextInput.h"
#include "G3D/stringutils.h"
+#include "G3D/fileutils.h"
+#include "G3D/FileSystem.h"
#include <deque>
#include <iostream>
namespace G3D {
+std::string Any::resolveStringAsFilename() const {
+ verifyType(STRING);
+ std::string f = FileSystem::resolve(string(), sourceDirectory());
+ if (FileSystem::exists(f)) {
+ return f;
+ } else {
+ const std::string& s = System::findDataFile(string(), false);
+ if (s.empty()) {
+ return string();
+ } else {
+ return s;
+ }
+ }
+}
+
+
+bool Any::nameBeginsWith(const std::string& s) const {
+ return nameBeginsWith(s.c_str());
+}
+
+
+bool Any::nameEquals(const std::string& s) const {
+ // If std::string has a fast hash compare, use it first
+ return (name() == s) || nameEquals(s.c_str());
+}
+
+
+inline static char toLower(char c) {
+ return ((c >= 'A') && (c <= 'Z')) ? (c - 'A' + 'a') : c;
+}
+
+
+bool Any::nameBeginsWith(const char* s) const {
+ verifyType(Any::ARRAY, Any::TABLE);
+
+ const char* n = name().c_str();
+ // Walk through character-by-character
+ while ((*s != '\0') && (*n != '\0')) {
+ if (toLower(*s) != toLower(*n)) {
+ // Mismatch
+ return false;
+ }
+ ++s; ++n;
+ }
+ // Make sure s ran out no later than n
+ return (*s == '\0');
+}
+
+
+bool Any::nameEquals(const char* s) const {
+ verifyType(Any::ARRAY, Any::TABLE);
+#ifdef G3D_WIN32
+ return stricmp(name().c_str(), s) == 0;
+#else
+ return strcasecmp(name().c_str(), s) == 0;
+#endif
+
+}
+
+
void Any::beforeRead() const {
if (isPlaceholder()) {
// Tried to read from a placeholder--throw an exception as if
@@ -675,11 +737,10 @@ bool Any::operator!=(const Any& x) const {
static void getDeserializeSettings(TextInput::Settings& settings) {
settings.cppBlockComments = true;
settings.cppLineComments = true;
- settings.otherLineComments = true;
- settings.otherCommentCharacter = '#';
+ settings.otherLineComments = false;
settings.generateCommentTokens = true;
settings.singleQuotedStrings = false;
- settings.msvcSpecials = false;
+ settings.msvcFloatSpecials = false;
settings.caseSensitive = false;
}
@@ -708,7 +769,7 @@ void Any::load(const std::string& filename) {
TextInput::Settings settings;
getDeserializeSettings(settings);
- TextInput ti(filename, settings);
+ TextInput ti(FileSystem::resolve(filename), settings);
deserialize(ti);
}
@@ -870,12 +931,6 @@ static bool isClose(const char c) {
}
-/** True if \a s is a C++ name operator */
-static bool isNameOperator(const std::string& s) {
- return s == "." || s == "::" || s == "->";
-}
-
-
void Any::deserializeName(TextInput& ti, Token& token, std::string& name) {
debugAssert(token.type() == Token::SYMBOL);
std::string s = token.string();
@@ -928,6 +983,9 @@ void Any::deserialize(TextInput& ti, Token& token) {
"File ended without a properly formed Any");
}
+ // Do we need to read one more token after the end?
+ bool needRead = true;
+
switch (token.type()) {
case Token::STRING:
m_type = STRING;
@@ -951,8 +1009,41 @@ void Any::deserialize(TextInput& ti, Token& token) {
break;
case Token::SYMBOL:
- // Named Array, Named Table, Array, Table, or NONE
- if (toUpper(token.string()) == "NONE") {
+ // Pragma, Named Array, Named Table, Array, Table, or NONE
+ if (token.string() == "#") {
+ // Pragma
+
+ // Currently, "include" is the only pragma allowed
+ token = ti.read();
+ if (! ((token.type() == Token::SYMBOL) &&
+ (token.string() == "include"))) {
+ throw ParseError(ti.filename(), token.line(), token.character(),
+ "Expected 'include' pragma after '#'");
+ }
+
+ ti.readSymbol("(");
+ const std::string& includeName = ti.readString();
+
+ // Find the include file
+ const std::string& myPath = filenamePath(ti.filename());
+ std::string t = pathConcat(myPath, includeName);
+
+ if (! FileSystem::exists(t)) {
+ // Try and find it, starting with cwd
+ t = System::findDataFile(includeName);
+ }
+
+ // Read the included file
+ load(t);
+
+ // Update the source information
+ ensureData();
+ m_data->source.filename +=
+ format(" [included from %s:%d(%d)]", ti.filename().c_str(), token.line(), token.character());
+
+ ti.readSymbol(")");
+
+ } else if (toUpper(token.string()) == "NONE") {
// Nothing left to do; we initialized to NONE originally
ensureData();
m_data->source.set(ti, token);
@@ -982,6 +1073,7 @@ void Any::deserialize(TextInput& ti, Token& token) {
ensureData();
m_data->name = name;
}
+ needRead = false;
} // if NONE
break;
@@ -996,7 +1088,7 @@ void Any::deserialize(TextInput& ti, Token& token) {
m_data->comment = comment;
}
- if (m_type != ARRAY && m_type != TABLE) {
+ if (needRead) {
// Array and table already consumed their last token
token = ti.read();
}
@@ -1016,9 +1108,9 @@ static bool isSeparator(char c) {
void Any::readUntilCommaOrClose(TextInput& ti, Token& token) {
- while (! ((token.type() == Token::SYMBOL) &&
- (isClose(token.string()[0])) ||
- isSeparator(token.string()[0]))) {
+ bool atClose = (token.type() == Token::SYMBOL) && isClose(token.string()[0]);
+ bool atComma = isSeparator(token.string()[0]);
+ while (! (atClose || atComma)) {
switch (token.type()) {
case Token::NEWLINE:
case Token::COMMENT:
@@ -1030,6 +1122,10 @@ void Any::readUntilCommaOrClose(TextInput& ti, Token& token) {
throw ParseError(ti.filename(), token.line(), token.character(),
"Expected a comma or close paren");
}
+
+ // Update checks
+ atComma = isSeparator(token.string()[0]);
+ atClose = (token.type() == Token::SYMBOL) && isClose(token.string()[0]);
}
}
@@ -1076,13 +1172,13 @@ void Any::deserializeBody(TextInput& ti, Token& token) {
}
key = token.string();
- // Consume everything up to the = sign
+ // Consume everything up to the = sign, returning the "=" sign.
token = ti.readSignificant();
if ((token.type() != Token::SYMBOL) || (token.string() != "=")) {
throw ParseError(ti.filename(), token.line(), token.character(), "Expected =");
} else {
- // Consume (don't consume comments--we want the value pointed to by a to get those).
+ // Read the next token, which is the value (don't consume comments--we want the value pointed to by a to get those).
token = ti.read();
}
}
@@ -1154,6 +1250,15 @@ const Any::Source& Any::source() const {
}
+std::string Any::sourceDirectory() const {
+ if (m_data) {
+ return FilePath::parent(m_data->source.filename);
+ } else {
+ return "";
+ }
+}
+
+
void Any::verify(bool value, const std::string& message) const {
beforeRead();
if (! value) {
@@ -1185,6 +1290,14 @@ void Any::verifyName(const std::string& n) const {
}
+void Any::verifyName(const std::string& n, const std::string& m) const {
+ beforeRead();
+ const std::string& x = toUpper(name());
+ verify(beginsWith(x, toUpper(n)) ||
+ beginsWith(x, toUpper(m)), "Name must begin with " + n + " or " + m);
+}
+
+
void Any::verifyType(Type t) const {
beforeRead();
if (type() != t) {
diff --git a/dep/g3dlite/source/AnyVal.cpp b/dep/g3dlite/source/AnyVal.cpp
new file mode 100644
index 00000000000..7b98486523a
--- /dev/null
+++ b/dep/g3dlite/source/AnyVal.cpp
@@ -0,0 +1,1379 @@
+/**
+ @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::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::NEWLINE:
+ m_type = STRING;
+ m_value = new std::string(t.readNewline());
+ break;
+
+ case Token::COMMENT:
+ m_type = STRING;
+ m_value = new std::string(t.readComment());
+ 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;
+ }
+}
+
+
+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/dep/g3dlite/source/AreaMemoryManager.cpp b/dep/g3dlite/source/AreaMemoryManager.cpp
new file mode 100644
index 00000000000..00cb33dc91f
--- /dev/null
+++ b/dep/g3dlite/source/AreaMemoryManager.cpp
@@ -0,0 +1,87 @@
+/**
+ @file AreaMemoryManager.cpp
+
+ @maintainer Morgan McGuire, http://graphics.cs.williams.edu
+
+ @created 2009-01-20
+ @edited 2009-01-20
+
+ Copyright 2000-2009, Morgan McGuire.
+ All rights reserved.
+ */
+
+#include "G3D/AreaMemoryManager.h"
+#include "G3D/System.h"
+
+namespace G3D {
+
+AreaMemoryManager::Buffer::Buffer(size_t size) : m_size(size), m_used(0) {
+ // Allocate space for a lot of buffers.
+ m_first = (uint8*)::malloc(m_size);
+}
+
+
+AreaMemoryManager::Buffer::~Buffer() {
+ ::free(m_first);
+}
+
+
+void* AreaMemoryManager::Buffer::alloc(size_t s) {
+ if (s + m_used > m_size) {
+ return NULL;
+ } else {
+ void* old = m_first + m_used;
+ m_used += s;
+ return old;
+ }
+}
+
+
+bool AreaMemoryManager::isThreadsafe() const {
+ return false;
+}
+
+
+AreaMemoryManager::Ref AreaMemoryManager::create(size_t sizeHint) {
+ return new AreaMemoryManager(sizeHint);
+}
+
+
+AreaMemoryManager::AreaMemoryManager(size_t sizeHint) : m_sizeHint(sizeHint) {
+ debugAssert(sizeHint > 0);
+}
+
+
+AreaMemoryManager::~AreaMemoryManager() {
+ deallocateAll();
+}
+
+
+size_t AreaMemoryManager::bytesAllocated() const {
+ return m_sizeHint * m_bufferArray.size();
+}
+
+
+void* AreaMemoryManager::alloc(size_t s) {
+ void* n = (m_bufferArray.size() > 0) ? m_bufferArray.last()->alloc(s) : NULL;
+ if (n == NULL) {
+ // This buffer is full
+ m_bufferArray.append(new Buffer(max(s, m_sizeHint)));
+ return m_bufferArray.last()->alloc(s);
+ } else {
+ return n;
+ }
+}
+
+
+void AreaMemoryManager::free(void* x) {
+ // Intentionally empty; we block deallocate
+}
+
+
+void AreaMemoryManager::deallocateAll() {
+ m_bufferArray.deleteAll();
+ m_bufferArray.clear();
+}
+
+}
diff --git a/dep/g3dlite/BinaryFormat.cpp b/dep/g3dlite/source/BinaryFormat.cpp
index d3991378f45..d3991378f45 100644
--- a/dep/g3dlite/BinaryFormat.cpp
+++ b/dep/g3dlite/source/BinaryFormat.cpp
diff --git a/dep/g3dlite/BinaryInput.cpp b/dep/g3dlite/source/BinaryInput.cpp
index 65a9976fe04..c879715c3f0 100644
--- a/dep/g3dlite/BinaryInput.cpp
+++ b/dep/g3dlite/source/BinaryInput.cpp
@@ -5,7 +5,7 @@
Copyright 2001-2007, Morgan McGuire. All rights reserved.
@created 2001-08-09
- @edited 2005-02-24
+ @edited 2010-03-05
<PRE>
@@ -37,8 +37,9 @@
#include "G3D/Array.h"
#include "G3D/fileutils.h"
#include "G3D/Log.h"
+#include "G3D/FileSystem.h"
#include <zlib.h>
-
+#include "zip.h"
#include <cstring>
namespace G3D {
@@ -272,28 +273,40 @@ BinaryInput::BinaryInput(
_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();
+ std::string zipfile;
+ if (FileSystem::inZipfile(m_filename, zipfile)) {
+ // Load from zipfile
+// zipRead(filename, v, s);
+
+ std::string internalFile = m_filename.substr(zipfile.length() + 1);
+ struct zip* z = zip_open(zipfile.c_str(), ZIP_CHECKCONS, NULL);
+ {
+ struct zip_stat info;
+ zip_stat_init( &info ); // TODO: Docs unclear if zip_stat_init is required.
+ zip_stat(z, internalFile.c_str(), ZIP_FL_NOCASE, &info);
+ m_bufferLength = m_length = info.size;
+ // sets machines up to use MMX, if they want
+ m_buffer = reinterpret_cast<uint8*>(System::alignedMalloc(m_length, 16));
+ struct zip_file* zf = zip_fopen( z, internalFile.c_str(), ZIP_FL_NOCASE );
+ {
+ int64 test = zip_fread( zf, m_buffer, m_length );
+ debugAssertM(test == m_length,
+ internalFile + " was corrupt because it unzipped to the wrong size.");
+ (void)test;
}
- m_freeBuffer = true;
- } else {
- Log::common()->printf("Warning: File not found: %s\n", m_filename.c_str());
+ zip_fclose( zf );
+ }
+ zip_close( z );
+
+ if (compressed) {
+ decompress();
}
+ m_freeBuffer = true;
return;
}
// Figure out how big the file is and verify that it exists.
- m_length = fileLength(m_filename);
+ m_length = FileSystem::size(m_filename);
// Read the file into memory
FILE* file = fopen(m_filename.c_str(), "rb");
@@ -460,6 +473,42 @@ std::string BinaryInput::readString() {
return readString(n);
}
+static bool isNewline(char c) {
+ return c == '\n' || c == '\r';
+}
+
+std::string BinaryInput::readStringNewline() {
+ int64 n = 0;
+
+ if ((m_pos + m_alreadyRead + n) < (m_length - 1)) {
+ prepareToRead(1);
+ }
+
+ if ( ((m_pos + m_alreadyRead + n) < (m_length - 1)) &&
+ ! isNewline(m_buffer[m_pos + n])) {
+
+ ++n;
+ while ( ((m_pos + m_alreadyRead + n) < (m_length - 1)) &&
+ ! isNewline(m_buffer[m_pos + n])) {
+
+ prepareToRead(1);
+ ++n;
+ }
+ }
+
+ const std::string s = readString(n);
+
+ // Consume the newline
+ char firstNLChar = readUInt8();
+
+ // Consume the 2nd newline
+ if (isNewline(m_buffer[m_pos + 1]) && (m_buffer[m_pos + 1] != firstNLChar)) {
+ readUInt8();
+ }
+
+ return s;
+}
+
std::string BinaryInput::readStringEven() {
std::string x = readString();
diff --git a/dep/g3dlite/BinaryOutput.cpp b/dep/g3dlite/source/BinaryOutput.cpp
index 2de46c6d4bb..054211d906c 100644
--- a/dep/g3dlite/BinaryOutput.cpp
+++ b/dep/g3dlite/source/BinaryOutput.cpp
@@ -1,22 +1,27 @@
/**
@file BinaryOutput.cpp
- @author Morgan McGuire, graphics3d.com
- Copyright 2002-2007, Morgan McGuire, All rights reserved.
+ @author Morgan McGuire, http://graphics.cs.williams.edu
+ Copyright 2002-2010, Morgan McGuire, All rights reserved.
@created 2002-02-20
- @edited 2008-01-07
+ @edited 2010-03-17
*/
#include "G3D/platform.h"
#include "G3D/BinaryOutput.h"
#include "G3D/fileutils.h"
+#include "G3D/FileSystem.h"
#include "G3D/stringutils.h"
#include "G3D/Array.h"
#include <zlib.h>
-
+#include "G3D/Log.h"
#include <cstring>
+#ifdef G3D_LINUX
+# include <errno.h>
+#endif
+
// 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.
@@ -155,7 +160,7 @@ void BinaryOutput::reserveBytesWhenOutOfMemory(size_t bytes) {
//debugPrintf("Writing %d bytes to disk\n", writeBytes);
const char* mode = (m_alreadyWritten > 0) ? "ab" : "wb";
- FILE* file = fopen(m_filename.c_str(), mode);
+ FILE* file = FileSystem::fopen(m_filename.c_str(), mode);
debugAssert(file);
size_t count = fwrite(m_buffer, 1, writeBytes, file);
@@ -317,14 +322,17 @@ void BinaryOutput::commit(bool flush) {
parseFilename(m_filename, root, pathArray, base, ext);
path = root + stringJoin(pathArray, '/');
- if (! fileExists(path, false)) {
- createDirectory(path);
+ if (! FileSystem::exists(path, false)) {
+ FileSystem::createDirectory(path);
}
const char* mode = (m_alreadyWritten > 0) ? "ab" : "wb";
- FILE* file = fopen(m_filename.c_str(), mode);
+ FILE* file = FileSystem::fopen(m_filename.c_str(), mode);
+ if (! file) {
+ logPrintf("Error %d while trying to open \"%s\"\n", errno, m_filename.c_str());
+ }
m_ok = (file != NULL) && m_ok;
if (m_ok) {
@@ -340,7 +348,7 @@ void BinaryOutput::commit(bool flush) {
if (flush) {
fflush(file);
}
- fclose(file);
+ FileSystem::fclose(file);
file = NULL;
}
}
diff --git a/dep/g3dlite/Box.cpp b/dep/g3dlite/source/Box.cpp
index f7c112ae3a5..f7c112ae3a5 100644
--- a/dep/g3dlite/Box.cpp
+++ b/dep/g3dlite/source/Box.cpp
diff --git a/dep/g3dlite/source/Box2D.cpp b/dep/g3dlite/source/Box2D.cpp
new file mode 100644
index 00000000000..ea5a47af1a9
--- /dev/null
+++ b/dep/g3dlite/source/Box2D.cpp
@@ -0,0 +1,113 @@
+/**
+ @file Box.cpp
+ Box class
+
+ @maintainer Morgan McGuire, http://graphics.cs.williams.edu
+
+ @created 2001-06-02
+ @edited 2008-12-27
+*/
+
+#include "G3D/Box2D.h"
+#include "G3D/CoordinateFrame.h"
+#include "G3D/Rect2D.h"
+
+namespace G3D {
+
+bool Box2D::overlaps1Way(const Box2D& other) const {
+ for (int a = 0; a < 2; ++a) {
+
+ float t = other.m_corner[0].dot(m_axisin[a]);
+
+ // Find the extent of box 2 on m_axisin a
+ float tMin = t;
+ float tMax = t;
+
+ for (int c = 1; c < 4; ++c) {
+ t = other.m_corner[c].dot(m_axisin[a]);
+
+ if (t < tMin) {
+ tMin = t;
+ } else if (t > tMax) {
+ tMax = t;
+ }
+ }
+
+ // We have to subtract off the origin
+
+ // See if [tMin, tMax] intersects [0, 1]
+ if ((tMin > 1 + origin[a]) || (tMax < origin[a])) {
+ // There was no intersection along this dimension;
+ // the boxes cannot possibly overlap.
+ return false;
+ }
+ }
+
+ // There was no dimension along which there is no intersection.
+ // Therefore the boxes overlap.
+ return true;
+}
+
+
+void Box2D::computeAxes() {
+ m_axis[0] = m_corner[1] - m_corner[0];
+ m_axis[1] = m_corner[3] - m_corner[0];
+
+ // Make the length of each m_axisin = 1/edge length so we know any
+ // dot product must be less than 1 to fall within the edge.
+ float len[2];
+ for (int a = 0; a < 2; ++a) {
+ float lenSq = m_axis[a].squaredLength();
+ m_axisin[a] = m_axis[a] / lenSq;
+ origin[a] = m_corner[0].dot(m_axisin[a]);
+ len[a] = sqrt(lenSq);
+ m_axis[a] /= len[a];
+ }
+
+ // w * h
+ m_area = len[0] * len[1];
+
+
+ m_center = (m_corner[0] + m_corner[2]) * 0.5f;
+}
+
+
+Box2D::Box2D(const Vector2& center, float w, float h, float angle) {
+ Vector2 X( cos(angle), sin(angle));
+ Vector2 Y(-sin(angle), cos(angle));
+
+ X *= w / 2;
+ Y *= h / 2;
+
+ m_corner[0] = center - X - Y;
+ m_corner[1] = center + X - Y;
+ m_corner[2] = center + X + Y;
+ m_corner[3] = center - X + Y;
+
+ computeAxes();
+}
+
+
+Box2D::Box2D(const AABox2D& b) {
+ for (int i = 0; i < 4; ++i) {
+ m_corner[i] = b.corner(i);
+ }
+
+ computeAxes();
+}
+
+
+Box2D::Box2D(const Vector2& min, const Vector2& max) {
+ *this = Box2D(Rect2D::xyxy(min, max));
+}
+
+
+Box2D::Box2D(const CFrame& frame, Box2D& b) {
+ for (int i = 0; i < 4; ++i) {
+ m_corner[i] = frame.pointToWorldSpace(Vector3(b.corner(i), 0)).xy();
+ }
+ computeAxes();
+}
+
+
+} // G3D
diff --git a/dep/g3dlite/source/BumpMapPreprocess.cpp b/dep/g3dlite/source/BumpMapPreprocess.cpp
new file mode 100644
index 00000000000..20281caf8cb
--- /dev/null
+++ b/dep/g3dlite/source/BumpMapPreprocess.cpp
@@ -0,0 +1,43 @@
+/**
+ \file BumpMapPreprocess.cpp
+
+ \maintainer Morgan McGuire, http://graphics.cs.williams.edu
+
+ \created 2010-01-28
+ \edited 2010-01-28
+
+ Copyright 2000-2010, Morgan McGuire.
+ All rights reserved.
+ */
+#include "G3D/BumpMapPreprocess.h"
+#include "G3D/Any.h"
+#include "G3D/stringutils.h"
+
+namespace G3D {
+
+BumpMapPreprocess::BumpMapPreprocess(const Any& any) {
+ *this = BumpMapPreprocess();
+ for (Any::AnyTable::Iterator it = any.table().begin(); it.hasMore(); ++it) {
+ const std::string& key = toLower(it->key);
+ if (key == "lowpassfilter") {
+ lowPassFilter = it->value;
+ } else if (key == "zextentpixels") {
+ zExtentPixels = it->value;
+ } else if (key == "scalezbynz") {
+ scaleZByNz = it->value;
+ } else {
+ any.verify(false, "Illegal key: " + it->key);
+ }
+ }
+}
+
+
+BumpMapPreprocess::operator Any() const {
+ Any any(Any::TABLE, "BumpMapPreprocess");
+ any["lowPassFilter"] = lowPassFilter;
+ any["zExtentPixels"] = zExtentPixels;
+ any["scaleZByNz"] = scaleZByNz;
+ return any;
+}
+
+}
diff --git a/dep/g3dlite/Capsule.cpp b/dep/g3dlite/source/Capsule.cpp
index 2ad3891c960..2ad3891c960 100644
--- a/dep/g3dlite/Capsule.cpp
+++ b/dep/g3dlite/source/Capsule.cpp
diff --git a/dep/g3dlite/CollisionDetection.cpp b/dep/g3dlite/source/CollisionDetection.cpp
index 77eef0a5500..77eef0a5500 100644
--- a/dep/g3dlite/CollisionDetection.cpp
+++ b/dep/g3dlite/source/CollisionDetection.cpp
diff --git a/dep/g3dlite/source/Color1.cpp b/dep/g3dlite/source/Color1.cpp
new file mode 100644
index 00000000000..04f3f9412b1
--- /dev/null
+++ b/dep/g3dlite/source/Color1.cpp
@@ -0,0 +1,58 @@
+/**
+ @file Color1.cpp
+
+ Color class.
+
+ @author Morgan McGuire, http://graphics.cs.williams.edu
+
+ @created 2007-01-30
+ @edited 2009-03-27
+ */
+
+#include "G3D/platform.h"
+#include "G3D/Color1.h"
+#include "G3D/Color1uint8.h"
+#include "G3D/BinaryInput.h"
+#include "G3D/BinaryOutput.h"
+#include "G3D/Color3.h"
+
+namespace G3D {
+
+const Color1& Color1::one() {
+ static const Color1 x(1.0f);
+ return x;
+}
+
+
+const Color1& Color1::zero() {
+ const static Color1 x(0.0f);
+ return x;
+}
+
+
+Color1::Color1(BinaryInput& bi) {
+ deserialize(bi);
+}
+
+
+Color3 Color1::rgb() const {
+ return Color3(value, value, value);
+}
+
+
+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/dep/g3dlite/source/Color1uint8.cpp b/dep/g3dlite/source/Color1uint8.cpp
new file mode 100644
index 00000000000..21cd564ba92
--- /dev/null
+++ b/dep/g3dlite/source/Color1uint8.cpp
@@ -0,0 +1,38 @@
+/**
+ @file Color1uint8.cpp
+
+ @author Morgan McGuire, http://graphics.cs.williams.edu
+
+ @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/dep/g3dlite/source/Color3.cpp b/dep/g3dlite/source/Color3.cpp
new file mode 100644
index 00000000000..deb0bd87ee7
--- /dev/null
+++ b/dep/g3dlite/source/Color3.cpp
@@ -0,0 +1,384 @@
+/**
+ @file Color3.cpp
+
+ Color class.
+
+ @author Morgan McGuire, http://graphics.cs.williams.edu
+
+ @created 2001-06-02
+ @edited 2010-01-28
+ */
+
+#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"
+#include "G3D/Any.h"
+#include "G3D/stringutils.h"
+
+namespace G3D {
+
+Color3::Color3(const Any& any) {
+ *this = Color3::zero();
+ any.verifyName("Color3");
+ std::string name = toLower(any.name());
+
+ switch (any.type()) {
+ case Any::TABLE:
+
+ for (Any::AnyTable::Iterator it = any.table().begin(); it.hasMore(); ++it) {
+ const std::string& key = toLower(it->key);
+ if (key == "r") {
+ r = it->value;
+ } else if (key == "g") {
+ g = it->value;
+ } else if (key == "b") {
+ b = it->value;
+ } else {
+ any.verify(false, "Illegal key: " + it->key);
+ }
+ }
+ break;
+
+ case Any::ARRAY:
+ if (name == "color3") {
+ any.verifySize(3);
+ r = any[0];
+ g = any[1];
+ b = any[2];
+ } else if (name == "color3::one") {
+ any.verifySize(0);
+ *this = one();
+ } else if (name == "color3::zero") {
+ any.verifySize(0);
+ *this = zero();
+ } else if (name == "color3::fromargb") {
+ *this = Color3::fromARGB((int)any[0].number());
+ } else {
+ any.verify(false, "Expected Color3 constructor");
+ }
+ break;
+
+ default:
+ any.verify(false, "Bad Color3 constructor");
+ }
+}
+
+
+Color3::operator Any() const {
+ Any a(Any::ARRAY, "Color3");
+ a.append(r, g, b);
+ return a;
+}
+
+
+Color3 Color3::ansiMap(uint32 i) {
+ static const Color3 map[] =
+ {Color3::black(), Color3::red() * 0.75f, Color3::green() * 0.75f, Color3::yellow() * 0.75f,
+ Color3::blue() * 0.75f, Color3::purple() * 0.75f, Color3::cyan() * 0.75f, Color3::white() * 0.75f,
+ Color3::white() * 0.90f, Color3::red(), Color3::green(), Color3::yellow(), Color3::blue(),
+ Color3::purple(), Color3::cyan(), Color3::white()};
+
+ return map[i & 15];
+}
+
+
+Color3 Color3::pastelMap(uint32 i) {
+ uint32 x = Crypto::crc32(&i, sizeof(uint32));
+ // Create fairly bright, saturated colors
+ Vector3 v(((x >> 22) & 1023) / 1023.0f,
+ (((x >> 11) & 2047) / 2047.0f) * 0.5f + 0.25f,
+ ((x & 2047) / 2047.0f) * 0.75f + 0.25f);
+ return Color3::fromHSV(v);
+}
+
+
+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;
+}
+
+
+bool Color3::isFinite() const {
+ return G3D::isFinite(r) && G3D::isFinite(g) && G3D::isFinite(b);
+}
+
+
+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) {
+ if (fScalar != 0.0f) {
+ float fInvScalar = 1.0f / fScalar;
+ r *= fInvScalar;
+ g *= fInvScalar;
+ b *= fInvScalar;
+ } else {
+ r = (float)G3D::finf();
+ g = (float)G3D::finf();
+ b = (float)G3D::finf();
+ }
+
+ 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 = iMin(5, 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/dep/g3dlite/source/Color3uint8.cpp b/dep/g3dlite/source/Color3uint8.cpp
new file mode 100644
index 00000000000..a744710c752
--- /dev/null
+++ b/dep/g3dlite/source/Color3uint8.cpp
@@ -0,0 +1,45 @@
+/**
+ @file Color3uint8.cpp
+
+ @author Morgan McGuire, http://graphics.cs.williams.edu
+
+ @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/dep/g3dlite/source/Color4.cpp b/dep/g3dlite/source/Color4.cpp
new file mode 100644
index 00000000000..eab09eb9c7e
--- /dev/null
+++ b/dep/g3dlite/source/Color4.cpp
@@ -0,0 +1,192 @@
+/**
+ @file Color4.cpp
+
+ Color class.
+
+ @author Morgan McGuire, http://graphics.cs.williams.edu
+ @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 2009-11-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"
+#include "G3D/Any.h"
+#include "G3D/stringutils.h"
+
+namespace G3D {
+
+Color4::Color4(const Any& any) {
+ *this = Color4::zero();
+ any.verifyName("Color4");
+
+ if (any.type() == Any::TABLE) {
+ for (Any::AnyTable::Iterator it = any.table().begin(); it.hasMore(); ++it) {
+ const std::string& key = toLower(it->key);
+ if (key == "r") {
+ r = it->value;
+ } else if (key == "g") {
+ g = it->value;
+ } else if (key == "b") {
+ b = it->value;
+ } else if (key == "a") {
+ a = it->value;
+ } else {
+ any.verify(false, "Illegal key: " + it->key);
+ }
+ }
+ } else if (toLower(any.name()) == "color4") {
+ r = any[0];
+ g = any[1];
+ b = any[2];
+ a = any[3];
+ } else {
+ any.verifyName("Color4::fromARGB");
+ *this = Color4::fromARGB((int)any[0].number());
+ }
+}
+
+
+Color4::operator Any() const {
+ Any any(Any::ARRAY, "Color4");
+ any.append(r, g, b, a);
+ return any;
+}
+
+
+const Color4& Color4::one() {
+ const static Color4 x(1.0f, 1.0f, 1.0f, 1.0f);
+ return x;
+}
+
+
+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::finf(), (float)G3D::finf(), (float)G3D::finf(), (float)G3D::finf());
+ return c;
+}
+
+
+const Color4& Color4::nan() {
+ static Color4 c((float)G3D::fnan(), (float)G3D::fnan(), (float)G3D::fnan(), (float)G3D::fnan());
+ 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::finf();
+ g = (float)G3D::finf();
+ b = (float)G3D::finf();
+ a = (float)G3D::finf();
+ }
+
+ return *this;
+}
+
+//----------------------------------------------------------------------------
+
+std::string Color4::toString() const {
+ return G3D::format("(%g, %g, %g, %g)", r, g, b, a);
+}
+
+//----------------------------------------------------------------------------
+
+}; // namespace
+
diff --git a/dep/g3dlite/source/Color4uint8.cpp b/dep/g3dlite/source/Color4uint8.cpp
new file mode 100644
index 00000000000..5cc3a578aca
--- /dev/null
+++ b/dep/g3dlite/source/Color4uint8.cpp
@@ -0,0 +1,47 @@
+/**
+ @file Color4uint8.cpp
+
+ @author Morgan McGuire, http://graphics.cs.williams.edu
+
+ @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/dep/g3dlite/source/Cone.cpp b/dep/g3dlite/source/Cone.cpp
new file mode 100644
index 00000000000..3104b8424a7
--- /dev/null
+++ b/dep/g3dlite/source/Cone.cpp
@@ -0,0 +1,79 @@
+/**
+ @file Cone.cpp
+
+ Cone class
+
+ @maintainer Morgan McGuire, http://graphics.cs.williams.edu
+
+ @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/dep/g3dlite/source/ConvexPolyhedron.cpp b/dep/g3dlite/source/ConvexPolyhedron.cpp
new file mode 100644
index 00000000000..5fa76e3ed41
--- /dev/null
+++ b/dep/g3dlite/source/ConvexPolyhedron.cpp
@@ -0,0 +1,457 @@
+/**
+ @file ConvexPolyhedron.cpp
+
+ @author Morgan McGuire, http://graphics.cs.williams.edu
+
+ @created 2001-11-11
+ @edited 2009-08-10
+
+ Copyright 2000-2009, 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
+}
+
+
+ConvexPolygon::ConvexPolygon(const Vector3& v0, const Vector3& v1, const Vector3& v2) {
+ _vertex.append(v0, v1, v2);
+}
+
+
+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/dep/g3dlite/CoordinateFrame.cpp b/dep/g3dlite/source/CoordinateFrame.cpp
index 9b639b62082..7f9a4c098a5 100644
--- a/dep/g3dlite/CoordinateFrame.cpp
+++ b/dep/g3dlite/source/CoordinateFrame.cpp
@@ -6,7 +6,7 @@
@maintainer Morgan McGuire, http://graphics.cs.williams.edu
@created 2001-06-02
- @edited 2009-11-13
+ @edited 2010-03-13
Copyright 2000-2010, Morgan McGuire.
All rights reserved.
@@ -26,23 +26,52 @@
#include "G3D/UprightFrame.h"
#include "G3D/Any.h"
#include "G3D/stringutils.h"
+#include "G3D/PhysicsFrame.h"
+#include "G3D/UprightFrame.h"
namespace G3D {
+
+std::string CoordinateFrame::toXYZYPRDegreesString() const {
+ UprightFrame uframe(*this);
+
+ return format("CFrame::fromXYZYPRDegrees(% 5.1ff, % 5.1ff, % 5.1ff, % 5.1ff, % 5.1ff, % 5.1ff)",
+ uframe.translation.x, uframe.translation.y, uframe.translation.z,
+ toDegrees(uframe.yaw), toDegrees(uframe.pitch), 0.0f);
+}
+
+
CoordinateFrame::CoordinateFrame(const Any& any) {
- any.verifyName("CFrame");
- if (toUpper(any.name()) == "CFRAME") {
+ *this = CFrame();
+
+ const std::string& n = toUpper(any.name());
+
+ if (beginsWith(n, "VECTOR3")) {
+ translation = any;
+ } else if (beginsWith(n, "MATRIX3")) {
+ rotation = any;
+ } else if ((n == "CFRAME") || (n == "COORDINATEFRAME")) {
any.verifyType(Any::TABLE, Any::ARRAY);
- if (any.type() == Any::TABLE) {
- rotation = any["rotation"];
- translation = any["translation"];
- } else {
+ if (any.type() == Any::ARRAY) {
any.verifySize(2);
rotation = any[0];
translation = any[1];
+ } else {
+ for (Any::AnyTable::Iterator it = any.table().begin(); it.hasMore(); ++it) {
+ const std::string& n = toLower(it->key);
+ if (n == "translation") {
+ translation = Vector3(it->value);
+ } else if (n == "rotation") {
+ rotation = Matrix3(it->value);
+ } else {
+ any.verify(false, "Illegal table key: " + it->key);
+ }
+ }
}
+ } else if (beginsWith(n, "PHYSICSFRAME") || beginsWith(n, "PFRAME")) {
+ *this = PhysicsFrame(any);
} else {
- any.verifyName("CFrame::fromXYZYPRDegrees");
+ any.verifyName("CFrame::fromXYZYPRDegrees", "CoordinateFrame::fromXYZYPRDegrees");
any.verifyType(Any::ARRAY);
any.verifySize(3, 6);
@@ -370,12 +399,12 @@ CoordinateFrame CoordinateFrame::lerp(
} else if (alpha == 0.0f) {
return *this;
} else {
- Quat q1 = Quat(this->rotation);
- Quat q2 = Quat(other.rotation);
+ const Quat q1(this->rotation);
+ const Quat q2(other.rotation);
return CoordinateFrame(
q1.slerp(q2, alpha).toRotationMatrix(),
- this->translation * (1 - alpha) + other.translation * alpha);
+ translation * (1 - alpha) + other.translation * alpha);
}
}
@@ -383,7 +412,7 @@ CoordinateFrame CoordinateFrame::lerp(
void CoordinateFrame::pointToWorldSpace(const Array<Vector3>& v, Array<Vector3>& vout) const {
vout.resize(v.size());
- for (int i = v.size() - 1; i >= 0; --i) {
+ for (int i = 0; i < v.size(); ++i) {
vout[i] = pointToWorldSpace(v[i]);
}
}
@@ -392,7 +421,7 @@ void CoordinateFrame::pointToWorldSpace(const Array<Vector3>& v, Array<Vector3>&
void CoordinateFrame::normalToWorldSpace(const Array<Vector3>& v, Array<Vector3>& vout) const {
vout.resize(v.size());
- for (int i = v.size() - 1; i >= 0; --i) {
+ for (int i = 0; i < v.size(); ++i) {
vout[i] = normalToWorldSpace(v[i]);
}
}
diff --git a/dep/g3dlite/Crypto.cpp b/dep/g3dlite/source/Crypto.cpp
index c69b23375ce..c69b23375ce 100644
--- a/dep/g3dlite/Crypto.cpp
+++ b/dep/g3dlite/source/Crypto.cpp
diff --git a/dep/g3dlite/source/Crypto_md5.cpp b/dep/g3dlite/source/Crypto_md5.cpp
new file mode 100644
index 00000000000..c7ee535d61e
--- /dev/null
+++ b/dep/g3dlite/source/Crypto_md5.cpp
@@ -0,0 +1,471 @@
+/**
+ @file Crypto_md5.cpp
+
+ @author Morgan McGuire, http://graphics.cs.williams.edu
+ 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/dep/g3dlite/Cylinder.cpp b/dep/g3dlite/source/Cylinder.cpp
index 7a7b9f9440d..7a7b9f9440d 100644
--- a/dep/g3dlite/Cylinder.cpp
+++ b/dep/g3dlite/source/Cylinder.cpp
diff --git a/dep/g3dlite/source/FileSystem.cpp b/dep/g3dlite/source/FileSystem.cpp
new file mode 100644
index 00000000000..3a1765714b0
--- /dev/null
+++ b/dep/g3dlite/source/FileSystem.cpp
@@ -0,0 +1,859 @@
+/**
+ @file FileSystem.cpp
+
+ @author Morgan McGuire, http://graphics.cs.williams.edu
+
+ @author 2002-06-06
+ @edited 2010-04-10
+ */
+#include "G3D/FileSystem.h"
+#include "G3D/System.h"
+#include "G3D/stringutils.h"
+#include "G3D/fileutils.h"
+#include <sys/stat.h>
+#include <sys/types.h>
+#include "zip.h"
+#include "G3D/g3dfnmatch.h"
+#include "G3D/BinaryInput.h"
+#include "G3D/BinaryOutput.h"
+
+#ifdef G3D_WIN32
+ // Needed for _getcwd
+# include <direct.h>
+
+ // Needed for _findfirst
+# include <io.h>
+
+#define stat64 _stat64
+#else
+# include <dirent.h>
+# include <fnmatch.h>
+# include <unistd.h>
+# define _getcwd getcwd
+# define _stat stat
+#endif
+
+namespace G3D {
+
+static FileSystem* common = NULL;
+
+FileSystem& FileSystem::instance() {
+ init();
+ return *common;
+}
+
+
+void FileSystem::init() {
+ if (common == NULL) {
+ common = new FileSystem();
+ }
+}
+
+
+void FileSystem::cleanup() {
+ if (common != NULL) {
+ delete common;
+ common = NULL;
+ }
+}
+
+FileSystem::FileSystem() : m_cacheLifetime(10) {}
+
+/////////////////////////////////////////////////////////////
+
+bool FileSystem::Dir::contains(const std::string& f) const {
+
+ for (int i = 0; i < nodeArray.size(); ++i) {
+# ifdef G3D_WIN32
+ if (stricmp(f.c_str(), nodeArray[i].name.c_str()) == 0) {
+ return true;
+ }
+# else
+ if (f == nodeArray[i].name) {
+ return true;
+ }
+# endif
+ }
+ return false;
+}
+
+void FileSystem::Dir::computeZipListing(const std::string& zipfile, const std::string& pathInsideZipfile) {
+ struct zip* z = zip_open( FilePath::removeTrailingSlash(zipfile).c_str(), ZIP_CHECKCONS, NULL );
+ debugAssert(z);
+
+ int count = zip_get_num_files( z );
+ Set<std::string> alreadyAdded;
+ for (int i = 0; i < count; ++i) {
+ struct zip_stat info;
+ zip_stat_init( &info ); // TODO: Docs unclear if zip_stat_init is required.
+ zip_stat_index( z, i, ZIP_FL_NOCASE, &info );
+
+ // Fully-qualified name of a file inside zipfile
+ std::string name = info.name;
+
+ if (beginsWith(name, pathInsideZipfile)) {
+ // We found something inside the directory we were looking for,
+ // so the directory itself must exist
+ exists = true;
+
+ // For building the cached directory listing, extract only elements that do not contain
+ // additional subdirectories.
+
+ int start = pathInsideZipfile.size();
+ if ((int(name.length()) > start) && isSlash(name[start])) {
+ ++start;
+ }
+ int end = findSlash(name, start);
+ if (end == -1) {
+ // There are no more slashes; add this name
+ name = name.substr(start);
+ if (alreadyAdded.insert(name)) {
+ Entry& e = nodeArray.next();
+ e.name = name;
+ e.type = FILE_TYPE;
+ }
+ } else {
+ // There are more slashes, indicating that this is a directory
+ name = name.substr(start, end);
+ if (alreadyAdded.insert(name)) {
+ Entry& e = nodeArray.next();
+ e.name = name;
+ e.type = DIR_TYPE;
+ }
+ }
+ }
+ }
+
+ zip_close(z);
+ z = NULL;
+}
+
+
+FileSystem::Dir& FileSystem::getContents(const std::string& path, bool forceUpdate) {
+ const std::string& key =
+# if defined(G3D_WIN32)
+ FilePath::canonicalize(FilePath::removeTrailingSlash(toLower(FilePath::canonicalize(resolve(path)))));
+# else
+ FilePath::canonicalize(FilePath::removeTrailingSlash(FilePath::canonicalize(resolve(path))));
+# endif
+
+ RealTime now = System::time();
+ Dir& dir = m_cache.getCreate(key);
+
+ if ((now > dir.lastChecked + cacheLifetime()) || forceUpdate) {
+ dir = Dir();
+
+ // Out of date: update
+ dir.lastChecked = now;
+
+ struct _stat st;
+ const bool exists = _stat(key.c_str(), &st) != -1;
+ const bool isDirectory = (st.st_mode & S_IFDIR) != 0;
+
+ // Does this path exist on the real filesystem?
+ if (exists && isDirectory) {
+
+ // Is this path actually a directory?
+ if (isDirectory) {
+ dir.exists = true;
+ // Update contents
+# ifdef G3D_WIN32
+ const std::string& filespec = FilePath::concat(key, "*");
+ struct _finddata_t fileinfo;
+ intptr_t handle = _findfirst(filespec.c_str(), &fileinfo);
+ debugAssert(handle != -1);
+ int result = 0;
+ do {
+ if ((strcmp(fileinfo.name, ".") != 0) && (strcmp(fileinfo.name, "..") != 0)) {
+ Entry& e = dir.nodeArray.next();
+ e.name = fileinfo.name;
+ if ((fileinfo.attrib & _A_SUBDIR) != 0) {
+ e.type = DIR_TYPE;
+ } else {
+ e.type = FILE_TYPE;
+ }
+ }
+
+ result = _findnext(handle, &fileinfo);
+ } while (result == 0);
+ _findclose(handle);
+
+# else
+ DIR* listing = opendir(key.c_str());
+ debugAssertM(listing, "opendir failed on '" + key + "'");
+ struct dirent* entry = readdir(listing);
+ while (entry != NULL) {
+ if ((strcmp(entry->d_name, "..") != 0) && (strcmp(entry->d_name, ".") != 0)) {
+ Entry& e = dir.nodeArray.next();
+ e.name = entry->d_name;
+
+# ifdef _DIRENT_HAVE_D_TYPE
+ // Not all POSIX systems support this field
+ // http://www.delorie.com/gnu/docs/glibc/libc_270.html
+ switch (entry->d_type) {
+ case DT_DIR:
+ e.type = DIR_TYPE;
+ break;
+
+ case DT_REG:
+ e.type = FILE_TYPE;
+ break;
+
+ case DT_UNKNOWN:
+ default:
+ e.type = UNKNOWN;
+ break;
+ }
+# endif
+ }
+ entry = readdir(listing);
+ }
+ closedir(listing);
+ listing = NULL;
+ entry = NULL;
+# endif
+ }
+
+ } else {
+ std::string zip;
+
+ if (exists && isZipfile(path)) {
+ // This is a zipfile; get its root
+ dir.isZipfile = true;
+ dir.computeZipListing(path, "");
+
+ } else if (inZipfile(path, zip)) {
+
+ // There is a zipfile somewhere in the path. Does
+ // the rest of the path exist inside the zipfile?
+ dir.inZipfile = true;
+
+ dir.computeZipListing(zip, path.substr(zip.length() + 1));
+ }
+ }
+ }
+
+ return dir;
+}
+
+
+bool FileSystem::_inZipfile(const std::string& path, std::string& z) {
+ // Reject trivial cases before parsing
+ if (path.find('.') == std::string::npos) {
+ // There is no zipfile possible, since G3D requires
+ // an extension on zipfiles.
+ return false;
+ }
+
+ // Look at all sub-paths containing periods.
+ // For each, ask if it is a zipfile.
+ int current = 0;
+ current = path.find('.', current);
+
+ while (current != -1) {
+ // xxxxx/foo.zip/yyyyy
+ current = path.find('.', current);
+
+ // Look forward for the next slash
+ int s = findSlash(path, current);
+
+ if (s == -1) {
+ // No more slashes
+ return false;
+ }
+
+ z = path.substr(0, s);
+ if (_isZipfile(z)) {
+ return true;
+ }
+
+ current = s + 1;
+ }
+
+ z = "";
+ return false;
+}
+
+
+bool FileSystem::_isZipfile(const std::string& filename) {
+ if (FilePath::ext(filename).empty()) {
+ return false;
+ }
+
+ FILE* f = fopen(FilePath::removeTrailingSlash(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;
+}
+
+
+FILE* FileSystem::_fopen(const char* filename, const char* mode) {
+ for (const char* m = mode; *m != '\0'; ++m) {
+ if (*m == 'w') {
+ // Purge the cache entry for the parent of this directory
+ _clearCache(FilePath::parent(_resolve(filename)));
+ break;
+ }
+ }
+ return ::fopen(filename, mode);
+}
+
+
+void FileSystem::_clearCache(const std::string& path) {
+ if ((path == "") || FilePath::isRoot(path)) {
+ m_cache.clear();
+ } else {
+ Array<std::string> keys;
+ m_cache.getKeys(keys);
+
+ const std::string& prefix =
+# ifdef G3D_WIN32
+ toLower(FilePath::canonicalize(FilePath::removeTrailingSlash(_resolve(path))));
+# else
+ FilePath::canonicalize(FilePath::removeTrailingSlash(_resolve(path)));
+# endif
+ const std::string& prefixSlash = prefix + "/";
+
+ for (int k = 0; k < keys.size(); ++k) {
+ const std::string& key = keys[k];
+ if ((key == prefix) || beginsWith(key, prefixSlash)) {
+ m_cache.remove(keys[k]);
+ }
+ }
+ }
+}
+
+
+void FileSystem::_setCacheLifetime(float t) {
+ m_cacheLifetime = t;
+}
+
+
+void FileSystem::_createDirectory(const std::string& dir) {
+
+ if (dir == "") {
+ return;
+ }
+
+ std::string d = _resolve(dir);
+
+ // Add a trailing / if there isn't one.
+ switch (d[d.size() - 1]) {
+ case '/':
+ case '\\':
+ break;
+
+ default:
+ d += "/";
+ }
+
+ // If it already exists, do nothing
+ if (_exists(FilePath::removeTrailingSlash(d))) {
+ return;
+ }
+
+ // Parse the name apart
+ std::string root, base, ext;
+ Array<std::string> path;
+
+ std::string lead;
+ FilePath::parse(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 (! _exists(p)) {
+ // 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
+ }
+ }
+
+ _clearCache(FilePath::parent(FilePath::removeTrailingSlash(d)));
+}
+
+
+void FileSystem::_copyFile(const std::string& source, const std::string& dest) {
+# ifdef G3D_WIN32
+ // TODO: handle case where srcPath is in a zipfile
+ CopyFileA(source.c_str(), dest.c_str(), FALSE);
+ _clearCache(FilePath::parent(_resolve(dest)));
+# else
+ // 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
+}
+
+
+bool FileSystem::_exists(const std::string& f, bool trustCache) {
+
+ if (FilePath::isRoot(f)) {
+# ifdef G3D_WIN32
+ const std::string& winname = toLower(f.substr(0, 1)) + ":\\";
+ return _drives().contains(winname);
+# else
+ return true;
+# endif
+ }
+
+ std::string path = FilePath::removeTrailingSlash(f);
+ std::string parentPath = FilePath::parent(path);
+
+ const Dir& entry = getContents(parentPath, ! trustCache);
+
+ if (FilePath::containsWildcards(f)) {
+ if (! entry.exists) {
+ // The directory didn't exist, so neither do its contents
+ return false;
+ }
+
+ const std::string& pattern = FilePath::baseExt(path);
+
+# ifdef G3D_WIN32
+ static const int flags = FNM_CASEFOLD;
+# else
+ static const int flags = 0;
+# endif
+
+ // See if any element of entry matches the wild card
+ for (int i = 0; i < entry.nodeArray.size(); ++i) {
+ if (FilePath::matches(entry.nodeArray[i].name, pattern, flags)) {
+ return true;
+ }
+ }
+
+ // Could not find a match
+ return false;
+
+ } else {
+ return entry.exists && entry.contains(FilePath::baseExt(path));
+ }
+}
+
+
+bool FileSystem::_isDirectory(const std::string& filename) {
+ // TODO: work with zipfiles and cache
+ struct _stat st;
+ const bool exists = _stat(FilePath::removeTrailingSlash(filename).c_str(), &st) != -1;
+ return exists && ((st.st_mode & S_IFDIR) != 0);
+}
+
+
+std::string FileSystem::_resolve(const std::string& filename, const std::string& cwd) {
+ if (filename.size() >= 1) {
+ if (isSlash(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) && isSlash(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
+ }
+ }
+
+ // Prepend the working directory.
+ return FilePath::concat(cwd, filename);
+}
+
+
+std::string FileSystem::_currentDirectory() {
+ static const int N = 2048;
+ char buffer[N];
+
+ _getcwd(buffer, N);
+ return std::string(buffer);
+}
+
+
+bool FileSystem::_isNewer(const std::string& src, const std::string& dst) {
+ // TODO: work with cache and zipfiles
+ 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));
+}
+
+
+int64 FileSystem::_size(const std::string& filename) {
+ struct stat64 st;
+ int result = stat64(filename.c_str(), &st);
+
+ if (result == -1) {
+ std::string zip, contents;
+ if (zipfileExists(filename, zip, contents)) {
+ int64 requiredMem;
+
+ struct zip *z = zip_open( zip.c_str(), ZIP_CHECKCONS, NULL );
+ debugAssertM(z != NULL, zip + ": zip open failed.");
+ {
+ struct zip_stat info;
+ zip_stat_init( &info ); // Docs unclear if zip_stat_init is required.
+ int success = zip_stat( z, contents.c_str(), ZIP_FL_NOCASE, &info );
+ debugAssertM(success == 0, zip + ": " + contents + ": zip stat failed.");
+ requiredMem = info.size;
+ }
+ zip_close(z);
+ return requiredMem;
+ } else {
+ return -1;
+ }
+ }
+
+ return st.st_size;
+}
+
+
+void FileSystem::listHelper(const std::string& shortSpec, const std::string& parentPath, Array<std::string>& result, const ListSettings& settings) {
+ Dir& dir = getContents(parentPath, false);
+
+ if (! dir.exists) {
+ return;
+ }
+
+ for (int i = 0; i < dir.nodeArray.size(); ++i) {
+ Entry& entry = dir.nodeArray[i];
+ // See if it matches the spec
+ if (FilePath::matches(entry.name, shortSpec, settings.caseSensitive)) {
+
+ if ((entry.type == UNKNOWN) && ! (settings.files && settings.directories)) {
+ // Update the type
+ entry.type = isDirectory(FilePath::concat(parentPath, entry.name)) ? DIR_TYPE : FILE_TYPE;
+ }
+
+ if ((settings.files && settings.directories) ||
+ (settings.files && (entry.type == FILE_TYPE)) ||
+ (settings.directories && (entry.type == DIR_TYPE))) {
+
+ if (settings.includeParentPath) {
+ result.append(FilePath::concat(parentPath, entry.name));
+ } else {
+ result.append(entry.name);
+ }
+ }
+ } // match
+
+ if (settings.recursive && (entry.type == DIR_TYPE)) {
+ listHelper(shortSpec, FilePath::concat(parentPath, entry.name), result, settings);
+ }
+ } // for
+}
+
+
+void FileSystem::_list(const std::string& spec, Array<std::string>& result, const ListSettings& settings) {
+ const std::string& shortSpec = FilePath::baseExt(spec);
+ const std::string& parentPath = FilePath::parent(spec);
+
+ listHelper(shortSpec, parentPath, result, settings);
+}
+
+
+
+#ifdef G3D_WIN32
+const Array<std::string>& FileSystem::_drives() {
+ if (m_winDrive.length() == 0) {
+ // See http://msdn.microsoft.com/en-us/library/aa364975(VS.85).aspx
+ static const size_t bufSize = 5000;
+ char bufData[bufSize];
+ GetLogicalDriveStringsA(bufSize, bufData);
+
+ // Drive list is a series of NULL-terminated strings, itself terminated with a NULL.
+ for (int i = 0; bufData[i] != '\0'; ++i) {
+ const char* thisString = bufData + i;
+ m_winDrive.append(toLower(thisString));
+ i += strlen(thisString) + 1;
+ }
+ }
+
+ return m_winDrive;
+}
+#endif
+
+/////////////////////////////////////////////////////////////////////
+
+bool FilePath::isRoot(const std::string& f) {
+# ifdef G3D_WIN32
+ if (f.length() < 2) {
+ return false;
+ }
+
+ if (f[1] == ':') {
+ if (f.length() == 2) {
+ // e.g., "x:"
+ return true;
+ } else if ((f.length() == 3) && isSlash(f[2])) {
+ // e.g., "x:\"
+ return true;
+ }
+ }
+
+ if (isSlash(f[0]) && isSlash(f[1])) {
+ // e.g., "\\foo\"
+ return true;
+ }
+# else
+ if (f == "/") {
+ return true;
+ }
+# endif
+
+ return false;
+}
+
+
+std::string FilePath::removeTrailingSlash(const std::string& f) {
+ bool removeSlash = ((endsWith(f, "/") || endsWith(f, "\\"))) && ! isRoot(f);
+
+ return removeSlash ? f.substr(0, f.length() - 1) : f;
+}
+
+
+std::string FilePath::concat(const std::string& dirname, const std::string& file) {
+ // Ensure that the directory ends in a slash
+ if (! dirname.empty() &&
+ ! isSlash(dirname[dirname.size() - 1]) &&
+ (dirname[dirname.size() - 1] != ':')) {
+ return dirname + '/' + file;
+ } else {
+ return dirname + file;
+ }
+}
+
+
+std::string FilePath::ext(const std::string& filename) {
+ int i = filename.rfind(".");
+ if (i >= 0) {
+ return filename.substr(i + 1, filename.length() - i);
+ } else {
+ return "";
+ }
+}
+
+
+std::string FilePath::baseExt(const std::string& filename) {
+ int i = findLastSlash(filename);
+
+# ifdef G3D_WIN32
+ int 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 FilePath::base(const std::string& path) {
+ std::string filename = baseExt(path);
+ int i = filename.rfind(".");
+ if (i == -1) {
+ // No extension
+ return filename;
+ } else {
+ return filename.substr(0, i);
+ }
+}
+
+
+std::string FilePath::parent(const std::string& path) {
+ int i = findLastSlash(removeTrailingSlash(path));
+
+# ifdef G3D_WIN32
+ int j = path.rfind(":");
+ if ((i == -1) && (j >= 0)) {
+ i = j;
+ }
+# endif
+
+ if (i == -1) {
+ return "";
+ } else {
+ return path.substr(0, i + 1);
+ }
+}
+
+
+bool FilePath::containsWildcards(const std::string& filename) {
+ return (filename.find('*') != std::string::npos) || (filename.find('?') != std::string::npos);
+}
+
+
+bool FilePath::matches(const std::string& path, const std::string& pattern, bool caseSensitive) {
+ int flags = FNM_PERIOD | FNM_NOESCAPE | FNM_PATHNAME;
+ if (! caseSensitive) {
+ flags |= FNM_CASEFOLD;
+ }
+ return g3dfnmatch(pattern.c_str(), path.c_str(), flags) == 0;
+}
+
+
+static int fixslash(int c) {
+ return (c == '\\') ? '/' : c;
+}
+
+
+std::string FilePath::canonicalize(std::string x) {
+ std::transform(x.begin(), x.end(), x.begin(), fixslash);
+ return x;
+}
+
+
+void FilePath::parse
+(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;
+ }
+}
+
+}
diff --git a/dep/g3dlite/source/GCamera.cpp b/dep/g3dlite/source/GCamera.cpp
new file mode 100644
index 00000000000..0ffc7eb7374
--- /dev/null
+++ b/dep/g3dlite/source/GCamera.cpp
@@ -0,0 +1,511 @@
+/**
+ @file GCamera.cpp
+
+ @author Morgan McGuire, http://graphics.cs.williams.edu
+ @author Jeff Marsceill, 08jcm@williams.edu
+
+ @created 2005-07-20
+ @edited 2010-02-22
+*/
+#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"
+#include "G3D/Any.h"
+#include "G3D/stringutils.h"
+
+namespace G3D {
+
+GCamera::GCamera(const Any& any) {
+ any.verifyName("GCamera");
+ any.verifyType(Any::TABLE);
+ *this = GCamera();
+
+ const Any::AnyTable& table = any.table();
+ Any::AnyTable::Iterator it = table.begin();
+ while (it.hasMore()) {
+ const std::string& k = toUpper(it->key);
+ if (k == "FOVDIRECTION") {
+ const std::string& v = toUpper(it->value);
+ if (v == "HORIZONTAL") {
+ m_direction = HORIZONTAL;
+ } else if (v == "VERTICAL") {
+ m_direction = VERTICAL;
+ } else {
+ any.verify(false, "fovDirection must be \"HORIZONTAL\" or \"VERTICAL\"");
+ }
+ } else if (k == "COORDINATEFRAME") {
+ m_cframe = it->value;
+ } else if (k == "FOVDEGREES") {
+ m_fieldOfView = toRadians(it->value.number());
+ } else if (k == "NEARPLANEZ") {
+ m_nearPlaneZ = it->value;
+ } else if (k == "FARPLANEZ") {
+ m_farPlaneZ = it->value;
+ } else if (k == "PIXELOFFSET") {
+ m_pixelOffset = it->value;
+ } else {
+ any.verify(false, std::string("Illegal key in table: ") + it->key);
+ }
+ ++it;
+ }
+}
+
+
+GCamera::operator Any() const {
+ Any any(Any::TABLE, "GCamera");
+
+ any.set("fovDirection", std::string((m_direction == HORIZONTAL) ? "HORIZONTAL" : "VERTICAL"));
+ any.set("fovDegrees", toDegrees(m_fieldOfView));
+ any.set("nearPlaneZ", nearPlaneZ());
+ any.set("farPlaneZ", farPlaneZ());
+ any.set("coordinateFrame", coordinateFrame());
+ any.set("pixelOffset", pixelOffset());
+
+ return any;
+}
+
+
+GCamera::GCamera() {
+ setNearPlaneZ(-0.2f);
+ setFarPlaneZ(-150.0f);
+ setFieldOfView((float)toRadians(90.0f), HORIZONTAL);
+}
+
+
+GCamera::GCamera(const Matrix4& proj, const CFrame& frame) {
+ float left, right, bottom, top, nearval, farval;
+ proj.getPerspectiveProjectionParameters(left, right, bottom, top, nearval, farval);
+ setNearPlaneZ(-nearval);
+ setFarPlaneZ(-farval);
+ float x = right;
+
+ // Assume horizontal field of view
+ setFieldOfView(atan2(x, -m_nearPlaneZ) * 2.0f, HORIZONTAL);
+ setCoordinateFrame(frame);
+}
+
+
+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);
+
+ debugAssert(m_fieldOfView < toRadians(180));
+ 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());
+
+ Vector3 origin = m_cframe.translation;
+
+ float cx = screenWidth / 2.0f;
+ float cy = screenHeight / 2.0f;
+
+ float vw = viewportWidth(viewport);
+ float vh = viewportHeight(viewport);
+
+ Vector3 direction = Vector3( (x - cx) * vw / screenWidth,
+ -(y - cy) * vh / screenHeight,
+ m_nearPlaneZ);
+
+ direction = m_cframe.vectorToWorldSpace(direction);
+
+ // Normalize the direction (we didn't do it before)
+ direction = direction.direction();
+
+ return Ray::fromOriginAndDirection(origin, direction);
+}
+
+
+void GCamera::getProjectPixelMatrix(const Rect2D& viewport, Matrix4& P) const {
+ getProjectUnitMatrix(viewport, P);
+ float screenWidth = viewport.width();
+ float screenHeight = viewport.height();
+
+ float sx = screenWidth / 2.0;
+ float sy = screenHeight / 2.0;
+
+ P = Matrix4(sx, 0, 0, sx + viewport.x0() - m_pixelOffset.x,
+ 0, -sy, 0, sy + viewport.y0() + m_pixelOffset.y,
+ 0, 0, 1, 0,
+ 0, 0, 0, 1) * P;
+}
+
+
+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;
+
+ float s = 1.0f;
+ if (m_direction == VERTICAL) {
+ y = -m_nearPlaneZ * tan(m_fieldOfView / 2);
+ x = y * (screenWidth / screenHeight);
+ s = screenHeight;
+ } else { //m_direction == HORIZONTAL
+ x = -m_nearPlaneZ * tan(m_fieldOfView / 2);
+ y = x * (screenHeight / screenWidth);
+ s = screenWidth;
+ }
+
+ n = -m_nearPlaneZ;
+ f = -m_farPlaneZ;
+ r = x - m_pixelOffset.x/s;
+ l = -x - m_pixelOffset.x/s;
+ t = y + m_pixelOffset.y/s;
+ b = -y + m_pixelOffset.y/s;
+
+ 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 >= finf()) {
+ // 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 - m_pixelOffset.x, v.y - m_pixelOffset.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 {
+ (void)viewport;
+ if (z >= 0) {
+ return finf();
+ }
+ 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 zn = m_nearPlaneZ;
+ const float zf = m_farPlaneZ;
+ float xx, zz, yy;
+
+ float halfFOV = m_fieldOfView * 0.5f;
+
+ // This computes the normal, which is based on the complement of the
+ // halfFOV angle, so the equations are "backwards"
+ if (m_direction == VERTICAL) {
+ yy = -cosf(halfFOV);
+ xx = yy * viewport.height() / viewport.width();
+ zz = -sinf(halfFOV);
+ } else {
+ xx = -cosf(halfFOV);
+ yy = xx * viewport.width() / viewport.height();
+ zz = -sinf(halfFOV);
+ }
+
+ // Near face (ccw from UR)
+ fr.vertexPos.append(
+ Vector4( x, y, zn, 1),
+ Vector4(-x, y, zn, 1),
+ Vector4(-x, -y, zn, 1),
+ Vector4( x, -y, zn, 1));
+
+ // Far face (ccw from UR, from origin)
+ if (m_farPlaneZ == -finf()) {
+ fr.vertexPos.append(Vector4( x, y, zn, 0),
+ Vector4(-x, y, zn, 0),
+ Vector4(-x, -y, zn, 0),
+ Vector4( x, -y, zn, 0));
+ } else {
+ // Finite
+ const float s = zf / zn;
+ fr.vertexPos.append(Vector4( x * s, y * s, zf, 1),
+ Vector4(-x * s, y * s, zf, 1),
+ Vector4(-x * s, -y * s, zf, 1),
+ Vector4( x * s, -y * s, zf, 1));
+ }
+
+ 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(xx, 0, zz), 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, yy, zz), 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 < finf()) {
+ 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/2, h/2, z);
+ outUL = Vector3(-w/2, h/2, z);
+ outLL = Vector3(-w/2, -h/2, z);
+ outLR = Vector3( w/2, -h/2, 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);
+ m_pixelOffset.serialize(bo);
+}
+
+
+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();
+ m_pixelOffset.deserialize(bi);
+}
+
+
+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/dep/g3dlite/source/GImage.cpp b/dep/g3dlite/source/GImage.cpp
new file mode 100644
index 00000000000..19a2b1e71f4
--- /dev/null
+++ b/dep/g3dlite/source/GImage.cpp
@@ -0,0 +1,1166 @@
+/**
+ \file GImage.cpp
+ \author Morgan McGuire, http://graphics.cs.williams.edu
+ Copyright 2002-2010, Morgan McGuire
+
+ \created 2002-05-27
+ \edited 2010-01-04
+ */
+#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 "G3D/fileutils.h"
+
+#ifdef G3D_LINUX
+# include <png.h>
+#else
+# include "png.h"
+#endif
+
+#include <sys/stat.h>
+#include <assert.h>
+#include <sys/types.h>
+
+//////////////////////////////////////////////////////////////////////////////////////////////
+
+namespace G3D {
+
+void GImage::LtoRGBA(
+ const uint8* in,
+ uint8* out,
+ int numPixels) {
+
+ for (int i = 0; i < numPixels; ++i) {
+ int v = in[i];
+ int i4 = i * 4;
+
+ out[i4 + 0] = v;
+ out[i4 + 1] = v;
+ out[i4 + 2] = v;
+ out[i4 + 3] = 255;
+ }
+}
+
+
+void GImage::LtoRGB(
+ const uint8* in,
+ uint8* out,
+ int numPixels) {
+
+ for (int i = 0; i < numPixels; ++i) {
+ int v = in[i];
+ int i3 = i * 3;
+
+ out[i3 + 0] = v;
+ out[i3 + 1] = v;
+ out[i3 + 2] = v;
+ }
+}
+
+
+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_BINARY:
+ 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(m_width >= 0);
+ debugAssert(m_height >= 0);
+ debugAssert(m_channels == 1 || m_channels == 3 || m_channels == 4);
+ debugAssert(m_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;
+
+ m_width = xmax - xmin + 1;
+ m_height = ymax - ymin + 1;
+ m_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
+ m_byte = (uint8*)m_memMan->alloc(m_width * m_height * 3);
+
+ if ((paletteType == 1) && (planes == 3)) {
+
+ Color3uint8* pixel = pixel3();
+
+ // Iterate over each scan line
+ for (int row = 0; row < m_height; ++row) {
+ // Read each scan line once per plane
+ for (int plane = 0; plane < planes; ++plane) {
+ int p = row * m_width;
+ int p1 = p + m_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 < m_width * m_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 < m_width * m_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 > m_width * m_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 = toUpper(filenameExt(filename));
+
+ if ((extension == "PPM") || (extension == "PGM") || (extension == "PBM")) {
+ // There are two PPM formats (binary and ASCII); we handle them differently
+ if (dataLen > 3) {
+ if (!memcmp(data, "P6", 2) || !memcmp(data, "P5", 2)) {
+ return PPM_BINARY;
+ } 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) ||!memcmp(data, "P5", 2))) {
+ return PPM_BINARY;
+ }
+
+ 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,
+ const MemoryManager::Ref& m) :
+ m_memMan(m),
+ m_byte(NULL),
+ m_channels(0),
+ m_width(0),
+ m_height(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,
+ const MemoryManager::Ref& m) :
+ m_memMan(m),
+ m_byte(NULL),
+ m_channels(0),
+ m_width(0),
+ m_height(0) {
+
+ 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,
+ const MemoryManager::Ref& mem) :
+ m_memMan(mem),
+ m_byte(0),
+ m_channels(0),
+ m_width(0),
+ m_height(0) {
+
+ resize(width, height, channels);
+}
+
+
+void GImage::resize(
+ int width,
+ int height,
+ int channels,
+ bool zero) {
+
+ debugAssert(width >= 0);
+ debugAssert(height >= 0);
+ debugAssert(channels >= 1);
+
+ clear();
+
+ m_width = width;
+ m_height = height;
+ m_channels = channels;
+ size_t sz = width * height * channels;
+
+ if (sz > 0) {
+ m_byte = (uint8*)m_memMan->alloc(sz);
+ if (zero) {
+ System::memset(m_byte, 0, sz);
+ }
+ debugAssert(isValidHeapPointer(m_byte));
+ }
+}
+
+
+void GImage::_copy(
+ const GImage& other) {
+
+ clear();
+
+ m_width = other.m_width;
+ m_height = other.m_height;
+ m_channels = other.m_channels;
+ int s = m_width * m_height * m_channels * sizeof(uint8);
+ m_byte = (uint8*)m_memMan->alloc(s);
+ debugAssert(isValidHeapPointer(m_byte));
+ memcpy(m_byte, other.m_byte, s);
+}
+
+
+void GImage::flipHorizontal() {
+ uint8 temp[4];
+ int rowBytes = m_width * m_channels;
+ for (int y = 0; y < m_height; ++y) {
+ uint8* row = m_byte + y * rowBytes;
+ for (int x = 0; x < m_width / 2; ++x) {
+ System::memcpy(temp, row + x * m_channels, m_channels);
+ System::memcpy(row + x * m_channels, row + (m_width - x - 1) * m_channels, m_channels);
+ System::memcpy(row + (m_width - x - 1) * m_channels, temp, m_channels);
+ }
+ }
+}
+
+
+void GImage::flipVertical() {
+ uint8* old = m_byte;
+ m_byte = (uint8*)m_memMan->alloc(m_width * m_height * m_channels);
+
+ // We could do this with only a single-row temp buffer, but then
+ // we'd have to copy twice as much data.
+ int rowBytes = m_width * m_channels;
+ for (int y = 0; y < m_height; ++y) {
+ System::memcpy(m_byte + y * rowBytes, old + (m_height - y - 1) * rowBytes, rowBytes);
+ }
+
+ m_memMan->free(old);
+}
+
+
+void GImage::rotate90CW(int numTimes) {
+
+ uint8* old = NULL;
+ numTimes = iWrap(numTimes, 4);
+ if (numTimes > 0) {
+ (uint8*)m_memMan->alloc(m_width * m_height * m_channels);
+ }
+ for (int j = 0; j < numTimes; ++j) {
+ {
+ uint8* temp = old;
+ old = m_byte;
+ m_byte = temp;
+ }
+
+ {
+ int temp = m_width;
+ m_width = m_height;
+ m_height = temp;
+ }
+
+ int rowBytes = m_width * m_channels;
+ for (int y = 0; y < m_height; ++y) {
+ for (int x = 0; x < m_width; ++x) {
+ uint8* dst = m_byte + x + y * rowBytes;
+ uint8* src = old + y + (m_height - x - 1) * rowBytes;
+ System::memcpy(dst, src, m_channels);
+ }
+ }
+ }
+ m_memMan->free(old);
+}
+
+
+
+GImage::GImage(
+ const GImage& other,
+ const MemoryManager::Ref& m) : m_memMan(m), m_byte(NULL) {
+
+ _copy(other);
+}
+
+
+GImage::~GImage() {
+ clear();
+}
+
+
+void GImage::clear() {
+ m_width = 0;
+ m_height = 0;
+ m_memMan->free(m_byte);
+ m_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.m_width < srcX + srcWidth) ||
+ (src.m_height < srcY + srcHeight) ||
+ (srcY < 0) ||
+ (srcX < 0)) {
+
+ return false;
+ }
+
+ dest.resize(srcWidth, srcHeight, src.m_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.m_width < srcX + srcWidth) ||
+ (src.m_height < srcY + srcHeight) ||
+ (dest.m_width < destX + srcWidth) ||
+ (dest.m_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.m_width + srcX) * src.channels();
+ uint8* destRow = dest.byte() +
+ ((i + destY) * dest.width() + destX) * dest.channels();
+ memcpy(destRow, srcRow, srcWidth * src.m_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 {
+ return UNKNOWN;
+ }
+ // Don't put PPM here, since it has two versions
+}
+
+
+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_BINARY:
+ 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);
+ }
+
+ int N = m_width * m_height;
+ for (int i = 0; i < N; ++i) {
+ output.byte()[i * 4 + 0] = byte()[i * m_channels + 0];
+ output.byte()[i * 4 + 1] = byte()[i * m_channels + 1];
+ output.byte()[i * 4 + 2] = byte()[i * m_channels + 2];
+ output.byte()[i * 4 + 3] = alpha.byte()[i * alpha.m_channels];
+ }
+}
+
+
+void GImage::stripAlpha(GImage& output) const {
+
+ if (output.m_width != m_width || output.m_height != m_height || output.m_channels != 3) {
+ output.resize(m_width, m_height, 3);
+ }
+
+ int N = m_width * m_height;
+ for (int i = 0; i < N; ++i) {
+ output.byte()[i * 3 + 0] = byte()[i * m_channels + 0];
+ output.byte()[i * 3 + 1] = byte()[i * m_channels + 1];
+ output.byte()[i * 3 + 2] = byte()[i * m_channels + 2];
+ }
+}
+
+
+int GImage::sizeInMemory() const {
+ return sizeof(GImage) + m_width * m_height * m_channels;
+}
+
+
+void GImage::computeNormalMap(
+ const GImage& bump,
+ GImage& normal,
+ const BumpMapPreprocess& preprocess) {
+ computeNormalMap(bump.m_width, bump.m_height, bump.m_channels,
+ bump.byte(), normal, preprocess);
+}
+
+void GImage::computeNormalMap(
+ int width,
+ int height,
+ int channels,
+ const uint8* src,
+ GImage& normal,
+ const BumpMapPreprocess& preprocess) {
+
+ float whiteHeightInPixels = preprocess.zExtentPixels;
+ bool lowPassBump = preprocess.lowPassFilter;
+ bool scaleHeightByNz = preprocess.scaleZByNz;
+
+ if (whiteHeightInPixels < 0.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) * -whiteHeightInPixels;
+ }
+
+ 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();
+
+ // 1/s for the scale factor that each ELEVATION should be multiplied by.
+ // We avoid actually multiplying by this and instead just divide it out of z.
+ float elevationInvScale = 255.0f / whiteHeightInPixels;
+
+ 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 ELEVATION(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. Does not
+ // go out of bounds because the above is computed mod (width, height)
+ delta.y = -( ELEVATION(-1, -1) * 1 + ELEVATION( 0, -1) * 2 + ELEVATION( 1, -1) * 1 +
+ -ELEVATION(-1, 1) * 1 + -ELEVATION( 0, 1) * 2 + -ELEVATION( 1, 1) * 1);
+
+ delta.x = -(-ELEVATION(-1, -1) * 1 + ELEVATION( 1, -1) * 1 +
+ -ELEVATION(-1, 0) * 2 + ELEVATION( 1, 0) * 2 +
+ -ELEVATION(-1, 1) * 1 + ELEVATION( 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 * elevationInvScale;
+
+ // Delta is now scaled in pixels; normalize
+ delta = delta.direction();
+
+ // Copy over the bump value into the alpha channel.
+ float H = B[j] / 255.0f;
+
+ if (lowPassBump) {
+ H = (ELEVATION(-1, -1) + ELEVATION( 0, -1) + ELEVATION(1, -1) +
+ ELEVATION(-1, 0) + ELEVATION( 0, 0) + ELEVATION(1, 0) +
+ ELEVATION(-1, 1) + ELEVATION( 0, 1) + ELEVATION(1, 1)) / (255.0f * 9.0f);
+ }
+# undef ELEVATION
+
+ 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.0f);
+
+ // Pack into byte range
+ delta = delta * 127.5f + Vector3(127.5f, 127.5f, 127.5f);
+ 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 (m_channels) {
+ case 1:
+ return;
+
+ case 3:
+ {
+ // Average
+ Color3uint8* src = (Color3uint8*)m_byte;
+ m_byte = NULL;
+ resize(m_width, m_height, 1);
+ for (int i = m_width * m_height - 1; i >= 0; --i) {
+ const Color3uint8 s = src[i];
+ uint8& d = m_byte[i];
+ d = ((int)s.r + (int)s.g + (int)s.b) / 3;
+ }
+ m_memMan->free(src);
+ }
+ break;
+
+ case 4:
+ {
+ // Average
+ Color4uint8* src = (Color4uint8*)m_byte;
+ m_byte = NULL;
+ resize(m_width, m_height, 1);
+ for (int i = m_width * m_height - 1; i >= 0; --i) {
+ const Color4uint8 s = src[i];
+ uint8& d = m_byte[i];
+ d = ((int)s.r + (int)s.g + (int)s.b) / 3;
+ }
+ m_memMan->free(src);
+ }
+ return;
+
+ default:
+ alwaysAssertM(false, "Bad number of channels in input image");
+ }
+}
+
+
+void GImage::convertToRGBA() {
+ switch (m_channels) {
+ case 1:
+ {
+ // Spread
+ uint8* old = m_byte;
+ m_byte = NULL;
+ resize(m_width, m_height, 4);
+ for (int i = m_width * m_height - 1; i >= 0; --i) {
+ const uint8 s = old[i];
+ Color4uint8& d = ((Color4uint8*)m_byte)[i];
+ d.r = d.g = d.b = s;
+ d.a = 255;
+ }
+ m_memMan->free(m_byte);
+ }
+ break;
+
+ case 3:
+ {
+ // Add alpha
+ Color3uint8* old = (Color3uint8*)m_byte;
+ m_byte = NULL;
+ resize(m_width, m_height, 4);
+ for (int i = m_width * m_height - 1; i >= 0; --i) {
+ const Color3uint8 s = old[i];
+ Color4uint8& d = ((Color4uint8*)m_byte)[i];
+ d.r = s.r;
+ d.g = s.g;
+ d.b = s.b;
+ d.a = 255;
+ }
+ m_memMan->free(old);
+ }
+ break;
+
+ case 4:
+ // Already RGBA
+ return;
+
+ default:
+ alwaysAssertM(false, "Bad number of channels in input image");
+ }
+}
+
+
+void GImage::convertToRGB() {
+ switch (m_channels) {
+ case 1:
+ {
+ // Spread
+ uint8* old = m_byte;
+ m_byte = NULL;
+ resize(m_width, m_height, 3);
+ for (int i = m_width * m_height - 1; i >= 0; --i) {
+ const uint8 s = old[i];
+ Color3uint8& d = ((Color3uint8*)m_byte)[i];
+ d.r = d.g = d.b = s;
+ }
+ m_memMan->free(old);
+ }
+ break;
+
+ case 3:
+ return;
+
+ case 4:
+ // Strip alpha
+ {
+ Color4uint8* old = (Color4uint8*)m_byte;
+ m_byte = NULL;
+ resize(m_width, m_height, 3);
+ for (int i = m_width * m_height - 1; i >= 0; --i) {
+ const Color4uint8 s = old[i];
+ Color3uint8& d = ((Color3uint8*)m_byte)[i];
+ d.r = s.r;
+ d.g = s.g;
+ d.b = s.b;
+ }
+ m_memMan->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.m_height; ++y) {
+ for (int x = 0; x < im.m_width; ++x) {
+ bool checker = isOdd((x / checkerSize) + (y / checkerSize));
+ const Color4uint8& color = checker ? A : B;
+ for (int c = 0; c < im.m_channels; ++c) {
+ uint8* v = im.byte() + (x + y * im.m_width) * im.m_channels + c;
+ *v = color[c];
+ }
+ }
+ }
+}
+
+}
+
diff --git a/dep/g3dlite/source/GImage_bayer.cpp b/dep/g3dlite/source/GImage_bayer.cpp
new file mode 100644
index 00000000000..3d08e8ade5f
--- /dev/null
+++ b/dep/g3dlite/source/GImage_bayer.cpp
@@ -0,0 +1,298 @@
+/**
+ @file GImage_bayer.cpp
+ @author Morgan McGuire, http://graphics.cs.williams.edu
+ @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/dep/g3dlite/source/GImage_bmp.cpp b/dep/g3dlite/source/GImage_bmp.cpp
new file mode 100644
index 00000000000..425a7e1a1d2
--- /dev/null
+++ b/dep/g3dlite/source/GImage_bmp.cpp
@@ -0,0 +1,717 @@
+/**
+ @file GImage_bmp.cpp
+ @author Morgan McGuire, http://graphics.cs.williams.edu
+ @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(m_channels == 1 || m_channels == 3);
+ out.setEndian(G3D_LITTLE_ENDIAN);
+
+ uint8 red;
+ uint8 green;
+ uint8 blue;
+ int pixelBufferSize = m_width * m_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(m_width);
+ out.writeUInt32(m_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 = m_width * 3;
+
+ if (BMScanWidth & 3) {
+ BMPadding = 4 - (BMScanWidth & 3);
+ } else {
+ BMPadding = 0;
+ }
+
+ int hStart = m_height - 1;
+ int hEnd = -1;
+ int hDir = -1;
+ int dest;
+
+ // Write the pixel data
+ for (int h = hStart; h != hEnd; h += hDir) {
+ dest = m_channels * h * m_width;
+ for (int w = 0; w < m_width; ++w) {
+
+ if (m_channels == 3) {
+ red = m_byte[dest];
+ green = m_byte[dest + 1];
+ blue = m_byte[dest + 2];
+ } else {
+ red = m_byte[dest];
+ green = m_byte[dest];
+ blue = m_byte[dest];
+ }
+
+ out.writeUInt8(blue);
+ out.writeUInt8(green);
+ out.writeUInt8(red);
+
+ dest += m_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());
+ }
+
+ m_channels = 3;
+ // Skip to the BITMAPINFOHEADER's width and height
+ input.skip(16);
+
+ m_width = input.readUInt32();
+ m_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 (m_height < 0) {
+ m_height = -m_height;
+ hStart = 0;
+ hEnd = m_height;
+ hDir = 1;
+ } else {
+ //height = height;
+ hStart = m_height - 1;
+ hEnd = -1;
+ hDir = -1;
+ }
+
+ m_byte = (uint8*)m_memMan->alloc(m_width * m_height * 3);
+ debugAssert(m_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 = (m_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 * m_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 < m_width) {
+ int src = 3 * ((BMGroup & pow2[i]) >> i);
+
+ m_byte[dest] = palette[src];
+ m_byte[dest + 1] = palette[src + 1];
+ m_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 = (m_width + 1) >> 1;
+ if (BMScanWidth & 3) {
+ BMScanWidth += 4 - (BMScanWidth & 3);
+ }
+
+ for (int h = hStart; h != hEnd; h += hDir) {
+
+ currPixel = 0;
+ dest = 3 * h * m_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 < m_width) {
+ int tsrc = src[i];
+
+ m_byte[dest] = palette[tsrc];
+ m_byte[dest + 1] = palette[tsrc + 1];
+ m_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 = m_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 < m_width) {
+ dest = 3 * ((h * m_width) + currPixel);
+ int src = 3 * BMPixel8;
+
+ m_byte[dest] = palette[src];
+ m_byte[dest + 1] = palette[src + 1];
+ m_byte[dest + 2] = palette[src + 2];
+
+ ++currPixel;
+ }
+ }
+ }
+
+ } else if (bitCount == 16) {
+
+ m_memMan->free(m_byte);
+ m_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 = m_width * 3;
+
+ if (BMScanWidth & 3) {
+ BMPadding = 4 - (BMScanWidth & 3);
+ } else {
+ BMPadding = 0;
+ }
+
+ for (int h = hStart; h != hEnd; h += hDir) {
+ dest = 3 * h * m_width;
+ for (int w = 0; w < m_width; ++w) {
+
+ blue = input.readUInt8();
+ green = input.readUInt8();
+ red = input.readUInt8();
+
+ m_byte[dest] = red;
+ m_byte[dest + 1] = green;
+ m_byte[dest + 2] = blue;
+
+ dest += 3;
+ }
+
+ if (BMPadding) {
+ input.skip(2);
+ }
+ }
+
+ } else if (bitCount == 32) {
+
+ m_memMan->free(m_byte);
+ m_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.
+ m_memMan->free(m_byte);
+ m_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();
+
+ m_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);
+
+ m_width = input.readUInt8();
+ m_height = input.readUInt8();
+ int numColors = input.readUInt8();
+
+ m_byte = (uint8*)m_memMan->alloc(m_width * m_height * m_channels);
+ debugAssert(m_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 (m_height < 0) {
+ m_height = -m_height;
+ hStart = 0;
+ hEnd = m_height;
+ hDir = 1;
+ } else {
+ //height = height;
+ hStart = m_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 = (m_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 * m_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 < m_width) {
+ int src = ((BMGroup & pow2[i]) >> i);
+
+ m_byte[dest] = palette[src].r;
+ m_byte[dest + 1] = palette[src].g;
+ m_byte[dest + 2] = palette[src].b;
+
+ ++currPixel;
+ dest += 4;
+ }
+ }
+ }
+ }
+
+ } else if (bitsPerPixel == 4) {
+
+ // For bitmaps, each scanline is dword-aligned.
+ int BMScanWidth = (m_width + 1) >> 1;
+ if (BMScanWidth & 3) {
+ BMScanWidth += 4 - (BMScanWidth & 3);
+ }
+
+ for (int h = hStart; h != hEnd; h += hDir) {
+
+ currPixel = 0;
+ dest = 4 * h * m_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 < m_width) {
+ int tsrc = src[i];
+
+ m_byte[dest] = palette[tsrc].r;
+ m_byte[dest + 1] = palette[tsrc].g;
+ m_byte[dest + 2] = palette[tsrc].b;
+
+ ++currPixel;
+ dest += 4;
+ }
+ }
+ }
+ }
+
+ } else if (bitsPerPixel == 8) {
+
+ // For bitmaps, each scanline is dword-aligned.
+ BMScanWidth = m_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 < m_width) {
+ dest = 4 * ((h * m_width) + currPixel);
+ int src = BMPixel8;
+
+ m_byte[dest] = palette[src].r;
+ m_byte[dest + 1] = palette[src].g;
+ m_byte[dest + 2] = palette[src].b;
+
+ ++currPixel;
+ }
+ }
+ }
+ }
+
+ // Read the mask into the alpha channel
+ int bitsPerRow = m_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 = m_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 < m_width); ++x, ++j) {
+ int bit = (byte >> (7 - j)) & 0x01;
+ pixel4(x, y).a = (1 - bit) * 0xFF;
+ }
+ }
+ }
+
+}
+
+
+}
diff --git a/dep/g3dlite/source/GImage_jpeg.cpp b/dep/g3dlite/source/GImage_jpeg.cpp
new file mode 100644
index 00000000000..0b0521f31e7
--- /dev/null
+++ b/dep/g3dlite/source/GImage_jpeg.cpp
@@ -0,0 +1,446 @@
+/**
+ @file GImage_jpeg.cpp
+ @author Morgan McGuire, http://graphics.cs.williams.edu
+ @created 2002-05-27
+ @edited 2009-04-20
+ */
+#include "G3D/platform.h"
+#include "G3D/GImage.h"
+#include "G3D/BinaryInput.h"
+#include "G3D/BinaryOutput.h"
+
+#include <cstring>
+
+extern "C" {
+#ifdef G3D_LINUX
+# include <jconfig.h>
+# include <jpeglib.h>
+#else
+# include "jconfig.h"
+# include "jpeglib.h"
+#endif
+}
+
+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 (m_channels != 3) {
+ // Convert to three channel
+ GImage tmp = *this;
+ tmp.convertToRGB();
+ tmp.encodeJPEG(out);
+ return;
+ }
+
+ debugAssert(m_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 = m_width * m_height * 3 + 200;
+ JOCTET* compressed_data = (JOCTET*)System::malloc(buffer_size);
+ jpeg_memory_dest(&cinfo, compressed_data, buffer_size);
+
+
+ cinfo.image_width = m_width;
+ cinfo.image_height = m_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] = &(m_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;
+
+ m_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
+ m_width = cinfo.output_width;
+ m_height = cinfo.output_height;
+
+ // Prepare the pointer object for the pixel data
+ m_byte = (uint8*)m_memMan->alloc(m_width * m_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 = &(m_byte[loc * 3]);
+ uint8* endScan = scan + (m_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 = m_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 = &(m_byte[loc * 3]);
+ uint8* endScan = scan + m_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/dep/g3dlite/source/GImage_png.cpp b/dep/g3dlite/source/GImage_png.cpp
new file mode 100644
index 00000000000..875c7015cd1
--- /dev/null
+++ b/dep/g3dlite/source/GImage_png.cpp
@@ -0,0 +1,276 @@
+/**
+ @file GImage_png.cpp
+ @author Morgan McGuire, http://graphics.cs.williams.edu
+ @created 2002-05-27
+ @edited 2009-04-20
+ */
+#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 {
+
+ if (! (m_channels == 1 || m_channels == 3 || m_channels == 4)) {
+ throw GImage::Error(format("Illegal channels for PNG: %d", m_channels), out.getFilename());
+ }
+ if (m_width <= 0) {
+ throw GImage::Error(format("Illegal width for PNG: %d", m_width), out.getFilename());
+ }
+ if (m_height <= 0) {
+ throw GImage::Error(format("Illegal height for PNG: %d", m_height), out.getFilename());
+ }
+
+ // PNG library requires that the height * pointer size fit within an int
+ if (png_uint_32(m_height) * png_sizeof(png_bytep) > PNG_UINT_32_MAX) {
+ 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);
+ png_color_8_struct sig_bit;
+
+ switch (m_channels) {
+ case 1:
+ png_set_IHDR(png_ptr, info_ptr, m_width, m_height, 8, PNG_COLOR_TYPE_GRAY,
+ PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_BASE, PNG_FILTER_TYPE_BASE);
+ sig_bit.red = 0;
+ sig_bit.green = 0;
+ sig_bit.blue = 0;
+ sig_bit.alpha = 0;
+ sig_bit.gray = 8;
+ break;
+
+ case 3:
+ png_set_IHDR(png_ptr, info_ptr, m_width, m_height, 8, PNG_COLOR_TYPE_RGB,
+ PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_BASE, PNG_FILTER_TYPE_BASE);
+
+ sig_bit.red = 8;
+ sig_bit.green = 8;
+ sig_bit.blue = 8;
+ sig_bit.alpha = 0;
+ sig_bit.gray = 0;
+ break;
+
+ case 4:
+ png_set_IHDR(png_ptr, info_ptr, m_width, m_height, 8, PNG_COLOR_TYPE_RGBA,
+ PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_BASE, PNG_FILTER_TYPE_BASE);
+ sig_bit.red = 8;
+ sig_bit.green = 8;
+ sig_bit.blue = 8;
+ sig_bit.alpha = 8;
+ sig_bit.gray = 0;
+ break;
+
+ default:
+ png_destroy_write_struct(&png_ptr, &info_ptr);
+ throw GImage::Error("Unsupported number of channels for PNG.", out.getFilename());
+ }
+
+
+ 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[m_height];
+
+ for (int i=0; i < m_height; ++i) {
+ row_pointers[i] = (png_bytep)&m_byte[m_width * m_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 == NULL) {
+ throw GImage::Error("Unable to initialize PNG decoder.", input.getFilename());
+ }
+
+ png_infop info_ptr = png_create_info_struct(png_ptr);
+ if (info_ptr == NULL) {
+ 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 == NULL) {
+ 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, NULL, 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());
+ }
+
+ m_width = static_cast<uint32>(png_width);
+ m_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_expand(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)) ) {
+
+ m_channels = 4;
+ m_byte = (uint8*)m_memMan->alloc(m_width * m_height * 4);
+
+ } else if ((color_type == PNG_COLOR_TYPE_RGB) ||
+ (color_type == PNG_COLOR_TYPE_PALETTE)) {
+
+ m_channels = 3;
+ m_byte = (uint8*)System::malloc(m_width * m_height * 3);
+
+ } else if (color_type == PNG_COLOR_TYPE_GRAY) {
+
+ m_channels = 1;
+
+ // Round up to the nearest 8 rows to avoid a bug in the PNG decoder
+ int h = iCeil(m_height / 8) * 8;
+ int sz = m_width * h;
+ m_byte = (uint8*)m_memMan->alloc(sz);
+
+ } 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)m_height; ++y) {
+ png_bytep rowPointer = &m_byte[m_width * m_channels * y];
+ png_read_rows(png_ptr, &rowPointer, 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/dep/g3dlite/source/GImage_ppm.cpp b/dep/g3dlite/source/GImage_ppm.cpp
new file mode 100644
index 00000000000..f3065c6038b
--- /dev/null
+++ b/dep/g3dlite/source/GImage_ppm.cpp
@@ -0,0 +1,217 @@
+/**
+ @file GImage_ppm.cpp
+ @author Morgan McGuire, http://graphics.cs.williams.edu
+ @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 {
+
+ TextOutput::Settings ppmOptions;
+ ppmOptions.convertNewlines = false;
+ ppmOptions.numColumns = 70;
+ ppmOptions.wordWrap = TextOutput::Settings::WRAP_WITHOUT_BREAKING;
+ TextOutput ppm(ppmOptions);
+
+ switch (m_channels) {
+ case 1:
+ {
+ ppm.printf("P2\n%d %d\n255\n", m_width, m_height);
+
+ const Color1uint8* c = this->pixel1();
+ // Insert newlines every 70 characters max
+ for (uint32 i = 0; i < (uint32)(m_width * m_height); ++i) {
+ ppm.printf("%d%c", c[i].value, (i % (70/4) == 0) ? '\n' : ' ');
+ }
+ }
+ break;
+
+ case 3:
+ {
+ ppm.printf("P3\n%d %d\n255\n", m_width, m_height);
+
+ const Color3uint8* c = this->pixel3();
+ // Insert newlines every 70 characters max
+ for (uint32 i = 0; i < (uint32)(m_width * m_height); ++i) {
+ ppm.printf("%d %d %d%c", c[i].r, c[i].g, c[i].b,
+ (i % (70/12) == 0) ?
+ '\n' : ' ');
+ }
+ }
+ break;
+ default:
+ alwaysAssertM(false, "PPM requires either 1 or 3 channels exactly.");
+ }
+
+ const std::string& s = ppm.commitString();
+ out.writeBytes(s.c_str(), s.length());
+}
+
+
+void GImage::encodePPM(
+ BinaryOutput& out) const {
+
+ // http://netpbm.sourceforge.net/doc/ppm.html
+ if (m_channels == 3) {
+ std::string header = format("P6 %d %d 255 ", m_width, m_height);
+ out.writeBytes(header.c_str(), header.size());
+ out.writeBytes(this->pixel3(), m_width * m_height * 3);
+ } else if (m_channels == 1) {
+ std::string header = format("P5 %d %d 255 ", m_width, m_height);
+ out.writeBytes(header.c_str(), header.size());
+ out.writeBytes(this->pixel1(), m_width * m_height);
+ } else {
+ alwaysAssertM(false, "PPM requires either 1 or 3 channels exactly.");
+ }
+}
+
+
+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.cppLineComments = 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;
+ }
+
+ m_width = ppmWidth;
+ m_height = ppmHeight;
+ m_channels = 3;
+ // always scale down to 1 byte per channel
+ m_byte = (uint8*)m_memMan->alloc(m_width * m_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)(m_width * m_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 = *(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') && (head[1] != '5'))) {
+ 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();
+
+ if (head[1] == '6') {
+ // 3 channel
+ resize(w, h, 3);
+ input.readBytes(m_byte, m_width * m_height * 3);
+ } else if (head[1] == '5') {
+ // 1 channel
+ resize(w, h, 1);
+ input.readBytes(m_byte, m_width * m_height);
+ }
+}
+
+}
diff --git a/dep/g3dlite/source/GImage_tga.cpp b/dep/g3dlite/source/GImage_tga.cpp
new file mode 100644
index 00000000000..fdcd59af726
--- /dev/null
+++ b/dep/g3dlite/source/GImage_tga.cpp
@@ -0,0 +1,236 @@
+/**
+ @file GImage_tga.cpp
+ @author Morgan McGuire, http://graphics.cs.williams.edu
+ @created 2002-05-27
+ @edited 2009-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(m_width);
+ out.writeUInt16(m_height);
+
+ // Color depth
+ if (m_channels == 1) {
+ // Force RGB mode
+ out.writeUInt8(8 * 3);
+ } else {
+ out.writeUInt8(8 * m_channels);
+ }
+
+ // Image descriptor
+ if (m_channels < 4) {
+ // 0 alpha bits
+ out.writeUInt8(0);
+ } else {
+ // 8 alpha bits
+ out.writeUInt8(8);
+ }
+
+ // Image ID (zero length)
+
+ if (m_channels == 1) {
+ // Pixels are upside down in BGR format.
+ for (int y = m_height - 1; y >= 0; --y) {
+ for (int x = 0; x < m_width; ++x) {
+ uint8 p = (m_byte[(y * m_width + x)]);
+ out.writeUInt8(p);
+ out.writeUInt8(p);
+ out.writeUInt8(p);
+ }
+ }
+ } else if (m_channels == 3) {
+ // Pixels are upside down in BGR format.
+ for (int y = m_height - 1; y >= 0; --y) {
+ for (int x = 0; x < m_width; ++x) {
+ uint8* p = &(m_byte[3 * (y * m_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 = m_height - 1; y >= 0; --y) {
+ for (int x = 0; x < m_width; ++x) {
+ uint8* p = &(m_byte[4 * (y * m_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 ");
+}
+
+inline static void readBGR(uint8* byte, BinaryInput& bi) {
+ int b = bi.readUInt8();
+ int g = bi.readUInt8();
+ int r = bi.readUInt8();
+
+ byte[0] = r;
+ byte[1] = g;
+ byte[2] = b;
+}
+
+inline static void readBGRA(uint8* byte, BinaryInput& bi) {
+ readBGR(byte, bi);
+ byte[3] = bi.readUInt8();
+}
+
+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 && imageType != 10) {
+ throw Error("TGA images must be type 2 (Uncompressed truecolor) or 10 (Run-length truecolor)", input.getFilename());
+ }
+
+ // Color map specification
+ input.skip(5);
+
+ // Image specification
+
+ // Skip x and y offsets
+ input.skip(4);
+
+ m_width = input.readInt16();
+ m_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) {
+ m_channels = 4;
+ } else {
+ m_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);
+
+ m_byte = (uint8*)m_memMan->alloc(m_width * m_height * m_channels);
+ debugAssert(m_byte);
+
+ // Pixel data
+ int x;
+ int y;
+
+ if (imageType == 2) {
+ // Uncompressed
+ if (m_channels == 3) {
+ for (y = m_height - 1; y >= 0; --y) {
+ for (x = 0; x < m_width; ++x) {
+ int i = (x + y * m_width) * 3;
+ readBGR(m_byte + i, input);
+ }
+ }
+ } else {
+ for (y = m_height - 1; y >= 0; --y) {
+ for (x = 0; x < m_width; ++x) {
+ int i = (x + y * m_width) * 4;
+ readBGRA(m_byte + i, input);
+ }
+ }
+ }
+ } else if (imageType == 10) {
+
+ // Run-length encoded
+ for (y = m_height - 1; y >= 0; --y) {
+ for (int x = 0; x < m_width; /* intentionally no x increment */) {
+ // The specification guarantees that no packet will wrap past the end of a row
+ const uint8 repetitionCount = input.readUInt8();
+ const uint8 numValues = (repetitionCount & (~128)) + 1;
+ int byteOffset = (x + y * m_width) * 3;
+
+ if (repetitionCount & 128) {
+ // When the high bit is 1, this is a run-length packet
+ if (m_channels == 3) {
+ Color3uint8 value;
+ readBGR((uint8*)(&value), input);
+ for (int i = 0; i < numValues; ++i, ++x) {
+ for (int b = 0; b < 3; ++b, ++byteOffset) {
+ m_byte[byteOffset] = value[b];
+ }
+ }
+ } else {
+ Color4uint8 value;
+ readBGRA((uint8*)(&value), input);
+ for (int i = 0; i < numValues; ++i, ++x) {
+ for (int b = 0; b < 3; ++b, ++byteOffset) {
+ m_byte[byteOffset] = value[b];
+ }
+ }
+ }
+
+ } else {
+ // When the high bit is 0, this is a raw packet
+ for (int i = 0; i < numValues; ++i, ++x, byteOffset += m_channels) {
+ readBGR(m_byte + byteOffset, input);
+ }
+ }
+ }
+ }
+ } else {
+ alwaysAssertM(false, "Unsupported type");
+ }
+}
+
+}
diff --git a/dep/g3dlite/source/GLight.cpp b/dep/g3dlite/source/GLight.cpp
new file mode 100644
index 00000000000..553b493c1a3
--- /dev/null
+++ b/dep/g3dlite/source/GLight.cpp
@@ -0,0 +1,275 @@
+/**
+ @file GLight.cpp
+
+ @maintainer Morgan McGuire, http://graphics.cs.williams.edu
+
+ @created 2003-11-12
+ @edited 2009-11-16
+*/
+#include "G3D/GLight.h"
+#include "G3D/Sphere.h"
+#include "G3D/CoordinateFrame.h"
+#include "G3D/Any.h"
+#include "G3D/stringutils.h"
+
+namespace G3D {
+
+GLight::GLight(const Any& any) {
+ any.verifyName("GLight");
+
+ if (any.type() == Any::TABLE) {
+ *this = GLight();
+ Vector3 spotTarget;
+ bool hasSpotTarget = false;
+ for (Any::AnyTable::Iterator it = any.table().begin(); it.hasMore(); ++it) {
+ const std::string& key = toLower(it->key);
+ if (key == "position") {
+ position = it->value;
+ } else if (key == "rightdirection") {
+ rightDirection = it->value;
+ } else if (key == "spotdirection") {
+ spotDirection = Vector3(it->value).directionOrZero();
+ } else if (key == "spottarget") {
+ spotTarget = it->value;
+ hasSpotTarget = true;
+ } else if (key == "spotcutoff") {
+ spotCutoff = it->value.number();
+ } else if (key == "spotsquare") {
+ spotSquare = it->value.boolean();
+ } else if (key == "attenuation") {
+ attenuation[0] = it->value[0].number();
+ attenuation[1] = it->value[1].number();
+ attenuation[2] = it->value[2].number();
+ } else if (key == "color") {
+ color = it->value;
+ } else if (key == "enabled") {
+ enabled = it->value.boolean();
+ } else if (key == "specular") {
+ specular = it->value.boolean();
+ } else if (key == "diffuse") {
+ diffuse = it->value.boolean();
+ } else {
+ any.verify(false, "Illegal key: " + it->key);
+ }
+ }
+ if (hasSpotTarget) {
+ spotDirection = (spotTarget - position.xyz()).direction();
+ }
+ } else if (toLower(any.name()) == "glight::directional") {
+
+ *this = directional(any[0], any[1],
+ (any.size() > 2) ? any[2] : Any(true),
+ (any.size() > 3) ? any[3] : Any(true));
+
+ } else if (toLower(any.name()) == "glight::point") {
+
+ *this = point(any[0], any[1],
+ (any.size() > 2) ? any[2] : Any(1),
+ (any.size() > 3) ? any[3] : Any(0),
+ (any.size() > 4) ? any[4] : Any(0.5f),
+ (any.size() > 5) ? any[5] : Any(true),
+ (any.size() > 6) ? any[6] : Any(true));
+
+ } else if (toLower(any.name()) == "glight::spot") {
+
+ *this = spot(any[0], any[1], any[2], any[3],
+ (any.size() > 4) ? any[4] : Any(1),
+ (any.size() > 5) ? any[5] : Any(0),
+ (any.size() > 6) ? any[6] : Any(0),
+ (any.size() > 7) ? any[7] : Any(true),
+ (any.size() > 8) ? any[8] : Any(true));
+ } else {
+ any.verify(false, "Unrecognized name");
+ }
+}
+
+
+GLight::operator Any() const {
+ Any a(Any::TABLE, "GLight");
+ a.set("position", position.operator Any());
+ a.set("rightDirection", rightDirection.operator Any());
+ a.set("spotDirection", spotDirection.operator Any());
+ a.set("spotCutoff", spotCutoff);
+ a.set("spotSquare", spotSquare);
+
+ Any att(Any::ARRAY);
+ att.append(attenuation[0], attenuation[1], attenuation[2]);
+ a.set("attenuation", att);
+ a.set("color", color.operator Any());
+ a.set("enabled", enabled);
+ a.set("specular", specular);
+ a.set("diffuse", diffuse);
+ return a;
+}
+
+
+GLight::GLight() :
+ position(0, 0, 0, 0),
+ rightDirection(0,0,0),
+ spotDirection(0, 0, -1),
+ spotCutoff(180),
+ spotSquare(false),
+ color(Color3::white()),
+ enabled(false),
+ specular(true),
+ diffuse(true) {
+
+ attenuation[0] = 1.0;
+ attenuation[1] = 0.0;
+ attenuation[2] = 0.0;
+}
+
+
+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) &&
+ (rightDirection == other.rightDirection) &&
+ (spotDirection == other.spotDirection) &&
+ (spotCutoff == other.spotCutoff) &&
+ (spotSquare == other.spotSquare) &&
+ (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(), finf());
+ } else {
+ // Avoid divide by zero
+ cutoff = max(cutoff, 0.00001f);
+ float maxIntensity = max(color.r, max(color.g, color.b));
+
+ float radius = finf();
+
+ 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);
+
+ }
+}
+
+
+CoordinateFrame GLight::frame() const {
+ CoordinateFrame f;
+ if (rightDirection == Vector3::zero()) {
+ // No specified right direction; choose one automatically
+ if (position.w == 0) {
+ // Directional light
+ f.lookAt(-position.xyz());
+ } else {
+ // Spot light
+ f.lookAt(spotDirection);
+ }
+ } else {
+ const Vector3& Z = -spotDirection.direction();
+ Vector3 X = rightDirection.direction();
+
+ // Ensure the vectors are not too close together
+ while (abs(X.dot(Z)) > 0.9f) {
+ X = Vector3::random();
+ }
+
+ // Ensure perpendicular
+ X -= Z * Z.dot(X);
+ const Vector3& Y = Z.cross(X);
+
+ f.rotation.setColumn(Vector3::X_AXIS, X);
+ f.rotation.setColumn(Vector3::Y_AXIS, Y);
+ f.rotation.setColumn(Vector3::Z_AXIS, Z);
+ }
+ f.translation = position.xyz();
+
+ return f;
+}
+
+
+} // G3D
diff --git a/dep/g3dlite/source/GThread.cpp b/dep/g3dlite/source/GThread.cpp
new file mode 100644
index 00000000000..607e4b3572f
--- /dev/null
+++ b/dep/g3dlite/source/GThread.cpp
@@ -0,0 +1,229 @@
+/**
+ @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"
+#include "G3D/GMutex.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(SpawnBehavior behavior) {
+
+ debugAssertM(! started(), "Thread has already executed.");
+ if (started()) {
+ return false;
+ }
+
+ m_status = STATUS_STARTED;
+
+ if (behavior == USE_CURRENT_THREAD) {
+ // Run on this thread
+ m_status = STATUS_RUNNING;
+ threadMain();
+ m_status = STATUS_COMPLETED;
+ return true;
+ }
+
+# 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() {
+ if (m_status == STATUS_COMPLETED) {
+ // Must be done
+ return;
+ }
+
+# 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 implementation
+GMutex::GMutex() {
+#ifdef G3D_WIN32
+ ::InitializeCriticalSection(&m_handle);
+#else
+ int ret = pthread_mutexattr_init(&m_attr);
+ debugAssert(ret == 0);
+ ret = pthread_mutexattr_settype(&m_attr, PTHREAD_MUTEX_RECURSIVE);
+ debugAssert(ret == 0);
+ ret = pthread_mutex_init(&m_handle, &m_attr);
+ debugAssert(ret == 0);
+#endif
+}
+
+GMutex::~GMutex() {
+ //TODO: Debug check for locked
+#ifdef G3D_WIN32
+ ::DeleteCriticalSection(&m_handle);
+#else
+ int ret = pthread_mutex_destroy(&m_handle);
+ debugAssert(ret == 0);
+ ret = pthread_mutexattr_destroy(&m_attr);
+ debugAssert(ret == 0);
+#endif
+}
+
+bool GMutex::tryLock() {
+#ifdef G3D_WIN32
+ return (::TryEnterCriticalSection(&m_handle) != 0);
+#else
+ return (pthread_mutex_trylock(&m_handle) == 0);
+#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/dep/g3dlite/source/GUniqueID.cpp b/dep/g3dlite/source/GUniqueID.cpp
new file mode 100644
index 00000000000..84c853e0e31
--- /dev/null
+++ b/dep/g3dlite/source/GUniqueID.cpp
@@ -0,0 +1,78 @@
+/**
+ @file GUniqueID.cpp
+ @author Morgan McGuire, http://graphics.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/dep/g3dlite/source/Image1.cpp b/dep/g3dlite/source/Image1.cpp
new file mode 100644
index 00000000000..a61f7faa633
--- /dev/null
+++ b/dep/g3dlite/source/Image1.cpp
@@ -0,0 +1,224 @@
+/**
+ @file Image1.cpp
+
+ @maintainer Morgan McGuire, http://graphics.cs.williams.edu
+
+ @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, fmt);
+ 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/dep/g3dlite/source/Image1uint8.cpp b/dep/g3dlite/source/Image1uint8.cpp
new file mode 100644
index 00000000000..de2cbbc130b
--- /dev/null
+++ b/dep/g3dlite/source/Image1uint8.cpp
@@ -0,0 +1,212 @@
+/**
+ @file Image1uint8.cpp
+
+ @maintainer Morgan McGuire, http://graphics.cs.williams.edu
+
+ @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, fmt);
+ 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/dep/g3dlite/source/Image3.cpp b/dep/g3dlite/source/Image3.cpp
new file mode 100644
index 00000000000..0d85bdf45da
--- /dev/null
+++ b/dep/g3dlite/source/Image3.cpp
@@ -0,0 +1,225 @@
+/**
+ @file Image3.cpp
+
+ @maintainer Morgan McGuire, http://graphics.cs.williams.edu
+
+ @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, fmt);
+ 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/dep/g3dlite/source/Image3uint8.cpp b/dep/g3dlite/source/Image3uint8.cpp
new file mode 100644
index 00000000000..86595bbd1f6
--- /dev/null
+++ b/dep/g3dlite/source/Image3uint8.cpp
@@ -0,0 +1,225 @@
+/**
+ @file Image3uint8.cpp
+
+ @maintainer Morgan McGuire, http://graphics.cs.williams.edu
+
+ @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, fmt);
+ 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/dep/g3dlite/source/Image4.cpp b/dep/g3dlite/source/Image4.cpp
new file mode 100644
index 00000000000..c6f2b10640d
--- /dev/null
+++ b/dep/g3dlite/source/Image4.cpp
@@ -0,0 +1,226 @@
+/**
+ @file Image4.cpp
+
+ @maintainer Morgan McGuire, http://graphics.cs.williams.edu
+
+ @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, wrap);
+}
+
+
+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/dep/g3dlite/source/Image4uint8.cpp b/dep/g3dlite/source/Image4uint8.cpp
new file mode 100644
index 00000000000..a94ddb12d03
--- /dev/null
+++ b/dep/g3dlite/source/Image4uint8.cpp
@@ -0,0 +1,222 @@
+/**
+ @file Image4uint8.cpp
+
+ @maintainer Morgan McGuire, http://graphics.cs.williams.edu
+
+ @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, fmt);
+ 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/dep/g3dlite/source/ImageFormat.cpp b/dep/g3dlite/source/ImageFormat.cpp
new file mode 100644
index 00000000000..46618c64480
--- /dev/null
+++ b/dep/g3dlite/source/ImageFormat.cpp
@@ -0,0 +1,588 @@
+/**
+ @file ImageFormat.cpp
+
+ @maintainer Morgan McGuire, http://graphics.cs.williams.edu
+
+ @created 2003-05-23
+ @edited 2010-03-30
+ */
+
+#include "GLG3D/glheaders.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();
+ }
+}
+
+
+ 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",
+ "R11G11B10F",
+ "RGB9E10F",
+
+ "RGB8I",
+ "RGB8UI",
+
+ "RGBA8UI",
+
+ "ARGB8",
+ "BGR8",
+
+ "R8",
+
+ "RG8",
+ "RG8I",
+ "RG8UI",
+
+ "RG16F",
+
+ "RGBA8",
+ "RGBA16",
+ "RGBA16F",
+ "RGBA32F",
+
+ "RGBA32UI",
+
+ "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",
+
+ "SRGB8",
+ "SRGBA8",
+
+ "SL8",
+ "SLA8",
+
+ "SRGB_DXT1",
+ "SRGBA_DXT1",
+ "SRGBA_DXT3",
+ "SRGBA_DXT5",
+
+ "DEPTH16",
+ "DEPTH24",
+ "DEPTH32",
+ "DEPTH32F",
+
+ "STENCIL1",
+ "STENCIL4",
+ "STENCIL8",
+ "STENCIL16",
+
+ "DEPTH24_STENCIL8",
+ ""
+ };
+
+const std::string& ImageFormat::name() const {
+ debugAssert(code < CODE_NUM);
+ return nameArray[code];
+}
+
+
+const ImageFormat* ImageFormat::fromString(const std::string& s) {
+
+ for (int i = 0; ! nameArray[i].empty(); ++i) {
+ if (s == nameArray[i]) {
+ return fromCode(ImageFormat::Code(i));
+ }
+ }
+ return NULL;
+}
+
+
+const ImageFormat* ImageFormat::fromCode(ImageFormat::Code code) {
+ switch (code) {
+ case ImageFormat::CODE_L8:
+ return ImageFormat::L8();
+
+ case ImageFormat::CODE_L16:
+ return ImageFormat::L16();
+
+ case ImageFormat::CODE_L16F:
+ return ImageFormat::L16F();
+
+ case ImageFormat::CODE_L32F:
+ return ImageFormat::L32F();
+
+ case ImageFormat::CODE_A8:
+ return ImageFormat::A8();
+
+ case ImageFormat::CODE_A16:
+ return ImageFormat::A16();
+
+ case ImageFormat::CODE_A16F:
+ return ImageFormat::A16F();
+
+ case ImageFormat::CODE_A32F:
+ return ImageFormat::A32F();
+
+ case ImageFormat::CODE_LA4:
+ return ImageFormat::LA4();
+
+ case ImageFormat::CODE_LA8:
+ return ImageFormat::LA8();
+
+ case ImageFormat::CODE_LA16:
+ return ImageFormat::LA16();
+
+ case ImageFormat::CODE_LA16F:
+ return ImageFormat::LA16F();
+ break;
+ case ImageFormat::CODE_LA32F:
+ return ImageFormat::LA32F();
+
+ case ImageFormat::CODE_RGB5:
+ return ImageFormat::RGB5();
+
+ case ImageFormat::CODE_RGB5A1:
+ return ImageFormat::RGB5A1();
+
+ case ImageFormat::CODE_RGB8:
+ return ImageFormat::RGB8();
+
+ case ImageFormat::CODE_RGB10:
+ return ImageFormat::RGB10();
+
+ case ImageFormat::CODE_RGB10A2:
+ return ImageFormat::RGB10A2();
+
+ case ImageFormat::CODE_RGB16:
+ return ImageFormat::RGB16();
+
+ case ImageFormat::CODE_RGB32F:
+ return ImageFormat::RGB32F();
+
+ case ImageFormat::CODE_R11G11B10F:
+ return ImageFormat::R11G11B10F();
+
+ case ImageFormat::CODE_RGB9E5F:
+ return ImageFormat::RGB9E5F();
+
+ case ImageFormat::CODE_RGB8I:
+ return ImageFormat::RGB8I();
+
+ case ImageFormat::CODE_RGB8UI:
+ return ImageFormat::RGB8UI();
+
+ case ImageFormat::CODE_ARGB8:
+ return NULL;
+
+ case ImageFormat::CODE_BGR8:
+ return ImageFormat::BGR8();
+
+ case ImageFormat::CODE_R8:
+ return ImageFormat::R8();
+
+ case ImageFormat::CODE_RG8:
+ return ImageFormat::RG8();
+
+ case ImageFormat::CODE_RG8I:
+ return ImageFormat::RG8I();
+
+ case ImageFormat::CODE_RG8UI:
+ return ImageFormat::RG8UI();
+
+ case ImageFormat::CODE_RG16F:
+ return ImageFormat::RG16F();
+
+ case ImageFormat::CODE_RGBA8:
+ return ImageFormat::RGBA8();
+
+ case ImageFormat::CODE_RGBA16:
+ return ImageFormat::RGBA16();
+
+ case ImageFormat::CODE_RGBA16F:
+ return ImageFormat::RGBA16F();
+
+ case ImageFormat::CODE_RGBA32F:
+ return ImageFormat::RGBA32F();
+
+ case ImageFormat::CODE_RGBA32UI:
+ return ImageFormat::RGBA32UI();
+
+ case ImageFormat::CODE_BAYER_RGGB8:
+ // TODO
+ case ImageFormat::CODE_BAYER_GRBG8:
+ // TODO
+ case ImageFormat::CODE_BAYER_GBRG8:
+ // TODO
+ case ImageFormat::CODE_BAYER_BGGR8:
+ // TODO
+ case ImageFormat::CODE_BAYER_RGGB32F:
+ // TODO
+ case ImageFormat::CODE_BAYER_GRBG32F:
+ // TODO
+ case ImageFormat::CODE_BAYER_GBRG32F:
+ // TODO
+ case ImageFormat::CODE_BAYER_BGGR32F:
+ // TODO
+
+ case ImageFormat::CODE_HSV8:
+ // TODO
+ case ImageFormat::CODE_HSV32F:
+ // TODO
+ return NULL;
+ break;
+
+ 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_SRGB8:
+ return ImageFormat::SRGB8();
+ break;
+
+ case ImageFormat::CODE_SRGBA8:
+ return ImageFormat::SRGBA8();
+ break;
+
+ case ImageFormat::CODE_SL8:
+ return ImageFormat::SL8();
+ break;
+
+ case ImageFormat::CODE_SLA8:
+ return ImageFormat::SLA8();
+ break;
+
+ case ImageFormat::CODE_SRGB_DXT1:
+ return ImageFormat::SRGB_DXT1();
+ break;
+
+ case ImageFormat::CODE_SRGBA_DXT1:
+ return ImageFormat::SRGBA_DXT1();
+ break;
+
+ case ImageFormat::CODE_SRGBA_DXT3:
+ return ImageFormat::SRGBA_DXT3();
+ break;
+
+ case ImageFormat::CODE_SRGBA_DXT5:
+ return ImageFormat::SRGBA_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(enumname, cmpnts, cmprssd, glf, glbf, lb, ab, rb, gb, bb, db, sb, hbpt, pbpt, gldf, opq, fp, code, cs) \
+ const ImageFormat* ImageFormat::enumname() { \
+ static const ImageFormat format(cmpnts, cmprssd, glf, glbf, lb, ab, rb, gb, bb, db, sb, hbpt, pbpt, gldf, opq, fp, code, cs); \
+ 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(R8, 1, UNCOMP_FORMAT, GL_R8, GL_RED, 0, 0, 8, 0, 0, 0, 0, 8, 8, GL_UNSIGNED_BYTE, OPAQUE_FORMAT, INT_FORMAT, ImageFormat::CODE_R8, ImageFormat::COLOR_SPACE_RGB);
+
+DEFINE_TEXTUREFORMAT_METHOD(RG8, 2, UNCOMP_FORMAT, GL_RG8, GL_RG, 0, 0, 8, 8, 0, 0, 0, 16, 16, GL_UNSIGNED_BYTE, OPAQUE_FORMAT, INT_FORMAT, ImageFormat::CODE_RG8, ImageFormat::COLOR_SPACE_RGB);
+
+// The base format for integer formats must be *_INTEGER even though the spec doesn't state this
+DEFINE_TEXTUREFORMAT_METHOD(RG8I, 2, UNCOMP_FORMAT, GL_RG8I, GL_RG_INTEGER, 0, 0, 8, 8, 0, 0, 0, 16, 16, GL_UNSIGNED_BYTE, OPAQUE_FORMAT, INT_FORMAT, ImageFormat::CODE_RG8I, ImageFormat::COLOR_SPACE_RGB);
+
+DEFINE_TEXTUREFORMAT_METHOD(RG8UI, 2, UNCOMP_FORMAT, GL_RG8UI, GL_RG_INTEGER, 0, 0, 8, 8, 0, 0, 0, 16, 16, GL_UNSIGNED_BYTE, OPAQUE_FORMAT, INT_FORMAT, ImageFormat::CODE_RG8UI, ImageFormat::COLOR_SPACE_RGB);
+
+DEFINE_TEXTUREFORMAT_METHOD(RG16F, 2, UNCOMP_FORMAT, GL_RG16F, GL_RG, 0, 0, 16, 16, 0, 0, 0, 32, 32, GL_FLOAT, OPAQUE_FORMAT, FLOAT_FORMAT, ImageFormat::CODE_RG16F, 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_RGBA16F, 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);
+
+// The base format for integer formats must be *_INTEGER even though the spec doesn't state this
+DEFINE_TEXTUREFORMAT_METHOD(RGBA32UI, 4, UNCOMP_FORMAT, GL_RGBA32UI, GL_RGBA_INTEGER, 0, 32, 32, 32, 32, 0, 0, 32*4, 32*4, GL_UNSIGNED_INT, CLEAR_FORMAT, INT_FORMAT, ImageFormat::CODE_RGBA32UI, ImageFormat::COLOR_SPACE_RGB);
+
+// Unsigned
+DEFINE_TEXTUREFORMAT_METHOD(R11G11B10F, 3, UNCOMP_FORMAT, GL_R11F_G11F_B10F_EXT, GL_RGB, 0, 0, 11, 11, 10, 0, 0, 32, 32, GL_FLOAT, OPAQUE_FORMAT, FLOAT_FORMAT, ImageFormat::CODE_R11G11B10F, ImageFormat::COLOR_SPACE_RGB);
+
+// Unsigned
+DEFINE_TEXTUREFORMAT_METHOD(RGB9E5F, 3, UNCOMP_FORMAT, GL_RGB9_E5_EXT, GL_RGB, 0, 0, 14, 14, 14, 0, 0, 32, 32, GL_FLOAT, OPAQUE_FORMAT, FLOAT_FORMAT, ImageFormat::CODE_RGB9E5F, ImageFormat::COLOR_SPACE_RGB);
+
+// The base format for integer formats must be *_INTEGER even though the spec doesn't state this
+DEFINE_TEXTUREFORMAT_METHOD(RGB8I, 3, UNCOMP_FORMAT, GL_RGB8I_EXT, GL_RGB_INTEGER, 0, 0, 8, 8, 8, 0, 0, 32, 24, GL_UNSIGNED_BYTE, OPAQUE_FORMAT, INT_FORMAT, ImageFormat::CODE_RGB8I, ImageFormat::COLOR_SPACE_RGB);
+
+DEFINE_TEXTUREFORMAT_METHOD(RGB8UI, 3, UNCOMP_FORMAT, GL_RGB8UI_EXT, GL_RGB_INTEGER, 0, 0, 8, 8, 8, 0, 0, 32, 24, GL_UNSIGNED_BYTE, OPAQUE_FORMAT, INT_FORMAT, ImageFormat::CODE_RGB8UI, ImageFormat::COLOR_SPACE_RGB);
+
+DEFINE_TEXTUREFORMAT_METHOD(RGBA8UI, 4, UNCOMP_FORMAT, GL_RGBA8UI_EXT, GL_RGBA_INTEGER, 0, 0, 8, 8, 8, 8, 0, 32, 32, GL_UNSIGNED_BYTE, OPAQUE_FORMAT, INT_FORMAT, ImageFormat::CODE_RGBA8UI, 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(SRGB8, 3, UNCOMP_FORMAT, GL_SRGB8, GL_RGB, 0, 0, 8, 8, 8, 0, 0, 32, 24, GL_UNSIGNED_BYTE, OPAQUE_FORMAT, INT_FORMAT, ImageFormat::CODE_SRGB8, ImageFormat::COLOR_SPACE_SRGB);
+
+DEFINE_TEXTUREFORMAT_METHOD(SRGBA8, 4, UNCOMP_FORMAT, GL_SRGB8_ALPHA8, GL_RGBA, 0, 8, 8, 8, 8, 0, 0, 32, 24, GL_UNSIGNED_BYTE, CLEAR_FORMAT, INT_FORMAT, ImageFormat::CODE_SRGBA8, ImageFormat::COLOR_SPACE_SRGB);
+
+DEFINE_TEXTUREFORMAT_METHOD(SL8, 1, UNCOMP_FORMAT, GL_SLUMINANCE8, GL_LUMINANCE, 8, 0, 0, 0, 0, 0, 0, 8, 8, GL_UNSIGNED_BYTE, OPAQUE_FORMAT, INT_FORMAT, ImageFormat::CODE_SL8, ImageFormat::COLOR_SPACE_SRGB);
+
+DEFINE_TEXTUREFORMAT_METHOD(SLA8, 2, UNCOMP_FORMAT, GL_SLUMINANCE8_ALPHA8, GL_LUMINANCE_ALPHA, 8, 8, 0, 0, 0, 0, 0, 16, 16, GL_UNSIGNED_BYTE, CLEAR_FORMAT, INT_FORMAT, ImageFormat::CODE_SLA8, ImageFormat::COLOR_SPACE_SRGB);
+
+DEFINE_TEXTUREFORMAT_METHOD(SRGB_DXT1, 3, COMP_FORMAT, GL_COMPRESSED_SRGB_S3TC_DXT1_EXT, GL_RGB, 0, 0, 0, 0, 0, 0, 0, 64, 64, GL_UNSIGNED_BYTE, OPAQUE_FORMAT, INT_FORMAT, ImageFormat::CODE_SRGB_DXT1, ImageFormat::COLOR_SPACE_SRGB);
+
+DEFINE_TEXTUREFORMAT_METHOD(SRGBA_DXT1, 4, COMP_FORMAT, GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT1_EXT, GL_RGBA, 0, 0, 0, 0, 0, 0, 0, 64, 64, GL_UNSIGNED_BYTE, CLEAR_FORMAT, INT_FORMAT, ImageFormat::CODE_SRGBA_DXT1, ImageFormat::COLOR_SPACE_SRGB);
+
+DEFINE_TEXTUREFORMAT_METHOD(SRGBA_DXT3, 4, COMP_FORMAT, GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT3_EXT, GL_RGBA, 0, 0, 0, 0, 0, 0, 0, 128, 128, GL_UNSIGNED_BYTE, CLEAR_FORMAT, INT_FORMAT, ImageFormat::CODE_SRGBA_DXT3, ImageFormat::COLOR_SPACE_SRGB);
+
+DEFINE_TEXTUREFORMAT_METHOD(SRGBA_DXT5, 4, COMP_FORMAT, GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT5_EXT, GL_RGBA, 0, 0, 0, 0, 0, 0, 0, 128, 128, GL_UNSIGNED_BYTE, CLEAR_FORMAT, INT_FORMAT, ImageFormat::CODE_SRGBA_DXT5, ImageFormat::COLOR_SPACE_SRGB);
+
+DEFINE_TEXTUREFORMAT_METHOD(DEPTH16, 1, UNCOMP_FORMAT, GL_DEPTH_COMPONENT16_ARB, GL_DEPTH_COMPONENT, 0, 0, 0, 0, 0, 16, 0, 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, 24, 0, 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, 32, 0, 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, 32, 0, 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, 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, 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, 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, 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/dep/g3dlite/source/ImageFormat_convert.cpp b/dep/g3dlite/source/ImageFormat_convert.cpp
new file mode 100644
index 00000000000..ecefe6319c7
--- /dev/null
+++ b/dep/g3dlite/source/ImageFormat_convert.cpp
@@ -0,0 +1,1307 @@
+#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) {
+ (void)bayerAlg;
+ (void)dstRowPadBits;
+ 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 = s.rgb();
+ }
+ 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/dep/g3dlite/source/Intersect.cpp b/dep/g3dlite/source/Intersect.cpp
new file mode 100644
index 00000000000..929a2e4e670
--- /dev/null
+++ b/dep/g3dlite/source/Intersect.cpp
@@ -0,0 +1,844 @@
+/**
+ @file Intersect.cpp
+
+ @maintainer Morgan McGuire, http://graphics.cs.williams.edu
+
+ @created 2009-06-29
+ @edited 2009-06-29
+
+ Copyright 2000-2009, Morgan McGuire.
+ All rights reserved.
+
+ From the G3D Innovation Engine
+ http://g3d.sf.net
+ */
+#include "G3D/Intersect.h"
+
+namespace G3D {
+
+#ifdef _MSC_VER
+// Turn on fast floating-point optimizations
+#pragma float_control( push )
+#pragma fp_contract( on )
+#pragma fenv_access( off )
+#pragma float_control( except, off )
+#pragma float_control( precise, off )
+#endif
+
+bool __fastcall Intersect::rayAABox(const Ray& ray, const AABox& box) {
+ switch (ray.classification) {
+ case Ray::MMM:
+
+ if ((ray.m_origin.x < box.lo.x) || (ray.m_origin.y < box.lo.y) || (ray.m_origin.z < box.lo.z)
+ || (ray.jbyi * box.lo.x - box.hi.y + ray.c_xy > 0)
+ || (ray.ibyj * box.lo.y - box.hi.x + ray.c_yx > 0)
+ || (ray.jbyk * box.lo.z - box.hi.y + ray.c_zy > 0)
+ || (ray.kbyj * box.lo.y - box.hi.z + ray.c_yz > 0)
+ || (ray.kbyi * box.lo.x - box.hi.z + ray.c_xz > 0)
+ || (ray.ibyk * box.lo.z - box.hi.x + ray.c_zx > 0)
+ )
+ return false;
+
+ return true;
+
+ case Ray::MMP:
+
+ if ((ray.m_origin.x < box.lo.x) || (ray.m_origin.y < box.lo.y) || (ray.m_origin.z > box.hi.z)
+ || (ray.jbyi * box.lo.x - box.hi.y + ray.c_xy > 0)
+ || (ray.ibyj * box.lo.y - box.hi.x + ray.c_yx > 0)
+ || (ray.jbyk * box.hi.z - box.hi.y + ray.c_zy > 0)
+ || (ray.kbyj * box.lo.y - box.lo.z + ray.c_yz < 0)
+ || (ray.kbyi * box.lo.x - box.lo.z + ray.c_xz < 0)
+ || (ray.ibyk * box.hi.z - box.hi.x + ray.c_zx > 0)
+ )
+ return false;
+
+ return true;
+
+ case Ray::MPM:
+
+ if ((ray.m_origin.x < box.lo.x) || (ray.m_origin.y > box.hi.y) || (ray.m_origin.z < box.lo.z)
+ || (ray.jbyi * box.lo.x - box.lo.y + ray.c_xy < 0)
+ || (ray.ibyj * box.hi.y - box.hi.x + ray.c_yx > 0)
+ || (ray.jbyk * box.lo.z - box.lo.y + ray.c_zy < 0)
+ || (ray.kbyj * box.hi.y - box.hi.z + ray.c_yz > 0)
+ || (ray.kbyi * box.lo.x - box.hi.z + ray.c_xz > 0)
+ || (ray.ibyk * box.lo.z - box.hi.x + ray.c_zx > 0)
+ )
+ return false;
+
+ return true;
+
+ case Ray::MPP:
+
+ if ((ray.m_origin.x < box.lo.x) || (ray.m_origin.y > box.hi.y) || (ray.m_origin.z > box.hi.z)
+ || (ray.jbyi * box.lo.x - box.lo.y + ray.c_xy < 0)
+ || (ray.ibyj * box.hi.y - box.hi.x + ray.c_yx > 0)
+ || (ray.jbyk * box.hi.z - box.lo.y + ray.c_zy < 0)
+ || (ray.kbyj * box.hi.y - box.lo.z + ray.c_yz < 0)
+ || (ray.kbyi * box.lo.x - box.lo.z + ray.c_xz < 0)
+ || (ray.ibyk * box.hi.z - box.hi.x + ray.c_zx > 0)
+ )
+ return false;
+
+ return true;
+
+ case Ray::PMM:
+
+ if ((ray.m_origin.x > box.hi.x) || (ray.m_origin.y < box.lo.y) || (ray.m_origin.z < box.lo.z)
+ || (ray.jbyi * box.hi.x - box.hi.y + ray.c_xy > 0)
+ || (ray.ibyj * box.lo.y - box.lo.x + ray.c_yx < 0)
+ || (ray.jbyk * box.lo.z - box.hi.y + ray.c_zy > 0)
+ || (ray.kbyj * box.lo.y - box.hi.z + ray.c_yz > 0)
+ || (ray.kbyi * box.hi.x - box.hi.z + ray.c_xz > 0)
+ || (ray.ibyk * box.lo.z - box.lo.x + ray.c_zx < 0)
+ )
+ return false;
+
+ return true;
+
+ case Ray::PMP:
+
+ if ((ray.m_origin.x > box.hi.x) || (ray.m_origin.y < box.lo.y) || (ray.m_origin.z > box.hi.z)
+ || (ray.jbyi * box.hi.x - box.hi.y + ray.c_xy > 0)
+ || (ray.ibyj * box.lo.y - box.lo.x + ray.c_yx < 0)
+ || (ray.jbyk * box.hi.z - box.hi.y + ray.c_zy > 0)
+ || (ray.kbyj * box.lo.y - box.lo.z + ray.c_yz < 0)
+ || (ray.kbyi * box.hi.x - box.lo.z + ray.c_xz < 0)
+ || (ray.ibyk * box.hi.z - box.lo.x + ray.c_zx < 0)
+ )
+ return false;
+
+ return true;
+
+ case Ray::PPM:
+
+ if ((ray.m_origin.x > box.hi.x) || (ray.m_origin.y > box.hi.y) || (ray.m_origin.z < box.lo.z)
+ || (ray.jbyi * box.hi.x - box.lo.y + ray.c_xy < 0)
+ || (ray.ibyj * box.hi.y - box.lo.x + ray.c_yx < 0)
+ || (ray.jbyk * box.lo.z - box.lo.y + ray.c_zy < 0)
+ || (ray.kbyj * box.hi.y - box.hi.z + ray.c_yz > 0)
+ || (ray.kbyi * box.hi.x - box.hi.z + ray.c_xz > 0)
+ || (ray.ibyk * box.lo.z - box.lo.x + ray.c_zx < 0)
+ )
+ return false;
+
+ return true;
+
+ case Ray::PPP:
+
+ if ((ray.m_origin.x > box.hi.x) || (ray.m_origin.y > box.hi.y) || (ray.m_origin.z > box.hi.z)
+ || (ray.jbyi * box.hi.x - box.lo.y + ray.c_xy < 0)
+ || (ray.ibyj * box.hi.y - box.lo.x + ray.c_yx < 0)
+ || (ray.jbyk * box.hi.z - box.lo.y + ray.c_zy < 0)
+ || (ray.kbyj * box.hi.y - box.lo.z + ray.c_yz < 0)
+ || (ray.kbyi * box.hi.x - box.lo.z + ray.c_xz < 0)
+ || (ray.ibyk * box.hi.z - box.lo.x + ray.c_zx < 0)) {
+ return false;
+ }
+
+ return true;
+
+ case Ray::OMM:
+
+ if((ray.m_origin.x < box.lo.x) || (ray.m_origin.x > box.hi.x)
+ || (ray.m_origin.y < box.lo.y) || (ray.m_origin.z < box.lo.z)
+ || (ray.jbyk * box.lo.z - box.hi.y + ray.c_zy > 0)
+ || (ray.kbyj * box.lo.y - box.hi.z + ray.c_yz > 0)
+ )
+ return false;
+
+ return true;
+
+ case Ray::OMP:
+
+ if((ray.m_origin.x < box.lo.x) || (ray.m_origin.x > box.hi.x)
+ || (ray.m_origin.y < box.lo.y) || (ray.m_origin.z > box.hi.z)
+ || (ray.jbyk * box.hi.z - box.hi.y + ray.c_zy > 0)
+ || (ray.kbyj * box.lo.y - box.lo.z + ray.c_yz < 0)
+ )
+ return false;
+
+ return true;
+
+ case Ray::OPM:
+
+ if((ray.m_origin.x < box.lo.x) || (ray.m_origin.x > box.hi.x)
+ || (ray.m_origin.y > box.hi.y) || (ray.m_origin.z < box.lo.z)
+ || (ray.jbyk * box.lo.z - box.lo.y + ray.c_zy < 0)
+ || (ray.kbyj * box.hi.y - box.hi.z + ray.c_yz > 0)
+ )
+ return false;
+
+ return true;
+
+ case Ray::OPP:
+
+ if((ray.m_origin.x < box.lo.x) || (ray.m_origin.x > box.hi.x)
+ || (ray.m_origin.y > box.hi.y) || (ray.m_origin.z > box.hi.z)
+ || (ray.jbyk * box.hi.z - box.lo.y + ray.c_zy < 0)
+ || (ray.kbyj * box.hi.y - box.lo.z + ray.c_yz < 0)
+ )
+ return false;
+
+ return true;
+
+ case Ray::MOM:
+
+ if((ray.m_origin.y < box.lo.y) || (ray.m_origin.y > box.hi.y)
+ || (ray.m_origin.x < box.lo.x) || (ray.m_origin.z < box.lo.z)
+ || (ray.kbyi * box.lo.x - box.hi.z + ray.c_xz > 0)
+ || (ray.ibyk * box.lo.z - box.hi.x + ray.c_zx > 0)
+ )
+ return false;
+
+ return true;
+
+ case Ray::MOP:
+
+ if((ray.m_origin.y < box.lo.y) || (ray.m_origin.y > box.hi.y)
+ || (ray.m_origin.x < box.lo.x) || (ray.m_origin.z > box.hi.z)
+ || (ray.kbyi * box.lo.x - box.lo.z + ray.c_xz < 0)
+ || (ray.ibyk * box.hi.z - box.hi.x + ray.c_zx > 0)
+ )
+ return false;
+
+ return true;
+
+ case Ray::POM:
+
+ if((ray.m_origin.y < box.lo.y) || (ray.m_origin.y > box.hi.y)
+ || (ray.m_origin.x > box.hi.x) || (ray.m_origin.z < box.lo.z)
+ || (ray.kbyi * box.hi.x - box.hi.z + ray.c_xz > 0)
+ || (ray.ibyk * box.lo.z - box.lo.x + ray.c_zx < 0)
+ )
+ return false;
+
+ return true;
+
+ case Ray::POP:
+
+ if((ray.m_origin.y < box.lo.y) || (ray.m_origin.y > box.hi.y)
+ || (ray.m_origin.x > box.hi.x) || (ray.m_origin.z > box.hi.z)
+ || (ray.kbyi * box.hi.x - box.lo.z + ray.c_xz < 0)
+ || (ray.ibyk * box.hi.z - box.lo.x + ray.c_zx < 0)
+ )
+ return false;
+
+ return true;
+
+ case Ray::MMO:
+
+ if((ray.m_origin.z < box.lo.z) || (ray.m_origin.z > box.hi.z)
+ || (ray.m_origin.x < box.lo.x) || (ray.m_origin.y < box.lo.y)
+ || (ray.jbyi * box.lo.x - box.hi.y + ray.c_xy > 0)
+ || (ray.ibyj * box.lo.y - box.hi.x + ray.c_yx > 0)
+ )
+ return false;
+
+ return true;
+
+ case Ray::MPO:
+
+ if((ray.m_origin.z < box.lo.z) || (ray.m_origin.z > box.hi.z)
+ || (ray.m_origin.x < box.lo.x) || (ray.m_origin.y > box.hi.y)
+ || (ray.jbyi * box.lo.x - box.lo.y + ray.c_xy < 0)
+ || (ray.ibyj * box.hi.y - box.hi.x + ray.c_yx > 0)
+ )
+ return false;
+
+ return true;
+
+ case Ray::PMO:
+
+ if((ray.m_origin.z < box.lo.z) || (ray.m_origin.z > box.hi.z)
+ || (ray.m_origin.x > box.hi.x) || (ray.m_origin.y < box.lo.y)
+ || (ray.jbyi * box.hi.x - box.hi.y + ray.c_xy > 0)
+ || (ray.ibyj * box.lo.y - box.lo.x + ray.c_yx < 0)
+ )
+ return false;
+
+ return true;
+
+ case Ray::PPO:
+
+ if((ray.m_origin.z < box.lo.z) || (ray.m_origin.z > box.hi.z)
+ || (ray.m_origin.x > box.hi.x) || (ray.m_origin.y > box.hi.y)
+ || (ray.jbyi * box.hi.x - box.lo.y + ray.c_xy < 0)
+ || (ray.ibyj * box.hi.y - box.lo.x + ray.c_yx < 0)
+ )
+ return false;
+
+ return true;
+
+ case Ray::MOO:
+
+ if((ray.m_origin.x < box.lo.x)
+ || (ray.m_origin.y < box.lo.y) || (ray.m_origin.y > box.hi.y)
+ || (ray.m_origin.z < box.lo.z) || (ray.m_origin.z > box.hi.z)
+ )
+ return false;
+
+ return true;
+
+ case Ray::POO:
+
+ if((ray.m_origin.x > box.hi.x)
+ || (ray.m_origin.y < box.lo.y) || (ray.m_origin.y > box.hi.y)
+ || (ray.m_origin.z < box.lo.z) || (ray.m_origin.z > box.hi.z)
+ )
+ return false;
+
+ return true;
+
+ case Ray::OMO:
+
+ if((ray.m_origin.y < box.lo.y)
+ || (ray.m_origin.x < box.lo.x) || (ray.m_origin.x > box.hi.x)
+ || (ray.m_origin.z < box.lo.z) || (ray.m_origin.z > box.hi.z)
+ )
+ return false;
+
+ case Ray::OPO:
+
+ if((ray.m_origin.y > box.hi.y)
+ || (ray.m_origin.x < box.lo.x) || (ray.m_origin.x > box.hi.x)
+ || (ray.m_origin.z < box.lo.z) || (ray.m_origin.z > box.hi.z)
+ )
+ return false;
+
+ case Ray::OOM:
+
+ if((ray.m_origin.z < box.lo.z)
+ || (ray.m_origin.x < box.lo.x) || (ray.m_origin.x > box.hi.x)
+ || (ray.m_origin.y < box.lo.y) || (ray.m_origin.y > box.hi.y)
+ )
+ return false;
+
+ case Ray::OOP:
+
+ if((ray.m_origin.z > box.hi.z)
+ || (ray.m_origin.x < box.lo.x) || (ray.m_origin.x > box.hi.x)
+ || (ray.m_origin.y < box.lo.y) || (ray.m_origin.y > box.hi.y)
+ )
+ return false;
+
+ return true;
+
+ }
+
+ return false;
+}
+
+
+bool __fastcall Intersect::rayAABox(const Ray& ray, const AABox& box, float& time) {
+
+ switch (ray.classification) {
+ case Ray::MMM:
+ {
+ if ((ray.m_origin.x < box.lo.x) || (ray.m_origin.y < box.lo.y) || (ray.m_origin.z < box.lo.z)
+ || (ray.jbyi * box.lo.x - box.hi.y + ray.c_xy > 0)
+ || (ray.ibyj * box.lo.y - box.hi.x + ray.c_yx > 0)
+ || (ray.jbyk * box.lo.z - box.hi.y + ray.c_zy > 0)
+ || (ray.kbyj * box.lo.y - box.hi.z + ray.c_yz > 0)
+ || (ray.kbyi * box.lo.x - box.hi.z + ray.c_xz > 0)
+ || (ray.ibyk * box.lo.z - box.hi.x + ray.c_zx > 0)) {
+ return false;
+ }
+
+ // compute the intersection distance
+
+ time = (box.hi.x - ray.m_origin.x) * ray.m_invDirection.x;
+ float t1 = (box.hi.y - ray.m_origin.y) * ray.m_invDirection.y;
+ if (t1 > time) {
+ time = t1;
+ }
+
+ float t2 = (box.hi.z - ray.m_origin.z) * ray.m_invDirection.z;
+ if (t2 > time) {
+ time = t2;
+ }
+
+ return true;
+ }
+
+ case Ray::MMP:
+ {
+ if ((ray.m_origin.x < box.lo.x) || (ray.m_origin.y < box.lo.y) || (ray.m_origin.z > box.hi.z)
+ || (ray.jbyi * box.lo.x - box.hi.y + ray.c_xy > 0)
+ || (ray.ibyj * box.lo.y - box.hi.x + ray.c_yx > 0)
+ || (ray.jbyk * box.hi.z - box.hi.y + ray.c_zy > 0)
+ || (ray.kbyj * box.lo.y - box.lo.z + ray.c_yz < 0)
+ || (ray.kbyi * box.lo.x - box.lo.z + ray.c_xz < 0)
+ || (ray.ibyk * box.hi.z - box.hi.x + ray.c_zx > 0)) {
+ return false;
+ }
+
+ time = (box.hi.x - ray.m_origin.x) * ray.m_invDirection.x;
+ float t1 = (box.hi.y - ray.m_origin.y) * ray.m_invDirection.y;
+ if (t1 > time) {
+ time = t1;
+ }
+ float t2 = (box.lo.z - ray.m_origin.z) * ray.m_invDirection.z;
+ if (t2 > time) {
+ time = t2;
+ }
+
+ return true;
+ }
+
+ case Ray::MPM:
+ {
+ if ((ray.m_origin.x < box.lo.x) || (ray.m_origin.y > box.hi.y) || (ray.m_origin.z < box.lo.z)
+ || (ray.jbyi * box.lo.x - box.lo.y + ray.c_xy < 0)
+ || (ray.ibyj * box.hi.y - box.hi.x + ray.c_yx > 0)
+ || (ray.jbyk * box.lo.z - box.lo.y + ray.c_zy < 0)
+ || (ray.kbyj * box.hi.y - box.hi.z + ray.c_yz > 0)
+ || (ray.kbyi * box.lo.x - box.hi.z + ray.c_xz > 0)
+ || (ray.ibyk * box.lo.z - box.hi.x + ray.c_zx > 0)) {
+ return false;
+ }
+
+ time = (box.hi.x - ray.m_origin.x) * ray.m_invDirection.x;
+ float t1 = (box.lo.y - ray.m_origin.y) * ray.m_invDirection.y;
+ if (t1 > time) {
+ time = t1;
+ }
+ float t2 = (box.hi.z - ray.m_origin.z) * ray.m_invDirection.z;
+ if (t2 > time) {
+ time = t2;
+ }
+
+ return true;
+ }
+
+ case Ray::MPP:
+ {
+ if ((ray.m_origin.x < box.lo.x) || (ray.m_origin.y > box.hi.y) || (ray.m_origin.z > box.hi.z)
+ || (ray.jbyi * box.lo.x - box.lo.y + ray.c_xy < 0)
+ || (ray.ibyj * box.hi.y - box.hi.x + ray.c_yx > 0)
+ || (ray.jbyk * box.hi.z - box.lo.y + ray.c_zy < 0)
+ || (ray.kbyj * box.hi.y - box.lo.z + ray.c_yz < 0)
+ || (ray.kbyi * box.lo.x - box.lo.z + ray.c_xz < 0)
+ || (ray.ibyk * box.hi.z - box.hi.x + ray.c_zx > 0)) {
+ return false;
+ }
+
+ time = (box.hi.x - ray.m_origin.x) * ray.m_invDirection.x;
+ float t1 = (box.lo.y - ray.m_origin.y) * ray.m_invDirection.y;
+ if (t1 > time) {
+ time = t1;
+ }
+ float t2 = (box.lo.z - ray.m_origin.z) * ray.m_invDirection.z;
+ if (t2 > time) {
+ time = t2;
+ }
+
+ return true;
+ }
+
+ case Ray::PMM:
+ {
+ if ((ray.m_origin.x > box.hi.x) || (ray.m_origin.y < box.lo.y) || (ray.m_origin.z < box.lo.z)
+ || (ray.jbyi * box.hi.x - box.hi.y + ray.c_xy > 0)
+ || (ray.ibyj * box.lo.y - box.lo.x + ray.c_yx < 0)
+ || (ray.jbyk * box.lo.z - box.hi.y + ray.c_zy > 0)
+ || (ray.kbyj * box.lo.y - box.hi.z + ray.c_yz > 0)
+ || (ray.kbyi * box.hi.x - box.hi.z + ray.c_xz > 0)
+ || (ray.ibyk * box.lo.z - box.lo.x + ray.c_zx < 0)) {
+ return false;
+ }
+
+ time = (box.lo.x - ray.m_origin.x) * ray.m_invDirection.x;
+ float t1 = (box.hi.y - ray.m_origin.y) * ray.m_invDirection.y;
+ if (t1 > time) {
+ time = t1;
+ }
+ float t2 = (box.hi.z - ray.m_origin.z) * ray.m_invDirection.z;
+ if (t2 > time) {
+ time = t2;
+ }
+
+ return true;
+ }
+
+
+ case Ray::PMP:
+ {
+ if ((ray.m_origin.x > box.hi.x) || (ray.m_origin.y < box.lo.y) || (ray.m_origin.z > box.hi.z)
+ || (ray.jbyi * box.hi.x - box.hi.y + ray.c_xy > 0)
+ || (ray.ibyj * box.lo.y - box.lo.x + ray.c_yx < 0)
+ || (ray.jbyk * box.hi.z - box.hi.y + ray.c_zy > 0)
+ || (ray.kbyj * box.lo.y - box.lo.z + ray.c_yz < 0)
+ || (ray.kbyi * box.hi.x - box.lo.z + ray.c_xz < 0)
+ || (ray.ibyk * box.hi.z - box.lo.x + ray.c_zx < 0)) {
+ return false;
+ }
+
+ time = (box.lo.x - ray.m_origin.x) * ray.m_invDirection.x;
+ float t1 = (box.hi.y - ray.m_origin.y) * ray.m_invDirection.y;
+ if (t1 > time) {
+ time = t1;
+ }
+ float t2 = (box.lo.z - ray.m_origin.z) * ray.m_invDirection.z;
+ if (t2 > time) {
+ time = t2;
+ }
+
+ return true;
+ }
+
+ case Ray::PPM:
+ {
+ if ((ray.m_origin.x > box.hi.x) || (ray.m_origin.y > box.hi.y) || (ray.m_origin.z < box.lo.z)
+ || (ray.jbyi * box.hi.x - box.lo.y + ray.c_xy < 0)
+ || (ray.ibyj * box.hi.y - box.lo.x + ray.c_yx < 0)
+ || (ray.jbyk * box.lo.z - box.lo.y + ray.c_zy < 0)
+ || (ray.kbyj * box.hi.y - box.hi.z + ray.c_yz > 0)
+ || (ray.kbyi * box.hi.x - box.hi.z + ray.c_xz > 0)
+ || (ray.ibyk * box.lo.z - box.lo.x + ray.c_zx < 0)) {
+ return false;
+ }
+
+ time = (box.lo.x - ray.m_origin.x) * ray.m_invDirection.x;
+ float t1 = (box.lo.y - ray.m_origin.y) * ray.m_invDirection.y;
+ if (t1 > time) {
+ time = t1;
+ }
+ float t2 = (box.hi.z - ray.m_origin.z) * ray.m_invDirection.z;
+ if (t2 > time) {
+ time = t2;
+ }
+
+ return true;
+ }
+
+ case Ray::PPP:
+ {
+ if ((ray.m_origin.x > box.hi.x) || (ray.m_origin.y > box.hi.y) || (ray.m_origin.z > box.hi.z)
+ || (ray.jbyi * box.hi.x - box.lo.y + ray.c_xy < 0)
+ || (ray.ibyj * box.hi.y - box.lo.x + ray.c_yx < 0)
+ || (ray.jbyk * box.hi.z - box.lo.y + ray.c_zy < 0)
+ || (ray.kbyj * box.hi.y - box.lo.z + ray.c_yz < 0)
+ || (ray.kbyi * box.hi.x - box.lo.z + ray.c_xz < 0)
+ || (ray.ibyk * box.hi.z - box.lo.x + ray.c_zx < 0)) {
+ return false;
+ }
+
+ time = (box.lo.x - ray.m_origin.x) * ray.m_invDirection.x;
+ float t1 = (box.lo.y - ray.m_origin.y) * ray.m_invDirection.y;
+ if (t1 > time) {
+ time = t1;
+ }
+ float t2 = (box.lo.z - ray.m_origin.z) * ray.m_invDirection.z;
+ if (t2 > time) {
+ time = t2;
+ }
+
+ return true;
+ }
+
+ case Ray::OMM:
+ {
+ if((ray.m_origin.x < box.lo.x) || (ray.m_origin.x > box.hi.x)
+ || (ray.m_origin.y < box.lo.y) || (ray.m_origin.z < box.lo.z)
+ || (ray.jbyk * box.lo.z - box.hi.y + ray.c_zy > 0)
+ || (ray.kbyj * box.lo.y - box.hi.z + ray.c_yz > 0)) {
+ return false;
+ }
+
+ time = (box.hi.y - ray.m_origin.y) * ray.m_invDirection.y;
+ float t2 = (box.hi.z - ray.m_origin.z) * ray.m_invDirection.z;
+ if (t2 > time) {
+ time = t2;
+ }
+
+ return true;
+ }
+
+ case Ray::OMP:
+ {
+ if((ray.m_origin.x < box.lo.x) || (ray.m_origin.x > box.hi.x)
+ || (ray.m_origin.y < box.lo.y) || (ray.m_origin.z > box.hi.z)
+ || (ray.jbyk * box.hi.z - box.hi.y + ray.c_zy > 0)
+ || (ray.kbyj * box.lo.y - box.lo.z + ray.c_yz < 0)) {
+ return false;
+ }
+
+ time = (box.hi.y - ray.m_origin.y) * ray.m_invDirection.y;
+ float t2 = (box.lo.z - ray.m_origin.z) * ray.m_invDirection.z;
+ if (t2 > time) {
+ time = t2;
+ }
+
+ return true;
+ }
+
+ case Ray::OPM:
+ {
+ if((ray.m_origin.x < box.lo.x) || (ray.m_origin.x > box.hi.x)
+ || (ray.m_origin.y > box.hi.y) || (ray.m_origin.z < box.lo.z)
+ || (ray.jbyk * box.lo.z - box.lo.y + ray.c_zy < 0)
+ || (ray.kbyj * box.hi.y - box.hi.z + ray.c_yz > 0)) {
+ return false;
+ }
+
+ time = (box.lo.y - ray.m_origin.y) * ray.m_invDirection.y;
+ float t2 = (box.hi.z - ray.m_origin.z) * ray.m_invDirection.z;
+ if (t2 > time) {
+ time = t2;
+ }
+
+ return true;
+ }
+
+ case Ray::OPP:
+ {
+ if((ray.m_origin.x < box.lo.x) || (ray.m_origin.x > box.hi.x)
+ || (ray.m_origin.y > box.hi.y) || (ray.m_origin.z > box.hi.z)
+ || (ray.jbyk * box.hi.z - box.lo.y + ray.c_zy < 0)
+ || (ray.kbyj * box.hi.y - box.lo.z + ray.c_yz < 0)) {
+ return false;
+ }
+
+ time = (box.lo.y - ray.m_origin.y) * ray.m_invDirection.y;
+ float t2 = (box.lo.z - ray.m_origin.z) * ray.m_invDirection.z;
+ if (t2 > time) {
+ time = t2;
+ }
+
+ return true;
+ }
+
+
+ case Ray::MOM:
+ {
+ if((ray.m_origin.y < box.lo.y) || (ray.m_origin.y > box.hi.y)
+ || (ray.m_origin.x < box.lo.x) || (ray.m_origin.z < box.lo.z)
+ || (ray.kbyi * box.lo.x - box.hi.z + ray.c_xz > 0)
+ || (ray.ibyk * box.lo.z - box.hi.x + ray.c_zx > 0)) {
+ return false;
+ }
+
+ time = (box.hi.x - ray.m_origin.x) * ray.m_invDirection.x;
+ float t2 = (box.hi.z - ray.m_origin.z) * ray.m_invDirection.z;
+ if (t2 > time) {
+ time = t2;
+ }
+
+ return true;
+ }
+
+
+ case Ray::MOP:
+ {
+ if((ray.m_origin.y < box.lo.y) || (ray.m_origin.y > box.hi.y)
+ || (ray.m_origin.x < box.lo.x) || (ray.m_origin.z > box.hi.z)
+ || (ray.kbyi * box.lo.x - box.lo.z + ray.c_xz < 0)
+ || (ray.ibyk * box.hi.z - box.hi.x + ray.c_zx > 0)) {
+ return false;
+ }
+
+ time = (box.hi.x - ray.m_origin.x) * ray.m_invDirection.x;
+ float t2 = (box.lo.z - ray.m_origin.z) * ray.m_invDirection.z;
+ if (t2 > time) {
+ time = t2;
+ }
+
+ return true;
+ }
+
+ case Ray::POM:
+ {
+ if((ray.m_origin.y < box.lo.y) || (ray.m_origin.y > box.hi.y)
+ || (ray.m_origin.x > box.hi.x) || (ray.m_origin.z < box.lo.z)
+ || (ray.kbyi * box.hi.x - box.hi.z + ray.c_xz > 0)
+ || (ray.ibyk * box.lo.z - box.lo.x + ray.c_zx < 0)) {
+ return false;
+ }
+
+ time = (box.lo.x - ray.m_origin.x) * ray.m_invDirection.x;
+ float t2 = (box.hi.z - ray.m_origin.z) * ray.m_invDirection.z;
+ if (t2 > time) {
+ time = t2;
+ }
+
+ return true;
+ }
+
+
+ case Ray::POP:
+ {
+ if((ray.m_origin.y < box.lo.y) || (ray.m_origin.y > box.hi.y)
+ || (ray.m_origin.x > box.hi.x) || (ray.m_origin.z > box.hi.z)
+ || (ray.kbyi * box.hi.x - box.lo.z + ray.c_xz < 0)
+ || (ray.ibyk * box.hi.z - box.lo.x + ray.c_zx < 0)) {
+ return false;
+ }
+
+ time = (box.lo.x - ray.m_origin.x) * ray.m_invDirection.x;
+ float t2 = (box.lo.z - ray.m_origin.z) * ray.m_invDirection.z;
+ if (t2 > time) {
+ time = t2;
+ }
+
+ return true;
+ }
+
+ case Ray::MMO:
+ {
+ if((ray.m_origin.z < box.lo.z) || (ray.m_origin.z > box.hi.z)
+ || (ray.m_origin.x < box.lo.x) || (ray.m_origin.y < box.lo.y)
+ || (ray.jbyi * box.lo.x - box.hi.y + ray.c_xy > 0)
+ || (ray.ibyj * box.lo.y - box.hi.x + ray.c_yx > 0)) {
+ return false;
+ }
+
+ time = (box.hi.x - ray.m_origin.x) * ray.m_invDirection.x;
+ float t1 = (box.hi.y - ray.m_origin.y) * ray.m_invDirection.y;
+ if (t1 > time) {
+ time = t1;
+ }
+
+ return true;
+ }
+
+ case Ray::MPO:
+ {
+ if((ray.m_origin.z < box.lo.z) || (ray.m_origin.z > box.hi.z)
+ || (ray.m_origin.x < box.lo.x) || (ray.m_origin.y > box.hi.y)
+ || (ray.jbyi * box.lo.x - box.lo.y + ray.c_xy < 0)
+ || (ray.ibyj * box.hi.y - box.hi.x + ray.c_yx > 0)) {
+ return false;
+ }
+
+ time = (box.hi.x - ray.m_origin.x) * ray.m_invDirection.x;
+ float t1 = (box.lo.y - ray.m_origin.y) * ray.m_invDirection.y;
+ if (t1 > time) {
+ time = t1;
+ }
+
+ return true;
+ }
+
+
+ case Ray::PMO:
+ {
+ if((ray.m_origin.z < box.lo.z) || (ray.m_origin.z > box.hi.z)
+ || (ray.m_origin.x > box.hi.x) || (ray.m_origin.y < box.lo.y)
+ || (ray.jbyi * box.hi.x - box.hi.y + ray.c_xy > 0)
+ || (ray.ibyj * box.lo.y - box.lo.x + ray.c_yx < 0)) {
+ return false;
+ }
+
+ time = (box.lo.x - ray.m_origin.x) * ray.m_invDirection.x;
+ float t1 = (box.hi.y - ray.m_origin.y) * ray.m_invDirection.y;
+ if (t1 > time) {
+ time = t1;
+ }
+
+ return true;
+ }
+
+ case Ray::PPO:
+ {
+ if((ray.m_origin.z < box.lo.z) || (ray.m_origin.z > box.hi.z)
+ || (ray.m_origin.x > box.hi.x) || (ray.m_origin.y > box.hi.y)
+ || (ray.jbyi * box.hi.x - box.lo.y + ray.c_xy < 0)
+ || (ray.ibyj * box.hi.y - box.lo.x + ray.c_yx < 0)) {
+ return false;
+ }
+
+ time = (box.lo.x - ray.m_origin.x) * ray.m_invDirection.x;
+ float t1 = (box.lo.y - ray.m_origin.y) * ray.m_invDirection.y;
+ if (t1 > time) {
+ time = t1;
+ }
+
+ return true;
+ }
+
+
+ case Ray::MOO:
+ {
+ if((ray.m_origin.x < box.lo.x)
+ || (ray.m_origin.y < box.lo.y) || (ray.m_origin.y > box.hi.y)
+ || (ray.m_origin.z < box.lo.z) || (ray.m_origin.z > box.hi.z)) {
+ return false;
+ }
+
+ time = (box.hi.x - ray.m_origin.x) * ray.m_invDirection.x;
+ return true;
+ }
+
+ case Ray::POO:
+ {
+ if ((ray.m_origin.x > box.hi.x)
+ || (ray.m_origin.y < box.lo.y) || (ray.m_origin.y > box.hi.y)
+ || (ray.m_origin.z < box.lo.z) || (ray.m_origin.z > box.hi.z)) {
+ return false;
+ }
+
+ time = (box.lo.x - ray.m_origin.x) * ray.m_invDirection.x;
+ return true;
+ }
+
+ case Ray::OMO:
+ {
+ if ((ray.m_origin.y < box.lo.y)
+ || (ray.m_origin.x < box.lo.x) || (ray.m_origin.x > box.hi.x)
+ || (ray.m_origin.z < box.lo.z) || (ray.m_origin.z > box.hi.z)) {
+ return false;
+ }
+
+ time = (box.hi.y - ray.m_origin.y) * ray.m_invDirection.y;
+ return true;
+ }
+
+ case Ray::OPO:
+ {
+ if ((ray.m_origin.y > box.hi.y)
+ || (ray.m_origin.x < box.lo.x) || (ray.m_origin.x > box.hi.x)
+ || (ray.m_origin.z < box.lo.z) || (ray.m_origin.z > box.hi.z)) {
+ return false;
+ }
+
+ time = (box.lo.y - ray.m_origin.y) * ray.m_invDirection.y;
+ return true;
+ }
+
+
+ case Ray::OOM:
+ {
+ if ((ray.m_origin.z < box.lo.z)
+ || (ray.m_origin.x < box.lo.x) || (ray.m_origin.x > box.hi.x)
+ || (ray.m_origin.y < box.lo.y) || (ray.m_origin.y > box.hi.y)) {
+ return false;
+ }
+
+ time = (box.hi.z - ray.m_origin.z) * ray.m_invDirection.z;
+ return true;
+ }
+
+ case Ray::OOP:
+ {
+ if ((ray.m_origin.z > box.hi.z)
+ || (ray.m_origin.x < box.lo.x) || (ray.m_origin.x > box.hi.x)
+ || (ray.m_origin.y < box.lo.y) || (ray.m_origin.y > box.hi.y)) {
+ return false;
+ }
+
+ time = (box.lo.z - ray.m_origin.z) * ray.m_invDirection.z;
+ return true;
+ }
+ }
+
+ return false;
+}
+
+#ifdef _MSC_VER
+// Turn off fast floating-point optimizations
+#pragma float_control( pop )
+#endif
+
+}
diff --git a/dep/g3dlite/Line.cpp b/dep/g3dlite/source/Line.cpp
index 195ae7197f2..195ae7197f2 100644
--- a/dep/g3dlite/Line.cpp
+++ b/dep/g3dlite/source/Line.cpp
diff --git a/dep/g3dlite/LineSegment.cpp b/dep/g3dlite/source/LineSegment.cpp
index 754600ad554..754600ad554 100644
--- a/dep/g3dlite/LineSegment.cpp
+++ b/dep/g3dlite/source/LineSegment.cpp
diff --git a/dep/g3dlite/Log.cpp b/dep/g3dlite/source/Log.cpp
index 07614fcf563..f437351cfbd 100644
--- a/dep/g3dlite/Log.cpp
+++ b/dep/g3dlite/source/Log.cpp
@@ -3,7 +3,7 @@
@maintainer Morgan McGuire, http://graphics.cs.williams.edu
@created 2001-08-04
- @edited 2009-01-15
+ @edited 2010-01-15
*/
#include "G3D/platform.h"
@@ -11,6 +11,7 @@
#include "G3D/format.h"
#include "G3D/Array.h"
#include "G3D/fileutils.h"
+#include "G3D/FileSystem.h"
#include <time.h>
#ifdef G3D_WIN32
@@ -43,7 +44,7 @@ Log::Log(const std::string& filename, int stripFromStackBottom) :
this->filename = filename;
- logFile = fopen(filename.c_str(), "w");
+ logFile = FileSystem::fopen(filename.c_str(), "w");
if (logFile == NULL) {
std::string drive, base, ext;
@@ -58,7 +59,7 @@ Log::Log(const std::string& filename, int stripFromStackBottom) :
logName = std::string("/tmp/") + logName;
#endif
- logFile = fopen(logName.c_str(), "w");
+ logFile = FileSystem::fopen(logName.c_str(), "w");
}
// Use a large buffer (although we flush in logPrintf)
diff --git a/dep/g3dlite/source/Matrix.cpp b/dep/g3dlite/source/Matrix.cpp
new file mode 100644
index 00000000000..7a668e59e2c
--- /dev/null
+++ b/dep/g3dlite/source/Matrix.cpp
@@ -0,0 +1,1802 @@
+/**
+ @file Matrix.cpp
+ @author Morgan McGuire, http://graphics.cs.williams.edu
+ */
+#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;
+ rank.resize(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.
+
+ float cofactor00 = elt[1][1] * elt[2][2] - elt[1][2] * elt[2][1];
+ float cofactor10 = elt[1][2] * elt[2][0] - elt[1][0] * elt[2][2];
+ float 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;
+
+ 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/dep/g3dlite/Matrix3.cpp b/dep/g3dlite/source/Matrix3.cpp
index b32d938f0f9..7e4de99621a 100644
--- a/dep/g3dlite/Matrix3.cpp
+++ b/dep/g3dlite/source/Matrix3.cpp
@@ -29,11 +29,17 @@ const float Matrix3::EPSILON = 1e-06f;
Matrix3::Matrix3(const Any& any) {
any.verifyName("Matrix3");
any.verifyType(Any::ARRAY);
- any.verifySize(9);
- for (int r = 0; r < 3; ++r) {
- for (int c = 0; c < 3; ++c) {
- elt[r][c] = any[r * 3 + c];
+ if (any.nameEquals("Matrix3::fromAxisAngle")) {
+ any.verifySize(2);
+ *this = Matrix3::fromAxisAngle(any[0], any[1].number());
+ } else {
+ any.verifySize(9);
+
+ for (int r = 0; r < 3; ++r) {
+ for (int c = 0; c < 3; ++c) {
+ elt[r][c] = any[r * 3 + c];
+ }
}
}
}
diff --git a/dep/g3dlite/Matrix4.cpp b/dep/g3dlite/source/Matrix4.cpp
index cd38a1a3602..2ce14f6c5d4 100644
--- a/dep/g3dlite/Matrix4.cpp
+++ b/dep/g3dlite/source/Matrix4.cpp
@@ -44,7 +44,12 @@ Matrix4::Matrix4(const Any& any) {
} else {
any.verify(false, "Matrix4::scale() takes either 1 or 3 arguments");
}
- } else {
+ } else if (name == "matrix4::translation") {
+ if (any.size() == 3) {
+ *this = translation(any[0], any[1], any[2]);
+ } else {
+ any.verify(false, "Matrix4::translation() takes either 1 or 3 arguments");
+ } } else {
any.verify(false, "Expected Matrix4 constructor");
}
}
diff --git a/dep/g3dlite/MemoryManager.cpp b/dep/g3dlite/source/MemoryManager.cpp
index 240188a1f0e..240188a1f0e 100644
--- a/dep/g3dlite/MemoryManager.cpp
+++ b/dep/g3dlite/source/MemoryManager.cpp
diff --git a/dep/g3dlite/source/MeshAlg.cpp b/dep/g3dlite/source/MeshAlg.cpp
new file mode 100644
index 00000000000..626fed92920
--- /dev/null
+++ b/dep/g3dlite/source/MeshAlg.cpp
@@ -0,0 +1,637 @@
+/**
+ @file MeshAlg.cpp
+
+ @maintainer Morgan McGuire, http://graphics.cs.williams.edu
+ @created 2003-09-14
+ @edited 2008-09-03
+
+ Copyright 2000-2009, 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 "G3D/Image1.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,
+ const Image1::Ref& height) {
+
+ 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 (height.notNull()) {
+ v.y = height->nearest(v.x * (height->width() - 1), v.z * (height->height() - 1)).value;
+ }
+ 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;
+ fakeVertexArray.resize(adjacentFaceArray.size());
+
+ for (int v = 0; v < adjacentFaceArray.size(); ++v) {
+ fakeVertexArray[v].faceIndex.resize(adjacentFaceArray[v].size());
+ for (int i = 0; i < fakeVertexArray[v].faceIndex.size(); ++i) {
+ fakeVertexArray[v].faceIndex[i] = adjacentFaceArray[v][i];
+ }
+ // 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;
+ area.resize(indexArray.size() / 3);
+ Array<double> magnitude;
+ magnitude.resize(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,
+ AABox& box,
+ Sphere& sphere) {
+
+ Array<Vector3> newArray;
+ newArray.resize(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,
+ AABox& box,
+ Sphere& sphere) {
+
+ Vector3 xmin, xmax, ymin, ymax, zmin, zmax;
+
+ // FIRST PASS: find 6 minima/maxima points
+ xmin.x = ymin.y = zmin.z = finf();
+ xmax.x = ymax.y = zmax.z = -finf();
+
+ 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 = AABox(min, max);
+
+ const float boxRadSq = (max - min).squaredMagnitude() * 0.25f;
+
+ if (boxRadSq >= radSq){
+ if (isNaN(center.x) || ! isFinite(rad)) {
+ sphere = Sphere(Vector3::zero(), finf());
+ } else {
+ sphere = Sphere(center, rad);
+ }
+ } else {
+ sphere = Sphere((max + min) * 0.5f, sqrt(boxRadSq));
+ }
+}
+
+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.");
+
+ 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];
+
+ const int i0 = face.vertexIndex[0];
+ const int i1 = face.vertexIndex[1];
+ const int i2 = face.vertexIndex[2];
+
+ const Vector3& v0 = vertexArray[i0];
+ const Vector3& v1 = vertexArray[i1];
+ const Vector3& v2 = vertexArray[i2];
+
+ const Vector2& t0 = texCoordArray[i0];
+ const Vector2& t1 = texCoordArray[i1];
+ const Vector2& t2 = texCoordArray[i2];
+
+ // See http://www.terathon.com/code/tangent.html for a derivation of the following code
+
+ // vertex edges
+ Vector3 ve1 = v1 - v0;
+ Vector3 ve2 = v2 - v0;
+
+ // texture edges
+ Vector2 te1 = t1 - t0;
+ Vector2 te2 = t2 - t0;
+
+ Vector3 n(ve1.cross(ve2).direction());
+ Vector3 t, b;
+
+ float r = te1.x * te2.y - te1.y * te2.x;
+ if (r == 0.0) {
+ // degenerate case
+ Vector3::generateOrthonormalBasis(t, b, n, true);
+ } else {
+ r = 1.0f / r;
+ t = (te2.y * ve1 - te1.y * ve2) * r;
+ b = (te2.x * ve1 - te1.x * ve2) * r;
+ }
+
+ 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];
+ Vector3& T = tangent[v];
+ Vector3& B = binormal[v];
+
+ debugAssertM(N.isUnit() || N.isZero(), "Input normals must have unit length");
+
+ T -= T.dot(N) * N;
+ B -= B.dot(N) * N;
+
+ // Normalize
+ T = T.directionOrZero();
+ B = B.directionOrZero();
+ }
+}
+
+
+
+} // G3D namespace
diff --git a/dep/g3dlite/source/MeshAlgAdjacency.cpp b/dep/g3dlite/source/MeshAlgAdjacency.cpp
new file mode 100644
index 00000000000..f0bf3382131
--- /dev/null
+++ b/dep/g3dlite/source/MeshAlgAdjacency.cpp
@@ -0,0 +1,745 @@
+/**
+ @file MeshAlgAdjacency.cpp
+
+ @maintainer Morgan McGuire, http://graphics.cs.williams.edu
+ @created 2003-09-14
+ @edited 2010-04-26
+
+ Copyright 2000-2010, Morgan McGuire.
+ All rights reserved.
+
+ */
+
+#include "G3D/Table.h"
+#include "G3D/MeshAlg.h"
+#include "G3D/Set.h"
+#include "G3D/Stopwatch.h"
+#include "G3D/SmallArray.h"
+#include "G3D/AreaMemoryManager.h"
+
+namespace G3D {
+
+/** Two-level table mapping index 0 -> index 1 -> list of face indices */
+class MeshEdgeTable {
+public:
+
+ /** We expect 2 faces per edge. */
+ typedef SmallArray<int, 2> FaceIndexArray;
+
+ class Edge {
+ public:
+ int i1;
+
+ FaceIndexArray faceIndexArray;
+ };
+
+ /** We expect at most 6 edges per vertex; that matches a typical regular grid mesh */
+ typedef SmallArray<Edge, 6> EdgeArray;
+
+ typedef Array< EdgeArray > ET;
+
+private:
+
+ ET table;
+
+public:
+
+ MeshEdgeTable() {
+ AreaMemoryManager::Ref mm = AreaMemoryManager::create();
+ table.clearAndSetMemoryManager(mm);
+ }
+
+ void clear() {
+ table.clear();
+ }
+
+ void resize(int maxV) {
+ table.resize(maxV);
+ }
+
+ /**
+ Inserts the faceIndex into the edge's face list.
+ The index may be a negative number indicating a backface.
+
+ \param v0 Vertex index 0
+ \param v1 Vertex index 1
+ */
+ void insert(int v0, int v1, int faceIndex) {
+
+ debugAssert(v0 <= v1);
+ EdgeArray& edgeArray = table[v0];
+ for (int i = 0; i < edgeArray.size(); ++i) {
+ if (edgeArray[i].i1 == v1) {
+ edgeArray[i].faceIndexArray.push(faceIndex);
+ return;
+ }
+ }
+
+ Edge& p = edgeArray.next();
+ p.i1 = v1;
+ p.faceIndexArray.push(faceIndex);
+ }
+
+ class Iterator {
+ friend class MeshEdgeTable;
+ private:
+
+ int m_i0;
+ /** Pair index */
+ int m_p;
+ ET& m_array;
+ EdgeArray* m_edgeArray;
+ bool m_end;
+
+ public:
+
+ int i0() const {
+ return m_i0;
+ }
+
+ int i1() const {
+ return (*m_edgeArray)[m_p].i1;
+ }
+
+ FaceIndexArray& faceIndex() {
+ return (*m_edgeArray)[m_p].faceIndexArray;
+ }
+
+ Iterator& operator++() {
+ if ((m_i0 >= 0) && (m_p < m_edgeArray->size() - 1)) {
+ ++m_p;
+ } else {
+ // Skip over elements with no face array
+ do {
+ ++m_i0;
+ if (m_i0 == m_array.size()) {
+ m_end = true;
+ return *this;
+ } else {
+ m_edgeArray = &m_array[m_i0];
+ m_p = 0;
+ }
+ } while (m_edgeArray->size() == 0);
+ }
+
+ return *this;
+ }
+
+ bool hasMore() const {
+ return ! m_end;
+ }
+
+ private:
+
+ Iterator(ET& a) : m_i0(-1), m_p(-1), m_array(a), m_edgeArray(NULL), m_end(false) {
+ ++(*this);
+ }
+
+ };
+
+ Iterator begin() {
+ return Iterator(table);
+ }
+};
+
+
+/**
+ 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) {
+ const SmallArray<int, 6>& src = vertexArray[v].faceIndex;
+ Array<int>& dst = adjacentFaceArray[v];
+ dst.resize(src.size());
+ for (int f = 0; f < dst.size(); ++f) {
+ dst[f] = src[f];
+ }
+ }
+}
+
+
+void MeshAlg::computeAdjacency(
+ const Array<Vector3>& vertexGeometry,
+ const Array<int>& indexArray,
+ Array<Face>& faceArray,
+ Array<Edge>& edgeArray,
+ Array<Vertex>& vertexArray) {
+
+ MeshEdgeTable edgeTable;
+
+ edgeArray.clear();
+ vertexArray.clear();
+ faceArray.clear();
+
+ // Face normals
+ Array<Vector3> faceNormal;
+ faceNormal.resize(indexArray.size() / 3);
+ faceArray.resize(faceNormal.size());
+
+ // This array has the same size as the vertex array
+ vertexArray.resize(vertexGeometry.size());
+
+ edgeTable.resize(vertexArray.size());
+
+ // Iterate through the triangle list
+ for (int q = 0, f = 0; q < indexArray.size(); ++f, q += 3) {
+
+ Vector3 vertex[3];
+ MeshAlg::Face& face = faceArray[f];
+
+ // 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
+ const Vector3& N = (vertex[1] - vertex[0]).cross(vertex[2] - vertex[0]);
+ faceNormal[f] = 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]];
+
+ if (i0 < i1) {
+ // The edge was directed in the same manner as in the face
+ edgeTable.insert(i0, i1, f);
+ } else {
+ // The edge was directed in the opposite manner as in the face
+ edgeTable.insert(i1, i0, ~f);
+ }
+ }
+ }
+
+ // For each edge in the edge table, create an edge in the edge array.
+ // Collapse every 2 edges from adjacent faces.
+
+ MeshEdgeTable::Iterator cur = edgeTable.begin();
+
+ Array<Edge> tempEdgeArray;
+ while (cur.hasMore()) {
+ MeshEdgeTable::FaceIndexArray& faceIndexArray = cur.faceIndex();
+
+ // 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.
+ float 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];
+ float 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] = cur.i0();
+ edge.vertexIndex[1] = cur.i1();
+
+ 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;
+ newIndex.resize(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;
+ newEdgeIndex.resize(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;
+ canonical.resize(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/dep/g3dlite/source/MeshAlgWeld.cpp b/dep/g3dlite/source/MeshAlgWeld.cpp
new file mode 100644
index 00000000000..6067f17c2fb
--- /dev/null
+++ b/dep/g3dlite/source/MeshAlgWeld.cpp
@@ -0,0 +1,213 @@
+/**
+ @file MeshAlgWeld.cpp
+
+ The MeshAlg::computeWeld method.
+
+ @maintainer Morgan McGuire, http://graphics.cs.williams.edu
+ @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/dep/g3dlite/source/MeshBuilder.cpp b/dep/g3dlite/source/MeshBuilder.cpp
new file mode 100644
index 00000000000..1bf2bab5d1c
--- /dev/null
+++ b/dep/g3dlite/source/MeshBuilder.cpp
@@ -0,0 +1,113 @@
+/**
+ @file MeshBuilder.cpp
+
+ @maintainer Morgan McGuire, http://graphics.cs.williams.edu
+
+ @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/dep/g3dlite/source/NetAddress.cpp b/dep/g3dlite/source/NetAddress.cpp
new file mode 100644
index 00000000000..64d692d4763
--- /dev/null
+++ b/dep/g3dlite/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/dep/g3dlite/source/NetworkDevice.cpp b/dep/g3dlite/source/NetworkDevice.cpp
new file mode 100644
index 00000000000..1fc794479f5
--- /dev/null
+++ b/dep/g3dlite/source/NetworkDevice.cpp
@@ -0,0 +1,1274 @@
+/**
+ @file NetworkDevice.cpp
+
+ @maintainer Morgan McGuire, morgan@cs.brown.edu
+ @created 2002-11-22
+ @edited 2006-02-24
+ */
+
+#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 "G3D/networkHelpers.h"
+
+
+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/dep/g3dlite/source/PhysicsFrame.cpp b/dep/g3dlite/source/PhysicsFrame.cpp
new file mode 100644
index 00000000000..30fbdf8d619
--- /dev/null
+++ b/dep/g3dlite/source/PhysicsFrame.cpp
@@ -0,0 +1,110 @@
+/**
+ @file PhysicsFrame.cpp
+
+ @maintainer Morgan McGuire, http://graphics.cs.williams.edu
+
+ @created 2002-07-09
+ @edited 2010-03-25
+*/
+
+#include "G3D/platform.h"
+#include "G3D/Any.h"
+#include "G3D/stringutils.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(const Any& a) {
+ const std::string& n = toLower(a.name());
+ *this = PhysicsFrame();
+
+ if (beginsWith(n, "vector3")) {
+ *this = PhysicsFrame(Vector3(a));
+ } else if (beginsWith(n, "matrix3")) {
+ *this = PhysicsFrame(Matrix3(a));
+ } else if (beginsWith(n, "cframe") || beginsWith(n, "coordinateframe")) {
+ *this = PhysicsFrame(CoordinateFrame(a));
+ } else if (beginsWith(n, "pframe") || beginsWith(n, "physicsframe")) {
+ if (a.type() == Any::ARRAY) {
+ a.verifySize(2);
+ rotation = a[0];
+ translation = a[1];
+ } else {
+ for (Any::AnyTable::Iterator it = a.table().begin(); it.hasMore(); ++it) {
+ const std::string& n = toLower(it->key);
+ if (n == "translation") {
+ translation = it->value;
+ } else if (n == "rotation") {
+ rotation = it->value;
+ } else {
+ a.verify(false, "Illegal table key: " + it->key);
+ }
+ }
+ }
+ }
+}
+
+
+PhysicsFrame PhysicsFrame::operator*(const PhysicsFrame& other) const {
+ PhysicsFrame result;
+
+ result.rotation = rotation * other.rotation;
+ result.translation = translation + rotation.toRotationMatrix() * other.translation;
+
+ return result;
+}
+
+
+PhysicsFrame::operator CoordinateFrame() 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/dep/g3dlite/source/PhysicsFrameSpline.cpp b/dep/g3dlite/source/PhysicsFrameSpline.cpp
new file mode 100644
index 00000000000..2dfdb6f9139
--- /dev/null
+++ b/dep/g3dlite/source/PhysicsFrameSpline.cpp
@@ -0,0 +1,80 @@
+/**
+ \file PhysicsFrameSpline.cpp
+
+ \author Morgan McGuire, http://graphics.cs.williams.edu
+ */
+#include "G3D/PhysicsFrameSpline.h"
+#include "G3D/Any.h"
+#include "G3D/stringutils.h"
+
+namespace G3D {
+
+PhysicsFrameSpline::PhysicsFrameSpline() {}
+
+PhysicsFrameSpline::PhysicsFrameSpline(const Any& any) {
+ *this = any;
+}
+
+PhysicsFrameSpline& PhysicsFrameSpline::operator=(const Any& any) {
+ const std::string& n = toLower(any.name());
+ *this = PhysicsFrameSpline();
+
+ if (n == "physicsframespline" || n == "pframespline") {
+ any.verifyName("PhysicsFrameSpline", "PFrameSpline");
+
+ for (Any::AnyTable::Iterator it = any.table().begin(); it.hasMore(); ++it) {
+ const std::string& k = toLower(it->key);
+ if (k == "cyclic") {
+ cyclic = it->value;
+ } else if (k == "control") {
+ const Any& v = it->value;
+ v.verifyType(Any::ARRAY);
+ control.resize(v.size());
+ for (int i = 0; i < control.size(); ++i) {
+ control[i] = v[i];
+ }
+ if (! any.containsKey("time")) {
+ time.resize(control.size());
+ for (int i = 0; i < time.size(); ++i) {
+ time[i] = i;
+ }
+ }
+ } else if (k == "finalinterval") {
+ finalInterval = it->value;
+ } else if (k == "time") {
+ const Any& v = it->value;
+ v.verifyType(Any::ARRAY);
+ time.resize(v.size());
+ for (int i = 0; i < time.size(); ++i) {
+ time[i] = v[i];
+ }
+ }
+ }
+ } else {
+ // Must be a PhysicsFrame constructor of some kind
+ append(any);
+ }
+ return *this;
+}
+
+
+void PhysicsFrameSpline::correct(PhysicsFrame& frame) const {
+ frame.rotation.unitize();
+}
+
+
+void PhysicsFrameSpline::ensureShortestPath(PhysicsFrame* A, int N) const {
+ for (int i = 1; i < N; ++i) {
+ const Quat& p = A[i - 1].rotation;
+ Quat& q = A[i].rotation;
+
+ float cosphi = p.dot(q);
+
+ if (cosphi < 0) {
+ // Going the long way, so change the order
+ q = -q;
+ }
+ }
+}
+
+}
diff --git a/dep/g3dlite/Plane.cpp b/dep/g3dlite/source/Plane.cpp
index 9b7991c0333..9b7991c0333 100644
--- a/dep/g3dlite/Plane.cpp
+++ b/dep/g3dlite/source/Plane.cpp
diff --git a/dep/g3dlite/source/PrecomputedRandom.cpp b/dep/g3dlite/source/PrecomputedRandom.cpp
new file mode 100644
index 00000000000..387ded35195
--- /dev/null
+++ b/dep/g3dlite/source/PrecomputedRandom.cpp
@@ -0,0 +1,125 @@
+/**
+ @file PrecomputedRandom.cpp
+
+ @maintainer Morgan McGuire, http://graphics.cs.williams.edu
+
+ @created 2009-03-31
+ @edited 2009-07-01
+
+ Copyright 2000-2009, Morgan McGuire.
+ All rights reserved.
+ */
+
+#include "G3D/PrecomputedRandom.h"
+#include "G3D/System.h"
+
+namespace G3D {
+
+PrecomputedRandom::PrecomputedRandom(int dataSize, uint32 seed) :
+ Random((void*)NULL),
+ m_hemiUniform(NULL),
+ m_sphereBits(NULL),
+ m_modMask(dataSize - 1),
+ m_freeData(true) {
+
+ alwaysAssertM(isPow2(dataSize), "dataSize must be a power of 2");
+ m_index = seed & m_modMask;
+
+ HemiUniformData* h;
+ SphereBitsData* s;
+ m_hemiUniform = h = (HemiUniformData*) System::malloc(sizeof(HemiUniformData) * dataSize);
+ m_sphereBits = s = (SphereBitsData*) System::malloc(sizeof(SphereBitsData) * dataSize);
+
+ Random r;
+
+ for (int i = 0; i < dataSize; ++i) {
+ h[i].uniform = r.uniform();
+ r.cosHemi(h[i].cosHemiX, h[i].cosHemiY, h[i].cosHemiZ);
+
+ s[i].bits = r.bits();
+ r.sphere(s[i].sphereX, s[i].sphereY, s[i].sphereZ);
+ }
+
+}
+
+
+PrecomputedRandom::PrecomputedRandom(const HemiUniformData* data1, const SphereBitsData* data2, int dataSize, uint32 seed) :
+ Random((void*)NULL),
+ m_hemiUniform(data1),
+ m_sphereBits(data2),
+ m_modMask(dataSize - 1),
+ m_freeData(false) {
+
+ m_index = seed & m_modMask;
+ alwaysAssertM(isPow2(dataSize), "dataSize must be a power of 2");
+}
+
+
+PrecomputedRandom::~PrecomputedRandom() {
+ if (m_freeData) {
+ System::free(const_cast<HemiUniformData*>(m_hemiUniform));
+ System::free(const_cast<SphereBitsData*>(m_sphereBits));
+ }
+}
+
+float PrecomputedRandom::uniform(float low, float high) {
+ m_index = (m_index + 1) & m_modMask;
+ return low + m_hemiUniform[m_index].uniform * (high - low);
+}
+
+
+float PrecomputedRandom::uniform() {
+ m_index = (m_index + 1) & m_modMask;
+ return m_hemiUniform[m_index].uniform;
+}
+
+
+void PrecomputedRandom::cosHemi(float& x, float& y, float& z) {
+ m_index = (m_index + 1) & m_modMask;
+ x = m_hemiUniform[m_index].cosHemiX;
+ y = m_hemiUniform[m_index].cosHemiY;
+ z = m_hemiUniform[m_index].cosHemiZ;
+}
+
+void PrecomputedRandom::cosPowHemi(const float k, float& x, float& y, float& z) {
+ // Computing a cosPowHemi costs 4 slow functions (pow, sqrt, sin,
+ // cos). We can do it with two, given a cosHemi sample, basically
+ // saving the cost of sin and cos and making a single 128-byte
+ // memory read (for a vector) instead of two (for adjacent uniform
+ // floats).
+
+ // cos^1 distribution sample
+ float cos1;
+ cosHemi(x, y, cos1);
+
+ // Fix the distribution by adjusting the cosine:
+ // rnd(cos^k t) = (rnd(cos(t))^2)^(1/k)
+
+ // produces cos^k distribution sample
+ z = pow(cos1, 2.0f / (1.0f + k));
+
+ // Rescale x and y by sqrt(1.0f - square(z)) / sqrt(x*x + y*y).
+ // Add a very tiny offset to handle the (almost impossibly unlikely) case where
+ // z = 1 and x^2+y^2 = 0.
+ static const float eps = 0.000001f;
+ const float s = sqrt((1.0f + eps - square(z)) / (square(x) + square(y) + eps));
+
+ x *= s;
+ y *= s;
+}
+
+
+uint32 PrecomputedRandom::bits() {
+ m_index = (m_index + 1) & m_modMask;
+ return m_sphereBits[m_index].bits;
+}
+
+
+void PrecomputedRandom::sphere(float& x, float& y, float& z) {
+ m_index = (m_index + 1) & m_modMask;
+ x = m_sphereBits[m_index].sphereX;
+ y = m_sphereBits[m_index].sphereY;
+ z = m_sphereBits[m_index].sphereZ;
+}
+
+}
diff --git a/dep/g3dlite/Quat.cpp b/dep/g3dlite/source/Quat.cpp
index 225c5b51acc..e06483b44cd 100644
--- a/dep/g3dlite/Quat.cpp
+++ b/dep/g3dlite/source/Quat.cpp
@@ -6,10 +6,12 @@
@author Morgan McGuire, graphics3d.com
@created 2002-01-23
- @edited 2006-01-31
+ @edited 2010-03-31
*/
#include "G3D/Quat.h"
+#include "G3D/Any.h"
+#include "G3D/stringutils.h"
#include "G3D/BinaryInput.h"
#include "G3D/BinaryOutput.h"
@@ -28,8 +30,22 @@ Quat Quat::fromAxisAngleRotation(
}
-Quat::Quat(
- const Matrix3& rot) {
+Quat::Quat(const class Any& a) {
+ *this = Quat();
+ if (beginsWith(toLower(a.name()), "matrix3")) {
+ *this = a;
+ } else {
+ a.verifyName("Quat");
+ a.verifyType(Any::ARRAY);
+ x = a[0];
+ y = a[1];
+ z = a[2];
+ w = a[3];
+ }
+}
+
+
+Quat::Quat(const Matrix3& rot) {
static const int plus1mod3[] = {1, 2, 0};
diff --git a/dep/g3dlite/Random.cpp b/dep/g3dlite/source/Random.cpp
index 2dda744a1ac..2dda744a1ac 100644
--- a/dep/g3dlite/Random.cpp
+++ b/dep/g3dlite/source/Random.cpp
diff --git a/dep/g3dlite/Ray.cpp b/dep/g3dlite/source/Ray.cpp
index 0436ef0b323..0436ef0b323 100644
--- a/dep/g3dlite/Ray.cpp
+++ b/dep/g3dlite/source/Ray.cpp
diff --git a/dep/g3dlite/source/Rect2D.cpp b/dep/g3dlite/source/Rect2D.cpp
new file mode 100644
index 00000000000..e4148315a58
--- /dev/null
+++ b/dep/g3dlite/source/Rect2D.cpp
@@ -0,0 +1,41 @@
+/**
+ @file Rect2D.cpp
+
+ @maintainer Morgan McGuire, http://graphics.cs.williams.edu
+
+ @created 2003-11-13
+ @created 2009-11-16
+
+ Copyright 2000-2009, Morgan McGuire.
+ All rights reserved.
+ */
+
+#include "G3D/platform.h"
+#include "G3D/Rect2D.h"
+#include "G3D/Any.h"
+#include "G3D/stringutils.h"
+
+namespace G3D {
+
+/** \param any Must either Rect2D::xywh(#, #, #, #) or Rect2D::xyxy(#, #, #, #)*/
+Rect2D::Rect2D(const Any& any) {
+ any.verifyName("Rect2D");
+ any.verifyType(Any::ARRAY);
+ any.verifySize(4);
+ if (toUpper(any.name()) == "RECT2D::XYWH") {
+ *this = Rect2D::xywh(any[0], any[1], any[2], any[3]);
+ } else {
+ any.verifyName("Rect2D::xyxy");
+ *this = Rect2D::xyxy(any[0], any[1], any[2], any[3]);
+ }
+}
+
+
+/** Converts the Rect2D to an Any. */
+Rect2D::operator Any() const {
+ Any any(Any::ARRAY, "Rect2D::xywh");
+ any.append(x0(), y0(), width(), height());
+ return any;
+}
+
+}
diff --git a/dep/g3dlite/ReferenceCount.cpp b/dep/g3dlite/source/ReferenceCount.cpp
index 2e1f117e0d9..2e1f117e0d9 100644
--- a/dep/g3dlite/ReferenceCount.cpp
+++ b/dep/g3dlite/source/ReferenceCount.cpp
diff --git a/dep/g3dlite/RegistryUtil.cpp b/dep/g3dlite/source/RegistryUtil.cpp
index fc4cebc2ee5..fc4cebc2ee5 100644
--- a/dep/g3dlite/RegistryUtil.cpp
+++ b/dep/g3dlite/source/RegistryUtil.cpp
diff --git a/dep/g3dlite/Sphere.cpp b/dep/g3dlite/source/Sphere.cpp
index 4ed0811cb29..4ed0811cb29 100644
--- a/dep/g3dlite/Sphere.cpp
+++ b/dep/g3dlite/source/Sphere.cpp
diff --git a/dep/g3dlite/source/SplineBase.cpp b/dep/g3dlite/source/SplineBase.cpp
new file mode 100644
index 00000000000..41221624b06
--- /dev/null
+++ b/dep/g3dlite/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/dep/g3dlite/source/Stopwatch.cpp b/dep/g3dlite/source/Stopwatch.cpp
new file mode 100644
index 00000000000..9b785d50295
--- /dev/null
+++ b/dep/g3dlite/source/Stopwatch.cpp
@@ -0,0 +1,119 @@
+/**
+ @file Stopwatch.cpp
+
+ @maintainer Morgan McGuire, http://graphics.cs.williams.edu
+
+ @created 2005-10-05
+ @edited 2009-03-14
+
+ Copyright 2000-2009, Morgan McGuire.
+ All rights reserved.
+ */
+
+#include "G3D/Stopwatch.h"
+#include "G3D/System.h"
+
+namespace G3D {
+
+Stopwatch::Stopwatch(const std::string& myName) :
+ myName(myName),
+ inBetween(false), lastTockTime(-1),
+ lastDuration(0), lastCycleCount(0), m_fps(0), emwaFPS(0),
+ m_smoothFPS(0), emwaDuration(0) {
+ computeOverhead();
+ reset();
+}
+
+
+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);
+
+ const double blend = 0.01;
+ emwaFPS = m_fps * blend + emwaFPS * (1.0 - blend);
+
+ double maxDiscrepancyPercentage = 0.25;
+ if (abs(emwaFPS - m_fps) > max(emwaFPS, m_fps) * maxDiscrepancyPercentage) {
+ // The difference between emwa and m_fps is way off, so
+ // update emwa directly.
+ emwaFPS = m_fps * 0.20 + emwaFPS * 0.80;
+ }
+
+ // 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 <= 20) {
+ if (::fabs(m_smoothFPS - emwaFPS) > 0.75) {
+ // Small number and display is off by more than 0.75; round to the nearest 0.1
+ m_smoothFPS = floor(emwaFPS * 10.0 + 0.5) / 10.0;
+ }
+ } else if (::fabs(m_smoothFPS - emwaFPS) > 1.25) {
+ // Large number and display is off by more than 1.25; round to the nearest 1.0
+ m_smoothFPS = floor(emwaFPS + 0.5);
+ }
+ }
+ lastTockTime = now;
+
+ alwaysAssertM(inBetween, "Stopwatch::tock() called without matching tick.");
+ inBetween = false;
+}
+
+
+void Stopwatch::reset() {
+ prevTime = startTime = System::time();
+ prevMark = "start";
+}
+
+
+void Stopwatch::after(const std::string& s) {
+ RealTime now = System::time();
+ debugPrintf("%s: %10s - %8fs since %s (%fs since start)\n",
+ myName.c_str(),
+ s.c_str(),
+ now - prevTime,
+ prevMark.c_str(),
+ now - startTime);
+ prevTime = now;
+ prevMark = s;
+}
+
+}
+
diff --git a/dep/g3dlite/System.cpp b/dep/g3dlite/source/System.cpp
index e03c4e8c6fa..5170ba44941 100644
--- a/dep/g3dlite/System.cpp
+++ b/dep/g3dlite/source/System.cpp
@@ -27,6 +27,7 @@
#include "G3D/Table.h"
#include "G3D/GMutex.h"
#include "G3D/units.h"
+#include "G3D/FileSystem.h"
#include <time.h>
#include <cstring>
@@ -340,40 +341,6 @@ void getG3DVersion(std::string& s) {
s = cstr;
}
-#if 0 // TODO: delete
-struct Directory {
- std::string path;
- Array<std::string> contents;
-};
-
-static bool maybeAddDirectory(const std::string& newPath, Array<Directory>& directoryArray, bool recurse = true) {
- if (fileExists(newPath)) {
- Directory& d = directoryArray.next();
- d.path = newPath;
- getFiles(pathConcat(newPath, "*"), d.contents);
- Array<std::string> dirs;
- getDirs(pathConcat(newPath, "*"), dirs);
- d.contents.append(dirs);
-
- if (recurse) {
- // Look for subdirectories
- static const std::string subdirs[] =
- {"font", "gui", "SuperShader", "cubemap", "icon", "material", "image", "md2", "md3", "ifs", "3ds", "sky", ""};
-
- for (int j = 0; j < dirs.size(); ++j) {
- for (int i = 0; ! subdirs[i].empty(); ++i) {
- if (dirs[j] == subdirs[i]) {
- maybeAddDirectory(pathConcat(newPath, dirs[j]), directoryArray, false);
- }
- }
- }
- }
- return true;
- } else {
- return false;
- }
-}
-#endif
std::string System::findDataFile
(const std::string& full,
@@ -385,14 +352,14 @@ std::string System::findDataFile
// First check if the file exists as requested. This will go
// through the FileSystemCache, so most calls do not touch disk.
- if (fileExists(full)) {
+ if (FileSystem::exists(full)) {
return full;
}
// Now check where we previously found this file.
std::string* last = lastFound.getPointer(full);
if (last != NULL) {
- if (fileExists(*last)) {
+ if (FileSystem::exists(*last)) {
// Even if cwd has changed the file is still present.
// We won't notice if it has been deleted, however.
return *last;
@@ -405,20 +372,33 @@ std::string System::findDataFile
// Places to look
static Array<std::string> directoryArray;
+ std::string initialAppDataDir(instance().m_appDataDir);
+ const char* g3dPath = getenv("G3DDATA");
+
if (directoryArray.size() == 0) {
// Initialize the directory array
RealTime t0 = System::time();
Array<std::string> baseDirArray;
-
- std::string initialAppDataDir(instance().m_appDataDir);
baseDirArray.append("");
if (! initialAppDataDir.empty()) {
baseDirArray.append(initialAppDataDir);
}
- const char* g3dPath = getenv("G3DDATA");
+# ifdef G3D_WIN32
+ if (g3dPath == NULL) {
+ // If running the demos under visual studio from the G3D.sln file,
+ // this will locate the data directory.
+ const char* paths[] = {"../data-files/", "../../data-files/", "../../../data-files/", NULL};
+ for (int i = 0; paths[i]; ++i) {
+ if (FileSystem::exists(pathConcat(paths[i], "G3D-DATA-README.TXT"))) {
+ g3dPath = paths[i];
+ break;
+ }
+ }
+ }
+# endif
if (g3dPath && (initialAppDataDir != g3dPath)) {
baseDirArray.append(g3dPath);
@@ -428,11 +408,11 @@ std::string System::findDataFile
{"font", "gui", "SuperShader", "cubemap", "icon", "material", "image", "md2", "md3", "ifs", "3ds", "sky", ""};
for (int j = 0; j < baseDirArray.size(); ++j) {
std::string d = baseDirArray[j];
- if (fileExists(d)) {
+ if ((d == "") || FileSystem::exists(d)) {
directoryArray.append(d);
for (int i = 0; ! subdirs[i].empty(); ++i) {
const std::string& p = pathConcat(d, subdirs[i]);
- if (fileExists(p)) {
+ if (FileSystem::exists(p)) {
directoryArray.append(p);
}
}
@@ -444,7 +424,7 @@ std::string System::findDataFile
for (int i = 0; i < directoryArray.size(); ++i) {
const std::string& p = pathConcat(directoryArray[i], full);
- if (fileExists(p)) {
+ if (FileSystem::exists(p)) {
lastFound.set(full, p);
return p;
}
@@ -454,9 +434,29 @@ std::string System::findDataFile
// Generate an error message
std::string locations;
for (int i = 0; i < directoryArray.size(); ++i) {
- locations += pathConcat(directoryArray[i], full) + "\n";
+ locations += "\'" + pathConcat(directoryArray[i], full) + "'\n";
}
- alwaysAssertM(false, "Could not find '" + full + "' in:\n" + locations);
+
+ std::string msg = "Could not find '" + full + "'.\n\n";
+ msg += "cwd = \'" + FileSystem::currentDirectory() + "\'\n";
+ if (g3dPath) {
+ msg += "G3DDATA = ";
+ if (! FileSystem::exists(g3dPath)) {
+ msg += "(illegal path!) ";
+ }
+ msg += std::string(g3dPath) + "\'\n";
+ } else {
+ msg += "(G3DDATA environment variable is undefined)\n";
+ }
+ msg += "GApp::Settings.dataDir = ";
+ if (! FileSystem::exists(initialAppDataDir)) {
+ msg += "(illegal path!) ";
+ }
+ msg += std::string(initialAppDataDir) + "\'\n";
+
+ msg += "\nLocations searched:\n" + locations;
+
+ alwaysAssertM(false, msg);
}
// Not found
@@ -474,19 +474,23 @@ std::string demoFindData(bool errorIfNotFound) {
if (g3dPath) {
return g3dPath;
# ifdef G3D_WIN32
- } else if (fileExists("../data")) {
+ } else if (FileSystem::exists("../data")) {
// G3D install on Windows
return "../data";
- } else if (fileExists("../data-files")) {
+ } else if (FileSystem::exists("../data-files")) {
// G3D source on Windows
return "../data-files";
+ } else if (FileSystem::exists("c:/libraries/G3D/data")) {
+ return "c:/libraries/G3D/data";
# else
- } else if (fileExists("../../../../data")) {
+ } else if (FileSystem::exists("../../../../data")) {
// G3D install on Unix
return "../../../../data";
- } else if (fileExists("../../../../data-files")) {
+ } else if (FileSystem::exists("../../../../data-files")) {
// G3D source on Unix
return "../../../../data-files";
+ } else if (FileSystem::exists("/usr/local/G3D/data")) {
+ return "/usr/local/G3D/data";
# endif
} else {
return "";
@@ -560,7 +564,7 @@ void System::getStandardProcessorExtensions() {
#endif
}
-#if defined(G3D_WIN32) && !defined(G3D_64BIT)
+#if defined(G3D_WIN32) && !defined(G3D_64BIT) /* G3DFIX: Don't check if on 64-bit Windows platforms */
#pragma message("Port System::memcpy SIMD to all platforms")
/** Michael Herf's fast memcpy */
void memcpyMMX(void* dst, const void* src, int nbytes) {
@@ -611,7 +615,7 @@ void memcpyMMX(void* dst, const void* src, int nbytes) {
#endif
void System::memcpy(void* dst, const void* src, size_t numBytes) {
-#if defined(G3D_WIN32) && !defined(G3D_64BIT)
+#if defined(G3D_WIN32) && !defined(G3D_64BIT) /* G3DFIX: Don't check if on 64-bit Windows platforms */
memcpyMMX(dst, src, numBytes);
#else
::memcpy(dst, src, numBytes);
@@ -621,7 +625,7 @@ void System::memcpy(void* dst, const void* src, size_t numBytes) {
/** Michael Herf's fastest memset. n32 must be filled with the same
character repeated. */
-#if defined(G3D_WIN32) && !defined(G3D_64BIT)
+#if defined(G3D_WIN32) && !defined(G3D_64BIT) /* G3DFIX: Don't check if on 64-bit Windows platforms */
#pragma message("Port System::memfill SIMD to all platforms")
// On x86 processors, use MMX
@@ -660,7 +664,7 @@ void memfill(void *dst, int n32, unsigned long i) {
void System::memset(void* dst, uint8 value, size_t numBytes) {
-#if defined(G3D_WIN32) && !defined(G3D_64BIT)
+#if defined(G3D_WIN32) && !defined(G3D_64BIT) /* G3DFIX: Don't check if on 64-bit Windows platforms */
uint32 v = value;
v = v + (v << 8) + (v << 16) + (v << 24);
G3D::memfill(dst, v, numBytes);
@@ -1677,7 +1681,7 @@ std::string System::currentDateString() {
// VC on Intel
void System::cpuid(CPUIDFunction func, uint32& areg, uint32& breg, uint32& creg, uint32& dreg) {
-#if !defined(G3D_64BIT)
+#if !defined(G3D_64BIT) /* G3DFIX: Don't check if on 64-bit platform */
// Can't copy from assembler direct to a function argument (which is on the stack) in VC.
uint32 a,b,c,d;
diff --git a/dep/g3dlite/TextInput.cpp b/dep/g3dlite/source/TextInput.cpp
index 7276d8c66b2..09abacc7391 100644
--- a/dep/g3dlite/TextInput.cpp
+++ b/dep/g3dlite/source/TextInput.cpp
@@ -6,7 +6,7 @@
@cite Based on a lexer written by Aaron Orenstein.
@created 2001-11-27
- @edited 2008-07-14
+ @edited 2010-07-03
*/
#include "G3D/fileutils.h"
@@ -33,36 +33,45 @@ Token TextInput::readSignificant() {
double Token::number() const {
if (_type == NUMBER) {
- std::string s = toLower(_string);
- if (s == "-1.#ind00") {
- return nan();
- }
+ return TextInput::parseNumber(_string);
+ } else {
+ return 0.0;
+ }
+}
- if (s == "1.#inf00") {
- return inf();
- }
- if (s == "-1.#inf00") {
- return -inf();
- }
+bool TextInput::parseBoolean(const std::string& _string) {
+ return toLower(_string) == "true";
+}
- 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;
+double TextInput::parseNumber(const std::string& _string) {
+ std::string s = toLower(_string);
+ if (s == "-1.#ind00" || s == "nan") {
+ return nan();
+ }
+
+ if (s == "1.#inf00" || s == "inf" || s == "+inf") {
+ return inf();
+ }
+
+ if (s == "-1.#inf00" || s == "-inf") {
+ 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 {
- return 0.0;
+ sscanf(_string.c_str(), "%lg", &n);
}
-}
+ return n;
+}
TextInput::Settings::Settings () :
cppBlockComments(true),
@@ -78,7 +87,8 @@ TextInput::Settings::Settings () :
singleQuoteCharacter('\''),
sourceFileName(),
startingLineNumberOffset(0),
- msvcSpecials(true),
+ msvcFloatSpecials(true),
+ simpleFloatSpecials(true),
proofSymbols(false),
caseSensitive(true)
{
@@ -117,6 +127,39 @@ Token TextInput::read() {
}
}
+
+std::string TextInput::readUntilNewlineAsString() {
+ // Go to the front of the next token
+ Token t = read();
+
+ // Reset the position to the start of this token
+ currentCharOffset = t.bytePosition();
+ stack.clear();
+
+ if (currentCharOffset == buffer.size()) {
+ // End of file
+ return "";
+ }
+
+ std::string s;
+
+ // Read until newline or eof
+ char c = '\0';
+ do {
+ c = buffer[currentCharOffset];
+ if (c == '\r' || c == '\n') {
+ // Done
+ break;
+ } else {
+ s += c;
+ ++currentCharOffset;
+ }
+ } while (currentCharOffset < buffer.size());
+
+ return s;
+}
+
+
static void toUpper(Set<std::string>& set) {
Array<std::string> symbols;
set.getMembers(symbols);
@@ -198,6 +241,7 @@ int TextInput::peekInputChar(int distance) {
Token TextInput::nextToken() {
Token t;
+ t._bytePosition = currentCharOffset;
t._line = lineNumber;
t._character = charNumber;
t._type = Token::END;
@@ -215,21 +259,24 @@ Token TextInput::nextToken() {
whitespaceDone = true;
// generate newlines tokens for '\n' and '\r' and '\r\n'
- if (options.generateNewlineTokens && isNewline(c)) {
- t._type = Token::NEWLINE;
- t._extendedType = Token::NEWLINE_TYPE;
- t._string = c;
-
- int c2 = peekInputChar(1);
- if (c == '\r' && c2 == '\n') {
- t._string += c2;
- }
+ while (isWhiteSpace(c)) {
+ if (options.generateNewlineTokens && isNewline(c)) {
+ t._type = Token::NEWLINE;
+ t._extendedType = Token::NEWLINE_TYPE;
+ t._bytePosition = currentCharOffset;
+ t._line = lineNumber;
+ t._character = charNumber;
+ t._string = c;
+
+ int c2 = peekInputChar(1);
+ if (c == '\r' && c2 == '\n') {
+ t._string += c2;
+ }
- eatInputChar();
- return t;
- } else {
- // Consume whitespace
- while (isWhiteSpace(c)) {
+ eatInputChar();
+ return t;
+ } else {
+ // Consume the single whitespace
c = eatAndPeekInputChar();
}
}
@@ -237,6 +284,7 @@ Token TextInput::nextToken() {
// update line and character number to include discarded whitespace
t._line = lineNumber;
t._character = charNumber;
+ t._bytePosition = currentCharOffset;
int c2 = peekInputChar(1);
@@ -293,13 +341,16 @@ Token TextInput::nextToken() {
eatInputChar();
eatInputChar();
+ // c is the next character we'll read, c2 is the one after *that*
c = peekInputChar();
c2 = peekInputChar(1);
while (! ((c == '*') && (c2 == '/')) && (c != EOF)) {
commentString += c;
+ // Eat input char may consume more than one character if there is a newline
eatInputChar();
- c = c2;
+
+ c = peekInputChar();
c2 = peekInputChar(1);
}
eatInputChar(); // eat closing '*'
@@ -324,6 +375,7 @@ Token TextInput::nextToken() {
t._line = lineNumber;
t._character = charNumber;
+ t._bytePosition = currentCharOffset;
// handle EOF
if (c == EOF) {
@@ -380,15 +432,29 @@ Token TextInput::nextToken() {
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.
+ if (options.signedNumbers) {
+ if (isDigit(c) || (c == '.' && isDigit(peekInputChar(1)))) {
+ // Negative number. 'c' is still the first digit, and is
+ // the next input char.
- goto numLabel;
+ goto numLabel;
+ } else {
+ char terminal = peekInputChar(3);
+ if (options.simpleFloatSpecials && (c == 'i') && (peekInputChar(1) == 'n') && (peekInputChar(2) == 'f') &&
+ ! isLetter(terminal) && (terminal != '_')) {
+ // negative infinity
+ t._type = Token::NUMBER;
+ t._extendedType = Token::FLOATING_POINT_TYPE;
+ t._string = "-inf";
+ eatInputChar(); // i
+ eatInputChar(); // n
+ eatInputChar(); // f
+ return t;
+ }
+ }
}
+
// plain -
return t;
@@ -403,13 +469,26 @@ Token TextInput::nextToken() {
return t;
}
- if (options.signedNumbers
- && (isDigit(c) || (c == '.' && isDigit(peekInputChar(1))))) {
+ if (options.signedNumbers) {
+ if (isDigit(c) || (c == '.' && isDigit(peekInputChar(1)))) {
+ // Positive number. 'c' is still the first digit, and is
+ // the next input char.
- // Positive number. 'c' is still the first digit, and is
- // the next input char.
-
- goto numLabel;
+ goto numLabel;
+ } else {
+ char terminal = peekInputChar(3);
+ if (options.simpleFloatSpecials && (c == 'i') && (peekInputChar(1) == 'n') && (peekInputChar(2) == 'f') &&
+ ! isLetter(terminal) && (terminal != '_')) {
+ // positive infinity
+ t._type = Token::NUMBER;
+ t._extendedType = Token::FLOATING_POINT_TYPE;
+ t._string = "+inf";
+ eatInputChar(); // i
+ eatInputChar(); // n
+ eatInputChar(); // f
+ return t;
+ }
+ }
}
return t;
@@ -596,7 +675,7 @@ numLabel:
c = eatAndPeekInputChar();
// Floating point specials (msvc format only)
- if (options.msvcSpecials && (c == '#')) {
+ if (options.msvcFloatSpecials && (c == '#')) {
isSpecial = true;
// We are reading a floating point special value
// of the form -1.#IND00, -1.#INF00, or 1.#INF00
@@ -607,8 +686,7 @@ numLabel:
}
if (test != 'I') {
throw BadMSVCSpecial
- (
- "Incorrect floating-point special (inf or nan) "
+ ("Incorrect floating-point special (inf or nan) "
"format.",
t.line(), charNumber);
}
@@ -714,6 +792,10 @@ numLabel:
}
}
+ if (options.simpleFloatSpecials && ((t._string == "nan") || (t._string == "inf"))) {
+ t._type = Token::NUMBER;
+ t._extendedType = Token::FLOATING_POINT_TYPE;
+ }
return t;
} else if (c == '\"') {
@@ -1052,6 +1134,8 @@ static const char* tokenTypeToString(Token::Type t) {
return "Token::NUMBER";
case Token::END:
return "Token::END";
+ case Token::NEWLINE:
+ return "Token::NEWLINE";
default:
debugAssertM(false, "Fell through switch");
return "?";
diff --git a/dep/g3dlite/TextOutput.cpp b/dep/g3dlite/source/TextOutput.cpp
index 11347252eba..3257f6fb9ce 100644
--- a/dep/g3dlite/TextOutput.cpp
+++ b/dep/g3dlite/source/TextOutput.cpp
@@ -3,15 +3,16 @@
@maintainer Morgan McGuire, http://graphics.cs.williams.edu
@created 2004-06-21
- @edited 2006-08-14
+ @edited 2010-03-14
- Copyright 2000-2006, Morgan McGuire.
+ Copyright 2000-2010, Morgan McGuire.
All rights reserved.
*/
#include "G3D/TextOutput.h"
#include "G3D/Log.h"
#include "G3D/fileutils.h"
+#include "G3D/FileSystem.h"
namespace G3D {
@@ -392,17 +393,17 @@ void TextOutput::vprintf(const char* formatString, va_list argPtr) {
void TextOutput::commit(bool flush) {
std::string p = filenamePath(filename);
- if (! fileExists(p, false)) {
- createDirectory(p);
+ if (! FileSystem::exists(p, false)) {
+ FileSystem::createDirectory(p);
}
- FILE* f = fopen(filename.c_str(), "wb");
+ FILE* f = FileSystem::fopen(filename.c_str(), "wb");
debugAssertM(f, "Could not open \"" + filename + "\"");
fwrite(data.getCArray(), 1, data.size(), f);
if (flush) {
fflush(f);
}
- fclose(f);
+ FileSystem::fclose(f);
}
diff --git a/dep/g3dlite/source/ThreadSet.cpp b/dep/g3dlite/source/ThreadSet.cpp
new file mode 100644
index 00000000000..ee3895fe9de
--- /dev/null
+++ b/dep/g3dlite/source/ThreadSet.cpp
@@ -0,0 +1,166 @@
+#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(GThread::SpawnBehavior lastBehavior) const {
+ ThreadSet* me = const_cast<ThreadSet*>(this);
+
+ Array<GThreadRef> unstarted;
+ me->m_lock.lock();
+ // Find the unstarted threads
+ for (int i = 0; i < m_thread.size(); ++i) {
+ if (! m_thread[i]->started()) {
+ unstarted.append(m_thread[i]);
+ }
+ }
+
+ int last = unstarted.size();
+ if (lastBehavior == GThread::USE_CURRENT_THREAD) {
+ // Save the last unstarted for the current thread
+ --last;
+ }
+
+ for (int i = 0; i < last; ++i) {
+ unstarted[i]->start(GThread::USE_NEW_THREAD);
+ }
+
+ me->m_lock.unlock();
+
+ // Start the last one on my thread
+ if ((unstarted.size() > 0) && (lastBehavior == GThread::USE_CURRENT_THREAD)) {
+ unstarted.last()->start(GThread::USE_CURRENT_THREAD);
+ }
+}
+
+
+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/dep/g3dlite/Triangle.cpp b/dep/g3dlite/source/Triangle.cpp
index 253438ad5fb..253438ad5fb 100644
--- a/dep/g3dlite/Triangle.cpp
+++ b/dep/g3dlite/source/Triangle.cpp
diff --git a/dep/g3dlite/UprightFrame.cpp b/dep/g3dlite/source/UprightFrame.cpp
index c80264bf4e8..c80264bf4e8 100644
--- a/dep/g3dlite/UprightFrame.cpp
+++ b/dep/g3dlite/source/UprightFrame.cpp
diff --git a/dep/g3dlite/Vector2.cpp b/dep/g3dlite/source/Vector2.cpp
index ec0737c3755..ec0737c3755 100644
--- a/dep/g3dlite/Vector2.cpp
+++ b/dep/g3dlite/source/Vector2.cpp
diff --git a/dep/g3dlite/source/Vector2int16.cpp b/dep/g3dlite/source/Vector2int16.cpp
new file mode 100644
index 00000000000..2a4035a4d09
--- /dev/null
+++ b/dep/g3dlite/source/Vector2int16.cpp
@@ -0,0 +1,47 @@
+/**
+ @file Vector2int16.cpp
+
+ @author Morgan McGuire, http://graphics.cs.williams.edu
+
+ @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/dep/g3dlite/Vector3.cpp b/dep/g3dlite/source/Vector3.cpp
index a53fa8269b7..55343e96ece 100644
--- a/dep/g3dlite/Vector3.cpp
+++ b/dep/g3dlite/source/Vector3.cpp
@@ -25,6 +25,7 @@
#include "G3D/Vector2.h"
#include "G3D/Color3.h"
#include "G3D/Vector4int8.h"
+#include "G3D/Vector4.h"
#include "G3D/Vector3int32.h"
#include "G3D/Any.h"
@@ -104,11 +105,7 @@ Vector3::Axis Vector3::primaryAxis() const {
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);
+ return Vector4(*this, 0.0f).hashCode();
}
std::ostream& operator<<(std::ostream& os, const Vector3& v) {
diff --git a/dep/g3dlite/source/Vector3int16.cpp b/dep/g3dlite/source/Vector3int16.cpp
new file mode 100644
index 00000000000..44069b85d8c
--- /dev/null
+++ b/dep/g3dlite/source/Vector3int16.cpp
@@ -0,0 +1,49 @@
+/**
+ @file Vector3int16.cpp
+
+ @author Morgan McGuire, http://graphics.cs.williams.edu
+
+ @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/dep/g3dlite/source/Vector3int32.cpp b/dep/g3dlite/source/Vector3int32.cpp
new file mode 100644
index 00000000000..3bd8e9f2bc2
--- /dev/null
+++ b/dep/g3dlite/source/Vector3int32.cpp
@@ -0,0 +1,57 @@
+/**
+ @file Vector3int32.cpp
+
+ @author Morgan McGuire, http://graphics.cs.williams.edu
+
+ @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/dep/g3dlite/Vector4.cpp b/dep/g3dlite/source/Vector4.cpp
index f6abc1a6e0c..b5f23d69950 100644
--- a/dep/g3dlite/Vector4.cpp
+++ b/dep/g3dlite/source/Vector4.cpp
@@ -4,7 +4,7 @@
@maintainer Morgan McGuire, http://graphics.cs.williams.edu
@created 2001-07-09
- @edited 2009-11-29
+ @edited 2010-07-05
*/
#include <stdlib.h>
@@ -69,12 +69,7 @@ const Vector4& Vector4::nan() {
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);
+ return HashTrait<uint128>::hashCode(*((uint128*)this));
}
diff --git a/dep/g3dlite/source/Vector4int8.cpp b/dep/g3dlite/source/Vector4int8.cpp
new file mode 100644
index 00000000000..70bd143e01d
--- /dev/null
+++ b/dep/g3dlite/source/Vector4int8.cpp
@@ -0,0 +1,58 @@
+/**
+ @file Vector4int8.cpp
+
+ Homogeneous vector class.
+
+ @maintainer Morgan McGuire, http://graphics.cs.williams.edu
+
+ @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/dep/g3dlite/source/Welder.cpp b/dep/g3dlite/source/Welder.cpp
new file mode 100644
index 00000000000..2db47722e64
--- /dev/null
+++ b/dep/g3dlite/source/Welder.cpp
@@ -0,0 +1,425 @@
+/**
+ @file Welder.cpp
+
+ @author Morgan McGuire, Kyle Whitson, Corey Taylor
+
+ @created 2008-07-30
+ @edited 2009-11-29
+ */
+
+#include "G3D/platform.h"
+#include "G3D/Vector2.h"
+#include "G3D/Vector3.h"
+#include "G3D/Sphere.h"
+#include "G3D/PointHashGrid.h"
+#include "G3D/Welder.h"
+#include "G3D/Stopwatch.h" // for profiling
+#include "G3D/AreaMemoryManager.h"
+#include "G3D/Any.h"
+#include "G3D/stringutils.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. Deallocating this is slow. */
+ 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) {
+ if (indexArrayArray[t] != NULL) {
+ 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) {
+ if (indexArrayArray[t] != NULL) {
+ 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. Scale up by 256 to avoid underflow when
+ // multiplying very small edges
+ const Vector3& n = (e0.cross(e1 * 256.0f)).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) {
+
+ if (normalSmoothingAngle <= 0) {
+ smoothNormalArray = normalArray;
+ return;
+ }
+
+
+ // Create an area memory manager for fast deallocation
+ MemoryManager::Ref mm = AreaMemoryManager::create(iRound(sizeof(VN) * normalArray.size() * 1.5));
+
+ 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, mm);
+ for (int v = 0; v < normalArray.size(); ++v) {
+ grid.insert(VN(vertexArray[v], normalArray[v]));
+ }
+
+ // TODO: this step could be done on multiple threads
+ 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. Avoid underflow by scaling up
+ sum += (N * 256.0f);
+ }
+ ++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.
+ 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.
+ if (unrolledFaceNormalArray.size() > 0) {
+ 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, AreaMemoryManager::create()),
+ vertexWeldRadius(vertRadius) {
+ }
+
+};
+} // Internal
+
+void Welder::weld(
+ Array<Vector3>& vertexArray,
+ Array<Vector2>& texCoordArray,
+ Array<Vector3>& normalArray,
+ Array<Array<int>*>& indexArrayArray,
+ const Welder::Settings& settings) {
+
+ _internal::WeldHelper(settings.vertexWeldRadius).process(
+ vertexArray, texCoordArray, normalArray, indexArrayArray,
+ settings.normalSmoothingAngle, settings.textureWeldRadius, settings.normalWeldRadius);
+
+}
+
+
+Welder::Settings::Settings(const Any& any) {
+ *this = Settings();
+ any.verifyName("Welder::Settings");
+ for (Any::AnyTable::Iterator it = any.table().begin(); it.hasMore(); ++it) {
+ const std::string& key = toLower(it->key);
+ if (key == "normalsmoothingangle") {
+ normalSmoothingAngle = it->value;
+ } else if (key == "vertexweldradius") {
+ vertexWeldRadius = it->value;
+ } else if (key == "textureweldradius") {
+ textureWeldRadius = it->value;
+ } else if (key == "normalweldradius") {
+ normalWeldRadius = it->value;
+ } else {
+ any.verify(false, "Illegal key: " + it->key);
+ }
+ }
+}
+
+Welder::Settings::operator Any() const {
+ Any a(Any::TABLE, "Welder::Settings");
+ a.set("normalSmoothingAngle", normalSmoothingAngle);
+ a.set("vertexWeldRadius", vertexWeldRadius);
+ a.set("textureWeldRadius", textureWeldRadius);
+ a.set("normalWeldRadius", normalWeldRadius);
+ return a;
+}
+
+} // G3D
diff --git a/dep/g3dlite/source/WinMain.cpp b/dep/g3dlite/source/WinMain.cpp
new file mode 100644
index 00000000000..3cee71084e4
--- /dev/null
+++ b/dep/g3dlite/source/WinMain.cpp
@@ -0,0 +1,159 @@
+/*
+ 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
+ (void)sw;
+ (void)szCmdLine;
+ (void)hInst;
+ (void)hPrev;
+
+#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/dep/g3dlite/source/XML.cpp b/dep/g3dlite/source/XML.cpp
new file mode 100644
index 00000000000..51f1a549ba0
--- /dev/null
+++ b/dep/g3dlite/source/XML.cpp
@@ -0,0 +1,216 @@
+/**
+ \file XML.h
+
+ \author Morgan McGuire
+ \maintainer Morgan McGuire
+
+ \created 2010-02-11
+ \edited 2010-02-24
+
+ Copyright 2000-2010, Morgan McGuire.
+ All rights reserved.
+ */
+
+#include "G3D/XML.h"
+#include "G3D/fileutils.h"
+#include "G3D/TextInput.h"
+#include "G3D/TextOutput.h"
+#include "G3D/stringutils.h"
+
+namespace G3D {
+
+XML::XML(TextInput& t) : m_type(VALUE) {
+ deserialize(t);
+}
+
+double XML::number() const {
+ return TextInput::parseNumber(m_value);
+}
+
+
+bool XML::boolean() const {
+ return TextInput::parseBoolean(m_value);
+}
+
+
+void XML::load(const std::string& filename) {
+ TextInput::Settings s;
+ s.cppBlockComments = false;
+ s.cppLineComments = false;
+ s.proofSymbols = false;
+
+ TextInput t(filename, s);
+ deserialize(t);
+}
+
+
+void XML::save(const std::string& filename) const {
+ std::string s;
+ unparse(s);
+ writeWholeFile(filename, s);
+}
+
+
+void XML::unparse(std::string &s) const {
+ TextOutput::Settings set;
+ set.wordWrap = TextOutput::Settings::WRAP_WITHOUT_BREAKING;
+ TextOutput t(set);
+
+ serialize(t);
+
+ t.commitString(s);
+}
+
+
+void XML::serialize(TextOutput& t) const {
+ if (m_type == VALUE) {
+ // Raw string; no quotes
+ t.writeSymbol(m_value);
+ } else {
+ t.printf("<%s", m_name.c_str());
+ for (AttributeTable::Iterator it = m_attribute.begin(); it.hasMore(); ++it) {
+ t.printf(" %s=\"%s\"", it->key.c_str(), it->value.m_value.c_str());
+ }
+ t.printf(">");
+ t.writeNewline();
+ t.pushIndent();
+ for (int i = 0; i < m_child.size(); ++i) {
+ m_child[i].serialize(t);
+ if (m_child[i].m_type == VALUE) {
+ // Only tags know to append a newline
+ t.writeNewline();
+ }
+ }
+ t.popIndent();
+ t.printf("</%s>", m_name.c_str());
+ t.writeNewline();
+ }
+}
+
+
+void XML::parse(const std::string& s) {
+ TextInput t(TextInput::FROM_STRING, s);
+ deserialize(t);
+}
+
+
+/** True if the next token begins the close tag */
+static bool atClose(TextInput& t, const std::string name) {
+ if ((t.peek().type() == Token::SYMBOL) && (t.peek().string() == "<")) {
+ // Need to keep looking ahead
+ Token p0 = t.read();
+ if ((t.peek().type() == Token::SYMBOL) && (t.peek().string() == "/")) {
+ // Check the name on the close tag. It *must* match if
+ // this is a well-formed document, but there might be a
+ // tag error.
+ Token p1 = t.read();
+ Token p2 = t.peek();
+ std::string s = p2.string();
+ debugAssertM(beginsWith(name, s), "Mismatched close tag");
+
+ // Put the tokens back
+ t.push(p1);
+ t.push(p0);
+ return true;
+ } else {
+ // Put the read token back
+ t.push(p0);
+ return false;
+ }
+ } else {
+ return false;
+ }
+}
+
+void XML::deserialize(TextInput& t) {
+ begin:
+ Token n = t.read();
+ m_attribute.clear();
+ m_child.clear();
+ m_name = "";
+ m_value = "";
+
+ if ((n.type() == Token::SYMBOL) && (n.string() == "<")) {
+ // Beginning a tag
+
+ // Read name
+ n = t.read();
+ debugAssert(n.type() == Token::SYMBOL);
+ bool isComment =
+ (n.string() == "!") &&
+ (t.peek().type() == Token::SYMBOL) &&
+ (t.peek().string() == "--");
+
+ // ignored tag: <?xml> or <!xml>
+ // comment tag: <!-- ... -->
+
+ if ((n.string() == "?") || ((n.string() == "!") && ! isComment)) {
+ // Ignore this tag
+ while (t.hasMore() && ! ((n.type() == Token::SYMBOL) && (n.string() == ">"))) {
+ n = t.read();
+ }
+ goto begin;
+ } else if (isComment) {
+ // Ignore until "-->"
+ bool prevWasDash = false;
+ while (t.hasMore() && ! ((n.type() == Token::SYMBOL) && (n.string() == ">") && prevWasDash)) {
+ prevWasDash = (n.type() == Token::SYMBOL) && (n.string() == "--");
+ n = t.read();
+ }
+ goto begin;
+ }
+
+ // Keep reading until no colon
+ m_name += n.string();
+ n = t.read();
+ while ((n.type() == Token::SYMBOL) && (n.string() == ":")) {
+ // tag with namespace: <x:y>
+ m_name += ":" + t.readSymbol();
+ n = t.read();
+ }
+
+ // Read end of tag/close
+ bool done = false;
+ while (! done) {
+ debugAssert(n.type() == Token::SYMBOL);
+ if (n.string() == "/") {
+ // empty-element tag: <foo/>
+ // Consume the close tag
+ t.readSymbol(">");
+ done = true;
+
+ } else if (n.string() == ">") {
+ // End of open tag: read children until close tag
+ while (! atClose(t, m_name)) {
+ m_child.next().deserialize(t);
+ }
+
+ // Read close tag (we wouldn't be here unless it parses correctly)
+ while (t.hasMore() && ! (t.readSymbol() == ">")) {}
+
+ done = true;
+ } else {
+ // Attribute pair
+ std::string k = n.string();
+ t.readSymbol("=");
+ std::string v = t.read().string();
+ m_attribute.set(k, v);
+
+ // Advance to next
+ n = t.read();
+ }
+ }
+ } else {
+ // Beginning embedded content. Read until the end of file or the next tag.
+ m_type = VALUE;
+ m_value += n.string();
+
+ n = t.peek();
+ while (t.hasMore() && ! ((n.type() == Token::SYMBOL) && (n.string() == "<"))) {
+ m_value += " " + t.read().string();
+ n = t.peek();
+ }
+ }
+}
+
+}
diff --git a/dep/g3dlite/source/constants.cpp b/dep/g3dlite/source/constants.cpp
new file mode 100644
index 00000000000..9ee3eb8736b
--- /dev/null
+++ b/dep/g3dlite/source/constants.cpp
@@ -0,0 +1,16 @@
+/**
+ @file constants.cpp
+
+ @maintainer Morgan McGuire, http://graphics.cs.williams.edu
+ @created 2009-05-20
+ @edited 2010-01-29
+*/
+#include "G3D/constants.h"
+#include "G3D/Any.h"
+#include "G3D/stringutils.h"
+
+namespace G3D {
+
+
+
+} // G3D
diff --git a/dep/g3dlite/debugAssert.cpp b/dep/g3dlite/source/debugAssert.cpp
index a87161b261f..41aad02c640 100644
--- a/dep/g3dlite/debugAssert.cpp
+++ b/dep/g3dlite/source/debugAssert.cpp
@@ -37,7 +37,7 @@ AssertionHook _debugHook = _handleDebugAssert_;
AssertionHook _failureHook = _handleErrorCheck_;
#ifdef G3D_LINUX
-#if SOMEONE_MADE_THIS_USEFUL
+#if 0 /* G3DFIX: Disabled to avoid requirement for X11 libraries */
Display* x11Display = NULL;
Window x11Window = 0;
#endif
@@ -252,7 +252,7 @@ void _releaseInputGrab_() {
ClipCursor(NULL);
#elif defined(G3D_LINUX)
-#if SOMEONE_MADE_THIS_USEFUL
+#if 0 /* G3DFIX: Disabled to avoid requirement for X11 libraries */
if (x11Display != NULL) {
XUngrabPointer(x11Display, CurrentTime);
XUngrabKeyboard(x11Display, CurrentTime);
diff --git a/dep/g3dlite/fileutils.cpp b/dep/g3dlite/source/fileutils.cpp
index 3f5eb579ba9..1867bf3338e 100644
--- a/dep/g3dlite/fileutils.cpp
+++ b/dep/g3dlite/source/fileutils.cpp
@@ -17,10 +17,11 @@
#include "G3D/stringutils.h"
#include "G3D/Set.h"
#include "G3D/g3dfnmatch.h"
+#include "G3D/FileSystem.h"
#include <sys/stat.h>
#include <sys/types.h>
-#if _HAVE_ZIP
+#if _HAVE_ZIP /* G3DFIX: Use ZIP-library only if defined */
#include "zip.h"
#endif
@@ -104,17 +105,18 @@ std::string readWholeFile(
std::string s;
debugAssert(filename != "");
- if (fileExists(filename, false)) {
+ debugAssertM(FileSystem::exists(filename), filename + " not found");
- int64 length = fileLength(filename);
+ if (! FileSystem::inZipfile(filename)) {
+ int64 length = FileSystem::size(filename);
char* buffer = (char*)System::alignedMalloc(length + 1, 16);
debugAssert(buffer);
- FILE* f = fopen(filename.c_str(), "rb");
+ FILE* f = FileSystem::fopen(filename.c_str(), "rb");
debugAssert(f);
int ret = fread(buffer, 1, length, f);
debugAssert(ret == length);(void)ret;
- fclose(f);
+ FileSystem::fclose(f);
buffer[length] = '\0';
s = std::string(buffer);
@@ -134,8 +136,6 @@ std::string readWholeFile(
buffer[length] = '\0';
s = std::string(buffer);
System::alignedFree(buffer);
- } else {
- debugAssertM(false, filename + " not found");
}
return s;
@@ -146,7 +146,7 @@ void zipRead(const std::string& file,
void*& data,
size_t& length) {
std::string zip, desiredFile;
-#if _HAVE_ZIP
+#if _HAVE_ZIP /* G3DFIX: Use ZIP-library only if defined */
if (zipfileExists(file, zip, desiredFile)) {
struct zip *z = zip_open( zip.c_str(), ZIP_CHECKCONS, NULL );
{
@@ -185,7 +185,7 @@ int64 fileLength(const std::string& filename) {
int result = _stat(filename.c_str(), &st);
if (result == -1) {
-#if _HAVE_ZIP
+#if _HAVE_ZIP /* G3DFIX: Use ZIP-library only if defined */
std::string zip, contents;
if(zipfileExists(filename, zip, contents)){
int64 requiredMem;
@@ -196,6 +196,7 @@ int64 fileLength(const std::string& filename) {
struct zip_stat info;
zip_stat_init( &info ); // TODO: Docs unclear if zip_stat_init is required.
int success = zip_stat( z, contents.c_str(), ZIP_FL_NOCASE, &info );
+ (void)success;
debugAssertM(success == 0, zip + ": " + contents + ": zip stat failed.");
requiredMem = info.size;
}
@@ -212,124 +213,6 @@ int64 fileLength(const std::string& filename) {
return st.st_size;
}
-/** Used by robustTmpfile. Returns nonzero if fread, fwrite, and fseek all
-succeed on the file.
- @author Morgan McGuire, http://graphics.cs.williams.edu */
-static int isFileGood(FILE* f) {
-
- int x, n, result;
-
- /* Must be a valid file handle */
- if (f == NULL) {
- return 0;
- }
-
- /* Try to write */
- x = 1234;
- n = fwrite(&x, sizeof(int), 1, f);
-
- if (n != 1) {
- return 0;
- }
-
- /* Seek back to the beginning */
- result = fseek(f, 0, SEEK_SET);
- if (result != 0) {
- return 0;
- }
-
- /* Read */
- n = fread(&x, sizeof(int), 1, f);
- if (n != 1) {
- return 0;
- }
-
- /* Seek back to the beginning again */
- fseek(f, 0, SEEK_SET);
-
- return 1;
-}
-
-FILE* createTempFile() {
- FILE* t = NULL;
-
-//# ifdef G3D_WIN32
- t = tmpfile();
-//# else
-// // On Unix, tmpfile generates a warning for any code that links against it.
-// const char* tempfilename = "/tmp/g3dtemp.XXXXXXXX";
-// mktemp(tempfilename);
-// t = fopen(tempfilename, "w");
-//# endif
-
-# ifdef _WIN32
- char* n = NULL;
-# endif
- char name[256];
-
- if (isFileGood(t)) {
- return t;
- }
-
-# ifdef G3D_WIN32
- /* tmpfile failed; try the tmpnam routine */
- t = fopen(tmpnam(NULL), "w+");
- if (isFileGood(t)) {
- return t;
- }
-
- n = _tempnam("c:/tmp/", "t");
- /* Try to create something in C:\tmp */
- t = fopen(n, "w+");
- if (isFileGood(t)) {
- return t;
- }
-
- /* Try c:\temp */
- n = _tempnam("c:/temp/", "t");
- t = fopen(n, "w+");
- if (isFileGood(t)) {
- return t;
- }
-
- /* try the current directory */
- n = _tempnam("./", "t");
- t = fopen(n, "w+");
- if (isFileGood(t)) {
- return t;
- }
-
- sprintf(name, "%s/tmp%d", "c:/temp", rand());
- t = fopen(name, "w+");
- if (isFileGood(t)) {
- return t;
- }
-
- /* Try some hardcoded paths */
- sprintf(name, "%s/tmp%d", "c:/tmp", rand());
- t = fopen(name, "w+");
- if (isFileGood(t)) {
- return t;
- }
-# else
- sprintf(name, "%s/tmp%d", "/tmp", rand());
- t = fopen(name, "w+");
- if (isFileGood(t)) {
- return t;
- }
-#endif
-
- sprintf(name, "tmp%d", rand());
- t = fopen(name, "w+");
- if (isFileGood(t)) {
- return t;
- }
-
- fprintf(stderr, "Unable to create a temporary file; robustTmpfile returning NULL\n");
-
- return NULL;
-}
-
///////////////////////////////////////////////////////////////////////////////
void writeWholeFile(
const std::string& filename,
@@ -342,11 +225,11 @@ void writeWholeFile(
parseFilename(filename, root, pathArray, base, ext);
path = root + stringJoin(pathArray, '/');
- if (! fileExists(path, false)) {
- createDirectory(path);
+ if (! FileSystem::exists(path, false)) {
+ FileSystem::createDirectory(path);
}
- FILE* file = fopen(filename.c_str(), "wb");
+ FILE* file = FileSystem::fopen(filename.c_str(), "wb");
debugAssert(file);
@@ -355,7 +238,8 @@ void writeWholeFile(
if (flush) {
fflush(file);
}
- fclose(file);
+
+ FileSystem::fclose(file);
}
///////////////////////////////////////////////////////////////////////////////
@@ -385,7 +269,7 @@ void createDirectory(
}
// If it already exists, do nothing
- if (fileExists(d.substr(0, d.size() - 1)), false) {
+ if (FileSystem::exists(d.substr(0, d.size() - 1))) {
return;
}
@@ -405,129 +289,22 @@ void createDirectory(
// Create any intermediate that doesn't exist
for (int i = 0; i < path.size(); ++i) {
p += "/" + path[i];
- if (! fileExists(p, false)) {
+ if (! FileSystem::exists(p)) {
// Windows only requires one argument to mkdir,
// where as unix also requires the permissions.
# ifndef G3D_WIN32
mkdir(p.c_str(), 0777);
-# else
+# else
_mkdir(p.c_str());
-# endif
+# endif
}
}
}
-///////////////////////////////////////////////////////////////////////////////
-
-class FileSystemCache {
-private:
-
- Table<std::string, Array<std::string> > m_files;
-
-public:
-
- bool fileExists(const std::string& filename) {
- const std::string& path = resolveFilename(filenamePath(filename));
- const std::string& name = filenameBaseExt(filename);
-
- bool neverBeforeSeen = false;
- Array<std::string>& fileList = m_files.getCreate(path, neverBeforeSeen);
- if (neverBeforeSeen) {
- if (! G3D::fileExists(path, true, false)) {
- // The path itself doesn't exist... back out our insertion (which makes fileList& invalid)
- m_files.remove(path);
- return false;
- }
-
- std::string spec = pathConcat(path, "*");
-
- // Will automatically recurse into zipfiles
- getFiles(spec, fileList);
- getDirs(spec, fileList);
-
-# ifdef G3D_WIN32
- {
- // Case insensitive
- for (int i = 0; i < fileList.size(); ++i) {
- fileList[i] = toLower(fileList[i]);
- }
- }
-# endif
- }
-
- if (filenameContainsWildcards(name)) {
- // See if anything matches
- for (int i = 0; i < fileList.size(); ++i) {
- if (g3dfnmatch(name.c_str(), fileList[i].c_str(), 0) == 0) {
- return true;
- }
- }
- return false;
- } else {
- // On windows, this is a lower-lower comparison, so it is case insensitive
- return fileList.contains(name);
- }
- }
-
- void clear() {
- m_files.clear();
- }
-
- static FileSystemCache& instance() {
- static FileSystemCache i;
- return i;
- }
-};
-
-
-void clearFileSystemCache() {
- FileSystemCache::instance().clear();
-}
-
-bool fileExists
-(const std::string& _filename,
- bool lookInZipfiles,
- bool trustCache) {
-
- if (_filename.empty()) {
- return false;
- }
-
- // Remove trailing slash from directories
- const std::string& filename = (endsWith(_filename, "/") || endsWith(_filename, "\\")) ? _filename.substr(0, _filename.length() - 1) : _filename;
-
- if (trustCache && lookInZipfiles) {
-# ifdef G3D_WIN32
- // Case insensitive
- return FileSystemCache::instance().fileExists(toLower(filename));
-# else
- return FileSystemCache::instance().fileExists(filename);
-# endif
- }
-
- // Useful for debugging
- //char curdir[1024]; _getcwd(curdir, 1024);
-
- struct _stat st;
- int ret = _stat(filename.c_str(), &st);
-
- // _stat returns zero on success
- bool exists = (ret == 0);
-
- if (! exists && lookInZipfiles) {
- // Does not exist standalone, but might exist in a zipfile
-
- // These output arguments will be ignored
- std::string zipDir, internalPath;
- return zipfileExists(filename, zipDir, internalPath);
- } else {
- return exists;
- }
-}
///////////////////////////////////////////////////////////////////////////////
-#if _HAVE_ZIP
+#if _HAVE_ZIP /* G3DFIX: Use ZIP-library only if defined */
/* 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){
@@ -566,7 +343,7 @@ static bool _zip_zipContains(const std::string& zipDir, const std::string& desir
// If no zipfile exists, outZipfile and outInternalFile are unchanged
bool zipfileExists(const std::string& filename, std::string& outZipfile,
std::string& outInternalFile){
-#if _HAVE_ZIP
+#if _HAVE_ZIP /* G3DFIX: Use ZIP-library only if defined */
Array<std::string> path;
std::string drive, base, ext, zipfile, infile;
parseFilename(filename, drive, path, base, ext);
@@ -607,12 +384,12 @@ bool zipfileExists(const std::string& filename, std::string& outZipfile,
return false;
}
- if (fileExists(zipfile, false)) {
+ if (FileSystem::exists(zipfile)) {
// 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 (FileSystem::isZipfile(zipfile)){
if (_zip_zipContains(zipfile, infile)){
outZipfile = zipfile;
@@ -641,7 +418,7 @@ std::string generateFilenameBase(const std::string& prefix, const std::string& s
// Note "template" is a reserved word in C++
std::string templat = prefix + System::currentDateString() + "_";
- getFiles(templat + "*", exist);
+ FileSystem::getFiles(templat + "*", exist);
// Remove extensions
for (int i = 0; i < exist.size(); ++i) {
@@ -910,7 +687,7 @@ static void getFileOrDirListNormal
# endif
}
-#if _HAVE_ZIP
+#if _HAVE_ZIP /* G3DFIX: Use ZIP-library only if defined */
/**
@param path The zipfile name (no trailing slash)
@param prefix Directory inside the zipfile. No leading slash, must have trailing slash if non-empty.
@@ -968,7 +745,7 @@ static void getFileOrDirListZip(const std::string& path,
Array<std::string>& files,
bool wantFiles,
bool includePath){
-#if _HAVE_ZIP
+#if _HAVE_ZIP /* G3DFIX: Use ZIP-library only if defined */
struct zip *z = zip_open( path.c_str(), ZIP_CHECKCONS, NULL );
Set<std::string> fileSet;
@@ -1004,8 +781,8 @@ static void determineFileOrDirList(
path = path.substr(0, path.length() -1);
}
- if ((path == "") || fileExists(path, false)) {
- if ((path != "") && isZipfile(path)) {
+ if ((path == "") || FileSystem::exists(path)) {
+ if ((path != "") && FileSystem::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");
diff --git a/dep/g3dlite/source/filter.cpp b/dep/g3dlite/source/filter.cpp
new file mode 100644
index 00000000000..72d6f0e05a7
--- /dev/null
+++ b/dep/g3dlite/source/filter.cpp
@@ -0,0 +1,32 @@
+/**
+ @file filter.cpp
+
+ @author Morgan McGuire, http://graphics.cs.williams.edu
+ @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/dep/g3dlite/format.cpp b/dep/g3dlite/source/format.cpp
index d9d1b516393..d9d1b516393 100644
--- a/dep/g3dlite/format.cpp
+++ b/dep/g3dlite/source/format.cpp
diff --git a/dep/g3dlite/source/g3dfnmatch.cpp b/dep/g3dlite/source/g3dfnmatch.cpp
new file mode 100644
index 00000000000..2d24c4126e6
--- /dev/null
+++ b/dep/g3dlite/source/g3dfnmatch.cpp
@@ -0,0 +1,223 @@
+/* $Id: g3dfnmatch.cpp,v 1.3 2010/03/15 05:01:23 morgan3d Exp $ */
+
+/* $OpenBSD: fnmatch.c,v 1.7 2000/03/23 19:13:51 millert Exp $ */
+
+/*
+ * Copyright (c) 1989, 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Guido van Rossum.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+#include "G3D/g3dfnmatch.h"
+
+#ifdef G3D_WIN32
+
+#include <ctype.h>
+#include <string.h>
+#include <stdio.h>
+
+namespace G3D {
+
+#define EOS '\0'
+
+#define RANGE_MATCH 1
+#define RANGE_NOMATCH 0
+#define RANGE_ERROR (-1)
+
+static int rangematch(const char *, char, int, char **);
+
+int
+g3dfnmatch(const char *pattern, const char *string, int flags)
+{
+ const char *stringstart;
+ char *newp;
+ char c, test;
+
+ for (stringstart = string;;)
+ switch (c = *pattern++) {
+ case EOS:
+ if ((flags & FNM_LEADING_DIR) && *string == '/')
+ return (0);
+ return (*string == EOS ? 0 : FNM_NOMATCH);
+ case '?':
+ if (*string == EOS)
+ return (FNM_NOMATCH);
+ if (*string == '/' && (flags & FNM_PATHNAME))
+ return (FNM_NOMATCH);
+ if (*string == '.' && (flags & FNM_PERIOD) &&
+ (string == stringstart ||
+ ((flags & FNM_PATHNAME) && *(string - 1) == '/')))
+ return (FNM_NOMATCH);
+ ++string;
+ break;
+ case '*':
+ c = *pattern;
+ /* Collapse multiple stars. */
+ while (c == '*')
+ c = *++pattern;
+
+ if (*string == '.' && (flags & FNM_PERIOD) &&
+ (string == stringstart ||
+ ((flags & FNM_PATHNAME) && *(string - 1) == '/')))
+ return (FNM_NOMATCH);
+
+ /* Optimize for pattern with * at end or before /. */
+ if (c == EOS) {
+ if (flags & FNM_PATHNAME)
+ return ((flags & FNM_LEADING_DIR) ||
+ strchr(string, '/') == NULL ?
+ 0 : FNM_NOMATCH);
+ else
+ return (0);
+ } else if (c == '/' && (flags & FNM_PATHNAME)) {
+ if ((string = strchr(string, '/')) == NULL)
+ return (FNM_NOMATCH);
+ break;
+ }
+
+ /* General case, use recursion. */
+ while ((test = *string) != EOS) {
+ if (!g3dfnmatch(pattern, string, flags & ~FNM_PERIOD))
+ return (0);
+ if (test == '/' && (flags & FNM_PATHNAME))
+ break;
+ ++string;
+ }
+ return (FNM_NOMATCH);
+ case '[':
+ if (*string == EOS)
+ return (FNM_NOMATCH);
+ if (*string == '/' && (flags & FNM_PATHNAME))
+ return (FNM_NOMATCH);
+ if (*string == '.' && (flags & FNM_PERIOD) &&
+ (string == stringstart ||
+ ((flags & FNM_PATHNAME) && *(string - 1) == '/')))
+ return (FNM_NOMATCH);
+
+ switch (rangematch(pattern, *string, flags, &newp)) {
+ case RANGE_ERROR:
+ /* not a good range, treat as normal text */
+ goto normal;
+ case RANGE_MATCH:
+ pattern = newp;
+ break;
+ case RANGE_NOMATCH:
+ return (FNM_NOMATCH);
+ }
+ ++string;
+ break;
+ case '\\':
+ if (!(flags & FNM_NOESCAPE)) {
+ if ((c = *pattern++) == EOS) {
+ c = '\\';
+ --pattern;
+ }
+ }
+ /* FALLTHROUGH */
+ default:
+ normal:
+ if (c != *string && !((flags & FNM_CASEFOLD) &&
+ (tolower((unsigned char)c) ==
+ tolower((unsigned char)*string))))
+ return (FNM_NOMATCH);
+ ++string;
+ break;
+ }
+ /* NOTREACHED */
+}
+
+static int
+rangematch(const char *pattern, char test, int flags, char **newp)
+{
+ int negate, ok;
+ char c, c2;
+
+ /*
+ * A bracket expression starting with an unquoted circumflex
+ * character produces unspecified results (IEEE 1003.2-1992,
+ * 3.13.2). This implementation treats it like '!', for
+ * consistency with the regular expression syntax.
+ * J.T. Conklin (conklin@ngai.kaleida.com)
+ */
+ if ((negate = (*pattern == '!' || *pattern == '^')))
+ ++pattern;
+
+ if (flags & FNM_CASEFOLD)
+ test = tolower((unsigned char)test);
+
+ /*
+ * A right bracket shall lose its special meaning and represent
+ * itself in a bracket expression if it occurs first in the list.
+ * -- POSIX.2 2.8.3.2
+ */
+ ok = 0;
+ c = *pattern++;
+ do {
+ if (c == '\\' && !(flags & FNM_NOESCAPE))
+ c = *pattern++;
+ if (c == EOS)
+ return (RANGE_ERROR);
+ if (c == '/' && (flags & FNM_PATHNAME))
+ return (RANGE_NOMATCH);
+ if ((flags & FNM_CASEFOLD))
+ c = tolower((unsigned char)c);
+ if (*pattern == '-'
+ && (c2 = *(pattern+1)) != EOS && c2 != ']') {
+ pattern += 2;
+ if (c2 == '\\' && !(flags & FNM_NOESCAPE))
+ c2 = *pattern++;
+ if (c2 == EOS)
+ return (RANGE_ERROR);
+ if (flags & FNM_CASEFOLD)
+ c2 = tolower((unsigned char)c2);
+ if (c <= test && test <= c2)
+ ok = 1;
+ } else if (c == test)
+ ok = 1;
+ } while ((c = *pattern++) != ']');
+
+ *newp = (char *)pattern;
+ return (ok == negate ? RANGE_NOMATCH : RANGE_MATCH);
+}
+
+}
+
+#else
+
+namespace G3D {
+int g3dfnmatch(const char * a, const char *b, int c) {
+ return fnmatch(a, b, c);
+}
+}
+
+#endif
+
diff --git a/dep/g3dlite/g3dmath.cpp b/dep/g3dlite/source/g3dmath.cpp
index ad85e9efb9b..84e8345bff4 100644
--- a/dep/g3dlite/g3dmath.cpp
+++ b/dep/g3dlite/source/g3dmath.cpp
@@ -11,6 +11,7 @@
#include <cstdlib>
#include <cstring>
+
namespace G3D {
float gaussRandom(float mean, float stdev) {
diff --git a/dep/g3dlite/source/license.cpp b/dep/g3dlite/source/license.cpp
new file mode 100644
index 00000000000..5049184cf9b
--- /dev/null
+++ b/dep/g3dlite/source/license.cpp
@@ -0,0 +1,73 @@
+/**
+ @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.sf.net), which\n"
+"is licensed under the \"Modified BSD\" Open Source license. The G3D library\n"
+"source code is Copyright © 2000-2010, Morgan McGuire, All rights reserved.\n"
+"This program uses The OpenGL Extension Wrangler Library, which \n"
+"is licensed under the \"Modified BSD\" Open Source license. \n"
+"The OpenGL Extension Wrangler Library source code is\n"
+"Copyright (C) 2002-2008, Milan Ikits <milan ikits[]ieee org>\n"
+"Copyright (C) 2002-2008, Marcelo E. Magallon <mmagallo[]debian org>\n"
+"Copyright (C) 2002, Lev Povalahev\n"
+"All rights reserved.\n\n"
+"The Modified BSD license is below, and requires the following statement:\n"
+"\n"
+"Redistribution and use in source and binary forms, with or without \n"
+"modification, are permitted provided that the following conditions are met:\n"
+"\n"
+"* Redistributions of source code must retain the above copyright notice, \n"
+" this list of conditions and the following disclaimer.\n"
+"* Redistributions in binary form must reproduce the above copyright notice, \n"
+" this list of conditions and the following disclaimer in the documentation \n"
+" and/or other materials provided with the distribution.\n"
+"* The name of the author may be used to endorse or promote products \n"
+" derived from this software without specific prior written permission.\n"
+"\n"
+"THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\" \n"
+"AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE \n"
+"IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE\n"
+"ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE \n"
+"LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR \n"
+"CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF \n"
+"SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS\n"
+"INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN\n"
+"CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)\n"
+"ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF\n"
+"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/dep/g3dlite/prompt.cpp b/dep/g3dlite/source/prompt.cpp
index 6a28e6462b4..d2c6f098033 100644
--- a/dep/g3dlite/prompt.cpp
+++ b/dep/g3dlite/source/prompt.cpp
@@ -21,6 +21,7 @@
# define _getch getchar
#endif
+#if 0 /* G3DFIX: exclude GUI prompt code */
#ifdef G3D_OSX
/*#ifdef __LP64__
@@ -37,9 +38,11 @@
*/
#endif
+#endif /* G3DFIX: exclude GUI prompt code */
namespace G3D {
+#if 0 /* G3DFIX: exclude GUI prompt code */
#ifdef G3D_WIN32
namespace _internal {
@@ -469,6 +472,7 @@ static int guiPrompt(
}
#endif
+#endif /* G3DFIX: exclude GUI prompt code */
/**
@@ -531,6 +535,8 @@ static int textPrompt(
return c;
}
+#if 0 /* G3DFIX: exclude GUI prompt code */
+
#ifdef G3D_OSX
// See http://developer.apple.com/documentation/Carbon/Reference/Carbon_Event_Manager_Ref/index.html
@@ -689,13 +695,15 @@ static int guiPrompt
#endif
+#endif /* G3DFIX: exclude GUI prompt code */
+
int prompt(
const char* windowTitle,
const char* prompt,
const char** choice,
int numChoices,
bool useGui) {
-
+#if 0 /* G3DFIX: exclude GUI prompt code */
#ifdef G3D_WIN32
if (useGui) {
// Build the message box
@@ -709,6 +717,7 @@ int prompt(
return guiPrompt(windowTitle, prompt, choice, numChoices);
}
#endif
+#endif /* G3DFIX: exclude GUI prompt code */
return textPrompt(windowTitle, prompt, choice, numChoices);
}
diff --git a/dep/g3dlite/stringutils.cpp b/dep/g3dlite/source/stringutils.cpp
index c3876ebb6a4..c3876ebb6a4 100644
--- a/dep/g3dlite/stringutils.cpp
+++ b/dep/g3dlite/source/stringutils.cpp
diff --git a/dep/g3dlite/source/uint128.cpp b/dep/g3dlite/source/uint128.cpp
new file mode 100644
index 00000000000..1f596fc3e51
--- /dev/null
+++ b/dep/g3dlite/source/uint128.cpp
@@ -0,0 +1,155 @@
+/**
+ @file uint128.cpp
+
+ @maintainer Morgan McGuire, http://graphics.cs.williams.edu
+ @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/src/server/collision/CMakeLists.txt b/src/server/collision/CMakeLists.txt
index 51ef171c5eb..38cabeba0ae 100644
--- a/src/server/collision/CMakeLists.txt
+++ b/src/server/collision/CMakeLists.txt
@@ -33,7 +33,7 @@ set(collision_STAT_SRCS
)
include_directories(
- ${CMAKE_SOURCE_DIR}/dep/g3dlite
+ ${CMAKE_SOURCE_DIR}/dep/g3dlite/include
${CMAKE_SOURCE_DIR}/src/server/shared
${CMAKE_SOURCE_DIR}/src/server/shared/Debugging
${CMAKE_SOURCE_DIR}/src/server/shared/Database
diff --git a/src/tools/vmap3_assembler/CMakeLists.txt b/src/tools/vmap3_assembler/CMakeLists.txt
index 1dfd3adf0d5..5d764e17a64 100644
--- a/src/tools/vmap3_assembler/CMakeLists.txt
+++ b/src/tools/vmap3_assembler/CMakeLists.txt
@@ -10,7 +10,7 @@
# implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
include_directories(
- ${CMAKE_SOURCE_DIR}/dep/g3dlite
+ ${CMAKE_SOURCE_DIR}/dep/g3dlite/include
${CMAKE_SOURCE_DIR}/src/server/shared
${CMAKE_SOURCE_DIR}/src/server/shared/Debugging
${CMAKE_SOURCE_DIR}/src/server/collision/Maps