aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--externals/g3dlite/G3D.lib/include/G3D/AABSPTree.h1609
-rw-r--r--externals/g3dlite/G3D.lib/include/G3D/AABox.h281
-rw-r--r--externals/g3dlite/G3D.lib/include/G3D/AnyVal.h506
-rw-r--r--externals/g3dlite/G3D.lib/include/G3D/Array.h1180
-rw-r--r--externals/g3dlite/G3D.lib/include/G3D/AtomicInt32.h166
-rw-r--r--externals/g3dlite/G3D.lib/include/G3D/BinaryFormat.h140
-rw-r--r--externals/g3dlite/G3D.lib/include/G3D/BinaryInput.h441
-rw-r--r--externals/g3dlite/G3D.lib/include/G3D/BinaryOutput.h421
-rw-r--r--externals/g3dlite/G3D.lib/include/G3D/BoundsTrait.h20
-rw-r--r--externals/g3dlite/G3D.lib/include/G3D/Box.h193
-rw-r--r--externals/g3dlite/G3D.lib/include/G3D/Capsule.h90
-rw-r--r--externals/g3dlite/G3D.lib/include/G3D/CollisionDetection.h1178
-rw-r--r--externals/g3dlite/G3D.lib/include/G3D/Color1.h129
-rw-r--r--externals/g3dlite/G3D.lib/include/G3D/Color1uint8.h107
-rw-r--r--externals/g3dlite/G3D.lib/include/G3D/Color3.h390
-rw-r--r--externals/g3dlite/G3D.lib/include/G3D/Color3uint8.h127
-rw-r--r--externals/g3dlite/G3D.lib/include/G3D/Color4.h324
-rw-r--r--externals/g3dlite/G3D.lib/include/G3D/Color4uint8.h133
-rw-r--r--externals/g3dlite/G3D.lib/include/G3D/Cone.h68
-rw-r--r--externals/g3dlite/G3D.lib/include/G3D/ConvexPolyhedron.h179
-rw-r--r--externals/g3dlite/G3D.lib/include/G3D/CoordinateFrame.h315
-rw-r--r--externals/g3dlite/G3D.lib/include/G3D/Crypto.h96
-rw-r--r--externals/g3dlite/G3D.lib/include/G3D/Cylinder.h92
-rw-r--r--externals/g3dlite/G3D.lib/include/G3D/Discovery.h589
-rw-r--r--externals/g3dlite/G3D.lib/include/G3D/EqualsTrait.h26
-rw-r--r--externals/g3dlite/G3D.lib/include/G3D/G3D.h150
-rw-r--r--externals/g3dlite/G3D.lib/include/G3D/G3DAll.h26
-rw-r--r--externals/g3dlite/G3D.lib/include/G3D/G3DGameUnits.h44
-rw-r--r--externals/g3dlite/G3D.lib/include/G3D/GCamera.h294
-rw-r--r--externals/g3dlite/G3D.lib/include/G3D/GImage.h550
-rw-r--r--externals/g3dlite/G3D.lib/include/G3D/GLight.h72
-rw-r--r--externals/g3dlite/G3D.lib/include/G3D/GThread.h169
-rw-r--r--externals/g3dlite/G3D.lib/include/G3D/GUniqueID.h69
-rw-r--r--externals/g3dlite/G3D.lib/include/G3D/HashTrait.h63
-rw-r--r--externals/g3dlite/G3D.lib/include/G3D/Image1.h79
-rw-r--r--externals/g3dlite/G3D.lib/include/G3D/Image1uint8.h80
-rw-r--r--externals/g3dlite/G3D.lib/include/G3D/Image3.h79
-rw-r--r--externals/g3dlite/G3D.lib/include/G3D/Image3uint8.h85
-rw-r--r--externals/g3dlite/G3D.lib/include/G3D/Image4.h80
-rw-r--r--externals/g3dlite/G3D.lib/include/G3D/Image4uint8.h85
-rw-r--r--externals/g3dlite/G3D.lib/include/G3D/ImageFormat.h362
-rw-r--r--externals/g3dlite/G3D.lib/include/G3D/Line.h105
-rw-r--r--externals/g3dlite/G3D.lib/include/G3D/LineSegment.h115
-rw-r--r--externals/g3dlite/G3D.lib/include/G3D/Log.h109
-rw-r--r--externals/g3dlite/G3D.lib/include/G3D/Map2D.h665
-rw-r--r--externals/g3dlite/G3D.lib/include/G3D/Matrix.h634
-rw-r--r--externals/g3dlite/G3D.lib/include/G3D/Matrix2.h69
-rw-r--r--externals/g3dlite/G3D.lib/include/G3D/Matrix3.h323
-rw-r--r--externals/g3dlite/G3D.lib/include/G3D/Matrix4.h206
-rw-r--r--externals/g3dlite/G3D.lib/include/G3D/MeshAlg.h718
-rw-r--r--externals/g3dlite/G3D.lib/include/G3D/MeshBuilder.h82
-rw-r--r--externals/g3dlite/G3D.lib/include/G3D/NetAddress.h132
-rw-r--r--externals/g3dlite/G3D.lib/include/G3D/NetworkDevice.h738
-rw-r--r--externals/g3dlite/G3D.lib/include/G3D/PhysicsFrame.h74
-rw-r--r--externals/g3dlite/G3D.lib/include/G3D/Plane.h161
-rw-r--r--externals/g3dlite/G3D.lib/include/G3D/PointAABSPTree.h1207
-rw-r--r--externals/g3dlite/G3D.lib/include/G3D/PointHashGrid.h894
-rw-r--r--externals/g3dlite/G3D.lib/include/G3D/Pointer.h275
-rw-r--r--externals/g3dlite/G3D.lib/include/G3D/PositionTrait.h7
-rw-r--r--externals/g3dlite/G3D.lib/include/G3D/Quat.h725
-rw-r--r--externals/g3dlite/G3D.lib/include/G3D/Queue.h355
-rw-r--r--externals/g3dlite/G3D.lib/include/G3D/Ray.h329
-rw-r--r--externals/g3dlite/G3D.lib/include/G3D/Rect2D.h391
-rw-r--r--externals/g3dlite/G3D.lib/include/G3D/ReferenceCount.h597
-rw-r--r--externals/g3dlite/G3D.lib/include/G3D/RegistryUtil.h97
-rw-r--r--externals/g3dlite/G3D.lib/include/G3D/Set.h160
-rw-r--r--externals/g3dlite/G3D.lib/include/G3D/Sphere.h143
-rw-r--r--externals/g3dlite/G3D.lib/include/G3D/Spline.h367
-rw-r--r--externals/g3dlite/G3D.lib/include/G3D/Stopwatch.h108
-rw-r--r--externals/g3dlite/G3D.lib/include/G3D/System.h390
-rw-r--r--externals/g3dlite/G3D.lib/include/G3D/Table.h770
-rw-r--r--externals/g3dlite/G3D.lib/include/G3D/TextInput.h693
-rw-r--r--externals/g3dlite/G3D.lib/include/G3D/TextOutput.h249
-rw-r--r--externals/g3dlite/G3D.lib/include/G3D/ThreadSet.h79
-rw-r--r--externals/g3dlite/G3D.lib/include/G3D/Triangle.h143
-rw-r--r--externals/g3dlite/G3D.lib/include/G3D/UprightFrame.h83
-rw-r--r--externals/g3dlite/G3D.lib/include/G3D/Vector2.h457
-rw-r--r--externals/g3dlite/G3D.lib/include/G3D/Vector2int16.h137
-rw-r--r--externals/g3dlite/G3D.lib/include/G3D/Vector3.h761
-rw-r--r--externals/g3dlite/G3D.lib/include/G3D/Vector3int16.h130
-rw-r--r--externals/g3dlite/G3D.lib/include/G3D/Vector3int32.h138
-rw-r--r--externals/g3dlite/G3D.lib/include/G3D/Vector4.h717
-rw-r--r--externals/g3dlite/G3D.lib/include/G3D/Vector4int8.h113
-rw-r--r--externals/g3dlite/G3D.lib/include/G3D/WeakCache.h90
-rw-r--r--externals/g3dlite/G3D.lib/include/G3D/WrapMode.h79
-rw-r--r--externals/g3dlite/G3D.lib/include/G3D/debug.h66
-rw-r--r--externals/g3dlite/G3D.lib/include/G3D/debugAssert.h236
-rw-r--r--externals/g3dlite/G3D.lib/include/G3D/debugPrintf.h62
-rw-r--r--externals/g3dlite/G3D.lib/include/G3D/enumclass.h141
-rw-r--r--externals/g3dlite/G3D.lib/include/G3D/fileutils.h246
-rw-r--r--externals/g3dlite/G3D.lib/include/G3D/filter.h29
-rw-r--r--externals/g3dlite/G3D.lib/include/G3D/format.h44
-rw-r--r--externals/g3dlite/G3D.lib/include/G3D/g3dmath.h810
-rw-r--r--externals/g3dlite/G3D.lib/include/G3D/platform.h256
-rw-r--r--externals/g3dlite/G3D.lib/include/G3D/prompt.h67
-rw-r--r--externals/g3dlite/G3D.lib/include/G3D/serialize.h30
-rw-r--r--externals/g3dlite/G3D.lib/include/G3D/splinefunc.h118
-rw-r--r--externals/g3dlite/G3D.lib/include/G3D/stringutils.h130
-rw-r--r--externals/g3dlite/G3D.lib/include/G3D/uint128.h51
-rw-r--r--externals/g3dlite/G3D.lib/include/G3D/vectorMath.h235
-rw-r--r--externals/g3dlite/G3D.lib/source/AABox.cpp342
-rw-r--r--externals/g3dlite/G3D.lib/source/AnyVal.cpp1381
-rw-r--r--externals/g3dlite/G3D.lib/source/BinaryFormat.cpp81
-rw-r--r--externals/g3dlite/G3D.lib/source/BinaryInput.cpp568
-rw-r--r--externals/g3dlite/G3D.lib/source/BinaryOutput.cpp518
-rw-r--r--externals/g3dlite/G3D.lib/source/Box.cpp393
-rw-r--r--externals/g3dlite/G3D.lib/source/Capsule.cpp179
-rw-r--r--externals/g3dlite/G3D.lib/source/CollisionDetection.cpp2152
-rw-r--r--externals/g3dlite/G3D.lib/source/Color1.cpp40
-rw-r--r--externals/g3dlite/G3D.lib/source/Color1uint8.cpp38
-rw-r--r--externals/g3dlite/G3D.lib/source/Color3.cpp321
-rw-r--r--externals/g3dlite/G3D.lib/source/Color3uint8.cpp45
-rw-r--r--externals/g3dlite/G3D.lib/source/Color4.cpp140
-rw-r--r--externals/g3dlite/G3D.lib/source/Color4uint8.cpp47
-rw-r--r--externals/g3dlite/G3D.lib/source/Cone.cpp79
-rw-r--r--externals/g3dlite/G3D.lib/source/ConvexPolyhedron.cpp449
-rw-r--r--externals/g3dlite/G3D.lib/source/CoordinateFrame.cpp381
-rw-r--r--externals/g3dlite/G3D.lib/source/Crypto.cpp70
-rw-r--r--externals/g3dlite/G3D.lib/source/Crypto_md5.cpp471
-rw-r--r--externals/g3dlite/G3D.lib/source/Cylinder.cpp176
-rw-r--r--externals/g3dlite/G3D.lib/source/Discovery.cpp170
-rw-r--r--externals/g3dlite/G3D.lib/source/GCamera.cpp399
-rw-r--r--externals/g3dlite/G3D.lib/source/GImage.cpp1065
-rw-r--r--externals/g3dlite/G3D.lib/source/GImage_bayer.cpp298
-rw-r--r--externals/g3dlite/G3D.lib/source/GImage_bmp.cpp716
-rw-r--r--externals/g3dlite/G3D.lib/source/GImage_jpeg.cpp445
-rw-r--r--externals/g3dlite/G3D.lib/source/GImage_png.cpp245
-rw-r--r--externals/g3dlite/G3D.lib/source/GImage_ppm.cpp185
-rw-r--r--externals/g3dlite/G3D.lib/source/GImage_tga.cpp179
-rw-r--r--externals/g3dlite/G3D.lib/source/GLight.cpp143
-rw-r--r--externals/g3dlite/G3D.lib/source/GThread.cpp203
-rw-r--r--externals/g3dlite/G3D.lib/source/GUniqueID.cpp78
-rw-r--r--externals/g3dlite/G3D.lib/source/Image1.cpp224
-rw-r--r--externals/g3dlite/G3D.lib/source/Image1uint8.cpp212
-rw-r--r--externals/g3dlite/G3D.lib/source/Image3.cpp224
-rw-r--r--externals/g3dlite/G3D.lib/source/Image3uint8.cpp225
-rw-r--r--externals/g3dlite/G3D.lib/source/Image4.cpp226
-rw-r--r--externals/g3dlite/G3D.lib/source/Image4uint8.cpp222
-rw-r--r--externals/g3dlite/G3D.lib/source/ImageFormat.cpp440
-rw-r--r--externals/g3dlite/G3D.lib/source/ImageFormat_convert.cpp1305
-rw-r--r--externals/g3dlite/G3D.lib/source/Line.cpp89
-rw-r--r--externals/g3dlite/G3D.lib/source/LineSegment.cpp236
-rw-r--r--externals/g3dlite/G3D.lib/source/Log.cpp157
-rw-r--r--externals/g3dlite/G3D.lib/source/Matrix.cpp1801
-rw-r--r--externals/g3dlite/G3D.lib/source/Matrix3.cpp1725
-rw-r--r--externals/g3dlite/G3D.lib/source/Matrix4.cpp433
-rw-r--r--externals/g3dlite/G3D.lib/source/MeshAlg.cpp733
-rw-r--r--externals/g3dlite/G3D.lib/source/MeshAlgAdjacency.cpp729
-rw-r--r--externals/g3dlite/G3D.lib/source/MeshAlgWeld.cpp213
-rw-r--r--externals/g3dlite/G3D.lib/source/MeshAlgWeld2.cpp377
-rw-r--r--externals/g3dlite/G3D.lib/source/MeshBuilder.cpp113
-rw-r--r--externals/g3dlite/G3D.lib/source/NetAddress.cpp164
-rw-r--r--externals/g3dlite/G3D.lib/source/NetworkDevice.cpp1362
-rw-r--r--externals/g3dlite/G3D.lib/source/PhysicsFrame.cpp77
-rw-r--r--externals/g3dlite/G3D.lib/source/Plane.cpp149
-rw-r--r--externals/g3dlite/G3D.lib/source/Quat.cpp583
-rw-r--r--externals/g3dlite/G3D.lib/source/Ray.cpp112
-rw-r--r--externals/g3dlite/G3D.lib/source/RegistryUtil.cpp290
-rw-r--r--externals/g3dlite/G3D.lib/source/Sphere.cpp196
-rw-r--r--externals/g3dlite/G3D.lib/source/SplineBase.cpp162
-rw-r--r--externals/g3dlite/G3D.lib/source/Stopwatch.cpp96
-rw-r--r--externals/g3dlite/G3D.lib/source/System.cpp1864
-rw-r--r--externals/g3dlite/G3D.lib/source/TextInput.cpp988
-rw-r--r--externals/g3dlite/G3D.lib/source/TextOutput.cpp452
-rw-r--r--externals/g3dlite/G3D.lib/source/ThreadSet.cpp147
-rw-r--r--externals/g3dlite/G3D.lib/source/Triangle.cpp135
-rw-r--r--externals/g3dlite/G3D.lib/source/UprightFrame.cpp132
-rw-r--r--externals/g3dlite/G3D.lib/source/Vector2.cpp211
-rw-r--r--externals/g3dlite/G3D.lib/source/Vector2int16.cpp47
-rw-r--r--externals/g3dlite/G3D.lib/source/Vector3.cpp493
-rw-r--r--externals/g3dlite/G3D.lib/source/Vector3int16.cpp49
-rw-r--r--externals/g3dlite/G3D.lib/source/Vector3int32.cpp57
-rw-r--r--externals/g3dlite/G3D.lib/source/Vector4.cpp475
-rw-r--r--externals/g3dlite/G3D.lib/source/Vector4int8.cpp58
-rw-r--r--externals/g3dlite/G3D.lib/source/WinMain.cpp155
-rw-r--r--externals/g3dlite/G3D.lib/source/debugAssert.cpp392
-rw-r--r--externals/g3dlite/G3D.lib/source/fileutils.cpp1092
-rw-r--r--externals/g3dlite/G3D.lib/source/filter.cpp32
-rw-r--r--externals/g3dlite/G3D.lib/source/format.cpp164
-rw-r--r--externals/g3dlite/G3D.lib/source/g3dmath.cpp70
-rw-r--r--externals/g3dlite/G3D.lib/source/license.cpp70
-rw-r--r--externals/g3dlite/G3D.lib/source/prompt.cpp716
-rw-r--r--externals/g3dlite/G3D.lib/source/stringutils.cpp231
-rw-r--r--externals/g3dlite/G3D.lib/source/uint128.cpp155
-rw-r--r--externals/g3dlite/delme0
-rw-r--r--externals/g3dlite/doc-files/changelog.dox1872
-rw-r--r--externals/g3dlite/doc-files/contributors.dox85
-rw-r--r--externals/g3dlite/doc-files/license.dox120
-rw-r--r--externals/g3dlite/win/VC90/g3dlite.vcproj463
-rw-r--r--externals/g3dlite/win/delme0
-rw-r--r--externals/g3dlite/win/g3dlite.sln25
-rw-r--r--externals/g3dlite/zip.lib/include/zip/ioapi.h75
-rw-r--r--externals/g3dlite/zip.lib/include/zip/unzip.h354
-rw-r--r--externals/g3dlite/zip.lib/include/zip/zip.h235
-rw-r--r--externals/g3dlite/zip.lib/source/crypt.h132
-rw-r--r--externals/g3dlite/zip.lib/source/ioapi.c177
-rw-r--r--externals/g3dlite/zip.lib/source/iowin32.c272
-rw-r--r--externals/g3dlite/zip.lib/source/iowin32.h23
-rw-r--r--externals/g3dlite/zip.lib/source/unzip.c1604
-rw-r--r--externals/g3dlite/zip.lib/source/zip.c1221
200 files changed, 68138 insertions, 0 deletions
diff --git a/externals/g3dlite/G3D.lib/include/G3D/AABSPTree.h b/externals/g3dlite/G3D.lib/include/G3D/AABSPTree.h
new file mode 100644
index 00000000000..1178fad93c3
--- /dev/null
+++ b/externals/g3dlite/G3D.lib/include/G3D/AABSPTree.h
@@ -0,0 +1,1609 @@
+/**
+ @file AABSPTree.h
+
+ @maintainer Morgan McGuire, matrix@graphics3d.com
+
+ @created 2004-01-11
+ @edited 2008-11-19
+
+ Copyright 2000-2008, Morgan McGuire.
+ All rights reserved.
+
+ */
+
+#ifndef G3D_KDTREE_H
+#define G3D_KDTREE_H
+
+#include "G3D/platform.h"
+#include "G3D/Array.h"
+#include "G3D/Table.h"
+#include "G3D/Vector2.h"
+#include "G3D/Vector3.h"
+#include "G3D/Vector4.h"
+#include "G3D/AABox.h"
+#include "G3D/Sphere.h"
+#include "G3D/Box.h"
+#include "G3D/Triangle.h"
+#include "G3D/Ray.h"
+#include "G3D/GCamera.h"
+#include "G3D/BinaryInput.h"
+#include "G3D/BinaryOutput.h"
+#include "G3D/CollisionDetection.h"
+#include "G3D/GCamera.h"
+#include "G3D/BoundsTrait.h"
+#include <algorithm>
+
+// If defined, in debug mode the tree is checked for consistency
+// as a way of detecting corruption due to implementation bugs
+// #define VERIFY_TREE
+
+template<> struct BoundsTrait<class G3D::Vector2> {
+ static void getBounds(const G3D::Vector2& v, G3D::AABox& out) { out = G3D::AABox(G3D::Vector3(v, 0)); }
+};
+
+template<> struct BoundsTrait<class G3D::Vector3> {
+ static void getBounds(const G3D::Vector3& v, G3D::AABox& out) { out = G3D::AABox(v); }
+};
+
+template<> struct BoundsTrait<class G3D::Vector4> {
+ static void getBounds(const G3D::Vector4& v, G3D::AABox& out) { out = G3D::AABox(v.xyz()); }
+};
+
+template<> struct BoundsTrait<class G3D::AABox> {
+ static void getBounds(const G3D::AABox& v, G3D::AABox& out) { out = v; }
+};
+
+template<> struct BoundsTrait<class G3D::Sphere> {
+ static void getBounds(const G3D::Sphere& s, G3D::AABox& out) { s.getBounds(out); }
+};
+
+template<> struct BoundsTrait<class G3D::Box> {
+ static void getBounds(const G3D::Box& b, G3D::AABox& out) { b.getBounds(out); }
+};
+
+template<> struct BoundsTrait<class G3D::Vector2*> {
+ static void getBounds(const G3D::Vector2*& v, G3D::AABox& out) { out = G3D::AABox(G3D::Vector3(*v, 0)); }
+};
+
+template<> struct BoundsTrait<class G3D::Vector3*> {
+ static void getBounds(const G3D::Vector3*& v, G3D::AABox& out) { out = G3D::AABox(*v); }
+};
+
+template<> struct BoundsTrait<class G3D::Vector4*> {
+ static void getBounds(const G3D::Vector4*& v, G3D::AABox& out) { out = G3D::AABox(v->xyz()); }
+};
+
+template<> struct BoundsTrait<class G3D::AABox*> {
+ static void getBounds(const G3D::AABox*& v, G3D::AABox& out) { out = *v; }
+};
+
+template<> struct BoundsTrait<class G3D::Sphere*> {
+ static void getBounds(const G3D::Sphere*& s, G3D::AABox& out) { s->getBounds(out); }
+};
+
+template<> struct BoundsTrait<class G3D::Box*> {
+ static void getBounds(const G3D::Box*& b, G3D::AABox& out) { b->getBounds(out); }
+};
+
+
+template<> struct BoundsTrait<class G3D::Triangle*> {
+ static void getBounds(const G3D::Triangle*& t, G3D::AABox& out) { t->getBounds(out); }
+};
+
+namespace G3D {
+ namespace _internal {
+
+ /**
+ Wraps a pointer value so that it can be treated as the instance itself;
+ convenient for inserting pointers into a Table but using the
+ object equality instead of pointer equality.
+ */
+ template<class Type>
+ class Indirector {
+ public:
+ Type* handle;
+
+ inline Indirector(Type* h) : handle(h) {}
+
+ inline Indirector() : handle(NULL) {}
+
+ /** Returns true iff the values referenced by the handles are equivalent. */
+ inline bool operator==(const Indirector& m) const {
+ return *handle == *(m.handle);
+ }
+
+ inline bool operator==(const Type& m) const {
+ return *handle == m;
+ }
+
+ inline size_t hashCode() const {
+ return handle->hashCode();
+ }
+ };
+ } // namespace internal
+} // namespace G3D
+
+template <class Handle> struct HashTrait<typename G3D::_internal::Indirector<Handle> > {
+ static size_t hashCode(const G3D::_internal::Indirector<Handle>& key) { return key.hashCode(); }
+};
+
+namespace G3D {
+
+/**
+ A set that supports spatial queries using a KD tree (axis-aligned
+ BSP tree) for speed.
+
+ KDTree allows you to quickly find objects in 3D that lie within
+ a box or along a ray. For large sets of objects it is much faster
+ than testing each object for a collision.
+
+ KDTree is as powerful as but more general than a Quad Tree, Oct
+ Tree, or regular KD tree that cycles through axes, but less general than an unconstrained BSP tree
+ (which is much slower to create).
+
+ Internally, objects
+ are arranged into a tree according to their
+ axis-aligned bounds. This increases the cost of insertion to
+ O(log n) but allows fast overlap queries.
+
+ <B>Template Parameters</B>
+ <DT>The template parameter <I>T</I> must be one for which
+ the following functions are all overloaded:
+
+ <pre>
+ T::T();</CODE> <I>(public constructor of no arguments)</I>
+ template <> struct HashTrait<T> { static size_t hashCode(int key); };
+ template<> struct BoundsTrait<T> { static void getBounds(const T& obj, G3D::AABox& out); };
+ </pre>
+
+ G3D provides these for common classes like G3D::Vector3 and G3D::Sphere.
+ If you use a custom class, or a pointer to a custom class, you will need
+ to define those functions.
+
+ <B>Moving %Set Members</B>
+ <DT>It is important that objects do not move without updating the
+ KDTree. If the axis-aligned bounds of an object are about
+ to change, KDTree::remove it before they change and
+ KDTree::insert it again afterward. For objects
+ where the hashCode and == operator are invariant with respect
+ to the 3D position,
+ you can use the KDTree::update method as a shortcut to
+ insert/remove an object in one step after it has moved.
+
+
+ Note: Do not mutate any value once it has been inserted into KDTree. Values
+ are copied interally. All KDTree iterators convert to pointers to constant
+ values to reinforce this.
+
+ If you want to mutate the objects you intend to store in a KDTree
+ simply insert <I>pointers</I> to your objects instead of the objects
+ themselves, and ensure that the above operations are defined. (And
+ actually, because values are copied, if your values are large you may
+ want to insert pointers anyway, to save space and make the balance
+ operation faster.)
+
+ <B>Dimensions</B>
+ Although designed as a 3D-data structure, you can use the KDTree
+ for data distributed along 2 or 1 axes by simply returning bounds
+ that are always zero along one or more dimensions.
+
+*/
+template< class T,
+ class BoundsFunc = BoundsTrait<T>,
+ class HashFunc = HashTrait<T>,
+ class EqualsFunc = EqualsTrait<T> >
+class KDTree {
+protected:
+#define TreeType KDTree<T, BoundsFunc, HashFunc, EqualsFunc>
+
+ /** Wrapper for a value that includes a cache of its bounds.
+ Except for the test value used in a set-query operation, there
+ is only ever one instance of the handle associated with any
+ value and the memberTable and Nodes maintain pointers to that
+ heap-allocated value.
+ */
+ class Handle {
+ public:
+ /** The bounds of each object are constrained to AABox::large */
+ AABox bounds;
+
+ /** Center of bounds. We cache this value to avoid recomputing it
+ during the median sort, and because MSVC 6 std::sort goes into
+ an infinite loop if we compute the midpoint on the fly (possibly
+ a floating point roundoff issue, where B<A and A<B both are true).*/
+ Vector3 center;
+
+ T value;
+
+ Handle() {}
+
+ inline Handle(const T& v) : value(v) {
+ BoundsFunc::getBounds(v, bounds);
+ bounds = bounds.intersect(AABox::large());
+ center = bounds.center();
+ }
+
+ inline bool operator==(const Handle& other) const {
+ return EqualsFunc::equals(value, other.value);
+ }
+
+ inline size_t hashCode() const {
+ return HashFunc::hashCode(value);
+ }
+ };
+
+ /** Returns the bounds of the sub array. Used by makeNode. */
+ static AABox computeBounds(
+ const Array<Handle*>& point,
+ int beginIndex,
+ int endIndex) {
+
+ Vector3 lo = Vector3::inf();
+ Vector3 hi = -lo;
+
+ debugAssertM(beginIndex <= endIndex, "No points");
+ for (int p = beginIndex; p <= endIndex; ++p) {
+ // This code is written with the vector min and max expanded
+ // because otherwise it compiles incorrectly with -O3 on
+ // gcc 3.4
+
+ const Vector3& pLo = point[p]->bounds.low();
+ const Vector3& pHi = point[p]->bounds.high();
+ for (int a = 0; a < 3; ++a) {
+ lo[a] = G3D::min(lo[a], pLo[a]);
+ hi[a] = G3D::max(hi[a], pHi[a]);
+ }
+ }
+
+ return AABox(lo, hi);
+ }
+
+ /** Compares centers */
+ class CenterComparator {
+ public:
+ Vector3::Axis sortAxis;
+
+ CenterComparator(Vector3::Axis a) : sortAxis(a) {}
+
+ inline int operator()(Handle* A, const Handle* B) const {
+ float a = A->center[sortAxis];
+ float b = B->center[sortAxis];
+
+ if (a < b) {
+ return 1;
+ } else if (a > b) {
+ return -1;
+ } else {
+ return 0;
+ }
+ }
+ };
+
+
+ /** Compares bounds for strict >, <, or overlap*/
+ class BoundsComparator {
+ public:
+ Vector3::Axis sortAxis;
+
+ BoundsComparator(Vector3::Axis a) : sortAxis(a) {}
+
+ inline int operator()(Handle* A, const Handle* B) const {
+ const AABox& a = A->bounds;
+ const AABox& b = B->bounds;
+
+ if (a.high()[sortAxis] < b.low()[sortAxis]) {
+ return 1;
+ } else if (a.low()[sortAxis] > b.high()[sortAxis]) {
+ return -1;
+ } else {
+ return 0;
+ }
+ }
+ };
+
+
+ /** Compares bounds to the sort location */
+ class Comparator {
+ public:
+ Vector3::Axis sortAxis;
+ float sortLocation;
+
+ Comparator(Vector3::Axis a, float l) : sortAxis(a), sortLocation(l) {}
+
+ inline int operator()(Handle* ignore, const Handle* handle) const {
+ const AABox& box = handle->bounds;
+ debugAssert(ignore == NULL);
+
+ if (box.high()[sortAxis] < sortLocation) {
+ // Box is strictly below the sort location
+ return -1;
+ } else if (box.low()[sortAxis] > sortLocation) {
+ // Box is strictly above the sort location
+ return 1;
+ } else {
+ // Box overlaps the sort location
+ return 0;
+ }
+ }
+ };
+
+ // Using System::malloc with this class provided no speed improvement.
+ class Node {
+ public:
+
+ /** Spatial bounds on all values at this node and its children, based purely on
+ the parent's splitting planes. May be infinite. */
+ AABox splitBounds;
+
+ Vector3::Axis splitAxis;
+
+ /** Location along the specified axis */
+ float splitLocation;
+
+ /** child[0] contains all values strictly
+ smaller than splitLocation along splitAxis.
+
+ child[1] contains all values strictly
+ larger.
+
+ Both may be NULL if there are not enough
+ values to bother recursing.
+ */
+ Node* child[2];
+
+ /** Array of values at this node (i.e., values
+ straddling the split plane + all values if
+ this is a leaf node).
+
+ This is an array of pointers because that minimizes
+ data movement during tree building, which accounts
+ for about 15% of the time cost of tree building.
+ */
+ Array<Handle*> valueArray;
+
+ /** For each object in the value array, a copy of its bounds.
+ Packing these into an array at the node level
+ instead putting them in the valueArray improves
+ cache coherence, which is about a 3x performance
+ increase when performing intersection computations.
+ */
+ Array<AABox> boundsArray;
+
+ /** Creates node with NULL children */
+ Node() {
+ splitAxis = Vector3::X_AXIS;
+ splitLocation = 0;
+ splitBounds = AABox(-Vector3::inf(), Vector3::inf());
+ for (int i = 0; i < 2; ++i) {
+ child[i] = NULL;
+ }
+ }
+
+ /**
+ Doesn't clone children.
+ */
+ Node(const Node& other) : valueArray(other.valueArray), boundsArray(other.boundsArray) {
+ splitAxis = other.splitAxis;
+ splitLocation = other.splitLocation;
+ splitBounds = other.splitBounds;
+ for (int i = 0; i < 2; ++i) {
+ child[i] = NULL;
+ }
+ }
+
+ /** Copies the specified subarray of pt into point, NULLs the children.
+ Assumes a second pass will set splitBounds. */
+ Node(const Array<Handle*>& pt) : valueArray(pt) {
+ splitAxis = Vector3::X_AXIS;
+ splitLocation = 0;
+ for (int i = 0; i < 2; ++i) {
+ child[i] = NULL;
+ }
+
+ boundsArray.resize(valueArray.size());
+ for (int i = 0; i < valueArray.size(); ++i) {
+ boundsArray[i] = valueArray[i]->bounds;
+ }
+ }
+
+ /** Deletes the children (but not the values) */
+ ~Node() {
+ for (int i = 0; i < 2; ++i) {
+ delete child[i];
+ }
+ }
+
+ /** Returns true if this node is a leaf (no children) */
+ inline bool isLeaf() const {
+ return (child[0] == NULL) && (child[1] == NULL);
+ }
+
+
+ /**
+ Recursively appends all handles and children's handles
+ to the array.
+ */
+ void getHandles(Array<Handle*>& handleArray) const {
+ handleArray.append(valueArray);
+ for (int i = 0; i < 2; ++i) {
+ if (child[i] != NULL) {
+ child[i]->getHandles(handleArray);
+ }
+ }
+ }
+
+ void verifyNode(const Vector3& lo, const Vector3& hi) {
+ // debugPrintf("Verifying: split %d @ %f [%f, %f, %f], [%f, %f, %f]\n",
+ // splitAxis, splitLocation, lo.x, lo.y, lo.z, hi.x, hi.y, hi.z);
+
+ debugAssertM(lo == splitBounds.low(),
+ format("lo = %s, splitBounds.lo = %s",
+ lo.toString().c_str(), splitBounds.low().toString().c_str()));
+ debugAssert(hi == splitBounds.high());
+
+ for (int i = 0; i < valueArray.length(); ++i) {
+ const AABox& b = valueArray[i]->bounds;
+ debugAssert(b == boundsArray[i]);
+
+ for(int axis = 0; axis < 3; ++axis) {
+ debugAssert(b.low()[axis] <= b.high()[axis]);
+ debugAssert(b.low()[axis] >= lo[axis]);
+ debugAssert(b.high()[axis] <= hi[axis]);
+ }
+ }
+
+ if (child[0] || child[1]) {
+ debugAssert(lo[splitAxis] < splitLocation);
+ debugAssert(hi[splitAxis] > splitLocation);
+ }
+
+ Vector3 newLo = lo;
+ newLo[splitAxis] = splitLocation;
+ Vector3 newHi = hi;
+ newHi[splitAxis] = splitLocation;
+
+ if (child[0] != NULL) {
+ child[0]->verifyNode(lo, newHi);
+ }
+
+ if (child[1] != NULL) {
+ child[1]->verifyNode(newLo, hi);
+ }
+ }
+
+
+ /**
+ Stores the locations of the splitting planes (the structure but not the content)
+ so that the tree can be quickly rebuilt from a previous configuration without
+ calling balance.
+ */
+ static void serializeStructure(const Node* n, BinaryOutput& bo) {
+ if (n == NULL) {
+ bo.writeUInt8(0);
+ } else {
+ bo.writeUInt8(1);
+ n->splitBounds.serialize(bo);
+ serialize(n->splitAxis, bo);
+ bo.writeFloat32(n->splitLocation);
+ for (int c = 0; c < 2; ++c) {
+ serializeStructure(n->child[c], bo);
+ }
+ }
+ }
+
+ /** Clears the member table */
+ static Node* deserializeStructure(BinaryInput& bi) {
+ if (bi.readUInt8() == 0) {
+ return NULL;
+ } else {
+ Node* n = new Node();
+ n->splitBounds.deserialize(bi);
+ deserialize(n->splitAxis, bi);
+ n->splitLocation = bi.readFloat32();
+ for (int c = 0; c < 2; ++c) {
+ n->child[c] = deserializeStructure(bi);
+ }
+ return n;
+ }
+ }
+
+ /** Returns the deepest node that completely contains bounds. */
+ Node* findDeepestContainingNode(const AABox& bounds) {
+
+ // See which side of the splitting plane the bounds are on
+ if (bounds.high()[splitAxis] < splitLocation) {
+ // Bounds are on the low side. Recurse into the child
+ // if it exists.
+ if (child[0] != NULL) {
+ return child[0]->findDeepestContainingNode(bounds);
+ }
+ } else if (bounds.low()[splitAxis] > splitLocation) {
+ // Bounds are on the high side, recurse into the child
+ // if it exists.
+ if (child[1] != NULL) {
+ return child[1]->findDeepestContainingNode(bounds);
+ }
+ }
+
+ // There was no containing child, so this node is the
+ // deepest containing node.
+ return this;
+ }
+
+
+ /** Appends all members that intersect the box.
+ If useSphere is true, members that pass the box test
+ face a second test against the sphere. */
+ void getIntersectingMembers(
+ const AABox& box,
+ const Sphere& sphere,
+ Array<T>& members,
+ bool useSphere) const {
+
+ // Test all values at this node
+ for (int v = 0; v < boundsArray.size(); ++v) {
+ const AABox& bounds = boundsArray[v];
+ if (bounds.intersects(box) &&
+ (! useSphere || bounds.intersects(sphere))) {
+ members.append(valueArray[v]->value);
+ }
+ }
+
+ // If the left child overlaps the box, recurse into it
+ if ((child[0] != NULL) && (box.low()[splitAxis] < splitLocation)) {
+ child[0]->getIntersectingMembers(box, sphere, members, useSphere);
+ }
+
+ // If the right child overlaps the box, recurse into it
+ if ((child[1] != NULL) && (box.high()[splitAxis] > splitLocation)) {
+ child[1]->getIntersectingMembers(box, sphere, members, useSphere);
+ }
+ }
+
+ /**
+ Recurse through the tree, assigning splitBounds fields.
+ */
+ void assignSplitBounds(const AABox& myBounds) {
+ splitBounds = myBounds;
+
+ AABox childBounds[2];
+ myBounds.split(splitAxis, splitLocation, childBounds[0], childBounds[1]);
+
+# if defined(G3D_DEBUG) && defined(VERIFY_TREE)
+ // Verify the split
+ for (int v = 0; v < boundsArray.size(); ++v) {
+ const AABox& bounds = boundsArray[v];
+ debugAssert(myBounds.contains(bounds));
+ }
+# endif
+
+ for (int c = 0; c < 2; ++c) {
+ if (child[c]) {
+ child[c]->assignSplitBounds(childBounds[c]);
+ }
+ }
+ }
+
+ /** Returns true if the ray intersects this node */
+ bool intersects(const Ray& ray, float distance) const {
+ // See if the ray will ever hit this node or its children
+ Vector3 location;
+ bool alreadyInsideBounds = false;
+ bool rayWillHitBounds =
+ CollisionDetection::collisionLocationForMovingPointFixedAABox(
+ ray.origin, ray.direction, splitBounds, location, alreadyInsideBounds);
+
+ bool canHitThisNode = (alreadyInsideBounds ||
+ (rayWillHitBounds && ((location - ray.origin).squaredLength() < square(distance))));
+
+ return canHitThisNode;
+ }
+
+ template<typename RayCallback>
+ void intersectRay(
+ const Ray& ray,
+ RayCallback& intersectCallback,
+ float& distance,
+ bool intersectCallbackIsFast) const {
+
+ if (! intersects(ray, distance)) {
+ // The ray doesn't hit this node, so it can't hit the children of the node.
+ return;
+ }
+
+ // Test for intersection against every object at this node.
+ for (int v = 0; v < valueArray.size(); ++v) {
+ bool canHitThisObject = true;
+
+ if (! intersectCallbackIsFast) {
+ // See if
+ Vector3 location;
+ const AABox& bounds = boundsArray[v];
+ bool alreadyInsideBounds = false;
+ bool rayWillHitBounds =
+ CollisionDetection::collisionLocationForMovingPointFixedAABox(
+ ray.origin, ray.direction, bounds, location, alreadyInsideBounds);
+
+ canHitThisObject = (alreadyInsideBounds ||
+ (rayWillHitBounds && ((location - ray.origin).squaredLength() < square(distance))));
+ }
+
+ if (canHitThisObject) {
+ // It is possible that this ray hits this object. Look for the intersection using the
+ // callback.
+ const T& value = valueArray[v]->value;
+ intersectCallback(ray, value, distance);
+ }
+ }
+
+ // There are three cases to consider next:
+ //
+ // 1. the ray can start on one side of the splitting plane and never enter the other,
+ // 2. the ray can start on one side and enter the other, and
+ // 3. the ray can travel exactly down the splitting plane
+
+ enum {NONE = -1};
+ int firstChild = NONE;
+ int secondChild = NONE;
+
+ if (ray.origin[splitAxis] < splitLocation) {
+
+ // The ray starts on the small side
+ firstChild = 0;
+
+ if (ray.direction[splitAxis] > 0) {
+ // The ray will eventually reach the other side
+ secondChild = 1;
+ }
+
+ } else if (ray.origin[splitAxis] > splitLocation) {
+
+ // The ray starts on the large side
+ firstChild = 1;
+
+ if (ray.direction[splitAxis] < 0) {
+ secondChild = 0;
+ }
+ } else {
+ // The ray starts on the splitting plane
+ if (ray.direction[splitAxis] < 0) {
+ // ...and goes to the small side
+ firstChild = 0;
+ } else if (ray.direction[splitAxis] > 0) {
+ // ...and goes to the large side
+ firstChild = 1;
+ }
+ }
+
+ // Test on the side closer to the ray origin.
+ if ((firstChild != NONE) && child[firstChild]) {
+ child[firstChild]->intersectRay(ray, intersectCallback, distance, intersectCallbackIsFast);
+ }
+
+ if (ray.direction[splitAxis] != 0) {
+ // See if there was an intersection before hitting the splitting plane.
+ // If so, there is no need to look on the far side and recursion terminates.
+ float distanceToSplittingPlane = (splitLocation - ray.origin[splitAxis]) / ray.direction[splitAxis];
+ if (distanceToSplittingPlane > distance) {
+ // We aren't going to hit anything else before hitting the splitting plane,
+ // so don't bother looking on the far side of the splitting plane at the other
+ // child.
+ return;
+ }
+ }
+
+ // Test on the side farther from the ray origin.
+ if ((secondChild != NONE) && child[secondChild]) {
+ child[secondChild]->intersectRay(ray, intersectCallback, distance, intersectCallbackIsFast);
+ }
+
+ }
+ };
+
+
+ /**
+ Recursively subdivides the subarray.
+
+ Clears the source array as soon as it is no longer needed.
+
+ Call assignSplitBounds() on the root node after making a tree.
+ */
+ Node* makeNode(
+ Array<Handle*>& source,
+ int valuesPerNode,
+ int numMeanSplits,
+ Array<Handle*>& temp) {
+
+ Node* node = NULL;
+
+ if (source.size() <= valuesPerNode) {
+ // Make a new leaf node
+ node = new Node(source);
+
+ // Set the pointers in the memberTable
+ for (int i = 0; i < source.size(); ++i) {
+ memberTable.set(Member(source[i]), node);
+ }
+ source.clear();
+
+ } else {
+ // Make a new internal node
+ node = new Node();
+
+ const AABox& bounds = computeBounds(source, 0, source.size() - 1);
+ const Vector3& extent = bounds.high() - bounds.low();
+
+ Vector3::Axis splitAxis = extent.primaryAxis();
+
+ float splitLocation;
+
+ // Arrays for holding the children
+ Array<Handle*> lt, gt;
+
+ if (numMeanSplits <= 0) {
+
+ source.medianPartition(lt, node->valueArray, gt, temp, CenterComparator(splitAxis));
+
+ // Choose the split location to be the center of whatever fell in the center
+ splitLocation = node->valueArray[0]->center[splitAxis];
+
+ // Some of the elements in the lt or gt array might really overlap the split location.
+ // Move them as needed.
+ for (int i = 0; i < lt.size(); ++i) {
+ const AABox& bounds = lt[i]->bounds;
+ if ((bounds.low()[splitAxis] <= splitLocation) && (bounds.high()[splitAxis] >= splitLocation)) {
+ node->valueArray.append(lt[i]);
+ // Remove this element and process the new one that
+ // is swapped in in its place.
+ lt.fastRemove(i); --i;
+ }
+ }
+
+ for (int i = 0; i < gt.size(); ++i) {
+ const AABox& bounds = gt[i]->bounds;
+ if ((bounds.low()[splitAxis] <= splitLocation) && (bounds.high()[splitAxis] >= splitLocation)) {
+ node->valueArray.append(gt[i]);
+ // Remove this element and process the new one that
+ // is swapped in in its place.
+ gt.fastRemove(i); --i;
+ }
+ }
+
+ if ((node->valueArray.size() > (source.size() / 2)) &&
+ (source.size() > 6)) {
+ // This was a bad partition; we ended up putting the splitting plane right in the middle of most of the
+ // objects. We could try to split on a different axis, or use a different partition (e.g., the extents mean,
+ // or geometric mean). This implementation falls back on the extents mean, since that case is already handled
+ // below.
+ numMeanSplits = 1;
+ }
+ }
+
+ // Note: numMeanSplits may have been increased by the code in the previous case above in order to
+ // force a re-partition.
+
+ if (numMeanSplits > 0) {
+ // Split along the mean
+ splitLocation =
+ bounds.high()[splitAxis] * 0.5f +
+ bounds.low()[splitAxis] * 0.5f;
+
+ debugAssertM(isFinite(splitLocation),
+ "Internal error: split location must be finite.");
+
+ source.partition(NULL, lt, node->valueArray, gt, Comparator(splitAxis, splitLocation));
+
+ // The Comparator ensures that elements are strictly on the correct side of the split
+ }
+
+
+# if defined(G3D_DEBUG) && defined(VERIFY_TREE)
+ debugAssert(lt.size() + node->valueArray.size() + gt.size() == source.size());
+ // Verify that all objects ended up on the correct side of the split.
+ // (i.e., make sure that the Array partition was correct)
+ for (int i = 0; i < lt.size(); ++i) {
+ const AABox& bounds = lt[i]->bounds;
+ debugAssert(bounds.high()[splitAxis] < splitLocation);
+ }
+
+ for (int i = 0; i < gt.size(); ++i) {
+ const AABox& bounds = gt[i]->bounds;
+ debugAssert(bounds.low()[splitAxis] > splitLocation);
+ }
+
+ for (int i = 0; i < node->valueArray.size(); ++i) {
+ const AABox& bounds = node->valueArray[i]->bounds;
+ debugAssert(bounds.high()[splitAxis] >= splitLocation);
+ debugAssert(bounds.low()[splitAxis] <= splitLocation);
+ }
+# endif
+
+ // The source array is no longer needed
+ source.clear();
+
+ node->splitAxis = splitAxis;
+ node->splitLocation = splitLocation;
+
+ // Update the bounds array and member table
+ node->boundsArray.resize(node->valueArray.size());
+ for (int i = 0; i < node->valueArray.size(); ++i) {
+ Handle* v = node->valueArray[i];
+ node->boundsArray[i] = v->bounds;
+ memberTable.set(Member(v), node);
+ }
+
+ if (lt.size() > 0) {
+ node->child[0] = makeNode(lt, valuesPerNode, numMeanSplits - 1, temp);
+ }
+
+ if (gt.size() > 0) {
+ node->child[1] = makeNode(gt, valuesPerNode, numMeanSplits - 1, temp);
+ }
+
+ }
+
+ return node;
+ }
+
+ /**
+ Recursively clone the passed in node tree, setting
+ pointers for members in the memberTable as appropriate.
+ called by the assignment operator.
+ */
+ Node* cloneTree(Node* src) {
+ Node* dst = new Node(*src);
+
+ // Make back pointers
+ for (int i = 0; i < dst->valueArray.size(); ++i) {
+ memberTable.set(Member(dst->valueArray[i]), dst);
+ }
+
+ // Clone children
+ for (int i = 0; i < 2; ++i) {
+ if (src->child[i] != NULL) {
+ dst->child[i] = cloneTree(src->child[i]);
+ }
+ }
+
+ return dst;
+ }
+
+ /**
+ Wrapper for a Handle; used to create a memberTable that acts like Table<Handle, Node*> but
+ stores only Handle* internally to avoid memory copies.
+ */
+ typedef _internal::Indirector<Handle> Member;
+
+ typedef Table<Member, Node*> MemberTable;
+
+ /** Maps members to the node containing them */
+ MemberTable memberTable;
+
+ Node* root;
+
+public:
+
+ /** To construct a balanced tree, insert the elements and then call
+ KDTree::balance(). */
+ KDTree() : root(NULL) {}
+
+
+ KDTree(const KDTree& src) : root(NULL) {
+ *this = src;
+ }
+
+
+ KDTree& operator=(const KDTree& src) {
+ delete root;
+ // Clone tree takes care of filling out the memberTable.
+ root = cloneTree(src.root);
+ return *this;
+ }
+
+
+ ~KDTree() {
+ clear();
+ }
+
+ /**
+ Throws out all elements of the set.
+ */
+ void clear() {
+ typedef typename Table<_internal::Indirector<Handle>, Node*>::Iterator It;
+
+ // Delete all handles stored in the member table
+ It cur = memberTable.begin();
+ It end = memberTable.end();
+ while (cur != end) {
+ delete cur->key.handle;
+ cur->key.handle = NULL;
+ ++cur;
+ }
+ memberTable.clear();
+
+ // Delete the tree structure itself
+ delete root;
+ root = NULL;
+ }
+
+ int size() const {
+ return memberTable.size();
+ }
+
+ /**
+ Inserts an object into the set if it is not
+ already present. O(log n) time. Does not
+ cause the tree to be balanced.
+ */
+ void insert(const T& value) {
+ if (contains(value)) {
+ // Already in the set
+ return;
+ }
+
+ Handle* h = new Handle(value);
+
+ if (root == NULL) {
+ // This is the first node; create a root node
+ root = new Node();
+ }
+
+ Node* node = root->findDeepestContainingNode(h->bounds);
+
+ // Insert into the node
+ node->valueArray.append(h);
+ node->boundsArray.append(h->bounds);
+
+ // Insert into the node table
+ Member m(h);
+ memberTable.set(m, node);
+ }
+
+ /** Inserts each elements in the array in turn. If the tree
+ begins empty (no structure and no elements), this is faster
+ than inserting each element in turn. You still need to balance
+ the tree at the end.*/
+ void insert(const Array<T>& valueArray) {
+ if (root == NULL) {
+ // Optimized case for an empty tree; don't bother
+ // searching or reallocating the root node's valueArray
+ // as we incrementally insert.
+ root = new Node();
+ root->valueArray.resize(valueArray.size());
+ root->boundsArray.resize(root->valueArray.size());
+ for (int i = 0; i < valueArray.size(); ++i) {
+ // Insert in opposite order so that we have the exact same
+ // data structure as if we inserted each (i.e., order is reversed
+ // from array).
+ Handle* h = new Handle(valueArray[i]);
+ int j = valueArray.size() - i - 1;
+ root->valueArray[j] = h;
+ root->boundsArray[j] = h->bounds;
+ memberTable.set(Member(h), root);
+ }
+
+ } else {
+ // Insert at appropriate tree depth.
+ for (int i = 0; i < valueArray.size(); ++i) {
+ insert(valueArray[i]);
+ }
+ }
+ }
+
+
+ /**
+ Returns true if this object is in the set, otherwise
+ returns false. O(1) time.
+ */
+ bool contains(const T& value) {
+ // Temporarily create a handle and member
+ Handle h(value);
+ return memberTable.containsKey(Member(&h));
+ }
+
+
+ /**
+ Removes an object from the set in O(1) time.
+ It is an error to remove members that are not already
+ present. May unbalance the tree.
+
+ Removing an element never causes a node (split plane) to be removed...
+ nodes are only changed when the tree is rebalanced. This behavior
+ is desirable because it allows the split planes to be serialized,
+ and then deserialized into an empty tree which can be repopulated.
+ */
+ void remove(const T& value) {
+ debugAssertM(contains(value),
+ "Tried to remove an element from a "
+ "KDTree that was not present");
+
+ // Get the list of elements at the node
+ Handle h(value);
+ Member m(&h);
+
+ Array<Handle*>& list = memberTable[m]->valueArray;
+
+ Handle* ptr = NULL;
+
+ // Find the element and remove it
+ for (int i = list.length() - 1; i >= 0; --i) {
+ if (list[i]->value == value) {
+ // This was the element. Grab the pointer so that
+ // we can delete it below
+ ptr = list[i];
+
+ // Remove the handle from the node
+ list.fastRemove(i);
+
+ // Remove the corresponding bounds
+ memberTable[m]->boundsArray.fastRemove(i);
+ break;
+ }
+ }
+
+ // Remove the member
+ memberTable.remove(m);
+
+ // Delete the handle data structure
+ delete ptr;
+ ptr = NULL;
+ }
+
+
+ /**
+ If the element is in the set, it is removed.
+ The element is then inserted.
+
+ This is useful when the == and hashCode methods
+ on <I>T</I> are independent of the bounds. In
+ that case, you may call update(v) to insert an
+ element for the first time and call update(v)
+ again every time it moves to keep the tree
+ up to date.
+ */
+ void update(const T& value) {
+ if (contains(value)) {
+ remove(value);
+ }
+ insert(value);
+ }
+
+
+ /**
+ Rebalances the tree (slow). Call when objects
+ have moved substantially from their original positions
+ (which unbalances the tree and causes the spatial
+ queries to be slow).
+
+ @param valuesPerNode Maximum number of elements to put at
+ a node.
+
+ @param numMeanSplits numMeanSplits = 0 gives a
+ fully axis aligned BSP-tree, where the balance operation attempts to balance
+ the tree so that every splitting plane has an equal number of left
+ and right children (i.e. it is a <B>median</B> split along that axis).
+ This tends to maximize average performance.
+
+ You can override this behavior by
+ setting a number of <B>mean</B> (average) splits. numMeanSplits = MAX_INT
+ creates a full oct-tree, which tends to optimize peak performance at the expense of
+ average performance. It tends to have better clustering behavior when
+ members are not uniformly distributed.
+ */
+ void balance(int valuesPerNode = 5, int numMeanSplits = 3) {
+ if (root == NULL) {
+ // Tree is empty
+ return;
+ }
+
+ // Get all handles and delete the old tree structure
+ Node* oldRoot = root;
+ for (int c = 0; c < 2; ++c) {
+ if (root->child[c] != NULL) {
+ root->child[c]->getHandles(root->valueArray);
+
+ // Delete the child; this will delete all structure below it
+ delete root->child[c];
+ root->child[c] = NULL;
+ }
+ }
+
+ Array<Handle*> temp;
+ // Make a new root. Work with a copy of the value array because
+ // makeNode clears the source array as it progresses
+ Array<Handle*> copy(oldRoot->valueArray);
+ root = makeNode(copy, valuesPerNode, numMeanSplits, temp);
+
+ // Throw away the old root node
+ delete oldRoot;
+ oldRoot = NULL;
+
+ // Walk the tree, assigning splitBounds. We start with unbounded
+ // space. This will override the current member table.
+ const AABox& LARGE = AABox::large();
+ root->assignSplitBounds(LARGE);
+
+# ifdef _DEBUG
+ {
+ // Ensure that the balanced tree is still correct
+ root->verifyNode(LARGE.low(), LARGE.high());
+ }
+# endif
+ }
+
+protected:
+
+ /**
+ @param parentMask The mask that this node returned from culledBy.
+ */
+ static void getIntersectingMembers(
+ const Array<Plane>& plane,
+ Array<T>& members,
+ Node* node,
+ uint32 parentMask) {
+
+ int dummy;
+
+ if (parentMask == 0) {
+ // None of these planes can cull anything
+ for (int v = node->valueArray.size() - 1; v >= 0; --v) {
+ members.append(node->valueArray[v]->value);
+ }
+
+ // Iterate through child nodes
+ for (int c = 0; c < 2; ++c) {
+ if (node->child[c]) {
+ getIntersectingMembers(plane, members, node->child[c], 0);
+ }
+ }
+ } else {
+
+ // Test values at this node against remaining planes
+ for (int v = node->boundsArray.size() - 1; v >= 0; --v) {
+ if (! node->boundsArray[v].culledBy(plane, dummy, parentMask)) {
+ members.append(node->valueArray[v]->value);
+ }
+ }
+
+ uint32 childMask = 0xFFFFFF;
+
+ // Iterate through child nodes
+ for (int c = 0; c < 2; ++c) {
+ if (node->child[c] &&
+ ! node->child[c]->splitBounds.culledBy(plane, dummy, parentMask, childMask)) {
+ // This node was not culled
+ getIntersectingMembers(plane, members, node->child[c], childMask);
+ }
+ }
+ }
+ }
+
+public:
+
+ /**
+ Returns all members inside the set of planes.
+ @param members The results are appended to this array.
+ */
+ void getIntersectingMembers(const Array<Plane>& plane, Array<T>& members) const {
+ if (root == NULL) {
+ return;
+ }
+
+ getIntersectingMembers(plane, members, root, 0xFFFFFF);
+ }
+
+ /**
+ Typically used to find all visible
+ objects inside the view frustum (see also GCamera::getClipPlanes)... i.e. all objects
+ <B>not<B> culled by frustum.
+
+ Example:
+ <PRE>
+ Array<Object*> visible;
+ tree.getIntersectingMembers(camera.frustum(), visible);
+ // ... Draw all objects in the visible array.
+ </PRE>
+ @param members The results are appended to this array.
+ */
+ void getIntersectingMembers(const GCamera::Frustum& frustum, Array<T>& members) const {
+ Array<Plane> plane;
+
+ for (int i = 0; i < frustum.faceArray.size(); ++i) {
+ plane.append(frustum.faceArray[i].plane);
+ }
+
+ getIntersectingMembers(plane, members);
+ }
+
+ /**
+ C++ STL style iterator variable. See beginBoxIntersection().
+ The iterator overloads the -> (dereference) operator, so this
+ acts like a pointer to the current member.
+ */
+ // This iterator turns Node::getIntersectingMembers into a
+ // coroutine. It first translates that method from recursive to
+ // stack based, then captures the system state (analogous to a Scheme
+ // continuation) after each element is appended to the member array,
+ // and allowing the computation to be restarted.
+ class BoxIntersectionIterator {
+ private:
+ friend class TreeType;
+
+ /** True if this is the "end" iterator instance */
+ bool isEnd;
+
+ /** The box that we're testing against. */
+ AABox box;
+
+ /** Node that we're currently looking at. Undefined if isEnd
+ is true. */
+ Node* node;
+
+ /** Nodes waiting to be processed */
+ // We could use backpointers within the tree and careful
+ // state management to avoid ever storing the stack-- but
+ // it is much easier this way and only inefficient if the
+ // caller uses post increment (which they shouldn't!).
+ Array<Node*> stack;
+
+ /** The next index of current->valueArray to return.
+ Undefined when isEnd is true.*/
+ int nextValueArrayIndex;
+
+ BoxIntersectionIterator() : isEnd(true) {}
+
+ BoxIntersectionIterator(const AABox& b, const Node* root) :
+ isEnd(root == NULL), box(b),
+ node(const_cast<Node*>(root)), nextValueArrayIndex(-1) {
+
+ // We intentionally start at the "-1" index of the current
+ // node so we can use the preincrement operator to move
+ // ourselves to element 0 instead of repeating all of the
+ // code from the preincrement method. Note that this might
+ // cause us to become the "end" instance.
+ ++(*this);
+ }
+
+ public:
+
+ inline bool operator!=(const BoxIntersectionIterator& other) const {
+ return ! (*this == other);
+ }
+
+ bool operator==(const BoxIntersectionIterator& other) const {
+ if (isEnd) {
+ return other.isEnd;
+ } else if (other.isEnd) {
+ return false;
+ } else {
+ // Two non-end iterators; see if they match. This is kind of
+ // silly; users shouldn't call == on iterators in general unless
+ // one of them is the end iterator.
+ if ((box != other.box) || (node != other.node) ||
+ (nextValueArrayIndex != other.nextValueArrayIndex) ||
+ (stack.length() != other.stack.length())) {
+ return false;
+ }
+
+ // See if the stacks are the same
+ for (int i = 0; i < stack.length(); ++i) {
+ if (stack[i] != other.stack[i]) {
+ return false;
+ }
+ }
+
+ // We failed to find a difference; they must be the same
+ return true;
+ }
+ }
+
+ /**
+ Pre increment.
+ */
+ BoxIntersectionIterator& operator++() {
+ ++nextValueArrayIndex;
+
+ bool foundIntersection = false;
+ while (! isEnd && ! foundIntersection) {
+
+ // Search for the next node if we've exhausted this one
+ while ((! isEnd) && (nextValueArrayIndex >= node->valueArray.length())) {
+ // If we entered this loop, then the iterator has exhausted the elements at
+ // node (possibly because it just switched to a child node with no members).
+ // This loop continues until it finds a node with members or reaches
+ // the end of the whole intersection search.
+
+ // If the right child overlaps the box, push it onto the stack for
+ // processing.
+ if ((node->child[1] != NULL) &&
+ (box.high()[node->splitAxis] > node->splitLocation)) {
+ stack.push(node->child[1]);
+ }
+
+ // If the left child overlaps the box, push it onto the stack for
+ // processing.
+ if ((node->child[0] != NULL) &&
+ (box.low()[node->splitAxis] < node->splitLocation)) {
+ stack.push(node->child[0]);
+ }
+
+ if (stack.length() > 0) {
+ // Go on to the next node (which may be either one of the ones we
+ // just pushed, or one from farther back the tree).
+ node = stack.pop();
+ nextValueArrayIndex = 0;
+ } else {
+ // That was the last node; we're done iterating
+ isEnd = true;
+ }
+ }
+
+ // Search for the next intersection at this node until we run out of children
+ while (! isEnd && ! foundIntersection && (nextValueArrayIndex < node->valueArray.length())) {
+ if (box.intersects(node->boundsArray[nextValueArrayIndex])) {
+ foundIntersection = true;
+ } else {
+ ++nextValueArrayIndex;
+ // If we exhaust this node, we'll loop around the master loop
+ // to find a new node.
+ }
+ }
+ }
+
+ return *this;
+ }
+
+ private:
+ /**
+ Post increment (much slower than preincrement!). Intentionally overloaded to preclude accidentally slow code.
+ */
+ BoxIntersectionIterator operator++(int);
+ /*{
+ BoxIntersectionIterator old = *this;
+ ++this;
+ return old;
+ }*/
+
+ public:
+
+ /** Overloaded dereference operator so the iterator can masquerade as a pointer
+ to a member */
+ const T& operator*() const {
+ alwaysAssertM(! isEnd, "Can't dereference the end element of an iterator");
+ return node->valueArray[nextValueArrayIndex]->value;
+ }
+
+ /** Overloaded dereference operator so the iterator can masquerade as a pointer
+ to a member */
+ T const * operator->() const {
+ alwaysAssertM(! isEnd, "Can't dereference the end element of an iterator");
+ return &(stack.last()->valueArray[nextValueArrayIndex]->value);
+ }
+
+ /** Overloaded cast operator so the iterator can masquerade as a pointer
+ to a member */
+ operator T*() const {
+ alwaysAssertM(! isEnd, "Can't dereference the end element of an iterator");
+ return &(stack.last()->valueArray[nextValueArrayIndex]->value);
+ }
+ };
+
+
+ /**
+ Iterates through the members that intersect the box
+ */
+ BoxIntersectionIterator beginBoxIntersection(const AABox& box) const {
+ return BoxIntersectionIterator(box, root);
+ }
+
+ BoxIntersectionIterator endBoxIntersection() const {
+ // The "end" iterator instance
+ return BoxIntersectionIterator();
+ }
+
+ /**
+ Appends all members whose bounds intersect the box.
+ See also KDTree::beginBoxIntersection.
+ */
+ void getIntersectingMembers(const AABox& box, Array<T>& members) const {
+ if (root == NULL) {
+ return;
+ }
+ root->getIntersectingMembers(box, Sphere(Vector3::zero(), 0), members, false);
+ }
+
+
+ /**
+ Invoke a callback for every member along a ray until the closest intersection is found.
+
+ @param callback either a function or an instance of a class with an overloaded operator() of the form:
+
+ <code>void callback(const Ray& ray, const T& object, float& distance)</code>. If the ray hits the object
+ before travelling distance <code>distance</code>, updates <code>distance</code> with the new distance to
+ the intersection, otherwise leaves it unmodified. A common example is:
+
+ <pre>
+ class Entity {
+ public:
+
+ void intersect(const Ray& ray, float& maxDist, Vector3& outLocation, Vector3& outNormal) {
+ float d = maxDist;
+
+ // ... search for intersection distance d
+
+ if ((d > 0) && (d < maxDist)) {
+ // Intersection occured
+ maxDist = d;
+ outLocation = ...;
+ outNormal = ...;
+ }
+ }
+ };
+
+ // Finds the surface normal and location of the first intersection with the scene
+ class Intersection {
+ public:
+ Entity* closestEntity;
+ Vector3 hitLocation;
+ Vector3 hitNormal;
+
+ void operator()(const Ray& ray, const Entity* entity, float& distance) {
+ entity->intersect(ray, distance, hitLocation, hitNormal);
+ }
+ };
+
+ KDTree<Entity*> scene;
+
+ Intersection intersection;
+ float distance = inf();
+ scene.intersectRay(camera.worldRay(x, y), intersection, distance);
+ </pre>
+
+
+ @param distance When the method is invoked, this is the maximum distance that the tree should search for an intersection.
+ On return, this is set to the distance to the first intersection encountered.
+
+ @param intersectCallbackIsFast If false, each object's bounds are tested before the intersectCallback is invoked.
+ If the intersect callback runs at the same speed or faster than AABox-ray intersection, set this to true.
+ */
+ template<typename RayCallback>
+ void intersectRay(
+ const Ray& ray,
+ RayCallback& intersectCallback,
+ float& distance,
+ bool intersectCallbackIsFast = false) const {
+
+ root->intersectRay(ray, intersectCallback, distance, intersectCallbackIsFast);
+
+ }
+
+
+ /**
+ @brief Finds all members whose bounding boxes intersect the sphere. The actual
+ elements may not intersect the sphere.
+
+ @param members The results are appended to this array.
+ */
+ void getIntersectingMembers(const Sphere& sphere, Array<T>& members) const {
+ if (root == NULL) {
+ return;
+ }
+
+ AABox box;
+ sphere.getBounds(box);
+ root->getIntersectingMembers(box, sphere, members, true);
+
+ }
+
+ /**
+ Stores the locations of the splitting planes (the structure but not the content)
+ so that the tree can be quickly rebuilt from a previous configuration without
+ calling balance.
+ */
+ void serializeStructure(BinaryOutput& bo) const {
+ Node::serializeStructure(root, bo);
+ }
+
+ /** Clears the member table */
+ void deserializeStructure(BinaryInput& bi) {
+ clear();
+ root = Node::deserializeStructure(bi);
+ }
+
+ /**
+ Returns an array of all members of the set. See also KDTree::begin.
+ */
+ void getMembers(Array<T>& members) const {
+ Array<Member> temp;
+ memberTable.getKeys(temp);
+ for (int i = 0; i < temp.size(); ++i) {
+ members.append(*(temp.handle));
+ }
+ }
+
+
+ /**
+ C++ STL style iterator variable. See begin().
+ Overloads the -> (dereference) operator, so this acts like a pointer
+ to the current member.
+ */
+ class Iterator {
+ private:
+ friend class TreeType;
+
+ // Note: this is a Table iterator, we are currently defining
+ // Set iterator
+ typename Table<Member, Node*>::Iterator it;
+
+ Iterator(const typename Table<Member, Node*>::Iterator& it) : it(it) {}
+
+ public:
+
+ inline bool operator!=(const Iterator& other) const {
+ return !(*this == other);
+ }
+
+ bool operator==(const Iterator& other) const {
+ return it == other.it;
+ }
+
+ /**
+ Pre increment.
+ */
+ Iterator& operator++() {
+ ++it;
+ return *this;
+ }
+
+ private:
+ /**
+ Post increment (slower than preincrement). Intentionally unimplemented to prevent slow code.
+ */
+ Iterator operator++(int);/* {
+ Iterator old = *this;
+ ++(*this);
+ return old;
+ }*/
+ public:
+
+ const T& operator*() const {
+ return it->key.handle->value;
+ }
+
+ T* operator->() const {
+ return &(it->key.handle->value);
+ }
+
+ operator T*() const {
+ return &(it->key.handle->value);
+ }
+ };
+
+
+ /**
+ C++ STL style iterator method. Returns the first member.
+ Use preincrement (++entry) to get to the next element (iteration
+ order is arbitrary).
+ Do not modify the set while iterating.
+ */
+ Iterator begin() const {
+ return Iterator(memberTable.begin());
+ }
+
+
+ /**
+ C++ STL style iterator method. Returns one after the last iterator
+ element.
+ */
+ Iterator end() const {
+ return Iterator(memberTable.end());
+ }
+#undef TreeType
+};
+
+/** @deprecated For backwards compatibility */
+#define AABSPTree KDTree
+
+}
+
+#endif
diff --git a/externals/g3dlite/G3D.lib/include/G3D/AABox.h b/externals/g3dlite/G3D.lib/include/G3D/AABox.h
new file mode 100644
index 00000000000..76c5d6d5195
--- /dev/null
+++ b/externals/g3dlite/G3D.lib/include/G3D/AABox.h
@@ -0,0 +1,281 @@
+/**
+ @file AABox.h
+
+ Axis-aligned box class
+
+ @maintainer Morgan McGuire, matrix@graphics3d.com
+
+ @created 2004-01-10
+ @edited 2006-02-10
+
+ Copyright 2000-2006, Morgan McGuire.
+ All rights reserved.
+ */
+
+#ifndef G3D_AABOX_H
+#define G3D_AABOX_H
+
+#include "G3D/platform.h"
+#include "G3D/Vector3.h"
+#include "G3D/debug.h"
+#include "G3D/Array.h"
+#include "G3D/Plane.h"
+
+namespace G3D {
+
+/**
+ An axis-aligned box.
+ */
+class AABox {
+private:
+
+ /** Optional argument placeholder */
+ static int dummy;
+
+ Vector3 lo;
+ Vector3 hi;
+
+public:
+
+ /** Does not initialize the fields */
+ inline AABox() {}
+
+ /**
+ Constructs a zero-area AABox at v.
+ */
+ inline explicit AABox(const Vector3& v) {
+ lo = hi = v;
+ }
+
+ /** Assumes that low is less than or equal to high along each dimension.
+ To have this automatically enforced, use
+ <code>AABox(low.min(high), low.max(high));</code>
+ */
+ inline AABox(const Vector3& low, const Vector3& high) {
+ set(low, high);
+ }
+
+ /** Assumes that low is less than or equal to high along each dimension.
+ */
+ inline void set(const Vector3& low, const Vector3& high) {
+ debugAssert(
+ (low.x <= high.x) &&
+ (low.y <= high.y) &&
+ (low.z <= high.z));
+ lo = low;
+ hi = high;
+ }
+
+ /**
+ Grows to include the bounds of a
+ */
+ inline void merge(const AABox& a) {
+ lo = lo.min(a.lo);
+ hi = hi.max(a.hi);
+ }
+
+ inline void merge(const Vector3& a) {
+ lo = lo.min(a);
+ hi = hi.max(a);
+ }
+
+ void serialize(class BinaryOutput& b) const;
+
+ void deserialize(class BinaryInput& b);
+
+ inline const Vector3& low() const {
+ return lo;
+ }
+
+ inline const Vector3& high() const {
+ return hi;
+ }
+
+ /**
+ The largest possible finite box.
+ */
+ static inline const AABox& maxFinite() {
+ static const AABox b = AABox(Vector3::minFinite(),
+ Vector3::maxFinite());
+ return b;
+ }
+
+ /** A large finite box. This is smaller than FLT_MAX
+ because it leaves room to add boxes together. */
+ static inline const AABox& large() {
+ static const AABox b = AABox(Vector3::minFinite() * 0.5f,
+ Vector3::maxFinite() * 0.5f);
+ return b;
+ }
+
+ static inline const AABox& inf() {
+ static const AABox b = AABox(-Vector3::inf(), Vector3::inf());
+ return b;
+ }
+
+ static inline const AABox& zero() {
+ static const AABox b = AABox(Vector3::zero(), Vector3::zero());
+ return b;
+ }
+
+ /**
+ Returns the centroid of the box.
+ */
+ inline Vector3 center() const {
+ return (lo + hi) * 0.5;
+ }
+
+ Vector3 corner(int index) const;
+
+ /**
+ Distance from corner(0) to the next corner along axis a.
+ */
+ inline float extent(int a) const {
+ debugAssert(a < 3);
+ return hi[a] - lo[a];
+ }
+
+
+ inline Vector3 extent() const {
+ return hi - lo;
+ }
+
+
+ /**
+ Splits the box into two AABoxes along the specified axis. low contains
+ the part that was closer to negative infinity along axis, high contains
+ the other part. Either may have zero volume.
+ */
+ void split(const Vector3::Axis& axis, float location, AABox& low, AABox& high) const;
+
+ /**
+ Conservative culling test for up to 32 planes.
+ Returns true if there exists a <CODE>plane[p]</CODE> for
+ which the entire object is in the negative half space
+ (opposite the plane normal).
+
+ <CODE>testMask</CODE> and <CODE>childMask</CODE>
+ are used for optimizing bounding volume hierarchies.
+ The version of this method that produces childMask
+ is slower than the version without; it should only
+ be used for parent nodes.
+
+ @param cullingPlaneIndex The index of the first plane for which
+ the entire object is in the negative half-space. The function
+ exits early when one plane is found. -1 when the function
+ returns false (i.e. when no plane culls the whole object).
+
+ @param testMask If bit <I>p</I> is 0, the
+ bounding volume automatically passes the culling test for
+ <CODE>plane[p]</CODE> (i.e. it is known that the volume
+ is entirely within the positive half space). The function
+ must return false if testMask is 0 and test all planes
+ when testMask is -1 (0xFFFFFFFF).
+
+ @param childMask Test mask for the children of this volume.
+
+ */
+ bool culledBy(
+ const Array<Plane>& plane,
+ int32& cullingPlaneIndex,
+ const uint32 testMask,
+ uint32& childMask) const;
+
+ /**
+ Conservative culling test that does not produce a mask for children.
+ */
+ bool culledBy(
+ const Array<Plane>& plane,
+ int32& cullingPlaneIndex = dummy,
+ const uint32 testMask = 0xFFFFFFFF) const;
+
+ /** less than or equal to containment */
+ inline bool contains(const AABox& other) const {
+ return
+ (other.hi.x <= hi.x) &&
+ (other.hi.y <= hi.y) &&
+ (other.hi.z <= hi.z) &&
+ (other.lo.x >= lo.x) &&
+ (other.lo.y >= lo.y) &&
+ (other.lo.z >= lo.z);
+ }
+
+ inline bool contains(
+ const Vector3& point) const {
+ return
+ (point.x >= lo.x) &&
+ (point.y >= lo.y) &&
+ (point.z >= lo.z) &&
+ (point.x <= hi.x) &&
+ (point.y <= hi.y) &&
+ (point.z <= hi.z);
+ }
+
+ inline float area() const {
+ Vector3 diag = hi - lo;
+ return 2.0f * (diag.x * diag.y + diag.y * diag.z + diag.x * diag.z);
+ }
+
+ inline float volume() const {
+ Vector3 diag = hi - lo;
+ return diag.x * diag.y * diag.z;
+ }
+
+ Vector3 randomInteriorPoint() const;
+
+ Vector3 randomSurfacePoint() const;
+
+ /** Returns true if there is any overlap */
+ bool intersects(const AABox& other) const;
+
+ /** Returns true if there is any overlap.
+ @cite Jim Arvo's algorithm from Graphics Gems II*/
+ bool intersects(const class Sphere& other) const;
+
+ /** Return the intersection of the two boxes */
+ AABox intersect(const AABox& other) const {
+ Vector3 H = hi.min(other.hi);
+ Vector3 L = lo.max(other.lo).min(H);
+ return AABox(L, H);
+ }
+
+ inline size_t hashCode() const {
+ return lo.hashCode() + hi.hashCode();
+ }
+
+ inline bool operator==(const AABox& b) const {
+ return (lo == b.lo) && (hi == b.hi);
+ }
+
+ inline bool operator!=(const AABox& b) const {
+ return !((lo == b.lo) && (hi == b.hi));
+ }
+
+ inline AABox operator+(const Vector3& v) const {
+ AABox out;
+ out.lo = lo + v;
+ out.hi = hi + v;
+ return out;
+ }
+
+ inline AABox operator-(const Vector3& v) const {
+ AABox out;
+ out.lo = lo - v;
+ out.hi = hi - v;
+ return out;
+ }
+
+ void getBounds(AABox& out) const {
+ out = *this;
+ }
+};
+
+}
+
+template <> struct HashTrait<G3D::AABox> {
+ static size_t hashCode(const G3D::AABox& key) { return key.hashCode(); }
+};
+
+
+
+#endif
diff --git a/externals/g3dlite/G3D.lib/include/G3D/AnyVal.h b/externals/g3dlite/G3D.lib/include/G3D/AnyVal.h
new file mode 100644
index 00000000000..8254dd73c93
--- /dev/null
+++ b/externals/g3dlite/G3D.lib/include/G3D/AnyVal.h
@@ -0,0 +1,506 @@
+/**
+ @file AnyVal.h
+ @author Morgan McGuire
+ @created 2006-06-11
+ @edited 2008-07-14
+ */
+
+#ifndef G3D_ANYVAL_H
+#define G3D_ANYVAL_H
+
+#include "G3D/platform.h"
+#include <string>
+#include "G3D/Array.h"
+#include "G3D/TextInput.h"
+
+namespace G3D {
+// Forward declarations for G3D types
+class Vector2;
+class Vector3;
+class Vector4;
+class Color1;
+class Color3;
+class Color4;
+class Quat;
+class Matrix2;
+class Matrix3;
+class Matrix4;
+class CoordinateFrame;
+class TextInput;
+class TextOutput;
+class BinaryInput;
+class BinaryOutput;
+class Rect2D;
+class AABox;
+
+/**
+ A generic value, useful for defining property trees that can
+ be loaded from and saved to disk. The values are intentionally
+ restricted to a small set.
+
+ When written to files, the syntax is as follows. Note that you can
+ nest arrays and tables in order to create full tree (i.e., XML-like)
+ structures as configuration files:
+
+ <table>
+ <tr><td>NULL</td><td><code>Nil</code></td></tr>
+ <tr><td>double</td><td><i>The number in printf double format</i></td></tr>
+ <tr><td>bool</td><td><code>true</code> <i>or</i> <code>false</code></td></tr>
+ <tr><td>std::string</td><td><i>The string in double-quotes (</i><code>"</code><i>)</i></td></tr>
+ <tr><td>Rect2D</td><td><code>R(</code><i>x<sub>0</sub></i><code>,</code><i>y<sub>0</sub></i><code>,</code><i>x<sub>1</sub></i><code>,</code><i>y<sub>1</sub></i><code>)</code></td></tr>
+ <tr><td>Color1</td><td><code>C1(</code><i>value</i><code>)</code></td></tr>
+ <tr><td>Color3</td><td><code>C3(</code><i>r</i><code>,</code><i>g</i><code>,</code><i>b</i><code>)</code></td></tr>
+ <tr><td>Color4</td><td><code>C4(</code><i>r</i><code>,</code><i>g</i><code>,</code><i>b</i><code>,</code><i>a</i><code>)</code></td></tr>
+ <tr><td>Vector2</td><td><code>V2(</code><i>x</i><code>,</code><i>y</i><code>)</code></td></tr>
+ <tr><td>Vector3</td><td><code>V3(</code><i>x</i><code>,</code><i>y</i><code>,</code><i>z</i><code>)</code></td></tr>
+ <tr><td>Vector4</td><td><code>V4(</code><i>x</i><code>,</code><i>y</i><code>,</code><i>z</i><code>,</code><i>w</i><code>)</code></td></tr>
+ <tr><td>Quat</td><td><code>V(</code>x<code>,</code>y<code>,</code>z<code>,</code>w<code>)</code></td></tr>
+ <tr><td>AABox</td><td><code>AAB(</code>low Vector3<code>, </code>high Vector3<code>)</code></td></tr>
+ <tr><td>Matrix2</td><td><code>M2(</code>r0c0<code>, </code>r0c1<code>,
+ <br>&nbsp;&nbsp;&nbsp;</code>r1c0<code>, </code>r1c1<code>)</code></td></tr>
+ <tr><td>Matrix3</td><td><code>M3(</code>r0c0<code>, </code>r0c1<code>, </code>r0c2<code>,
+ <br>&nbsp;&nbsp;&nbsp;</code>r1c0<code>, </code>r1c1<code>, </code>r1c2<code>,
+ <br>&nbsp;&nbsp;&nbsp;</code>r2c0<code>, </code>r2c1<code>, </code>r2c2<code>)</code></td></tr>
+ <tr><td>Matrix4</td><td><code>M4(</code>r0c0<code>, </code>r0c1<code>, </code>r0c2<code>, </code>r0c3<code>,
+ <br>&nbsp;&nbsp;&nbsp;</code>r1c0<code>, </code>r1c1<code>, </code>r1c2<code>, </code>r1c3<code>,
+ <br>&nbsp;&nbsp;&nbsp;</code>r2c0<code>, </code>r2c1<code>, </code>r2c2<code>, </code>r2c3<code>,
+ <br>&nbsp;&nbsp;&nbsp;</code>r3c0<code>, </code>r3c1<code>, </code>r3c2<code>, </code>r3c3<code>)</code></td></tr>
+ <tr><td>CoordinateFrame</td><td><code>CF(</code>r0c0<code>, </code>r0c1<code>, </code>r0c2<code>, </code>r0c3<code>,
+ <br>&nbsp;&nbsp;&nbsp;</code>r1c0<code>, </code>r1c1<code>, </code>r1c2<code>, </code>r1c3<code>,
+ <br>&nbsp;&nbsp;&nbsp;</code>r2c0<code>, </code>r2c1<code>, </code>r2c2<code>, </code>r2c3<code>)</code></td></tr>
+ <tr><td>CoordinateFrame</td><td><code>CF(V3(</code><i>x</i><code>, </code><i>y</i><code>, </code><i>z</i><code>), </code><i>yaw deg</i><code>, </code><i>pitch deg</i><code>, </code><i>optional roll deg</i><code>)</code></td></tr>
+
+ <tr><td>Array</td><td><code>[</code><i>element<sub>0</sub></i><code>, </code><i>element<sub>1</sub></i><code>, </code> ... <code>, </code><i>element<sub>n-1</sub></i><code>]</code></td></tr>
+ <tr><td>Table</td><td><code>{</code><i>symbol<sub>0</sub></i><code> = </code><i>value<sub>0</sub></i>
+ <br><code>&nbsp;</code><i>symbol<sub>1</sub></i><code> = </code><i>value<sub>1</sub></i>
+ <br><code>&nbsp;</code>...
+ <br><code>&nbsp;</code><i>symbol<sub>n-1</sub></i><code> = </code><i>value<sub>n-1</sub></i><code>}</code></td></tr>
+ </table>
+
+ See also boost::any for a more general purpose but slightly harder to use
+ "any" for C++.
+
+ The semantics of operator[] and the get() methods are slightly different;
+ operator[] acts more like a scripting language that automatically extends
+ arrays and tables instead of generating errors. get() has more strict semantics,
+ like a C++ class.
+
+ AnyVal uses copy-on-mutate, so that <code>AnyVal a = b</code> semantically copies <code>b</code> (like <code>int a = b</code> would), although in practice
+ it delays the copy until one is mutated so that it is still fast to "copy" large arrays and tables.
+
+ Reading example:
+ <pre>
+ AnyVal property = AnyVal::fromFile("c:/tmp/test.txt"));
+
+ Vector3 vel = property["angular velocity"]
+
+ <i>Using defaults to handle errors:
+ If there was no "enabled" value, this will return the default instead of failing</i>
+ bool enabled = property["enabled"].boolean(true);
+
+ </pre>
+
+ Writing to a file:
+ <pre>
+ AnyVal dict(AnyVal::TABLE);
+
+ dict["enabled"] = AnyVal(true);
+ dict["weight"] = 100;
+ dict["angular velocity"] = Vector3(1, -3, 4.5);
+
+ TextOutput t("c:/tmp/test.txt");
+ dict.serialize(t);
+ t.commit();
+ </pre>
+
+ Example of a data file:
+ <pre>
+ {
+ heights = [1, 17, 32]
+ model =
+ {
+ color = C3(1, 1, 1)
+ filename = "foo.md2"
+ }
+ position = V3(23, 14, 0)
+ name = "Elmer"
+ }
+ </pre>
+
+ <p>
+ <b>What's the difference from boost::any?</b>
+ <br>I think that AnyVal will be easier for novice C++ users. It addresses the problem that
+ even though G3D::TextInput makes reading configuration files extremely simple, many people
+ still don't use it. So AnyVal makes it ridiculously simple to read and write a tree of G3D
+ types to a file.
+
+ <i>AnyVal:</i>
+<pre>
+{
+AnyVal tree(TextInput("config.txt"));
+
+bool enabled = tree.get("enabled", false);
+Vector3 direction = tree.get("direction", Vector3::zero());
+...
+}
+</pre>
+
+<i>boost:</i>
+<pre>
+{
+bool enabled = false;
+Vector3 direction;
+Table<boost::any> tree;
+
+ ...write lots of file parsing code...
+
+ if (tree.containsKey("enabled")) {
+ const boost::any& val = tree["enabled"];
+ try {
+ enabled = any_cast<bool>(val);
+ } catch(const boost::bad_any_cast &) {
+ }
+ }
+
+ if (tree.containsKey("direction")) {
+ const boost::any& val = tree["direction"];
+ try {
+ direction = any_cast<Vector3>(val);
+ } catch(const boost::bad_any_cast &) {
+ }
+ }
+ ...
+}
+</pre>
+ */
+class AnyVal {
+public:
+
+ /** Array and table values are all Any.*/
+ enum Type {
+ NIL,
+ NUMBER,
+ BOOLEAN,
+ STRING,
+ VECTOR2,
+ VECTOR3,
+ VECTOR4,
+ MATRIX2,
+ MATRIX3,
+ MATRIX4,
+ QUAT,
+ COORDINATEFRAME,
+ COORDINATEFRAME2D,
+ CFRAME = COORDINATEFRAME,
+ CFRAME2D = COORDINATEFRAME2D,
+ COLOR1,
+ COLOR3,
+ COLOR4,
+ RECT2D,
+ AABOX2D = RECT2D,
+ AABOX,
+ ARRAY,
+ TABLE};
+
+ /** Base class for all AnyVal exceptions.*/
+ class Exception {
+ public:
+ virtual ~Exception() {}
+ };
+
+ /** Thrown when an inappropriate operation is performed (e.g., operator[] on a number) */
+ class WrongType : public Exception {
+ public:
+ Type expected;
+ Type actual;
+ WrongType() : expected(NIL), actual(NIL) {}
+ WrongType(Type e, Type a) : expected(e), actual(a) {}
+ };
+
+ /** Thrown by operator[] when a key is not present. */
+ class KeyNotFound : public Exception {
+ public:
+ std::string key;
+ KeyNotFound() {}
+ KeyNotFound(const std::string& k) : key(k) {}
+ };
+
+ class IndexOutOfBounds : public Exception {
+ public:
+ int index;
+ int size;
+ IndexOutOfBounds() : index(0), size(0) {}
+ IndexOutOfBounds(int i, int s) : index(i), size(s) {}
+ };
+
+ /** Thrown when deserialize() when the input is incorrectly formatted. */
+ class CorruptText : public Exception {
+ public:
+ std::string message;
+
+ /** Token where the problem occurred.*/
+ G3D::Token token;
+
+ CorruptText() {}
+ CorruptText(const std::string& s, const G3D::Token& t) : message(s), token(t) {}
+ };
+
+private:
+
+ Type m_type;
+ void* m_value;
+
+ /** For table and array types, *m_value is shared between multiple
+ instances. Mutation is allowed only if the reference count is
+ exactly 1, otherwise the mutating instance must copy the
+ value. This is not used for other types.
+ */
+ int* m_referenceCount;
+
+ /** Decrements the reference count (if there is one). If the
+ reference count is zero or does not exist. Calls delete on @a
+ m_value and sets it to NULL.
+ */
+ void deleteValue();
+
+ /** Returns a copy of the value. */
+ void* copyValue() const;
+
+ /** Assumes isSharedType. Ensures that this has a unique reference */
+ void makeMutable();
+
+ /** True if this is a shared value between multiple instances. */
+ inline bool isShared() const {
+ return m_referenceCount && (*m_referenceCount > 1);
+ }
+
+ /** True when m_value is a double pointer */
+ inline bool isSharedType() const {
+ return (m_type == TABLE) || (m_type == ARRAY);
+ }
+
+public:
+
+ AnyVal();
+
+ /** Deserialize */
+ explicit AnyVal(G3D::TextInput& t);
+
+ static AnyVal fromFile(const std::string& filename);
+
+ void load(const std::string& filename);
+
+ void save(const std::string& filename) const;
+
+ ///** Deserialize */
+ //explicit AnyVal(G3D::BinaryInput& t);
+
+ /** Construct a number */
+ AnyVal(double);
+ AnyVal(int);
+
+ // Explicit to avoid ambiguity with the 'double' constructor
+ // when an integer type is constructed
+ AnyVal(bool);
+ AnyVal(const G3D::Vector2&);
+ AnyVal(const G3D::Vector3&);
+ AnyVal(const G3D::Vector4&);
+
+ AnyVal(const G3D::Color1&);
+ AnyVal(const G3D::Color3&);
+ AnyVal(const G3D::Color4&);
+
+ AnyVal(const std::string&);
+ AnyVal(const char*);
+
+ AnyVal(const G3D::Quat&);
+
+ AnyVal(const G3D::Rect2D&);
+ AnyVal(const G3D::AABox&);
+
+ AnyVal(const G3D::CoordinateFrame&);
+ AnyVal(const G3D::Matrix2&);
+ AnyVal(const G3D::Matrix3&);
+ AnyVal(const G3D::Matrix4&);
+
+ AnyVal(const AnyVal&);
+
+ AnyVal(Type arrayOrTable);
+
+ AnyVal& operator=(const AnyVal&);
+
+ /** Frees the underlying storage */
+ ~AnyVal();
+
+ Type type() const;
+
+ bool isNil() const {
+ return type() == NIL;
+ }
+
+ void serialize(G3D::TextOutput& t) const;
+ //void serialize(G3D::BinaryOutput& t) const;
+ void deserialize(G3D::TextInput& t);
+ //void deserialize(G3D::BinaryInput& t);
+
+ /** Array dereference. If the index is out of bounds, IndexOutOfBounds is thrown */
+ const AnyVal& operator[](int) const;
+
+ /** Extend this array by one element. */
+ void append(const AnyVal&);
+
+ /** If the index is out of bounds, the array is resized. If the index is negative,
+ IndexOutOfBounds is thrown.*/
+ AnyVal& operator[](int);
+
+ /** If @a i is out of bounds or this is not an ARRAY, defaultVal is returned.*/
+ const AnyVal& get(int i, const AnyVal& defaultVal) const;
+
+ /** If out of bounds, IndexOutOfBounds is thrown. */
+ const AnyVal& get(int i) const;
+
+ /** Returns defaultVal if this is not a TABLE or the key is not found. */
+ const AnyVal& get(const std::string& key, const AnyVal& defaultVal) const;
+
+ /** Throws KeyNotFound exception if the key is not present.*/
+ const AnyVal& get(const std::string& key) const;
+
+ /** Table reference */
+ const AnyVal& operator[](const std::string&) const;
+
+ /** Table reference. If the element does not exist, it is created. */
+ AnyVal& operator[](const std::string&);
+
+ /** Table reference */
+ const AnyVal& operator[](const char*) const;
+
+ /** Table reference. If the element does not exist, it is created. */
+ AnyVal& operator[](const char*);
+
+ /** If this value is not a number throws a WrongType exception. */
+ double number() const;
+
+ /** If this value is not a number, returns defaultVal. */
+ double number(double defaultVal) const;
+
+ operator double () const {
+ return number();
+ }
+
+ operator float () const {
+ return (float)number();
+ }
+
+ bool boolean() const;
+ bool boolean(bool b) const;
+
+ operator bool() const {
+ return boolean();
+ }
+
+ const std::string& string() const;
+ const std::string& string(const std::string& defaultVal) const;
+
+ operator const std::string& () const {
+ return string();
+ }
+
+ const G3D::Rect2D& rect2D() const;
+ const G3D::Rect2D& rect2D(const G3D::Rect2D& defaultVal) const;
+
+ operator const Rect2D& () const {
+ return rect2D();
+ }
+
+ const G3D::AABox& aabox() const;
+ const G3D::AABox& aabox(const G3D::AABox& defaultVal) const;
+
+ operator const AABox& () const {
+ return aabox();
+ }
+
+ const G3D::Vector2& vector2() const;
+ const G3D::Vector2& vector2(const G3D::Vector2& defaultVal) const;
+
+ operator const Vector2& () const {
+ return vector2();
+ }
+
+ const G3D::Vector3& vector3() const;
+ const G3D::Vector3& vector3(const G3D::Vector3& defaultVal) const;
+
+ operator const Vector3& () {
+ return vector3();
+ }
+
+ const G3D::Vector4& vector4() const;
+ const G3D::Vector4& vector4(const G3D::Vector4& defaultVal) const;
+
+ operator const Vector4& () const {
+ return vector4();
+ }
+
+ const G3D::Color1& color1() const;
+ const G3D::Color1& color1(const G3D::Color1& defaultVal) const;
+
+ const G3D::Color3& color3() const;
+ const G3D::Color3& color3(const G3D::Color3& defaultVal) const;
+
+ operator const Color3& () const {
+ return color3();
+ }
+
+ const G3D::Color4& color4() const;
+ const G3D::Color4& color4(const G3D::Color4& defaultVal) const;
+
+ operator const Color4& () const {
+ return color4();
+ }
+
+ const G3D::CoordinateFrame& coordinateFrame() const;
+ const G3D::CoordinateFrame& coordinateFrame(const G3D::CoordinateFrame& defaultVal) const;
+
+ operator const CoordinateFrame& () const {
+ return coordinateFrame();
+ }
+
+ const G3D::Matrix2& matrix2() const;
+ const G3D::Matrix2& matrix2(const G3D::Matrix2& defaultVal) const;
+
+ operator const Matrix2& () const {
+ return matrix2();
+ }
+
+ const G3D::Matrix3& matrix3() const;
+ const G3D::Matrix3& matrix3(const G3D::Matrix3& defaultVal) const;
+
+ operator const Matrix3& () const {
+ return matrix3();
+ }
+
+ const G3D::Matrix4& matrix4() const;
+ const G3D::Matrix4& matrix4(const G3D::Matrix4& defaultVal) const;
+
+ operator const Matrix4& () const {
+ return matrix4();
+ }
+
+ const G3D::Quat& quat() const;
+ const G3D::Quat& quat(const G3D::Quat& defaultVal) const;
+
+ operator const Quat& () const {
+ return quat();
+ }
+
+ std::string toString() const;
+
+ /** Number of elements for an array or table.*/
+ int size() const;
+
+ /** For a table, returns the keys. */
+ void getKeys(G3D::Array<std::string>&) const;
+};
+
+}
+
+#endif
diff --git a/externals/g3dlite/G3D.lib/include/G3D/Array.h b/externals/g3dlite/G3D.lib/include/G3D/Array.h
new file mode 100644
index 00000000000..ae4eea8ab40
--- /dev/null
+++ b/externals/g3dlite/G3D.lib/include/G3D/Array.h
@@ -0,0 +1,1180 @@
+/**
+ @file Array.h
+
+ @maintainer Morgan McGuire, graphics3d.com
+ @cite Portions written by Aaron Orenstein, a@orenstein.name
+
+ @created 2001-03-11
+ @edited 2008-07-09
+
+ Copyright 2000-2008, Morgan McGuire, morgan@cs.williams.edu
+ All rights reserved.
+ */
+
+#ifndef G3D_ARRAY_H
+#define G3D_ARRAY_H
+
+#include "G3D/platform.h"
+#include "G3D/debug.h"
+#include "G3D/System.h"
+#ifdef G3D_DEBUG
+// For formatting error messages
+# include "G3D/format.h"
+#endif
+#include <vector>
+#include <algorithm>
+
+#ifdef _MSC_VER
+# include <new>
+
+# pragma warning (push)
+ // debug information too long
+# pragma warning( disable : 4312)
+# pragma warning( disable : 4786)
+#endif
+
+
+namespace G3D {
+
+/**
+ Constant for passing to Array::resize
+ */
+const bool DONT_SHRINK_UNDERLYING_ARRAY = false;
+
+/** Constant for Array::sort */
+const int SORT_INCREASING = 1;
+/** Constant for Array::sort */
+const int SORT_DECREASING = -1;
+
+/**
+ Dynamic 1D array.
+
+ Objects must have a default constructor (constructor that
+ takes no arguments) in order to be used with this template.
+ You will get the error "no appropriate default constructor found"
+ if they do not.
+
+ Do not use with objects that overload placement <code>operator new</code>,
+ since the speed of Array is partly due to pooled allocation.
+
+ If SSE is defined Arrays allocate the first element aligned to
+ 16 bytes.
+
+
+ Array is highly optimized compared to std::vector.
+ Array operations are less expensive than on std::vector and for large
+ amounts of data, Array consumes only 1.5x the total size of the
+ data, while std::vector consumes 2.0x. The default
+ array takes up zero heap space. The first resize (or append)
+ operation grows it to a reasonable internal size so it is efficient
+ to append to small arrays. Memory is allocated using
+ System::alignedMalloc, which produces pointers aligned to 16-byte
+ boundaries for use with SSE instructions and uses pooled storage for
+ fast allocation. When Array needs to copy
+ data internally on a resize operation it correctly invokes copy
+ constructors of the elements (the MSVC6 implementation of
+ std::vector uses realloc, which can create memory leaks for classes
+ containing references and pointers). Array provides a guaranteed
+ safe way to access the underlying data as a flat C array --
+ Array::getCArray. Although (T*)std::vector::begin() can be used for
+ this purpose, it is not guaranteed to succeed on all platforms.
+
+ To serialize an array, see G3D::serialize.
+
+ The template parameter MIN_ELEMENTS indicates the smallest number of
+ elements that will be allocated. The default of 10 is designed to avoid
+ the overhead of repeatedly allocating the array as it grows from 1, to 2, and so on.
+ If you are creating a lot of small Arrays, however, you may want to set this smaller
+ to reduce the memory cost. Once the array has been allocated, it will never
+ deallocate the underlying array unless MIN_ELEMENTS is set to 0, MIN_BYTES is 0, and the array
+ is empty.
+
+ Do not subclass an Array.
+ */
+template <class T, int MIN_ELEMENTS = 10, size_t MIN_BYTES = 32>
+class Array {
+private:
+ /** 0...num-1 are initialized elements, num...numAllocated-1 are not */
+ T* data;
+
+ int num;
+ int numAllocated;
+
+ void init(int n, int a) {
+ debugAssert(n <= a);
+ debugAssert(n >= 0);
+ this->num = 0;
+ this->numAllocated = 0;
+ data = NULL;
+ if (a > 0) {
+ resize(n);
+ } else {
+ data = NULL;
+ }
+ }
+
+ void _copy(const Array &other) {
+ init(other.num, other.num);
+ for (int i = 0; i < num; i++) {
+ data[i] = other.data[i];
+ }
+ }
+
+ /**
+ Returns true iff address points to an element of this array.
+ Used by append.
+ */
+ inline bool inArray(const T* address) {
+ return (address >= data) && (address < data + num);
+ }
+
+
+ /** Only compiled if you use the sort procedure. */
+ static bool __cdecl compareGT(const T& a, const T& b) {
+ return a > b;
+ }
+
+
+ /**
+ Allocates a new array of size numAllocated (not a parameter to the method)
+ and then copies at most oldNum elements from the old array to it. Destructors are
+ called for oldNum elements of the old array.
+ */
+ void realloc(int oldNum) {
+ T* oldData = data;
+
+ // The allocation is separate from the constructor invocation because we don't want
+ // to pay for the cost of constructors until the newly allocated
+ // elements are actually revealed to the application. They
+ // will be constructed in the resize() method.
+
+ data = (T*)System::alignedMalloc(sizeof(T) * numAllocated, 16);
+
+ // Call the copy constructors
+ {const int N = G3D::min(oldNum, numAllocated);
+ const T* end = data + N;
+ T* oldPtr = oldData;
+ for (T* ptr = data; ptr < end; ++ptr, ++oldPtr) {
+
+ // Use placement new to invoke the constructor at the location
+ // that we determined. Use the copy constructor to make the assignment.
+ const T* constructed = new (ptr) T(*oldPtr);
+
+ (void)constructed;
+ debugAssertM(constructed == ptr,
+ "new returned a different address than the one provided by Array.");
+ }}
+
+ // Call destructors on the old array (if there is no destructor, this will compile away)
+ {const T* end = oldData + oldNum;
+ for (T* ptr = oldData; ptr < end; ++ptr) {
+ ptr->~T();
+ }}
+
+
+ System::alignedFree(oldData);
+ }
+
+public:
+
+ /**
+ G3D C++ STL style iterator variable. Call begin() to get
+ the first iterator, pre-increment (++i) the iterator to get to
+ the next value. Use dereference (*i) to access the element.
+ */
+ typedef T* Iterator;
+ /** G3D C++ STL style const iterator in same style as Iterator. */
+ typedef const T* ConstIterator;
+
+ /** stl porting compatibility helper */
+ typedef Iterator iterator;
+ /** stl porting compatibility helper */
+ typedef ConstIterator const_iterator;
+ /** stl porting compatibility helper */
+ typedef T value_type;
+ /** stl porting compatibility helper */
+ typedef int size_type;
+ /** stl porting compatibility helper */
+ typedef int difference_type;
+
+ /**
+ C++ STL style iterator method. Returns the first iterator element.
+ Do not change the size of the array while iterating.
+ */
+ Iterator begin() {
+ return data;
+ }
+
+ ConstIterator begin() const {
+ return data;
+ }
+ /**
+ C++ STL style iterator method. Returns one after the last iterator
+ element.
+ */
+ ConstIterator end() const {
+ return data + num;
+ }
+
+ Iterator end() {
+ return data + num;
+ }
+
+ /**
+ The array returned is only valid until the next append() or resize call, or
+ the Array is deallocated.
+ */
+ T* getCArray() {
+ return data;
+ }
+
+ /**
+ The array returned is only valid until the next append() or resize call, or
+ the Array is deallocated.
+ */
+ const T* getCArray() const {
+ return data;
+ }
+
+ /** Creates a zero length array (no heap allocation occurs until resize). */
+ Array() {
+ init(0, 0);
+ }
+
+ /**
+ Creates an array of size.
+ */
+ Array(int size) {
+ init(size, size);
+ }
+
+ /**
+ Copy constructor
+ */
+ Array(const Array& other) {
+ _copy(other);
+ }
+
+ /**
+ Destructor does not delete() the objects if T is a pointer type
+ (e.g. T = int*) instead, it deletes the <B>pointers themselves</B> and
+ leaves the objects. Call deleteAll if you want to dealocate
+ the objects referenced. Do not call deleteAll if <CODE>T</CODE> is not a pointer
+ type (e.g. do call Array<Foo*>::deleteAll, do <B>not</B> call Array<Foo>::deleteAll).
+ */
+ ~Array() {
+ // Invoke the destructors on the elements
+ for (int i = 0; i < num; i++) {
+ (data + i)->~T();
+ }
+
+ System::alignedFree(data);
+ // Set to 0 in case this Array is global and gets referenced during app exit
+ data = NULL;
+ num = 0;
+ numAllocated = 0;
+ }
+
+
+ /**
+ Removes all elements. Use resize(0, false) or fastClear if you want to
+ remove all elements without deallocating the underlying array
+ so that future append() calls will be faster.
+ */
+ void clear(bool shrink = true) {
+ resize(0, shrink);
+ }
+
+ /** resize(0, false)
+ @deprecated*/
+ void fastClear() {
+ clear(false);
+ }
+
+ /**
+ Assignment operator.
+ */
+ Array& operator=(const Array& other) {
+ resize(other.num);
+ for (int i = 0; i < num; ++i) {
+ data[i] = other[i];
+ }
+ return *this;
+ }
+
+ Array& operator=(const std::vector<T>& other) {
+ resize((int)other.size());
+ for (int i = 0; i < num; ++i) {
+ data[i] = other[i];
+ }
+ return *this;
+ }
+
+ /**
+ Number of elements in the array.
+ */
+ inline int size() const {
+ return num;
+ }
+
+ /**
+ Number of elements in the array. (Same as size; this is just
+ here for convenience).
+ */
+ inline int length() const {
+ return size();
+ }
+
+ /**
+ Swaps element index with the last element in the array then
+ shrinks the array by one.
+ */
+ void fastRemove(int index, bool shrinkIfNecessary = false) {
+ debugAssert(index >= 0);
+ debugAssert(index < num);
+ data[index] = data[num - 1];
+ resize(size() - 1, shrinkIfNecessary);
+ }
+
+ /** Resizes without shrinking the underlying array. Same as resize(n, false).
+ @deprecated*/
+ void fastResize(int n) {
+ resize(n, false);
+ }
+
+
+ /**
+ Inserts at the specified index and shifts all other elements up by one.
+ */
+ void insert(int n, const T& value) {
+ // Add space for the extra element
+ resize(num + 1, false);
+
+ for (int i = num - 1; i > n; --i) {
+ data[i] = data[i - 1];
+ }
+ data[n] = value;
+ }
+
+ /** @param shrinkIfNecessary if false, memory will never be
+ reallocated when the array shrinks. This makes resizing much
+ faster but can waste memory. */
+ void resize(int n, bool shrinkIfNecessary = true) {
+ int oldNum = num;
+ num = n;
+
+ // Call the destructors on newly hidden elements if there are any
+ for (int i = num; i < oldNum; ++i) {
+ (data + i)->~T();
+ }
+
+ // Once allocated, always maintain MIN_ELEMENTS elements or 32 bytes, whichever is higher.
+ static const int minSize = G3D::max(MIN_ELEMENTS, (int)(MIN_BYTES / sizeof(T)));
+
+ if ((MIN_ELEMENTS == 0) && (MIN_BYTES == 0) && (n == 0) && shrinkIfNecessary) {
+ // Deallocate the array completely
+ numAllocated = 0;
+ System::alignedFree(data);
+ data = NULL;
+ return;
+ }
+
+ if (num > numAllocated) {
+ // Grow the underlying array
+
+ if (numAllocated == 0) {
+ // First allocation; grow to exactly the size requested to avoid wasting space.
+ numAllocated = n;
+ debugAssert(oldNum == 0);
+ realloc(oldNum);
+ } else {
+
+ if (num < minSize) {
+ // Grow to at least the minimum size
+ numAllocated = minSize;
+
+ } else {
+
+ // Increase the underlying size of the array. Grow aggressively
+ // up to 64k, less aggressively up to 400k, and then grow relatively
+ // slowly (1.5x per resize) to avoid excessive space consumption.
+ //
+ // These numbers are tweaked according to performance tests.
+
+ float growFactor = 3.0;
+
+ size_t oldSizeBytes = numAllocated * sizeof(T);
+ if (oldSizeBytes > 400000) {
+ // Avoid bloat
+ growFactor = 1.5;
+ } else if (oldSizeBytes > 64000) {
+ // This is what std:: uses at all times
+ growFactor = 2.0;
+ }
+
+ numAllocated = (num - numAllocated) + (int)(numAllocated * growFactor);
+
+ if (numAllocated < minSize) {
+ numAllocated = minSize;
+ }
+ }
+
+ realloc(oldNum);
+ }
+
+ } else if ((num <= numAllocated / 3) && shrinkIfNecessary && (num > minSize)) {
+ // Shrink the underlying array
+
+ // Only copy over old elements that still remain after resizing
+ // (destructors were called for others if we're shrinking)
+ realloc(iMin(num, oldNum));
+
+ }
+
+ // Call the constructors on newly revealed elements.
+ // Do not use parens because we don't want the intializer
+ // invoked for POD types.
+ for (int i = oldNum; i < num; ++i) {
+ new (data + i) T;
+ }
+ }
+
+ /**
+ Add an element to the end of the array. Will not shrink the underlying array
+ under any circumstances. It is safe to append an element that is already
+ in the array.
+ */
+ inline void append(const T& value) {
+
+ if (num < numAllocated) {
+ // This is a simple situation; just stick it in the next free slot using
+ // the copy constructor.
+ new (data + num) T(value);
+ ++num;
+ } else if (inArray(&value)) {
+ // The value was in the original array; resizing
+ // is dangerous because it may move the value
+ // we have a reference to.
+ T tmp = value;
+ append(tmp);
+ } else {
+ // Here we run the empty initializer where we don't have to, but
+ // this simplifies the computation.
+ resize(num + 1, DONT_SHRINK_UNDERLYING_ARRAY);
+ data[num - 1] = value;
+ }
+ }
+
+
+ inline void append(const T& v1, const T& v2) {
+ if (inArray(&v1) || inArray(&v2)) {
+ // Copy into temporaries so that the references won't break when
+ // the array resizes.
+ T t1 = v1;
+ T t2 = v2;
+ append(t1, t2);
+ } else if (num + 1 < numAllocated) {
+ // This is a simple situation; just stick it in the next free slot using
+ // the copy constructor.
+ new (data + num) T(v1);
+ new (data + num + 1) T(v2);
+ num += 2;
+ } else {
+ // Resize the array. Note that neither value is already in the array.
+ resize(num + 2, DONT_SHRINK_UNDERLYING_ARRAY);
+ data[num - 2] = v1;
+ data[num - 1] = v2;
+ }
+ }
+
+
+ inline void append(const T& v1, const T& v2, const T& v3) {
+ if (inArray(&v1) || inArray(&v2) || inArray(&v3)) {
+ T t1 = v1;
+ T t2 = v2;
+ T t3 = v3;
+ append(t1, t2, t3);
+ } else if (num + 2 < numAllocated) {
+ // This is a simple situation; just stick it in the next free slot using
+ // the copy constructor.
+ new (data + num) T(v1);
+ new (data + num + 1) T(v2);
+ new (data + num + 2) T(v3);
+ num += 3;
+ } else {
+ resize(num + 3, DONT_SHRINK_UNDERLYING_ARRAY);
+ data[num - 3] = v1;
+ data[num - 2] = v2;
+ data[num - 1] = v3;
+ }
+ }
+
+
+ inline void append(const T& v1, const T& v2, const T& v3, const T& v4) {
+ if (inArray(&v1) || inArray(&v2) || inArray(&v3) || inArray(&v4)) {
+ T t1 = v1;
+ T t2 = v2;
+ T t3 = v3;
+ T t4 = v4;
+ append(t1, t2, t3, t4);
+ } else if (num + 3 < numAllocated) {
+ // This is a simple situation; just stick it in the next free slot using
+ // the copy constructor.
+ new (data + num) T(v1);
+ new (data + num + 1) T(v2);
+ new (data + num + 2) T(v3);
+ new (data + num + 3) T(v4);
+ num += 4;
+ } else {
+ resize(num + 4, DONT_SHRINK_UNDERLYING_ARRAY);
+ data[num - 4] = v1;
+ data[num - 3] = v2;
+ data[num - 2] = v3;
+ data[num - 1] = v4;
+ }
+ }
+
+ /**
+ Returns true if the given element is in the array.
+ */
+ bool contains(const T& e) const {
+ for (int i = 0; i < size(); ++i) {
+ if ((*this)[i] == e) {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ /**
+ Append the elements of array. Cannot be called with this array
+ as an argument.
+ */
+ void append(const Array<T>& array) {
+ debugAssert(this != &array);
+ int oldNum = num;
+ int arrayLength = array.length();
+
+ resize(num + arrayLength, false);
+
+ for (int i = 0; i < arrayLength; i++) {
+ data[oldNum + i] = array.data[i];
+ }
+ }
+
+ /**
+ Pushes a new element onto the end and returns its address.
+ This is the same as A.resize(A.size() + 1, false); A.last()
+ */
+ inline T& next() {
+ resize(num + 1, false);
+ return last();
+ }
+
+ /**
+ Pushes an element onto the end (appends)
+ */
+ inline void push(const T& value) {
+ append(value);
+ }
+
+ inline void push(const Array<T>& array) {
+ append(array);
+ }
+
+ /** Alias to provide std::vector compatibility */
+ inline void push_back(const T& v) {
+ push(v);
+ }
+
+ /** "The member function removes the last element of the controlled sequence, which must be non-empty."
+ For compatibility with std::vector. */
+ inline void pop_back() {
+ pop();
+ }
+
+ /**
+ "The member function returns the storage currently allocated to hold the controlled
+ sequence, a value at least as large as size()"
+ For compatibility with std::vector.
+ */
+ int capacity() const {
+ return numAllocated;
+ }
+
+ /**
+ "The member function returns a reference to the first element of the controlled sequence,
+ which must be non-empty."
+ For compatibility with std::vector.
+ */
+ T& front() {
+ return (*this)[0];
+ }
+
+ /**
+ "The member function returns a reference to the first element of the controlled sequence,
+ which must be non-empty."
+ For compatibility with std::vector.
+ */
+ const T& front() const {
+ return (*this)[0];
+ }
+
+ /**
+ Removes the last element and returns it. By default, shrinks the underlying array.
+ */
+ inline T pop(bool shrinkUnderlyingArrayIfNecessary = true) {
+ debugAssert(num > 0);
+ T temp = data[num - 1];
+ resize(num - 1, shrinkUnderlyingArrayIfNecessary);
+ return temp;
+ }
+
+ /** Pops the last element and discards it without returning anything. Faster than pop.
+ By default, does not shrink the underlying array.*/
+ inline void popDiscard(bool shrinkUnderlyingArrayIfNecessary = false) {
+ debugAssert(num > 0);
+ resize(num - 1, shrinkUnderlyingArrayIfNecessary);
+ }
+
+
+ /**
+ "The member function swaps the controlled sequences between *this and str."
+ Note that this is slower than the optimal std implementation.
+
+ For compatibility with std::vector.
+ */
+ void swap(Array<T>& str) {
+ Array<T> temp = str;
+ str = *this;
+ *this = temp;
+ }
+
+
+ /**
+ Performs bounds checks in debug mode
+ */
+ inline T& operator[](int n) {
+ debugAssertM((n >= 0) && (n < num), format("Array index out of bounds. n = %d, size() = %d", n, num));
+ debugAssert(data!=NULL);
+ return data[n];
+ }
+
+ inline T& operator[](unsigned int n) {
+ debugAssertM(n < (unsigned int)num, format("Array index out of bounds. n = %d, size() = %d", n, num));
+ return data[n];
+ }
+
+ /**
+ Performs bounds checks in debug mode
+ */
+ inline const T& operator[](int n) const {
+ debugAssert((n >= 0) && (n < num));
+ debugAssert(data!=NULL);
+ return data[n];
+ }
+
+ inline const T& operator[](unsigned int n) const {
+ debugAssert((n < (unsigned int)num));
+ debugAssert(data!=NULL);
+ return data[n];
+ }
+
+ inline T& randomElement() {
+ debugAssert(num > 0);
+ debugAssert(data!=NULL);
+ return data[iRandom(0, num - 1)];
+ }
+
+ inline const T& randomElement() const {
+ debugAssert(num > 0);
+ debugAssert(data!=NULL);
+ return data[iRandom(0, num - 1)];
+ }
+
+ /**
+ Returns the last element, performing a check in
+ debug mode that there is at least one element.
+ */
+ inline const T& last() const {
+ debugAssert(num > 0);
+ debugAssert(data!=NULL);
+ return data[num - 1];
+ }
+
+ /** Returns element lastIndex() */
+ inline T& last() {
+ debugAssert(num > 0);
+ debugAssert(data!=NULL);
+ return data[num - 1];
+ }
+
+ /** Returns <i>size() - 1</i> */
+ inline int lastIndex() const {
+ debugAssertM(num > 0, "Array is empty");
+ return num - 1;
+ }
+
+ inline int firstIndex() const {
+ debugAssertM(num > 0, "Array is empty");
+ return 0;
+ }
+
+ /** Returns element firstIndex(), performing a check in debug mode to ensure that there is at least one */
+ inline T& first() {
+ debugAssertM(num > 0, "Array is empty");
+ return data[0];
+ }
+
+ inline const T& first() const {
+ debugAssertM(num > 0, "Array is empty");
+ return data[0];
+ }
+
+ /** Returns iFloor(size() / 2), throws an assertion in debug mode if the array is empty */
+ inline int middleIndex() const {
+ debugAssertM(num > 0, "Array is empty");
+ return num >> 1;
+ }
+
+ /** Returns element middleIndex() */
+ inline const T& middle() const {
+ debugAssertM(num > 0, "Array is empty");
+ return data[num >> 1];
+ }
+
+ /** Returns element middleIndex() */
+ inline T& middle() {
+ debugAssertM(num > 0, "Array is empty");
+ return data[num >> 1];
+ }
+
+ /**
+ Calls delete on all objects[0...size-1]
+ and sets the size to zero.
+ */
+ void deleteAll() {
+ for (int i = 0; i < num; i++) {
+ delete data[i];
+ }
+ resize(0);
+ }
+
+ /**
+ Returns the index of (the first occurance of) an index or -1 if
+ not found.
+ */
+ int findIndex(const T& value) const {
+ for (int i = 0; i < num; ++i) {
+ if (data[i] == value) {
+ return i;
+ }
+ }
+ return -1;
+ }
+
+ /**
+ Finds an element and returns the iterator to it. If the element
+ isn't found then returns end().
+ */
+ Iterator find(const T& value) {
+ for (int i = 0; i < num; ++i) {
+ if (data[i] == value) {
+ return data + i;
+ }
+ }
+ return end();
+ }
+
+ ConstIterator find(const T& value) const {
+ for (int i = 0; i < num; ++i) {
+ if (data[i] == value) {
+ return data + i;
+ }
+ }
+ return end();
+ }
+
+ /**
+ Removes count elements from the array
+ referenced either by index or Iterator.
+ */
+ void remove(Iterator element, int count = 1) {
+ debugAssert((element >= begin()) && (element < end()));
+ debugAssert((count > 0) && (element + count) <= end());
+ Iterator last = end() - count;
+
+ while(element < last) {
+ element[0] = element[count];
+ ++element;
+ }
+
+ resize(num - count);
+ }
+
+ void remove(int index, int count = 1) {
+ debugAssert((index >= 0) && (index < num));
+ debugAssert((count > 0) && (index + count <= num));
+
+ remove(begin() + index, count);
+ }
+
+ /**
+ Reverse the elements of the array in place.
+ */
+ void reverse() {
+ T temp;
+
+ int n2 = num / 2;
+ for (int i = 0; i < n2; ++i) {
+ temp = data[num - 1 - i];
+ data[num - 1 - i] = data[i];
+ data[i] = temp;
+ }
+ }
+
+ /**
+ Sort using a specific less-than function, e.g.:
+
+ <PRE>
+ bool __cdecl myLT(const MyClass& elem1, const MyClass& elem2) {
+ return elem1.x < elem2.x;
+ }
+ </PRE>
+
+ Note that for pointer arrays, the <CODE>const</CODE> must come
+ <I>after</I> the class name, e.g., <CODE>Array<MyClass*></CODE> uses:
+
+ <PRE>
+ bool __cdecl myLT(MyClass*const& elem1, MyClass*const& elem2) {
+ return elem1->x < elem2->x;
+ }
+ </PRE>
+ */
+ void sort(bool (__cdecl *lessThan)(const T& elem1, const T& elem2)) {
+ std::sort(data, data + num, lessThan);
+ }
+
+
+ /**
+ Sorts the array in increasing order using the > or < operator. To
+ invoke this method on Array<T>, T must override those operator.
+ You can overide these operators as follows:
+ <code>
+ bool T::operator>(const T& other) const {
+ return ...;
+ }
+ bool T::operator<(const T& other) const {
+ return ...;
+ }
+ </code>
+ */
+ void sort(int direction = SORT_INCREASING) {
+ if (direction == SORT_INCREASING) {
+ std::sort(data, data + num);
+ } else {
+ std::sort(data, data + num, compareGT);
+ }
+ }
+
+ /**
+ Sorts elements beginIndex through and including endIndex.
+ */
+ void sortSubArray(int beginIndex, int endIndex, int direction = SORT_INCREASING) {
+ if (direction == SORT_INCREASING) {
+ std::sort(data + beginIndex, data + endIndex + 1);
+ } else {
+ std::sort(data + beginIndex, data + endIndex + 1, compareGT);
+ }
+ }
+
+ void sortSubArray(int beginIndex, int endIndex, bool (__cdecl *lessThan)(const T& elem1, const T& elem2)) {
+ std::sort(data + beginIndex, data + endIndex + 1, lessThan);
+ }
+
+ /**
+ The StrictWeakOrdering can be either a class that overloads the function call operator() or
+ a function pointer of the form <code>bool (__cdecl *lessThan)(const T& elem1, const T& elem2)</code>
+ */
+ template<typename StrictWeakOrdering>
+ void sortSubArray(int beginIndex, int endIndex, StrictWeakOrdering& lessThan) {
+ std::sort(data + beginIndex, data + endIndex + 1, lessThan);
+ }
+
+ /** Uses < and == to evaluate operator(); this is the default comparator for Array::partition. */
+ class DefaultComparator {
+ public:
+ inline int operator()(const T& A, const T& B) const {
+ if (A < B) {
+ return 1;
+ } else if (A == B) {
+ return 0;
+ } else {
+ return -1;
+ }
+ }
+ };
+
+ /** The output arrays are resized with fastClear() so that if they are already of the same size
+ as this array no memory is allocated during partitioning.
+
+ @param comparator A function, or class instance with an overloaded operator() that compares
+ two elements of type <code>T</code> and returns 0 if they are equal, -1 if the second is smaller,
+ and 1 if the first is smaller (i.e., following the conventions of std::string::compare). For example:
+
+ <pre>
+ int compare(int A, int B) {
+ if (A < B) {
+ return 1;
+ } else if (A == B) {
+ return 0;
+ } else {
+ return -1;
+ }
+ }
+ </pre>
+ */
+ template<typename Comparator>
+ void partition(
+ const T& partitionElement,
+ Array<T>& ltArray,
+ Array<T>& eqArray,
+ Array<T>& gtArray,
+ const Comparator& comparator) const {
+
+ // Make sure all arrays are independent
+ debugAssert(&ltArray != this);
+ debugAssert(&eqArray != this);
+ debugAssert(&gtArray != this);
+ debugAssert(&ltArray != &eqArray);
+ debugAssert(&ltArray != &gtArray);
+ debugAssert(&eqArray != &gtArray);
+
+ // Clear the arrays
+ ltArray.fastClear();
+ eqArray.fastClear();
+ gtArray.fastClear();
+
+ // Form a table of buckets for lt, eq, and gt
+ Array<T>* bucket[3] = {&ltArray, &eqArray, &gtArray};
+
+ for (int i = 0; i < num; ++i) {
+ int c = comparator(partitionElement, data[i]);
+ debugAssertM(c >= -1 && c <= 1, "Comparator returned an illegal value.");
+
+ // Insert into the correct bucket, 0, 1, or 2
+ bucket[c + 1]->append(data[i]);
+ }
+ }
+
+ /**
+ Uses < and == on elements to perform a partition. See partition().
+ */
+ void partition(
+ const T& partitionElement,
+ Array<T>& ltArray,
+ Array<T>& eqArray,
+ Array<T>& gtArray) const {
+
+ partition(partitionElement, ltArray, eqArray, gtArray, typename Array<T>::DefaultComparator());
+ }
+
+ /**
+ Paritions the array into those below the median, those above the median, and those elements
+ equal to the median in expected O(n) time using quickselect. If the array has an even
+ number of different elements, the median for partition purposes is the largest value
+ less than the median.
+
+ @param tempArray used for working scratch space
+ @param comparator see parition() for a discussion.*/
+ template<typename Comparator>
+ void medianPartition(
+ Array<T>& ltMedian,
+ Array<T>& eqMedian,
+ Array<T>& gtMedian,
+ Array<T>& tempArray,
+ const Comparator& comparator) const {
+
+ ltMedian.fastClear();
+ eqMedian.fastClear();
+ gtMedian.fastClear();
+
+ // Handle trivial cases first
+ switch (size()) {
+ case 0:
+ // Array is empty; no parition is possible
+ return;
+
+ case 1:
+ // One element
+ eqMedian.append(first());
+ return;
+
+ case 2:
+ {
+ // Two element array; median is the smaller
+ int c = comparator(first(), last());
+
+ switch (c) {
+ case -1:
+ // first was bigger
+ eqMedian.append(last());
+ gtMedian.append(first());
+ break;
+
+ case 0:
+ // Both equal to the median
+ eqMedian.append(first(), last());
+ break;
+
+ case 1:
+ // Last was bigger
+ eqMedian.append(first());
+ gtMedian.append(last());
+ break;
+ }
+ }
+ return;
+ }
+
+ // All other cases use a recursive randomized median
+
+ // Number of values less than all in the current arrays
+ int ltBoost = 0;
+
+ // Number of values greater than all in the current arrays
+ int gtBoost = 0;
+
+ // For even length arrays, force the gt array to be one larger than the
+ // lt array:
+ // [1 2 3] size = 3, choose half = (s + 1) /2
+ //
+ int lowerHalfSize, upperHalfSize;
+ if (isEven(size())) {
+ lowerHalfSize = size() / 2;
+ upperHalfSize = lowerHalfSize + 1;
+ } else {
+ lowerHalfSize = upperHalfSize = (size() + 1) / 2;
+ }
+ const T* xPtr = NULL;
+
+ // Maintain pointers to the arrays; we'll switch these around during sorting
+ // to avoid copies.
+ const Array<T>* source = this;
+ Array<T>* lt = &ltMedian;
+ Array<T>* eq = &eqMedian;
+ Array<T>* gt = &gtMedian;
+ Array<T>* extra = &tempArray;
+
+ while (true) {
+ // Choose a random element -- choose the middle element; this is theoretically
+ // suboptimal, but for loosly sorted array is actually the best strategy
+
+ xPtr = &(source->middle());
+ if (source->size() == 1) {
+ // Done; there's only one element left
+ break;
+ }
+ const T& x = *xPtr;
+
+ // Note: partition (fast) clears the arrays for us
+ source->partition(x, *lt, *eq, *gt, comparator);
+
+ int L = lt->size() + ltBoost + eq->size();
+ int U = gt->size() + gtBoost + eq->size();
+ if ((L >= lowerHalfSize) &&
+ (U >= upperHalfSize)) {
+
+ // x must be the partition median
+ break;
+
+ } else if (L < lowerHalfSize) {
+
+ // x must be smaller than the median. Recurse into the 'gt' array.
+ ltBoost += lt->size() + eq->size();
+
+ // The new gt array will be the old source array, unless
+ // that was the this pointer (i.e., unless we are on the
+ // first iteration)
+ Array<T>* newGt = (source == this) ? extra : const_cast<Array<T>*>(source);
+
+ // Now set up the gt array as the new source
+ source = gt;
+ gt = newGt;
+
+ } else {
+
+ // x must be bigger than the median. Recurse into the 'lt' array.
+ gtBoost += gt->size() + eq->size();
+
+ // The new lt array will be the old source array, unless
+ // that was the this pointer (i.e., unless we are on the
+ // first iteration)
+ Array<T>* newLt = (source == this) ? extra : const_cast<Array<T>*>(source);
+
+ // Now set up the lt array as the new source
+ source = lt;
+ lt = newLt;
+ }
+ }
+
+ // Now that we know the median, make a copy of it (since we're about to destroy the array that it
+ // points into).
+ T median = *xPtr;
+ xPtr = NULL;
+
+ // Partition the original array (note that this fast clears for us)
+ partition(median, ltMedian, eqMedian, gtMedian, comparator);
+ }
+
+ /**
+ Computes a median partition using the default comparator and a dynamically allocated temporary
+ working array. If the median is not in the array, it is chosen to be the largest value smaller
+ than the true median.
+ */
+ void medianPartition(
+ Array<T>& ltMedian,
+ Array<T>& eqMedian,
+ Array<T>& gtMedian) const {
+
+ Array<T> temp;
+ medianPartition(ltMedian, eqMedian, gtMedian, temp, DefaultComparator());
+ }
+
+
+ /** Redistributes the elements so that the new order is statistically independent
+ of the original order. O(n) time.*/
+ void randomize() {
+ T temp;
+
+ for (int i = size() - 1; i >= 0; --i) {
+ int x = iRandom(0, i);
+
+ temp = data[i];
+ data[i] = data[x];
+ data[x] = temp;
+ }
+ }
+
+
+};
+
+
+/** Array::contains for C-arrays */
+template<class T> bool contains(const T* array, int len, const T& e) {
+ for (int i = len - 1; i >= 0; --i) {
+ if (array[i] == e) {
+ return true;
+ }
+ }
+ return false;
+}
+
+} // namespace
+
+#ifdef _MSC_VER
+# pragma warning (pop)
+#endif
+
+#endif
diff --git a/externals/g3dlite/G3D.lib/include/G3D/AtomicInt32.h b/externals/g3dlite/G3D.lib/include/G3D/AtomicInt32.h
new file mode 100644
index 00000000000..9d58e02efe5
--- /dev/null
+++ b/externals/g3dlite/G3D.lib/include/G3D/AtomicInt32.h
@@ -0,0 +1,166 @@
+/**
+ @file AtomicInt32.h
+
+ @maintainer Morgan McGuire, matrix@graphics3d.com
+
+ @created 2005-09-01
+ @edited 2006-06-21
+ */
+#ifndef G3D_ATOMICINT32_H
+#define G3D_ATOMICINT32_H
+
+#include "G3D/platform.h"
+#include "G3D/g3dmath.h"
+
+#if defined(G3D_OSX)
+ #include <libkern/OSAtomic.h>
+#endif
+
+namespace G3D {
+
+/**
+ An integer that may safely be used on different threads without
+ external locking.
+
+ On Win32, Linux, FreeBSD, and Mac OS X this is implemented without locks.
+
+ <B>BETA API</B> This is unsupported and may change
+ */
+class AtomicInt32 {
+private:
+# if defined(G3D_WIN32)
+ volatile long m_value;
+# elif defined(G3D_OSX)
+ int32_t m_value;
+# else
+ volatile int32 m_value;
+# endif
+
+
+public:
+
+ /** Initial value is undefined. */
+ AtomicInt32() {}
+
+ /** Atomic set */
+ explicit AtomicInt32(const int32 x) {
+ m_value = x;
+ }
+
+ /** Atomic set */
+ AtomicInt32(const AtomicInt32& x) {
+ m_value = x.m_value;
+ }
+
+ /** Atomic set */
+ const AtomicInt32& operator=(const int32 x) {
+ m_value = x;
+ return *this;
+ }
+
+ /** Atomic set */
+ void operator=(const AtomicInt32& x) {
+ m_value = x.m_value;
+ }
+
+ /** Returns the current value */
+ int32 value() const {
+ return m_value;
+ }
+
+ /** Returns the old value, before the add. */
+ int32 add(const int32 x) {
+# if defined(G3D_WIN32)
+
+ return InterlockedExchangeAdd(&m_value, x);
+
+# elif defined(G3D_LINUX) || defined(G3D_FREEBSD)
+
+ int32 old;
+ asm volatile ("lock; xaddl %0,%1"
+ : "=r"(old), "=m"(m_value) /* outputs */
+ : "0"(x), "m"(m_value) /* inputs */
+ : "memory", "cc");
+ return old;
+
+# elif defined(G3D_OSX)
+
+ int32 old = m_value;
+ OSAtomicAdd32(x, &m_value);
+ return old;
+
+# endif
+ }
+
+ /** Returns old value. */
+ int32 sub(const int32 x) {
+ return add(-x);
+ }
+
+ void increment() {
+# if defined(G3D_WIN32)
+ // Note: returns the newly incremented value
+ InterlockedIncrement(&m_value);
+# elif defined(G3D_LINUX) || defined(G3D_FREEBSD)
+ add(1);
+# elif defined(G3D_OSX)
+ // Note: returns the newly incremented value
+ OSAtomicIncrement32(&m_value);
+# endif
+ }
+
+ /** Returns zero if the result is zero after decrement, non-zero otherwise.*/
+ int32 decrement() {
+# if defined(G3D_WIN32)
+ // Note: returns the newly decremented value
+ return InterlockedDecrement(&m_value);
+# elif defined(G3D_LINUX) || defined(G3D_FREEBSD)
+ unsigned char nz;
+
+ asm volatile ("lock; decl %1;\n\t"
+ "setnz %%al"
+ : "=a" (nz)
+ : "m" (m_value)
+ : "memory", "cc");
+ return nz;
+# elif defined(G3D_OSX)
+ // Note: returns the newly decremented value
+ return OSAtomicDecrement32(&m_value);
+# endif
+ }
+
+
+ /** Atomic test-and-set: if <code>*this == comperand</code> then <code>*this := exchange</code> else do nothing.
+ In both cases, returns the old value of <code>*this</code>.
+
+ Performs an atomic comparison of this with the Comperand value.
+ If this is equal to the Comperand value, the Exchange value is stored in this.
+ Otherwise, no operation is performed.
+
+ Under VC6 the sign bit may be lost.
+
+ */
+ int32 compareAndSet(const int32 comperand, const int32 exchange) {
+# if defined(G3D_WIN32)
+ return InterlockedCompareExchange(&m_value, exchange, comperand);
+# elif defined(G3D_LINUX) || defined(G3D_FREEBSD)
+ int32 ret;
+ asm volatile ("lock; cmpxchgl %1, %2"
+ : "=a" (ret)
+ : "r" (exchange), "m" (m_value), "0"(comperand)
+ : "memory", "cc");
+ return ret;
+# elif defined(G3D_OSX)
+ int32 old = m_value;
+
+ OSAtomicCompareAndSwap32(comperand, exchange, &m_value);
+
+ return old;
+# endif
+ }
+
+};
+
+} // namespace
+
+#endif
diff --git a/externals/g3dlite/G3D.lib/include/G3D/BinaryFormat.h b/externals/g3dlite/G3D.lib/include/G3D/BinaryFormat.h
new file mode 100644
index 00000000000..53f0c144bf6
--- /dev/null
+++ b/externals/g3dlite/G3D.lib/include/G3D/BinaryFormat.h
@@ -0,0 +1,140 @@
+/**
+ @file BinaryFormat.h
+ @maintainer Morgan McGuire, matrix@graphics3d.com
+
+ @author 2005-06-03
+ @edited 2005-06-03
+
+ Copyright 2000-2005, Morgan McGuire.
+ All rights reserved.
+ */
+
+#ifndef G3D_BINARYFORMAT_H
+#define G3D_BINARYFORMAT_H
+
+#include "G3D/platform.h"
+#include "G3D/g3dmath.h"
+
+namespace G3D {
+
+class Vector2;
+class Vector2int16;
+class Vector3;
+class Vector3int16;
+class Vector4;
+class Vector4int16;
+class Color3;
+class Color3uint8;
+class Color4;
+class Color4uint8;
+
+/**
+ Some values like float16 and int128 have no current CPU data structure that implements them but are useful
+ for file formats and for GPUs.
+
+ CHUNK_BINFMT data follows the protocol.
+ */
+// Must be packed int 16 bits for the chunk reader
+// We can't name these just "INT8" etc. because some libraries #define names like that
+enum BinaryFormat {
+ FIRST_BINFMT = 1000,
+
+ BOOL8_BINFMT,
+ UINT8_BINFMT, INT8_BINFMT, UINT16_BINFMT, INT16_BINFMT, UINT32_BINFMT, INT32_BINFMT, UINT64_BINFMT, INT64_BINFMT, UINT128_BINFMT, INT128_BINFMT,
+ FLOAT16_BINFMT, FLOAT32_BINFMT, FLOAT64_BINFMT,
+ VECTOR2_BINFMT, VECTOR2INT16_BINFMT,
+ VECTOR3_BINFMT, VECTOR3INT16_BINFMT,
+ VECTOR4_BINFMT, VECTOR4INT16_BINFMT,
+ COLOR3_BINFMT, COLOR3UINT8_BINFMT, COLOR3INT16_BINFMT,
+ COLOR4_BINFMT, COLOR4UINT8_BINFMT, COLOR4INT16_BINFMT,
+ STRING_BINFMT, STRINGEVEN_BINFMT, STRING8_BINFMT, STRING16_BINFMT, STRING32_BINFMT,
+
+ CHUNK_BINFMT,
+
+ CUSTOM_BINFMT,
+
+ LAST_BINFMT
+};
+
+}
+
+/** A macro that maps G3D types to format constants.
+ (e.g. binaryFormatOf(Vector3) == VECTOR3_BINFMT).
+*/
+// This implementation is designed to meet the following constraints:
+// 1. Work around the many MSVC++ partial template bugs
+// 2. Work for primitive types (e.g. int)
+#define binaryFormatOf(T) (G3D::_internal::_BinaryFormat<T>::x())
+
+namespace G3D {
+namespace _internal {
+
+
+template<class T> class _BinaryFormat {
+public:
+ static BinaryFormat x() {
+ return CUSTOM_BINFMT;
+ }
+};
+}}
+
+
+/**
+ Macro to declare the underlying format (as will be returned by glFormatOf)
+ of a type. For example,
+
+ <PRE>
+ DECLARE_BINARYFORMATOF(Vector4, VECTOR4_BINFMT)
+ </PRE>
+
+ Use this so you can make vertex arrays of your own classes and not just
+ the standard ones.
+ */
+#define DECLARE_BINARYFORMATOF(CType, EnumType) \
+namespace G3D { \
+ namespace _internal { \
+ template<> class _BinaryFormat<CType> { \
+ public: \
+ static BinaryFormat x() { \
+ return EnumType; \
+ } \
+ }; \
+ } \
+}
+
+DECLARE_BINARYFORMATOF( bool, BOOL8_BINFMT )
+
+DECLARE_BINARYFORMATOF( uint8, UINT8_BINFMT )
+DECLARE_BINARYFORMATOF( int8, INT8_BINFMT )
+DECLARE_BINARYFORMATOF( uint16, UINT16_BINFMT )
+DECLARE_BINARYFORMATOF( int16, INT16_BINFMT )
+DECLARE_BINARYFORMATOF( uint32, UINT32_BINFMT )
+DECLARE_BINARYFORMATOF( int32, INT32_BINFMT )
+DECLARE_BINARYFORMATOF( uint64, UINT64_BINFMT )
+DECLARE_BINARYFORMATOF( int64, INT64_BINFMT )
+
+DECLARE_BINARYFORMATOF( float32, FLOAT32_BINFMT )
+DECLARE_BINARYFORMATOF( float64, FLOAT64_BINFMT )
+
+DECLARE_BINARYFORMATOF( Vector2, VECTOR2_BINFMT )
+DECLARE_BINARYFORMATOF( Vector2int16, VECTOR2INT16_BINFMT )
+DECLARE_BINARYFORMATOF( Vector3, VECTOR3_BINFMT )
+DECLARE_BINARYFORMATOF( Vector3int16, VECTOR3INT16_BINFMT )
+DECLARE_BINARYFORMATOF( Vector4, VECTOR4_BINFMT )
+DECLARE_BINARYFORMATOF( Vector4int16, VECTOR4INT16_BINFMT )
+
+DECLARE_BINARYFORMATOF( Color3, COLOR3_BINFMT )
+DECLARE_BINARYFORMATOF( Color3uint8, COLOR3UINT8_BINFMT )
+DECLARE_BINARYFORMATOF( Color4, COLOR4_BINFMT )
+DECLARE_BINARYFORMATOF( Color4uint8, COLOR4UINT8_BINFMT )
+
+namespace G3D {
+
+/** Returns -1 if the format is custom, otherwise the byte size
+ of a single element in this format.*/
+int32 byteSize(BinaryFormat f);
+
+
+} //G3D
+
+#endif
diff --git a/externals/g3dlite/G3D.lib/include/G3D/BinaryInput.h b/externals/g3dlite/G3D.lib/include/G3D/BinaryInput.h
new file mode 100644
index 00000000000..c0ab9052402
--- /dev/null
+++ b/externals/g3dlite/G3D.lib/include/G3D/BinaryInput.h
@@ -0,0 +1,441 @@
+/**
+ @file BinaryInput.h
+
+ @maintainer Morgan McGuire, graphics3d.com
+
+ @created 2001-08-09
+ @edited 2006-07-19
+
+ Copyright 2000-2007, Morgan McGuire.
+ All rights reserved.
+ */
+
+#ifndef G3D_BINARYINPUT_H
+#define G3D_BINARYINPUT_H
+
+#ifdef _MSC_VER
+// Disable conditional expression is constant, which occurs incorrectly on inlined functions
+# pragma warning(push)
+# pragma warning( disable : 4127 )
+#endif
+
+#include <assert.h>
+#include <string>
+#include <vector>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <stdio.h>
+#include "G3D/platform.h"
+#include "G3D/Array.h"
+#include "G3D/Color4.h"
+#include "G3D/Color3.h"
+#include "G3D/Vector4.h"
+#include "G3D/Vector3.h"
+#include "G3D/Vector2.h"
+#include "G3D/g3dmath.h"
+#include "G3D/debug.h"
+#include "G3D/System.h"
+
+
+namespace G3D {
+
+#if defined(G3D_WIN32) || defined(G3D_LINUX)
+ // Allow writing of integers to non-word aligned locations.
+ // This is legal on x86, but not on other platforms.
+ #define G3D_ALLOW_UNALIGNED_WRITES
+#endif
+
+/**
+ Sequential or random access byte-order independent binary file access.
+ Files compressed with zlib and beginning with an unsigned 32-bit int
+ size are transparently decompressed when the compressed = true flag is
+ specified to the constructor.
+
+ For every readX method there are also versions that operate on a whole
+ Array, std::vector, or C-array. e.g. readFloat32(Array<float32>& array, n)
+ These methods resize the array or std::vector to the appropriate size
+ before reading. For a C-array, they require the pointer to reference
+ a memory block at least large enough to hold <I>n</I> elements.
+
+ Most classes define serialize/deserialize methods that use BinaryInput,
+ BinaryOutput, TextInput, and TextOutput. There are text serializer
+ functions for primitive types (e.g. int, std::string, float, double) but not
+ binary serializers-- you <B>must</b> call the BinaryInput::readInt32 or
+ other appropriate function. This is because it would be very hard to
+ debug the error sequence: <CODE>serialize(1.0, bo); ... float f; deserialize(f, bi);</CODE>
+ in which a double is serialized and then deserialized as a float.
+ */
+class BinaryInput {
+private:
+
+ // The initial buffer will be no larger than this, but
+ // may grow if a large memory read occurs. 50 MB
+ enum {INITIAL_BUFFER_LENGTH = 50000000};
+
+ /**
+ is the file big or little endian
+ */
+ G3DEndian m_fileEndian;
+ std::string m_filename;
+
+ bool m_swapBytes;
+
+ /** Next position to read from in bitString during readBits. */
+ int m_bitPos;
+
+ /** Bits currently being read by readBits.
+ Contains at most 8 (low) bits. Note that
+ beginBits/readBits actually consumes one extra byte, which
+ will be restored by writeBits.*/
+ uint32 m_bitString;
+
+ /** 1 when between beginBits and endBits, 0 otherwise. */
+ int m_beginEndBits;
+
+ /** When operating on huge files, we cannot load the whole file into memory.
+ This is the file position to which buffer[0] corresponds.
+ */
+ int64 m_alreadyRead;
+
+ /**
+ Length of the entire file, in bytes.
+ For the length of the buffer, see bufferLength
+ */
+ int64 m_length;
+
+ /** Length of the array referenced by buffer. May go past the end of the file!*/
+ int64 m_bufferLength;
+ uint8* m_buffer;
+
+ /**
+ Next byte in file, relative to buffer.
+ */
+ int64 m_pos;
+
+ /**
+ When true, the buffer is freed in the destructor.
+ */
+ bool m_freeBuffer;
+
+ /** Ensures that we are able to read at least minLength from startPosition (relative
+ to start of file). */
+ void loadIntoMemory(int64 startPosition, int64 minLength = 0);
+
+ /** Verifies that at least this number of bytes can be read.*/
+ inline void prepareToRead(int64 nbytes) {
+ debugAssertM(m_length > 0, m_filename + " not found or corrupt.");
+ debugAssertM(m_pos + nbytes + m_alreadyRead <= m_length, "Read past end of file.");
+
+ if (m_pos + nbytes > m_bufferLength) {
+ loadIntoMemory(m_pos + m_alreadyRead, nbytes);
+ }
+ }
+
+ // Not implemented on purpose, don't use
+ BinaryInput(const BinaryInput&);
+ BinaryInput& operator=(const BinaryInput&);
+ bool operator==(const BinaryInput&);
+
+ /** Buffer is compressed; replace it with a decompressed version */
+ void decompress();
+public:
+
+ /** false, constant to use with the copyMemory option */
+ static const bool NO_COPY;
+
+ /**
+ If the file cannot be opened, a zero length buffer is presented.
+ Automatically opens files that are inside zipfiles.
+
+ @param compressed Set to true if and only if the file was
+ compressed using BinaryOutput's zlib compression. This has
+ nothing to do with whether the input is in a zipfile.
+ */
+ BinaryInput(
+ const std::string& filename,
+ G3DEndian fileEndian,
+ bool compressed = false);
+
+ /**
+ Creates input stream from an in memory source.
+ Unless you specify copyMemory = false, the data is copied
+ from the pointer, so you may deallocate it as soon as the
+ object is constructed. It is an error to specify copyMemory = false
+ and compressed = true.
+
+ To decompress part of a file, you can follow the following paradigm:
+
+ <PRE>
+ BinaryInput master(...);
+
+ // read from master to point where compressed data exists.
+
+ BinaryInput subset(master.getCArray() + master.getPosition(),
+ master.length() - master.getPosition(),
+ master.endian(), true, true);
+
+ // Now read from subset (it is ok for master to go out of scope)
+ </PRE>
+ */
+ BinaryInput(
+ const uint8* data,
+ int64 dataLen,
+ G3DEndian dataEndian,
+ bool compressed = false,
+ bool copyMemory = true);
+
+ virtual ~BinaryInput();
+
+ /** Change the endian-ness of the file. This only changes the
+ interpretation of the file for future read calls; the
+ underlying data is unmodified.*/
+ void setEndian(G3DEndian endian);
+
+ G3DEndian endian() const {
+ return m_fileEndian;
+ }
+
+ std::string getFilename() const {
+ return m_filename;
+ }
+
+ /**
+ Returns a pointer to the internal memory buffer.
+ May throw an exception for huge files.
+ */
+ const uint8* getCArray() const {
+ if (m_alreadyRead > 0) {
+ throw "Cannot getCArray for a huge file";
+ }
+ return m_buffer;
+ }
+
+ /**
+ Performs bounds checks in debug mode. [] are relative to
+ the start of the file, not the current position.
+ Seeks to the new position before reading (and leaves
+ that as the current position)
+ */
+ inline uint8 operator[](int64 n) {
+ setPosition(n);
+ return readUInt8();
+ }
+
+ /**
+ Returns the length of the file in bytes.
+ */
+ inline int64 getLength() const {
+ return m_length;
+ }
+
+ inline int64 size() const {
+ return getLength();
+ }
+
+ /**
+ Returns the current byte position in the file,
+ where 0 is the beginning and getLength() - 1 is the end.
+ */
+ inline int64 getPosition() const {
+ return m_pos + m_alreadyRead;
+ }
+
+ /**
+ Sets the position. Cannot set past length.
+ May throw a char* when seeking backwards more than 10 MB on a huge file.
+ */
+ inline void setPosition(int64 p) {
+ debugAssertM(p <= m_length, "Read past end of file");
+ m_pos = p - m_alreadyRead;
+ if ((m_pos < 0) || (m_pos > m_bufferLength)) {
+ loadIntoMemory(m_pos + m_alreadyRead);
+ }
+ }
+
+ /**
+ Goes back to the beginning of the file.
+ */
+ inline void reset() {
+ setPosition(0);
+ }
+
+ inline int8 readInt8() {
+ prepareToRead(1);
+ return m_buffer[m_pos++];
+ }
+
+ inline bool readBool8() {
+ return (readInt8() != 0);
+ }
+
+ inline uint8 readUInt8() {
+ prepareToRead(1);
+ return ((uint8*)m_buffer)[m_pos++];
+ }
+
+ uint16 inline readUInt16() {
+ prepareToRead(2);
+
+ m_pos += 2;
+ if (m_swapBytes) {
+ uint8 out[2];
+ out[0] = m_buffer[m_pos - 1];
+ out[1] = m_buffer[m_pos - 2];
+ return *(uint16*)out;
+ } else {
+ #ifdef G3D_ALLOW_UNALIGNED_WRITES
+ return *(uint16*)(&m_buffer[m_pos - 2]);
+ #else
+ uint8 out[2];
+ out[0] = m_buffer[m_pos - 2];
+ out[1] = m_buffer[m_pos - 1];
+ return *(uint16*)out;
+ #endif
+ }
+
+ }
+
+ inline int16 readInt16() {
+ uint16 a = readUInt16();
+ return *(int16*)&a;
+ }
+
+ inline uint32 readUInt32() {
+ prepareToRead(4);
+
+ m_pos += 4;
+ if (m_swapBytes) {
+ uint8 out[4];
+ out[0] = m_buffer[m_pos - 1];
+ out[1] = m_buffer[m_pos - 2];
+ out[2] = m_buffer[m_pos - 3];
+ out[3] = m_buffer[m_pos - 4];
+ return *(uint32*)out;
+ } else {
+ #ifdef G3D_ALLOW_UNALIGNED_WRITES
+ return *(uint32*)(&m_buffer[m_pos - 4]);
+ #else
+ uint8 out[4];
+ out[0] = m_buffer[m_pos - 4];
+ out[1] = m_buffer[m_pos - 3];
+ out[2] = m_buffer[m_pos - 2];
+ out[3] = m_buffer[m_pos - 1];
+ return *(uint32*)out;
+ #endif
+ }
+ }
+
+
+ inline int32 readInt32() {
+ uint32 a = readUInt32();
+ return *(int32*)&a;
+ }
+
+ uint64 readUInt64();
+
+ inline int64 readInt64() {
+ uint64 a = readUInt64();
+ return *(int64*)&a;
+ }
+
+ inline float32 readFloat32() {
+ union {
+ uint32 a;
+ float32 b;
+ };
+ a = readUInt32();
+ return b;
+ }
+
+ inline float64 readFloat64() {
+ union {
+ uint64 a;
+ float64 b;
+ };
+ a = readUInt64();
+ return b;
+ }
+
+ void readBytes(void* bytes, int64 n);
+
+ /**
+ Reads an n character string. The string is not
+ required to end in NULL in the file but will
+ always be a proper std::string when returned.
+ */
+ std::string readString(int64 n);
+
+ /**
+ Reads until NULL or the end of the file is encountered.
+ */
+ std::string readString();
+
+ /**
+ Reads until NULL or the end of the file is encountered.
+ If the string has odd length (including NULL), reads
+ another byte.
+ */
+ std::string readStringEven();
+
+
+ std::string readString32();
+
+ Vector4 readVector4();
+ Vector3 readVector3();
+ Vector2 readVector2();
+
+ Color4 readColor4();
+ Color3 readColor3();
+
+ /**
+ Skips ahead n bytes.
+ */
+ inline void skip(int64 n) {
+ setPosition(m_pos + m_alreadyRead + n);
+ }
+
+ /**
+ Returns true if the position is not at the end of the file
+ */
+ inline bool hasMore() const {
+ return m_pos + m_alreadyRead < m_length;
+ }
+
+ /** Prepares for bit reading via readBits. Only readBits can be
+ called between beginBits and endBits without corrupting the
+ data stream. */
+ void beginBits();
+
+ /** Can only be called between beginBits and endBits */
+ uint32 readBits(int numBits);
+
+ /** Ends bit-reading. */
+ void endBits();
+
+# define DECLARE_READER(ucase, lcase)\
+ void read##ucase(lcase* out, int64 n);\
+ void read##ucase(std::vector<lcase>& out, int64 n);\
+ void read##ucase(Array<lcase>& out, int64 n);
+
+ DECLARE_READER(Bool8, bool)
+ DECLARE_READER(UInt8, uint8)
+ DECLARE_READER(Int8, int8)
+ DECLARE_READER(UInt16, uint16)
+ DECLARE_READER(Int16, int16)
+ DECLARE_READER(UInt32, uint32)
+ DECLARE_READER(Int32, int32)
+ DECLARE_READER(UInt64, uint64)
+ DECLARE_READER(Int64, int64)
+ DECLARE_READER(Float32, float32)
+ DECLARE_READER(Float64, float64)
+# undef DECLARE_READER
+};
+
+
+}
+
+#ifdef _MSC_VER
+# pragma warning(pop)
+#endif
+
+#endif
diff --git a/externals/g3dlite/G3D.lib/include/G3D/BinaryOutput.h b/externals/g3dlite/G3D.lib/include/G3D/BinaryOutput.h
new file mode 100644
index 00000000000..d81ec56a67b
--- /dev/null
+++ b/externals/g3dlite/G3D.lib/include/G3D/BinaryOutput.h
@@ -0,0 +1,421 @@
+/**
+ @file BinaryOutput.h
+
+ @maintainer Morgan McGuire, graphics3d.com
+
+ @created 2001-08-09
+ @edited 2008-01-24
+
+ Copyright 2000-2006, Morgan McGuire.
+ All rights reserved.
+ */
+
+#ifndef G3D_BINARYOUTPUT_H
+#define G3D_BINARYOUTPUT_H
+
+#include "G3D/platform.h"
+#include <assert.h>
+#include <string>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <stdio.h>
+#include "G3D/Color4.h"
+#include "G3D/Color3.h"
+#include "G3D/Vector4.h"
+#include "G3D/Vector3.h"
+#include "G3D/Vector2.h"
+#include "G3D/g3dmath.h"
+#include "G3D/debug.h"
+#include "G3D/BinaryInput.h"
+#include "G3D/System.h"
+
+#ifdef _MSC_VER
+# pragma warning (push)
+// Conditional is constant (wrong in inline)
+# pragma warning (disable : 4127)
+#endif
+namespace G3D {
+
+/**
+ Sequential or random access byte-order independent binary file access.
+
+ The compress() call can be used to compress with zlib.
+
+ Any method call can trigger an out of memory error (thrown as char*)
+ when writing to "<memory>" instead of a file.
+
+ Compressed writing and seeking backwards is not supported for huge files
+ (i.e., BinaryOutput may have to dump the contents to disk if they
+ exceed available RAM).
+ */
+class BinaryOutput {
+private:
+ std::string m_filename;
+
+ bool m_committed;
+
+ /** 0 outside of beginBits...endBits, 1 inside */
+ int m_beginEndBits;
+
+ /** The current string of bits being built up by beginBits...endBits.
+ This string is treated semantically, as if the lowest bit was
+ on the left and the highest was on the right.*/
+ int8 m_bitString;
+
+ /** Position (from the lowest bit) currently used in bitString.*/
+ int m_bitPos;
+
+ // True if the file endianess does not match the machine endian
+ bool m_swapBytes;
+
+ G3DEndian m_fileEndian;
+
+ uint8* m_buffer;
+
+ /** Size of the elements used */
+ int m_bufferLen;
+
+ /** Underlying size of memory allocaded */
+ int m_maxBufferLen;
+
+ /** Next byte in file */
+ int m_pos;
+
+ /** is this initialized? */
+ bool m_init;
+
+ /** Number of bytes already written to the file.*/
+ size_t m_alreadyWritten;
+
+ bool m_ok;
+
+ void reserveBytesWhenOutOfMemory(size_t bytes);
+
+ void reallocBuffer(size_t bytes, size_t oldBufferLen);
+
+ /**
+ Make sure at least bytes can be written, resizing if
+ necessary.
+ */
+ inline void reserveBytes(int bytes) {
+ debugAssert(bytes > 0);
+ size_t oldBufferLen = (size_t)m_bufferLen;
+
+ m_bufferLen = iMax(m_bufferLen, (m_pos + bytes));
+ if (m_bufferLen > m_maxBufferLen) {
+ reallocBuffer(bytes, oldBufferLen);
+ }
+ }
+
+ // Not implemented on purpose, don't use
+ BinaryOutput(const BinaryOutput&);
+ BinaryOutput& operator=(const BinaryOutput&);
+ bool operator==(const BinaryOutput&);
+
+public:
+
+ /**
+ You must call setEndian() if you use this (memory) constructor.
+ */
+ BinaryOutput();
+
+ /**
+ Doesn't actually open the file; commit() does that.
+ Use "<memory>" as the filename if you're going to commit
+ to memory.
+ */
+ BinaryOutput(
+ const std::string& filename,
+ G3DEndian fileEndian);
+
+ ~BinaryOutput();
+
+ /** Compresses the data in the buffer in place,
+ preceeding it with a little-endian uint32 indicating
+ the uncompressed size.
+
+ Call immediately before commit().
+
+ Cannot be used for huge files (ones where the data
+ was already written to disk)-- will throw char*.
+ */
+ void compress();
+
+ /** True if no errors have been encountered.*/
+ bool ok() const;
+
+ /**
+ Returns a pointer to the internal memory buffer.
+ */
+ inline const uint8* getCArray() const {
+ return m_buffer;
+ }
+
+ void setEndian(G3DEndian fileEndian);
+
+ G3DEndian endian() const {
+ return m_fileEndian;
+ }
+
+ std::string getFilename() const {
+ return m_filename;
+ }
+
+ /**
+ Write the bytes to disk. It is ok to call this
+ multiple times; it will just overwrite the previous file.
+
+ Parent directories are created as needed if they do
+ not exist.
+
+ <B>Not</B> called from the destructor; you must call
+ it yourself.
+
+ @param flush If true (default) the file is ready for reading when the method returns, otherwise
+ the method returns immediately and writes the file in the background.
+ */
+ void commit(bool flush = true);
+
+ /**
+ Write the bytes to memory (which must be of
+ at least size() bytes).
+ */
+ void commit(uint8*);
+
+ /**
+ A memory BinaryOutput may be reset so that it can be written to again
+ without allocating new memory. The underlying array will not be deallocated,
+ but the reset structure will act like a newly intialized one.
+ */
+ void reset();
+
+
+ inline int length() const {
+ return (int)m_bufferLen + (int)m_alreadyWritten;
+ }
+
+ inline int size() const {
+ return length();
+ }
+
+ /**
+ Sets the length of the file to n, padding
+ with 0's past the current end. Does not
+ change the position of the next byte to be
+ written unless n < size().
+
+ Throws char* when resetting a huge file to be shorter
+ than its current length.
+ */
+ inline void setLength(int n) {
+ n = n - (int)m_alreadyWritten;
+
+ if (n < 0) {
+ throw "Cannot resize huge files to be shorter.";
+ }
+
+ if (n < m_bufferLen) {
+ m_pos = n;
+ }
+ if (n > m_bufferLen) {
+ reserveBytes(n - m_bufferLen);
+ }
+ }
+
+ /**
+ Returns the current byte position in the file,
+ where 0 is the beginning and getLength() - 1 is the end.
+ */
+ inline int64 position() const {
+ return (int64)m_pos + (int64)m_alreadyWritten;
+ }
+
+
+ /**
+ Sets the position. Can set past length, in which case
+ the file is padded with zeros up to one byte before the
+ next to be written.
+
+ May throw a char* exception when seeking backwards on a huge file.
+ */
+ inline void setPosition(int64 p) {
+ p = p - (int64)m_alreadyWritten;
+
+ if (p > m_bufferLen) {
+ setLength((int)(p + (int64)m_alreadyWritten));
+ }
+
+ if (p < 0) {
+ throw "Cannot seek more than 10 MB backwards on huge files.";
+ }
+
+ m_pos = (int)p;
+ }
+
+
+ void writeBytes(
+ const void* b,
+ int count) {
+
+ reserveBytes(count);
+ debugAssert(m_pos >= 0);
+ debugAssert(m_bufferLen >= count);
+ System::memcpy(m_buffer + m_pos, b, count);
+ m_pos += count;
+ }
+
+ /**
+ Writes a signed 8-bit integer to the current position.
+ */
+ inline void writeInt8(int8 i) {
+ reserveBytes(1);
+ m_buffer[m_pos] = *(uint8*)&i;
+ m_pos++;
+ }
+
+ inline void writeBool8(bool b) {
+ writeInt8(b ? 1 : 0);
+ }
+
+ inline void writeUInt8(uint8 i) {
+ reserveBytes(1);
+ m_buffer[m_pos] = i;
+ m_pos++;
+ }
+
+ void writeUInt16(uint16 u);
+
+ inline void writeInt16(int16 i) {
+ writeUInt16(*(uint16*)&i);
+ }
+
+ void writeUInt32(uint32 u);
+
+ inline void writeInt32(int32 i) {
+ debugAssert(m_beginEndBits == 0);
+ writeUInt32(*(uint32*)&i);
+ }
+
+ void writeUInt64(uint64 u);
+
+ inline void writeInt64(int64 i) {
+ writeUInt64(*(uint64*)&i);
+ }
+
+ inline void writeFloat32(float32 f) {
+ debugAssert(m_beginEndBits == 0);
+ union {
+ float32 a;
+ uint32 b;
+ };
+ a = f;
+ writeUInt32(b);
+ }
+
+ inline void writeFloat64(float64 f) {
+ union {
+ float64 a;
+ uint64 b;
+ };
+ a = f;
+ writeUInt64(b);
+ }
+
+ /**
+ Write a string with NULL termination.
+ */
+ inline void writeString(const std::string& s) {
+ writeString(s.c_str());
+ }
+
+ void writeString(const char* s);
+
+ /**
+ Write a string, ensuring that the total length
+ including NULL is even.
+ */
+ void writeStringEven(const std::string& s) {
+ writeStringEven(s.c_str());
+ }
+
+ void writeStringEven(const char* s);
+
+
+ void writeString32(const char* s);
+
+ /**
+ Write a string with a 32-bit length field in front
+ of it.
+ */
+ void writeString32(const std::string& s) {
+ writeString32(s.c_str());
+ }
+
+ void writeVector4(const Vector4& v);
+
+ void writeVector3(const Vector3& v);
+
+ void writeVector2(const Vector2& v);
+
+ void writeColor4(const Color4& v);
+
+ void writeColor3(const Color3& v);
+
+ /**
+ Skips ahead n bytes.
+ */
+ inline void skip(int n) {
+ if (m_pos + n > m_bufferLen) {
+ setLength((int)m_pos + (int)m_alreadyWritten + n);
+ }
+ m_pos += n;
+ }
+
+ /** Call before a series of BinaryOutput::writeBits calls. Only writeBits
+ can be called between beginBits and endBits without corrupting the stream.*/
+ void beginBits();
+
+ /** Write numBits from bitString to the output stream. Bits are numbered from
+ low to high.
+
+ Can only be
+ called between beginBits and endBits. Bits written are semantically
+ little-endian, regardless of the actual endian-ness of the system. That is,
+ <CODE>writeBits(0xABCD, 16)</CODE> writes 0xCD to the first byte and
+ 0xAB to the second byte. However, if used with BinaryInput::readBits, the ordering
+ is transparent to the caller.
+ */
+ void writeBits(uint32 bitString, int numBits);
+
+ /** Call after a series of BinaryOutput::writeBits calls. This will
+ finish out with zeros the last byte into which bits were written.*/
+ void endBits();
+
+
+# define DECLARE_WRITER(ucase, lcase)\
+ void write##ucase(const lcase* out, int n);\
+ void write##ucase(const std::vector<lcase>& out, int n);\
+ void write##ucase(const Array<lcase>& out, int n);
+
+ DECLARE_WRITER(Bool8, bool)
+ DECLARE_WRITER(UInt8, uint8)
+ DECLARE_WRITER(Int8, int8)
+ DECLARE_WRITER(UInt16, uint16)
+ DECLARE_WRITER(Int16, int16)
+ DECLARE_WRITER(UInt32, uint32)
+ DECLARE_WRITER(Int32, int32)
+ DECLARE_WRITER(UInt64, uint64)
+ DECLARE_WRITER(Int64, int64)
+ DECLARE_WRITER(Float32, float32)
+ DECLARE_WRITER(Float64, float64)
+# undef DECLARE_WRITER
+
+};
+
+}
+
+#ifdef _MSC_VER
+# pragma warning (pop)
+#endif
+
+#endif
diff --git a/externals/g3dlite/G3D.lib/include/G3D/BoundsTrait.h b/externals/g3dlite/G3D.lib/include/G3D/BoundsTrait.h
new file mode 100644
index 00000000000..31525dab73b
--- /dev/null
+++ b/externals/g3dlite/G3D.lib/include/G3D/BoundsTrait.h
@@ -0,0 +1,20 @@
+/**
+ @file BoundsTrait.h
+
+ @maintainer Morgan McGuire, morgan@cs.williams.edu
+ @created 2008-10-01
+ @edited 2008-10-01
+ Copyright 2000-2008, Morgan McGuire.
+ All rights reserved.
+ */
+
+#ifndef G3D_BOUNDSTRAIT_H
+#define G3D_BOUNDSTRAIT_H
+
+#include "G3D/platform.h"
+
+template<typename Value>
+struct BoundsTrait{};
+
+#endif
+
diff --git a/externals/g3dlite/G3D.lib/include/G3D/Box.h b/externals/g3dlite/G3D.lib/include/G3D/Box.h
new file mode 100644
index 00000000000..83e06a2f069
--- /dev/null
+++ b/externals/g3dlite/G3D.lib/include/G3D/Box.h
@@ -0,0 +1,193 @@
+/**
+ @file Box.h
+
+ Box class
+
+ @maintainer Morgan McGuire, matrix@graphics3d.com
+
+ @cite Portions based on Dave Eberly's Magic Software Library at <A HREF="http://www.magic-software.com">http://www.magic-software.com</A>
+ @created 2001-06-02
+ @edited 2007-06-05
+
+ Copyright 2000-2006, Morgan McGuire.
+ All rights reserved.
+ */
+
+#ifndef G3D_BOX_H
+#define G3D_BOX_H
+
+#include "G3D/platform.h"
+#include "G3D/Vector3.h"
+#include "G3D/Array.h"
+#include "G3D/Plane.h"
+
+namespace G3D {
+
+class CoordinateFrame;
+
+/**
+ An arbitrary 3D box, useful as a bounding box.
+
+
+ To construct a box from a coordinate frame, center and extent, use the idiom:
+
+ <CODE>Box box = cframe.toObjectSpace(Box(center - extent/2, center + extent/2));</CODE>
+ */
+class Box {
+private:
+
+ static int32 dummy;
+
+ friend class CoordinateFrame;
+
+ /**
+ <PRE>
+ 3 2 7 6
+
+ 0 1 4 5
+
+ front back (seen through front)
+ </PRE>
+ */
+ Vector3 _corner[8];
+
+ /**
+ Unit axes.
+ */
+ Vector3 _axis[3];
+
+ Vector3 _center;
+
+ /**
+ Extent along each axis.
+ */
+ Vector3 _extent;
+
+ float _area;
+ float _volume;
+
+ void init(
+ const Vector3& min,
+ const Vector3& max);
+
+public:
+
+ /**
+ Does not initialize the fields.
+ */
+ Box();
+
+ /**
+ Constructs a box from two opposite corners.
+ */
+ Box(
+ const Vector3& min,
+ const Vector3& max);
+
+ static Box inf();
+
+ Box(class BinaryInput& b);
+
+ Box(const class AABox& b);
+
+ void serialize(class BinaryOutput& b) const;
+ void deserialize(class BinaryInput& b);
+
+ /**
+ Returns the object to world transformation for
+ this box. localFrame().worldToObject(...) takes
+ objects into the space where the box axes are
+ (1,0,0), (0,1,0), (0,0,1). Note that there
+ is no scaling in this transformation.
+ */
+ CoordinateFrame localFrame() const;
+
+ void getLocalFrame(CoordinateFrame& frame) const;
+
+ /**
+ Returns the centroid of the box.
+ */
+ inline Vector3 center() const {
+ return _center;
+ }
+
+
+ inline Vector3 corner(int i) const {
+ debugAssert(i < 8);
+ return _corner[i];
+ }
+
+ /**
+ Unit length.
+ */
+ inline Vector3 axis(int a) const {
+ debugAssert(a < 3);
+ return _axis[a];
+ }
+
+ /**
+ Distance from corner(0) to the next corner
+ along the box's local axis a.
+ */
+ inline float extent(int a) const {
+ debugAssert(a < 3);
+ return (float)_extent[a];
+ }
+
+ inline Vector3 extent() const {
+ return _extent;
+ }
+
+ /**
+ Returns the four corners of a face (0 <= f < 6).
+ The corners are returned to form a counter clockwise quad facing outwards.
+ */
+ void getFaceCorners(
+ int f,
+ Vector3& v0,
+ Vector3& v1,
+ Vector3& v2,
+ Vector3& v3) const;
+
+
+ /**
+ See AABox::culledBy
+ */
+ bool culledBy(
+ const Array<Plane>& plane,
+ int32& cullingPlaneIndex,
+ const uint32 testMask,
+ uint32& childMask) const;
+
+ /**
+ Conservative culling test that does not produce a mask for children.
+ */
+ bool culledBy(
+ const Array<Plane>& plane,
+ int32& cullingPlaneIndex = dummy,
+ const uint32 testMask = -1) const;
+
+ bool contains(
+ const Vector3& point) const;
+
+ float area() const;
+
+ float volume() const;
+
+ void getRandomSurfacePoint(Vector3& P, Vector3& N = Vector3::dummy) const;
+
+ /**
+ Uniformly distributed on the interior (includes surface)
+ */
+ Vector3 randomInteriorPoint() const;
+
+ void getBounds(class AABox&) const;
+
+ bool isFinite() const {
+ return G3D::isFinite(_volume);
+ }
+};
+
+}
+
+#endif
diff --git a/externals/g3dlite/G3D.lib/include/G3D/Capsule.h b/externals/g3dlite/G3D.lib/include/G3D/Capsule.h
new file mode 100644
index 00000000000..237dcdab69d
--- /dev/null
+++ b/externals/g3dlite/G3D.lib/include/G3D/Capsule.h
@@ -0,0 +1,90 @@
+/**
+ @file Capsule.h
+
+ @maintainer Morgan McGuire, matrix@graphics3d.com
+
+ @created 2003-02-07
+ @edited 2005-08-20
+
+ Copyright 2000-2006, Morgan McGuire.
+ All rights reserved.
+ */
+
+#ifndef G3D_CAPSULE_H
+#define G3D_CAPSULE_H
+
+#include "G3D/platform.h"
+#include "G3D/g3dmath.h"
+#include "G3D/Vector3.h"
+
+namespace G3D {
+
+class Line;
+class AABox;
+/**
+ A shape formed by extruding a sphere along a line segment.
+ */
+class Capsule {
+private:
+ Vector3 p1;
+ Vector3 p2;
+
+ float _radius;
+public:
+
+
+ /** Uninitialized */
+ Capsule();
+ Capsule(class BinaryInput& b);
+ Capsule(const Vector3& _p1, const Vector3& _p2, float _r);
+ void serialize(class BinaryOutput& b) const;
+ void deserialize(class BinaryInput& b);
+
+ /** The line down the center of the capsule */
+ Line axis() const;
+
+ inline float radius() const {
+ return _radius;
+ }
+
+ /** Argument may be 0 or 1 */
+ inline Vector3 point(int i) const {
+ debugAssert(i == 0 || i == 1);
+ return (i == 0) ? p1 : p2;
+ }
+
+ /** Distance between the sphere centers. The total extent of the cylinder is
+ 2r + h. */
+ inline float height() const {
+ return (p1 - p2).magnitude();
+ }
+
+ inline Vector3 center() const {
+ return (p1 + p2) / 2.0;
+ }
+
+ /** Get a reference frame in which the center of mass is the origin and Y is the axis of the capsule.*/
+ void getReferenceFrame(class CoordinateFrame& cframe) const;
+
+ /**
+ Returns true if the point is inside the capsule or on its surface.
+ */
+ bool contains(const Vector3& p) const;
+
+ float volume() const;
+
+ float area() const;
+
+ /** Get axis aligned bounding box */
+ void getBounds(AABox& out) const;
+
+ /** Random world space point with outward facing normal. */
+ void getRandomSurfacePoint(Vector3& P, Vector3& N) const;
+
+ /** Point selected uniformly at random over the volume. */
+ Vector3 randomInteriorPoint() const;
+};
+
+} // namespace
+
+#endif
diff --git a/externals/g3dlite/G3D.lib/include/G3D/CollisionDetection.h b/externals/g3dlite/G3D.lib/include/G3D/CollisionDetection.h
new file mode 100644
index 00000000000..62f92c18d33
--- /dev/null
+++ b/externals/g3dlite/G3D.lib/include/G3D/CollisionDetection.h
@@ -0,0 +1,1178 @@
+/**
+ @file CollisionDetection.h
+
+
+ Moving collision detection for simple primitives.
+
+ @author Morgan McGuire, matrix@graphics3d.com
+ @cite Spherical collision based on Paul Nettle's
+ ftp://ftp.3dmaileffects.com/pub/FluidStudios/CollisionDetection/Fluid_Studios_Generic_Collision_Detection_for_Games_Using_Ellipsoids.pdf
+ and comments by Max McGuire. Ray-sphere intersection by Eric Haines.
+ Box-Box intersection written by Kevin Egan.
+ Thanks to Max McGuire of Iron Lore for various bug fixes.
+
+ @created 2001-11-19
+ @edited 2006-01-10
+
+ Copyright 2000-2006, Morgan McGuire.
+ All rights reserved.
+ */
+
+#ifndef G3D_COLLISIONDETECTION_H
+#define G3D_COLLISIONDETECTION_H
+
+#include "G3D/platform.h"
+#include "G3D/Vector3.h"
+#include "G3D/Plane.h"
+#include "G3D/Box.h"
+#include "G3D/Triangle.h"
+#include "G3D/Array.h"
+#include "G3D/Ray.h"
+#include "G3D/Line.h"
+
+namespace G3D {
+
+
+/**
+ Collision detection primitives and tools for building
+ higher order collision detection schemes.
+
+ These routines provide <I>moving</I> and static collision detection.
+ Moving collision detection allows the calculation of collisions that
+ occur during a period of time -- as opposed to the intersection of
+ two static bodies.
+
+ Moving collision detection routines detect collisions between
+ <I>only</I> static primitives and moving spheres or points. Since the
+ reference frame can be user defined, these functions can be used to
+ detect the collision between two moving bodies by subtracting
+ the velocity vector of one object from the velocity vector of the
+ sphere or point the detection is to occur with. This unified
+ velocity vector will act as if both objects are moving simultaneously.
+
+ Collisions are detected for single-sided objects only. That is,
+ no collision is detected when <I>leaving</I> a primitive or passing
+ through a plane or triangle opposite the normal... except for the
+ point-sphere calculation or when otherwise noted.
+
+ For a sphere, the collision location returned is the point in world
+ space where the surface of the sphere and the fixed object meet.
+ It is <B>not</B> the position of the center of the sphere at
+ the time of the collision.
+
+ The collision normal returned is the surface normal to the fixed
+ object at the collision location.
+
+ <p>
+ <b>Static Collision Detection:</b> (Neither object is moving)
+
+ <table>
+ <tr><td></td><td><b>Vector3</b></td><td><b>LineSegment</b></td><td><b>Ray *</b></td><td><b>Line</b></td><td><b>Plane</b></td><td><b>Triangle</b></td><td><b>Sphere</b></td><td><b>Cylinder</b></td><td><b>Capsule</b></td><td><b>AABox</b></td><td><b>Box</b></td></tr>
+ <tr><td><b>Vector3</b></td><td>Vector3::operator== Vector3::fuzzyEq G3D::distance</td><td bgcolor=#C0C0C0 colspan=10 ></td></tr>
+ <tr><td><b>LineSegment</b></td><td>LineSegment::closestPoint LineSegment::distance CollisionDetection::closestPointOnLineSegment</td><td></td><td bgcolor=#C0C0C0 colspan=9 ></td></tr>
+ <tr><td><b>Ray *</b></td><td>Ray::closestPoint Ray::distance</td><td></td><td></td><td bgcolor=#C0C0C0 colspan=8 ></td></tr>
+ <tr><td><b>Line</b></td><td>Line::closestPoint Line::distance</td><td></td><td>CollisionDetection::closestPointsBetweenLineAndLine</td><td></td><td bgcolor=#C0C0C0 colspan=7 ></td></tr>
+ <tr><td><b>Plane</b></td><td></td><td></td><td></td><td></td><td></td><td bgcolor=#C0C0C0 colspan=6 ></td></tr>
+ <tr><td><b>Triangle</b></td><td></td><td></td><td></td><td></td><td></td><td></td><td bgcolor=#C0C0C0 colspan=5 ></td></tr>
+ <tr><td><b>Sphere</b></td><td>Sphere::contains</td><td></td><td></td><td></td><td></td><td></td><td></td><td bgcolor=#C0C0C0 colspan=4 ></td></tr>
+ <tr><td><b>Cylinder</b></td><td>Cylinder::contains</td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td bgcolor=#C0C0C0 colspan=3 ></td></tr>
+ <tr><td><b>Capsule</b></td><td>Capsule::contains</td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td bgcolor=#C0C0C0 colspan=2 ></td></tr>
+ <tr><td><b>AABox</b></td><td>AABox::contains</td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td></td><td bgcolor=#C0C0C0 colspan=1 ></td></tr>
+ <tr><td><b>Box</b></td><td>Box::contains</td><td>(treat as Ray)</td><td>CollisionDetection::collisionTimeForMovingPointFixedBox</td><td>(treat as Ray)</td><td>CollisionDetection::penetrationDepthForFixedBoxFixedPlane</td><td>CollisionDetection::penetrationDepthForFixedBoxFixedPlane</td><td>CollisionDetection::penetrationDepthForFixedSphereFixedBox</td><td>None (use OPCODE)</td><td>CollisionDetection::movingSpherePassesThroughFixedBox</td><td>CollisionDetection::penetrationDepthForFixedBoxFixedBox</td><td>CollisionDetection::penetrationDepthForFixedBoxFixedBox</td></tr>
+ </table>
+
+ <p>
+ <b>Moving Collision Detection:</b>
+
+ <i>* Note: Moving collision detection against certain primitives is equivalent to static collision
+ detection against a bigger primitive. Ray, Line Segment == ``moving Point''; Capsule ==``moving Sphere''; Plane == ``moving Line''</i>
+ */
+class CollisionDetection {
+private:
+
+ /**
+ Default parameter if value passed to a function as reference is
+ not to be calculated. Must be explicitly supported by function.
+ */
+ static Vector3 ignore;
+
+ /**
+ Default parameter if value passed to a function as reference is
+ not to be calculated. Must be explicitly supported by function.
+ */
+ static bool ignoreBool;
+
+ /**
+ Default parameter if value passed to a function as reference is
+ not to be calculated. Must be explicitly supported by function.
+ */
+ static Array<Vector3> ignoreArray;
+
+
+ // Static class!
+ CollisionDetection() {}
+ virtual ~CollisionDetection() {}
+
+public:
+
+ /**
+ Converts an index [0, 15] to the corresponding separating axis.
+ Does not return normalized vector in the edge-edge case
+ (indices 6 through 15).
+
+ @param separatingAxisIndex Separating axis.
+ @param box1 Box 1.
+ @param box2 Box 2.
+
+ @return Axis that separates the two boxes.
+ */
+ static Vector3 separatingAxisForSolidBoxSolidBox(
+ const int separatingAxisIndex,
+ const Box & box1,
+ const Box & box2);
+
+ /**
+ Tests whether two boxes have axes that are parallel to
+ each other. If they are, axis1 and axis2 are set to be
+ the parallel axes for both box1 and box2 respectively.
+
+ @param ca Dot products of each of the boxes axes
+ @param epsilon Fudge factor (small unit by which the dot
+ products may vary and still be considered
+ zero).
+ @param axis1 Parallel Axis 1. [Post Condition]
+ @param axis2 Parallel Axis 2. [Post Condition]
+
+ @return true - If boxes have a parallel axis
+ @return false - otherwise.
+ */
+ static bool parallelAxisForSolidBoxSolidBox(
+ const double* ca,
+ const double epsilon,
+ int & axis1,
+ int & axis2);
+
+ /**
+ Calculates the projected distance between the two boxes along
+ the specified separating axis, negative distances correspond
+ to an overlap along that separating axis. The distance is not
+ divided by denominator dot(L, L), see
+ penetrationDepthForFixedSphereFixedBox() for more details
+
+ @param separatingAxisIndex
+ @param a Box 1's bounding sphere vector
+ @param b Box 2's bounding sphere vector
+ @param D Vector between Box 1 and Box 2's center points
+ @param c Pointer to array of dot products of the axes of Box 1
+ and Box 2.
+ @param ca Pointer to array of unsigned dot products of the axes
+ of Box 1 and Box 2.
+ @param ad Pointer to array of dot products of Box 1 axes and D.
+ @param bd Pointer to array of dot products of Box 2 axes and D.
+
+ @return Projected distance between the two boxes along the
+ specified separating axis.
+ */
+ static float projectedDistanceForSolidBoxSolidBox(
+ const int separatingAxisIndex,
+ const Vector3 & a,
+ const Vector3 & b,
+ const Vector3 & D,
+ const double* c,
+ const double* ca,
+ const double* ad,
+ const double* bd);
+
+
+ /**
+ Creates a set of standard information about two boxes in order to
+ solve for their collision. This information includes a vector to
+ the radius of the bounding sphere for each box, the vector between
+ each boxes' center and a series of dot products between differing
+ important vectors. These dot products include those between the axes
+ of both boxes (signed and unsigned values), and the dot products
+ between all the axes of box1 and the boxes' center vector and box2
+ and the boxes' center vector.
+
+ @pre The following space requirements must be met:
+ - c[] 9 elements
+ - ca[] 9 elements
+ - ad[] 3 elements
+ - bd[] 3 elements
+
+ @cite dobted from David Eberly's papers, variables used in this function
+ correspond to variables used in pages 6 and 7 in the pdf
+ http://www.magic-software.com/Intersection.html
+ http://www.magic-software.com/Documentation/DynamicCollisionDetection.pdf
+
+ @note Links are out-dated. (Kept to preserve origin and authorship)
+
+ @param box1 Box 1
+ @param box2 Box 2
+ @param a Box 1's bounding sphere vector
+ @param b Box 2's bounding sphere vector
+ @param D Vector between Box 1 and Box 2's center points
+ @param c Pointer to array of dot products of the axes of Box 1
+ and Box 2.
+ @param ca Pointer to array of unsigned dot products of the axes
+ of Box 1 and Box 2.
+ @param ad Pointer to array of dot products of Box 1 axes and D.
+ @param bd Pointer to array of dot products of Box 2 axes and D.
+ */
+ static void fillSolidBoxSolidBoxInfo(
+ const Box & box1,
+ const Box & box2,
+ Vector3 & a,
+ Vector3 & b,
+ Vector3 & D,
+ double* c,
+ double* ca,
+ double* ad,
+ double* bd);
+
+ /**
+ Performs a simple bounding sphere check between two boxes to determine
+ whether these boxes could <i>possibly</i> intersect. This is a very
+ cheap operation (three dot products, two sqrts and a few others). If
+ it returns true, an intersection is possible, but not necessarily
+ guaranteed.
+
+ @param a Vector from box A's center to an outer vertex
+ @param b Vector from box B's center to an outer vertex
+ @param D Distance between the centers of the two boxes
+
+ @return true - if possible intersection
+ @return false - otherwise (This does not guarantee an intersection)
+ */
+ static bool conservativeBoxBoxTest(
+ const Vector3 & a,
+ const Vector3 & b,
+ const Vector3 & D);
+
+ /**
+ Determines whether two fixed solid boxes intersect.
+
+ @note To speed up collision detection, the lastSeparatingAxis from
+ the previous time step can be passed in and that plane can be
+ checked first. If the separating axis was not saved, or if the
+ two boxes intersected then lastSeparatingAxis should equal -1.
+
+ @cite Adobted from David Eberly's papers, variables used in this function
+ correspond to variables used in pages 6 and 7 in the pdf
+ http://www.magic-software.com/Intersection.html
+ http://www.magic-software.com/Documentation/DynamicCollisionDetection.pdf
+
+ @param box1 Box 1.
+ @param box2 Box 2.
+ @param lastSeparatingAxis Last separating axis.
+ (optimization - see note)
+
+ @return true - Intersection.
+ @return false - otherwise.
+ */
+ static bool fixedSolidBoxIntersectsFixedSolidBox(
+ const Box& box1,
+ const Box& box2,
+ const int lastSeparatingAxis = -1);
+
+ /**
+ Calculates the closest points on two lines with each other. If the
+ lines are parallel then using the starting point, else calculate the
+ closest point on each line to the other.
+
+ @note This is very similiar to calculating the intersection of two lines.
+ Logically then, the two points calculated would be identical if calculated
+ with inifinite precision, but with the finite precision of floating point
+ calculations, these values could (will) differ as the line slope approaches
+ zero or inifinity.
+
+ @cite variables and algorithm based on derivation at the following website:
+ http://softsurfer.com/Archive/algorithm_0106/algorithm_0106.htm
+
+ @param line1 Line 1.
+ @param line2 Line 2.
+ @param closest1 Closest point on line 1.
+ @param closest2 Closest point on line 2.
+ */
+ static void closestPointsBetweenLineAndLine(
+ const Line & line1,
+ const Line & line2,
+ Vector3 & closest1,
+ Vector3 & closest2);
+
+ /**
+ Calculates the depth of penetration between two fixed boxes.
+ Contact normal faces away from box1 and into box2. If there is
+ contact, only one contact point is returned. The minimally
+ violated separating plane is computed
+ - if the separating axis corresponds to a face
+ the contact point is half way between the deepest vertex
+ and the face
+ - if the separating axis corresponds to two edges
+ the contact point is the midpoint of the smallest line
+ segment between the two edge lines
+
+ @note This is very similiar to calculating the intersection of two lines.
+ Logically then, the two points calculated would be identical if calculated
+ with inifinite precision, but with the finite precision of floating point
+ calculations, these values could (will) differ as the line slope approaches
+ zero or inifinity.
+
+ @cite adobted from David Eberly's papers, variables used in this function
+ correspond to variables used in pages 6 and 7 in the pdf
+ http://www.magic-software.com/Intersection.html
+ http://www.magic-software.com/Documentation/DynamicCollisionDetection.pdf
+
+ @param box1 Box 1
+ @param box2 Box 2
+ @param contactPoints Contact point between boxes. [Post Condition]
+ @param contactNormals Surface normal at contact point. [Post Condition]
+ @param lastSeparatingAxis Last separating axis. (Used for optimization)
+
+ @return Depth of penetration between the two boxes. If there is no
+ intersection between the boxes, then a negative value is returned.
+ */
+ static float penetrationDepthForFixedBoxFixedBox(
+ const Box& box1,
+ const Box& box2,
+ Array<Vector3>& contactPoints,
+ Array<Vector3>& contactNormals,
+ const int lastSeparatingAxis = -1);
+
+ /**
+ Calculates the depth of penetration between two fixed spheres as well
+ as the deepest point of Sphere A that penetrates Sphere B. The normal
+ returned points <B>away</B> from the object A, although it may
+ represent a perpendicular to either the faces of object B or object A
+ depending on their relative orientations.
+
+ @param sphereA Fixed Sphere A.
+ @param sphereB Fixed Sphere B.
+ @param contactPoints Sphere A's deepest point that penetrates Sphere B.
+ [Post Condition]
+ @param contactNormals Normal at penetration point. [Post Condition]
+
+ @return Depth of penetration. If there is no intersection between the
+ objects then the depth will be a negative value.
+ */
+ static float penetrationDepthForFixedSphereFixedSphere(
+ const class Sphere& sphereA,
+ const Sphere& sphereB,
+ Array<Vector3>& contactPoints,
+ Array<Vector3>& contactNormals = ignoreArray);
+
+ /**
+ Calculates the depth of penetration between a fixed sphere and a fixed
+ box as well as the deepest point of the sphere that penetrates the box
+ and the normal at that intersection.
+
+ @note There are three possible intersections between a sphere and box.
+ - Sphere completely contained in the box
+ - Sphere intersects one edge
+ - Sphere intersects one vertex
+
+ The contact point and contact normal vary for each of these situations.
+ - Sphere contained in Box:
+ - Normal is based on side of least penetration (as is the depth calculation).
+ - Point is based on center of sphere
+ - Sphere intersects one edge
+ - Normal is based on vector from the box center to the point of depth.
+ - Point is closest point to the sphere on the line
+ - Sphere intersects one vertex
+ - Normal is based on vector from the box center to the vertex of penetration.
+ - Point is vertex of penetration.
+
+ @cite Adapted from Jim Arvo's method in Graphics Gems
+ See also http://www.win.tue.nl/~gino/solid/gdc2001depth.pdf
+
+ @param sphere Fixed Sphere.
+ @param box Fixed Box.
+ @param contactPoints Sphere point that penetrates the box. [Post Condition]
+ @param contactNormals Normal at the penetration point. [Post Condition]
+
+ @return Depth of penetration. If there is no intersection between the
+ objects then the depth will be a negative value.
+ */
+ static float penetrationDepthForFixedSphereFixedBox(
+ const Sphere& sphere,
+ const Box& box,
+ Array<Vector3>& contactPoints,
+ Array<Vector3>& contactNormals = ignoreArray);
+
+ /**
+ Calculates the depth of penetration between a Fixed Sphere and a Fixed
+ Plane as well as the deepest point of the sphere that penetrates the plane
+ and the plane normal at that intersection.
+
+ @param sphere Fixed Sphere.
+ @param plane Fixed Plane.
+ @param contactPoints Sphere point that penetrates the plane.
+ [Post Condition]
+ @param contactNormals Normal at penetration point. [Post Condition]
+
+ @return Depth of penetration. If there is no intersection between the
+ objects then the depth will be a negative value.
+ */
+ static float penetrationDepthForFixedSphereFixedPlane(
+ const Sphere& sphereA,
+ const class Plane& planeB,
+ Array<Vector3>& contactPoints,
+ Array<Vector3>& contactNormals = ignoreArray);
+
+ /**
+ Calculates the depth of penetration between a fixed box and a fixed
+ plane as well as the vertexes of the box that penetrate the plane
+ and the plane normals at those intersections.
+
+ @param box Fixed Box.
+ @param plane Fixed Plane.
+ @param contactPoints Box points that penetrate the plane.
+ [Post Condition]
+ @param contactNormals Normals at penetration points [Post Condition]
+
+ @return Depth of penetration. If there is no intersection between the
+ objects then the depth will be a negative value.
+ */
+ static float penetrationDepthForFixedBoxFixedPlane(
+ const Box& box,
+ const Plane& plane,
+ Array<Vector3>& contactPoints,
+ Array<Vector3>& contactNormals = ignoreArray);
+
+ /**
+ Calculates time between the intersection of a moving point and a fixed
+ plane.
+
+ @note This is only a one sided collision test. The side defined by
+ the plane's surface normal is the only one tested. For a two sided
+ collision, call the function once for each side's surface normal.
+
+ @param point Moving point.
+ @param velocity Point's velocity.
+ @param plane Fixed plane.
+ @param location Location of collision. [Post Condition]
+ (Infinite vector on no collision)
+ @param outNormal Plane's surface normal. [Post Condition]
+
+ @return Time til collision. If there is no collision then the return
+ value will be inf().
+ */
+ static float collisionTimeForMovingPointFixedPlane(
+ const Vector3& point,
+ const Vector3& velocity,
+ const class Plane& plane,
+ Vector3& outLocation,
+ Vector3& outNormal = ignore);
+
+ /**
+ Calculates time between the intersection of a moving point and a fixed
+ triangle.
+
+ @note This is only a one sided collision test. The side defined by
+ the triangle's surface normal is the only one tested. For a two sided
+ collision, call the function once for each side's surface normal.
+
+ @param orig Moving point.
+ @param dir Point's velocity.
+ @param v0 Triangle vertex 1.
+ @param v1 Triangle vertex 2.
+ @param v2 Triangle vertex 3
+ @param location Location of collision. [Post Condition]
+ (Infinite vector on no collision)
+
+ @return Time til collision. If there is no collision then the return
+ value will be inf().
+ */
+ inline static float collisionTimeForMovingPointFixedTriangle(
+ const Vector3& orig,
+ const Vector3& dir,
+ const Vector3& v0,
+ const Vector3& v1,
+ const Vector3& v2) {
+ return Ray::fromOriginAndDirection(orig, dir).intersectionTime(v0, v1, v2);
+ }
+
+ /**
+ Calculates time between the intersection of a moving point and a fixed
+ triangle.
+
+ @note This is only a one sided collision test. The side defined by
+ the triangle's surface normal is the only one tested. For a two sided
+ collision, call the function once for each side's surface normal.
+
+ @param orig Moving point.
+ @param dir Point's velocity.
+ @param v0 Triangle vertex 1.
+ @param v1 Triangle vertex 2.
+ @param v2 Triangle vertex 3
+ @param location Location of collision. [Post Condition]
+ (Infinite vector on no collision)
+
+ @return Time til collision. If there is no collision then the return
+ value will be inf().
+ */
+ inline static float collisionTimeForMovingPointFixedTriangle(
+ const Vector3& orig,
+ const Vector3& dir,
+ const Vector3& v0,
+ const Vector3& v1,
+ const Vector3& v2,
+ Vector3& location) {
+ float t = collisionTimeForMovingPointFixedTriangle(orig, dir, v0, v1, v2);
+ if (t < inf()) {
+ location = orig + dir * t;
+ }
+ return t;
+ }
+
+ /**
+ Calculates time between the intersection of a moving point and a fixed
+ triangle.
+
+ @note This is only a one sided collision test. The side defined by
+ the triangle's surface normal is the only one tested. For a two sided
+ collision, call the function once for each side's surface normal.
+
+ @param orig Moving point.
+ @param dir Point's velocity.
+ @param tri Fixed triangle.
+ @param location Location of collision. [Post Condition]
+ (Infinite vector on no collision)
+ @param normal Triangle's surface normal. [Post Condition]
+
+ @return Time til collision. If there is no collision then the return
+ value will be inf().
+ */
+ inline static float collisionTimeForMovingPointFixedTriangle(
+ const Vector3& orig,
+ const Vector3& dir,
+ const Triangle& tri,
+ Vector3& location = ignore,
+ Vector3& normal = ignore) {
+
+ float t = collisionTimeForMovingPointFixedTriangle(
+ orig, dir, tri.vertex(0), tri.vertex(1), tri.vertex(2));
+
+ if ((t < inf()) && (&location != &ignore)) {
+ location = orig + dir * t;
+ normal = tri.normal();
+ }
+ return t;
+ }
+
+ /**
+ Calculates time between the intersection of a moving point and a fixed
+ triangle.
+
+ @note This is only a one sided collision test. The side defined by
+ the triangle's surface normal is the only one tested. For a two sided
+ collision, call the function once for each side's surface normal.
+
+ @param orig Moving point.
+ @param dir Point's velocity.
+ @param v0 Triangle vertex 1.
+ @param v1 Triangle vertex 2.
+ @param v2 Triangle vertex 3
+ @param location Location of collision. [Post Condition]
+ (Infinite vector on no collision)
+ @param normal Triangle's surface normal. [Post Condition]
+
+ @return Time til collision. If there is no collision then the return
+ value will be inf().
+ */
+ inline static float collisionTimeForMovingPointFixedTriangle(
+ const Vector3& orig,
+ const Vector3& dir,
+ const Vector3& v0,
+ const Vector3& v1,
+ const Vector3& v2,
+ Vector3& location,
+ Vector3& normal) {
+ float t = collisionTimeForMovingPointFixedTriangle(orig, dir, v0, v1, v2);
+ if (t < inf()) {
+ location = orig + dir * t;
+ normal = (v2 - v0).cross(v1 - v0).direction();
+ }
+ return t;
+ }
+
+ /**
+ Unlike other methods, does not support an output normal.
+ If the ray origin is inside the box, returns inf() but inside
+ is set to true.
+ <B>Beta API</B>
+
+ @cite Andrew Woo, from "Graphics Gems", Academic Press, 1990
+ @cite Optimized code by Pierre Terdiman, 2000 (~20-30% faster on my Celeron 500)
+ @cite Epsilon value added by Klaus Hartmann
+ @cite http://www.codercorner.com/RayAABB.cpp
+ */
+ static float collisionTimeForMovingPointFixedAABox(
+ const Vector3& point,
+ const Vector3& velocity,
+ const class AABox& box,
+ Vector3& outLocation,
+ bool& inside = ignoreBool,
+ Vector3& outNormal = ignore);
+
+ /**
+ Calculates time between the intersection of a moving point and a fixed
+ Axis-Aligned Box (AABox).
+
+ @note Avoids the sqrt from collisionTimeForMovingPointFixedAABox.
+
+ @param point Moving point.
+ @param velocity Sphere's velocity.
+ @param box Fixed AAbox.
+ @param location Location of collision. [Post Condition]
+ @param Inside Does the ray originate inside the box? [Post Condition]
+ @param normal Box's surface normal to collision [Post Condition]
+
+ @return Time til collision. If there is no collision then the return
+ value will be inf().
+ */
+ static bool collisionLocationForMovingPointFixedAABox(
+ const Vector3& point,
+ const Vector3& velocity,
+ const class AABox& box,
+ Vector3& outLocation,
+ bool& inside = ignoreBool,
+ Vector3& normal = ignore);
+
+ /**
+ Calculates time between the intersection of a moving point and a fixed
+ sphere.
+
+ @note When ray is starts inside the rectangle, the exiting intersection
+ is detected.
+
+ @param point Moving point.
+ @param velocity Point's velocity.
+ @param Sphere Fixed Sphere.
+ @param location Location of collision. [Post Condition]
+ @param outNormal Sphere's surface normal to collision [Post Condition]
+
+ @return Time til collision. If there is no collision then the return
+ value will be inf().
+ */
+ static float collisionTimeForMovingPointFixedSphere(
+ const Vector3& point,
+ const Vector3& velocity,
+ const class Sphere& sphere,
+ Vector3& outLocation,
+ Vector3& outNormal = ignore);
+
+ /**
+ Calculates time between the intersection of a moving point and a fixed
+ box.
+
+ @note If the point is already inside the box, no collision: inf is returned.
+
+ @param point Moving point.
+ @param velocity Sphere's velocity.
+ @param box Fixed box.
+ @param location Position of collision. [Post Condition]
+ @param outNormal Box's surface normal to collision [Post Condition]
+
+ @return Time til collision. If there is no collision then the return
+ value will be inf().
+ */
+ static float collisionTimeForMovingPointFixedBox(
+ const Vector3& point,
+ const Vector3& velocity,
+ const class Box& box,
+ Vector3& outLocation,
+ Vector3& outNormal = ignore);
+
+ /**
+ Calculates time between the intersection of a moving point and a fixed
+ rectangle defined by the points v0, v1, v2, & v3.
+
+ @note This is only a one sided collision test. The side defined by
+ the rectangle's surface normal is the only one tested. For a two sided
+ collision, call the function once for each side's surface normal.
+
+ @param point Moving point.
+ @param velocity Sphere's velocity.
+ @param v0 Rectangle vertex 1.
+ @param v1 Rectangle vertex 2.
+ @param v2 Rectangle vertex 3
+ @param v3 Rectangle vertex 4.
+ @param location Location of collision [Post Condition]
+ @param outNormal Rectangle's surface normal. [Post Condition]
+
+ @return Time til collision. If there is no collision then the return
+ value will be inf().
+ */
+ static float collisionTimeForMovingPointFixedRectangle(
+ const Vector3& point,
+ const Vector3& velocity,
+ const Vector3& v0,
+ const Vector3& v1,
+ const Vector3& v2,
+ const Vector3& v3,
+ Vector3& outLocation,
+ Vector3& outNormal = ignore);
+
+ /**
+ Calculates time between the intersection of a moving point and a fixed
+ capsule.
+
+ @param point Moving point.
+ @param velocity Point's velocity.
+ @param capsule Fixed capsule.
+ @param location Location of collision. [Post Condition]
+ @param outNormal Capsule's surface normal to collision [Post Condition]
+
+ @return Time til collision. If there is no collision then the return
+ value will be inf().
+ */
+ static float collisionTimeForMovingPointFixedCapsule(
+ const Vector3& point,
+ const Vector3& velocity,
+ const class Capsule& capsule,
+ Vector3& outLocation,
+ Vector3& outNormal = ignore);
+
+ /**
+ Calculates time between the intersection of a moving sphere and a fixed
+ triangle.
+
+ @param sphere Moving sphere.
+ @param velocity Sphere's velocity.
+ @param plane Fixed Plane.
+ @param location Location of collision -- not center position of sphere
+ at the collision time. [Post Condition]
+ @param outNormal Box's surface normal to collision [Post Condition]
+
+ @return Time til collision. If there is no collision then the return
+ value will be inf().
+ */
+ static float collisionTimeForMovingSphereFixedPlane(
+ const class Sphere& sphere,
+ const Vector3& velocity,
+ const class Plane& plane,
+ Vector3& outLocation,
+ Vector3& outNormal = ignore);
+
+ /**
+ Calculates time between the intersection of a moving sphere and a fixed
+ triangle.
+
+ @param sphere Moving sphere.
+ @param velocity Sphere's velocity.
+ @param triangle Fixed Triangle. (collisions can happen on the back side of the triangle)
+ @param outLocation Location of collision, if collision occurs -- not center position of sphere
+ at the collision time. If there is interpenetration at the start, this point may be inside
+ the sphere.
+ @param b Barycentric coordinates. These are not valid unless collision occurs.
+
+ @return Time til collision. If there is no collision then the return
+ value will be inf().
+ */
+ static float collisionTimeForMovingSphereFixedTriangle(
+ const class Sphere& sphere,
+ const Vector3& velocity,
+ const Triangle& triangle,
+ Vector3& outLocation,
+ float b[3] = (float*)&ignore);
+
+ /**
+ Calculates time between the intersection of a moving sphere and a fixed
+ rectangle defined by the points v0, v1, v2, & v3.
+
+ @param sphere Moving sphere.
+ @param velocity Sphere's velocity.
+ @param v0 Rectangle vertex 1.
+ @param v1 Rectangle vertex 2.
+ @param v2 Rectangle vertex 3
+ @param v3 Rectangle vertex 4.
+ @param location Location of collision -- not center position of sphere
+ at the collision time. [Post Condition]
+ @param outNormal Box's surface normal to collision [Post Condition]
+
+ @return Time til collision. If there is no collision then the return
+ value will be inf().
+ */
+ static float collisionTimeForMovingSphereFixedRectangle(
+ const class Sphere& sphere,
+ const Vector3& velocity,
+ const Vector3& v0,
+ const Vector3& v1,
+ const Vector3& v2,
+ const Vector3& v3,
+ Vector3& outLocation,
+ Vector3& outNormal = ignore);
+
+ /**
+ Calculates time between the intersection of a moving sphere and a fixed
+ box.
+
+ @note This function will not detect an intersection between a moving object
+ that is already interpenetrating the fixed object.
+
+ @param sphere Moving sphere.
+ @param velocity Sphere's velocity.
+ @param box Fixed box.
+ @param location Location of collision -- not center position of sphere
+ at the collision time. [Post Condition]
+ @param outNormal Box's surface normal to collision [Post Condition]
+
+ @return Time til collision. If there is no collision then the return
+ value will be inf().
+ */
+ static float collisionTimeForMovingSphereFixedBox(
+ const class Sphere& sphere,
+ const Vector3& velocity,
+ const class Box& box,
+ Vector3& outLocation,
+ Vector3& outNormal = ignore);
+
+ /**
+ Calculates time between the intersection of a moving sphere and a fixed
+ sphere.
+
+ @note This won't detect a collision if the sphere is already interpenetrating
+ the fixed sphere.
+
+ @param movingSphere Moving sphere.
+ @param velocity Sphere's velocity.
+ @param fixedSphere Fixed Sphere.
+ @param location Location of collision -- not center position of sphere
+ at the collision time. [Post Condition]
+ @param outNormal Sphere's surface normal to collision [Post Condition]
+
+ @return Time til collision. If there is no collision then the return
+ value will be inf().
+ */
+ static float collisionTimeForMovingSphereFixedSphere(
+ const class Sphere& sphere,
+ const Vector3& velocity,
+ const class Sphere& fixedSphere,
+ Vector3& outLocation,
+ Vector3& outNormal = ignore);
+
+ /**
+ Calculates time between the intersection of a moving sphere and a fixed
+ capsule.
+
+ @note This won't detect a collision if the sphere is already
+ interpenetrating the capsule.
+
+ @param sphere Moving sphere.
+ @param velocity Sphere's velocity.
+ @param capsule Fixed capsule.
+ @param location Location of collision -- not center position of sphere
+ at the collision time. [Post Condition]
+ @param outNormal Capsule's surface normal to the collision [Post Condition]
+
+ @return Time til collision. If there is no collision then the return
+ value will be inf().
+ */
+ static float collisionTimeForMovingSphereFixedCapsule(
+ const class Sphere& sphere,
+ const Vector3& velocity,
+ const class Capsule& capsule,
+ Vector3& outLocation,
+ Vector3& outNormal = ignore);
+
+ /**
+ Finds the direction of bounce that a sphere would have when it
+ intersects an object with the given time of collision, the
+ collision location and the collision normal.
+
+ @note This function works like a pong style ball bounce.
+
+ @param sphere Moving sphere.
+ @param velocity Sphere's velocity.
+ @param collisionTime Time of collision.
+ @param collisionLocation Collision location.
+ @param collisionNormal Surface collision normal.
+
+ @return Direction of bounce.
+ */
+ static Vector3 bounceDirection(
+ const class Sphere& sphere,
+ const Vector3& velocity,
+ const float collisionTime,
+ const Vector3& collisionLocation,
+ const Vector3& collisionNormal);
+
+ /**
+ Finds the direction of slide given a moving sphere, its velocity, the
+ time of collision and the collision location. This function works as
+ if the sphere intersects the surface and continues to hug it.
+
+ @note The result will work well for calculating the movement of a player
+ who collides with an object and continues moving along the object instead
+ of just bouncing off it.
+
+ @param sphere Moving sphere.
+ @param velocity Sphere's velocity.
+ @param collisionTime Time of collision
+ @param collisionLocation Collision location.
+
+ @return Direction of slide.
+ */
+ static Vector3 slideDirection(
+ const class Sphere& sphere,
+ const Vector3& velocity,
+ const float collisionTime,
+ const Vector3& collisionLocation);
+
+ /**
+ Finds the closest point on a line segment to a given point.
+
+ @param v0 line vertex 1.
+ @param v1 line vertex 2.
+ @param point External point.
+
+ @return Closests point to <code>point</code> on the line segment.
+ */
+ static Vector3 closestPointOnLineSegment(
+ const Vector3& v0,
+ const Vector3& v1,
+ const Vector3& point);
+
+ /**
+ Finds the closest point on a line segment to a given point.
+
+ @note This is an optimization to closestPointOnLineSegment. Edge length
+ and direction can be used in this function if already pre-calculated. This
+ prevents doing the same work twice.
+
+ @param v0 line vertex 0.
+ @param v1 line vertex 1.
+ @param edgeDirection The direction of the segment (unit length).
+ @param edgeLength The length of the segment.
+ @param point External point.
+
+ @return Closests point to <code>point</code> on the line segment.
+ */
+ static Vector3 closestPointOnLineSegment(
+ const Vector3& v0,
+ const Vector3& v1,
+ const Vector3& edgeDirection,
+ float edgeLength,
+ const Vector3& point);
+
+ /**
+ Finds the closest point on the perimeter of the triangle to an external point;
+ given a triangle defined by three points v0, v1, & v2, and the external point.
+
+ @param v0 Triangle vertex 0.
+ @param v1 Triangle vertex 1.
+ @param v2 Triangle vertex 2.
+ @param point External point.
+
+ @return Closests point to <code>point</code> on the perimeter of the
+ triangle.
+ */
+ static Vector3 closestPointOnTrianglePerimeter(
+ const Vector3& v0,
+ const Vector3& v1,
+ const Vector3& v2,
+ const Vector3& point);
+
+ /**
+ Finds the closest point on the perimeter of the triangle to an external point;
+ given a triangle defined by the array of points v, its edge directions and
+ their lengths, as well as the external point.
+
+ @note This is an optimization to closestPointToTrianglePerimeter. Edge length
+ and direction can be used in this function if already pre-calculated. This
+ prevents doing the same work twice.
+
+ @param v0 Triangle vertex 0.
+ @param v1 Triangle vertex 1.
+ @param v2 Triangle vertex 2.
+ @param point External point.
+ @param edgeIndex The point lies on the edge between v[edgeIndex] and v[(edgeIndex + 1) % 3]
+
+ @return Closests point to <code>point</code> on the perimeter of the
+ triangle.
+ */
+ static Vector3 closestPointOnTrianglePerimeter(
+ const Vector3 v[3],
+ const Vector3 edgeDirection[3],
+ const float edgeLength[3],
+ const Vector3& point,
+ int& edgeIndex);
+
+ /**
+ Tests whether a point is contained within the triangle defined by
+ v0, v1, and v2 and its plane's normal.
+
+ @param v0 Triangle vertex 0.
+ @param v1 Triangle vertex 1.
+ @param v2 Triangle vertex 2.
+ @param normal Normal to triangle's plane.
+ @param point The point in question.
+ @param primaryAxis Primary axis of triangle. This will be detected
+ if not given. This parameter is provided as an optimization.
+ @param b Barycentric coordinates; b[i] is the weight on v[i]
+
+ @return true - if point is inside the triangle.
+ @return false - otherwise
+ */
+ static bool isPointInsideTriangle(
+ const Vector3& v0,
+ const Vector3& v1,
+ const Vector3& v2,
+ const Vector3& normal,
+ const Vector3& point,
+ float b[3],
+ Vector3::Axis primaryAxis = Vector3::DETECT_AXIS);
+
+ inline static bool isPointInsideTriangle(
+ const Vector3& v0,
+ const Vector3& v1,
+ const Vector3& v2,
+ const Vector3& normal,
+ const Vector3& point,
+ Vector3::Axis primaryAxis = Vector3::DETECT_AXIS) {
+
+ float b[3];
+ return isPointInsideTriangle(v0, v1, v2, normal, point, b, primaryAxis);
+ }
+
+ /**
+ Tests for the intersection of a moving sphere and a fixed box in a
+ given time limit.
+
+ @note Returns true if any part of the sphere is inside the box
+ during the time period (inf means "ever"). Useful for
+ performing bounding-box collision detection.
+
+ @param sphere Moving sphere.
+ @param velocity Velocity of moving sphere.
+ @param box Fixed box.
+ @param timeLimit Time limit for intersection test.
+
+ @return true - if the two objects will touch.
+ @return false - if there is no intersection.
+ */
+ static bool movingSpherePassesThroughFixedBox(
+ const Sphere& sphere,
+ const Vector3& velocity,
+ const Box& box,
+ double timeLimit = inf());
+
+ /**
+ Tests for the intersection of a moving sphere and a fixed sphere in a
+ given time limit.
+
+ @note This function will not detect an intersection between a moving object
+ that is already interpenetrating the fixed object.
+
+ @param sphere Moving sphere.
+ @param velocity Velocity of moving sphere.
+ @param fixedSphere Fixed sphere.
+ @param timeLimit Time limit for intersection test.
+
+ @return true - if the two spheres will touch.
+ @return false - if there is no intersection.
+ */
+ static bool movingSpherePassesThroughFixedSphere(
+ const Sphere& sphere,
+ const Vector3& velocity,
+ const Sphere& fixedSphere,
+ double timeLimit = inf());
+
+ /**
+ Tests for the intersection of two fixed spheres.
+
+ @param sphere1 Fixed sphere 1.
+ @param sphere2 Fixed sphere 2.
+
+ @return true - if the two spheres touch.
+ @return false - if there is no intersection.
+ */
+ static bool fixedSolidSphereIntersectsFixedSolidSphere(
+ const Sphere& sphere1,
+ const Sphere& sphere2);
+
+ /**
+ Tests for the intersection of a fixed sphere and a fixed box.
+
+ @param sphere Fixed sphere.
+ @param box Fixed box.
+
+ @return true - if the two objects touch.
+ @return false - if there is no intersection.
+ */
+ static bool fixedSolidSphereIntersectsFixedSolidBox(
+ const Sphere& sphere,
+ const Box& box);
+
+ static bool fixedSolidSphereIntersectsFixedTriangle(
+ const Sphere& sphere,
+ const Triangle& triangle);
+
+ /**
+ Tests whether a point is inside a rectangle defined by the vertexes
+ v0, v1, v2, & v3, and the rectangle's plane normal.
+
+ @param v0 Rectangle vertex 1.
+ @param v1 Rectangle vertex 2.
+ @param v2 Rectangle vertex 3.
+ @param v3 Rectangle vertex 4.
+ @param normal Normal to rectangle's plane.
+ @param point The point in question.
+
+ @return true - if point is inside the rectangle.
+ @return false - otherwise
+ */
+ static bool isPointInsideRectangle(
+ const Vector3& v0,
+ const Vector3& v1,
+ const Vector3& v2,
+ const Vector3& v3,
+ const Vector3& normal,
+ const Vector3& point);
+
+ /**
+ Finds the closest point on the perimeter of the rectangle to an
+ external point; given a rectangle defined by four points v0, v1,
+ v2, & v3, and the external point.
+
+ @param v0 Rectangle vertex 1.
+ @param v1 Rectangle vertex 2.
+ @param v2 Rectangle vertex 3.
+ @param v3 Rectangle vertex 4.
+ @param point External point.
+
+ @return Closests point to <code>point</code> on the perimeter of the
+ rectangle.
+ */
+ static Vector3 closestPointToRectanglePerimeter(
+ const Vector3& v0,
+ const Vector3& v1,
+ const Vector3& v2,
+ const Vector3& v3,
+ const Vector3& point);
+
+ /**
+ Finds the closest point in the rectangle to an external point; Given
+ a rectangle defined by four points v0, v1, v2, & v3, and the external
+ point.
+
+ @param v0 Rectangle vertex 1.
+ @param v1 Rectangle vertex 2.
+ @param v2 Rectangle vertex 3
+ @param v3 Rectangle vertex 4.
+ @param point External point.
+
+ @return Closet point in the rectangle to the external point.
+ */
+ static Vector3 closestPointToRectangle(
+ const Vector3& v0,
+ const Vector3& v1,
+ const Vector3& v2,
+ const Vector3& v3,
+ const Vector3& point);
+};
+
+} // namespace
+
+#endif // G3D_COLLISIONDETECTION_H
diff --git a/externals/g3dlite/G3D.lib/include/G3D/Color1.h b/externals/g3dlite/G3D.lib/include/G3D/Color1.h
new file mode 100644
index 00000000000..1958da2b6cd
--- /dev/null
+++ b/externals/g3dlite/G3D.lib/include/G3D/Color1.h
@@ -0,0 +1,129 @@
+/**
+ @file Color1.h
+
+ Monochrome Color class
+
+ @maintainer Morgan McGuire, morgan@cs.williams.edu
+ @created 2007-01-31
+ @edited 2008-10-02
+
+ Copyright 2000-2008, Morgan McGuire.
+ All rights reserved.
+ */
+
+#ifndef G3D_COLOR1_H
+#define G3D_COLOR1_H
+
+#include "G3D/platform.h"
+#include "G3D/g3dmath.h"
+#include "G3D/HashTrait.h"
+#include <string>
+
+namespace G3D {
+
+/**
+ Monochrome color. This is just a float, but it has nice semantics
+ because a scaling by 255 automatically occurs when switching between
+ fixed point (Color1uint8) and floating point (Color1) formats.
+ */
+class Color1 {
+private:
+ // Hidden operators
+ bool operator<(const Color1&) const;
+ bool operator>(const Color1&) const;
+ bool operator<=(const Color1&) const;
+ bool operator>=(const Color1&) const;
+
+public:
+ float value;
+
+ /**
+ Initializes to 0
+ */
+ inline Color1() : value(0) {}
+
+ Color1(class BinaryInput& bi);
+
+ inline explicit Color1(float v) : value(v) {
+ }
+
+ Color1 (const class Color1uint8& other);
+
+ void serialize(class BinaryOutput& bo) const;
+ void deserialize(class BinaryInput& bi);
+
+ Color1 operator+ (const Color1& other) const {
+ return Color1(value + other.value);
+ }
+
+ Color1 operator+ (const float other) const {
+ return Color1(value + other);
+ }
+
+ Color1& operator+= (const Color1 other) {
+ value += other.value;
+ return *this;
+ }
+
+ Color1& operator-= (const Color1 other) {
+ value -= other.value;
+ return *this;
+ }
+
+ Color1 operator- (const Color1& other) const {
+ return Color1(value - other.value);
+ }
+
+ Color1 operator- (const float other) const {
+ return Color1(value - other);
+ }
+
+ Color1 operator- () const {
+ return Color1(-value);
+ }
+
+ Color1 operator* (const Color1& other) const {
+ return Color1(value * other.value);
+ }
+
+ Color1 operator* (const float other) const {
+ return Color1(value * other);
+ }
+
+ Color1 operator/ (const Color1& other) const {
+ return Color1(value / other.value);
+ }
+
+ Color1 operator/ (const float other) const {
+ return Color1(value / other);
+ }
+
+ inline Color1 max(const Color1& other) const {
+ return Color1(G3D::max(value, other.value));
+ }
+
+ inline Color1 min(const Color1& other) const {
+ return Color1(G3D::min(value, other.value));
+ }
+
+ inline Color1 lerp(const Color1& other, float a) const {
+ return Color1(value + (other.value - value) * a);
+
+ }
+
+ inline size_t hashCode() const {
+ return (size_t)(value * 0xFFFFFF);
+ }
+};
+
+}
+
+template <>
+struct HashTrait<G3D::Color1> {
+ static size_t hashCode(const G3D::Color1& key) {
+ return key.hashCode();
+ }
+};
+
+
+#endif
diff --git a/externals/g3dlite/G3D.lib/include/G3D/Color1uint8.h b/externals/g3dlite/G3D.lib/include/G3D/Color1uint8.h
new file mode 100644
index 00000000000..ffaa2d43ac4
--- /dev/null
+++ b/externals/g3dlite/G3D.lib/include/G3D/Color1uint8.h
@@ -0,0 +1,107 @@
+/**
+ @file Color1uint8.h
+
+ @maintainer Morgan McGuire, graphics3d.com
+
+ @created 2007-01-30
+ @edited 2007-01-30
+
+ Copyright 2000-2007, Morgan McGuire.
+ All rights reserved.
+ */
+
+#ifndef G3D_COLOR1UINT8_H
+#define G3D_COLOR1UINT8_H
+
+#include "G3D/platform.h"
+#include "G3D/g3dmath.h"
+
+namespace G3D {
+
+#if defined(G3D_WIN32)
+ // Switch to tight alignment
+ #pragma pack(push, 1)
+#endif
+
+
+/**
+ Represents a Color1 as a packed integer. Convenient
+ for creating unsigned int vertex arrays.
+
+ <B>WARNING</B>: Integer color formats are different than
+ integer vertex formats. The color channels are automatically
+ scaled by 255 (because OpenGL automatically scales integer
+ colors back by this factor). So Color3(1,1,1) == Color3uint8(255,255,255)
+ but Vector3(1,1,1) == Vector3int16(1,1,1).
+
+ <B>Note</B>:
+ Conversion of a float32 to uint8 is accomplished by min(iFloor(f * 256)) and
+ back to float32 by u / 255.0f. This gives equal size intervals.
+Consider a number line from 0 to 1 and a corresponding one from 0 to 255. If we use iRound(x * 255), then the mapping for three critical intervals are:
+
+<pre>
+let s = 0.5/255
+ float int size
+[0, s) -> 0 s
+[s, s * 3) -> 1 2*s
+(1 - s, 1] -> 255 s
+</pre>
+
+If we use max(floor(x * 256), 255), then we get:
+
+<pre>
+let s = 1/256
+ float int size
+[0, s) -> 0 s
+[s, 2 * s) -> 1 s
+(1 - s, 1] -> 255 s
+</PRE>
+and the intervals are all the same size, thus giving equal precision to all values.
+ */
+class Color1uint8 {
+private:
+ // Hidden operators
+ bool operator<(const Color1uint8&) const;
+ bool operator>(const Color1uint8&) const;
+ bool operator<=(const Color1uint8&) const;
+ bool operator>=(const Color1uint8&) const;
+
+public:
+
+ uint8 value;
+
+ Color1uint8() : value(0) {}
+
+ explicit Color1uint8(const uint8 _v) : value(_v) {}
+
+ Color1uint8(const class Color1& c);
+
+ Color1uint8(class BinaryInput& bi);
+
+ void serialize(class BinaryOutput& bo) const;
+
+ void deserialize(class BinaryInput& bi);
+
+ inline bool operator==(const Color1uint8& other) const {
+ return value == other.value;
+ }
+
+ inline bool operator!=(const Color1uint8& other) const {
+ return value != other.value;
+ }
+
+}
+
+#if defined(G3D_LINUX) || defined(G3D_OSX)
+ __attribute((aligned(1)))
+#endif
+
+;
+
+#ifdef G3D_WIN32
+ #pragma pack(pop)
+#endif
+
+}
+
+#endif
diff --git a/externals/g3dlite/G3D.lib/include/G3D/Color3.h b/externals/g3dlite/G3D.lib/include/G3D/Color3.h
new file mode 100644
index 00000000000..b8d0f94829a
--- /dev/null
+++ b/externals/g3dlite/G3D.lib/include/G3D/Color3.h
@@ -0,0 +1,390 @@
+/**
+ @file Color3.h
+
+ Color class
+
+ @maintainer Morgan McGuire, matrix@graphics3d.com
+ @cite Portions based on Dave Eberly's Magic Software Library
+ at <A HREF="http://www.magic-software.com">http://www.magic-software.com</A>
+
+ @created 2001-06-02
+ @edited 2008-07-17
+
+ Copyright 2000-2008, Morgan McGuire.
+ All rights reserved.
+ */
+
+#ifndef G3D_COLOR3_H
+#define G3D_COLOR3_H
+
+#include "G3D/platform.h"
+#include "G3D/g3dmath.h"
+#include "G3D/HashTrait.h"
+#include "G3D/Color1.h"
+#include <string>
+
+namespace G3D {
+
+/**
+ Do not subclass-- this implementation makes assumptions about the
+ memory layout.
+ */
+class Color3 {
+private:
+ // Hidden operators
+ bool operator<(const Color3&) const;
+ bool operator>(const Color3&) const;
+ bool operator<=(const Color3&) const;
+ bool operator>=(const Color3&) const;
+
+public:
+ /**
+ Does not initialize fields.
+ */
+ Color3();
+
+ explicit Color3(class BinaryInput& bi);
+
+ Color3(float r, float g, float b);
+ Color3(float v) : r(v), g(v), b(v) {}
+
+ explicit Color3(const class Vector3& v);
+
+ explicit Color3(const float value[3]);
+
+ /**
+ Initialize from another color.
+ */
+ Color3 (const Color3& other);
+
+ Color3 (const class Color3uint8& other);
+
+ /**
+ Initialize from an HTML-style color (e.g. 0xFF0000 == RED)
+ */
+ static Color3 fromARGB(uint32);
+
+ /** Returns one of the color wheel colors (e.g. RED, GREEN, CYAN).
+ Does not include white, black, or gray. */
+ static const Color3& wheelRandom();
+
+ /**
+ * Channel value.
+ */
+ float r, g, b;
+
+ void serialize(class BinaryOutput& bo) const;
+ void deserialize(class BinaryInput& bi);
+
+ // access vector V as V[0] = V.r, V[1] = V.g, V[2] = V.b
+ //
+ // WARNING. These member functions rely on
+ // (1) Color3 not having virtual functions
+ // (2) the data packed in a 3*sizeof(float) memory block
+ const float& operator[] (int i) const;
+ float& operator[] (int i);
+
+ // assignment and comparison
+ Color3& operator= (const Color3& rkVector);
+ bool operator== (const Color3& rkVector) const;
+ bool operator!= (const Color3& rkVector) const;
+ size_t hashCode() const;
+
+ // arithmetic operations
+ Color3 operator+ (const Color3& rkVector) const;
+ Color3 operator- (const Color3& rkVector) const;
+ Color3 operator* (float fScalar) const;
+ Color3 operator* (const Color3& rkVector) const;
+ Color3 operator/ (float fScalar) const;
+ Color3 operator- () const;
+
+ // arithmetic updates
+ Color3& operator+= (const Color3& rkVector);
+ Color3& operator-= (const Color3& rkVector);
+ Color3& operator*= (const Color3& rkVector);
+ Color3& operator*= (float fScalar);
+ Color3& operator/= (float fScalar);
+
+ bool fuzzyEq(const Color3& other) const;
+ bool fuzzyNe(const Color3& other) const;
+
+ inline operator float* () {
+ return (float*)this;
+ }
+
+ operator const float* () const {
+ return (float*)this;
+ }
+
+ // vector operations
+ float length () const;
+ Color3 direction() const;
+ float squaredLength () const;
+ float dot (const Color3& rkVector) const;
+ float unitize (float fTolerance = 1e-06);
+ Color3 cross (const Color3& rkVector) const;
+ Color3 unitCross (const Color3& rkVector) const;
+
+ inline Color3 pow(const Color3& other) const {
+ return Color3(::pow(r, other.r), ::pow(g, other.g), ::pow(b, other.b));
+ }
+
+ inline Color3 pow(float other) const {
+ return Color3(::pow(r, other), ::pow(g, other), ::pow(b, other));
+ }
+
+ /**@return the largest component */
+ inline float max() const {
+ return G3D::max(G3D::max(r, g), b);
+ }
+
+ inline Color3 max(const Color3& other) const {
+ return Color3(G3D::max(r, other.r), G3D::max(g, other.g), G3D::max(b, other.b));
+ }
+
+ inline Color3 min(const Color3& other) const {
+ return Color3(G3D::min(r, other.r), G3D::min(g, other.g), G3D::min(b, other.b));
+ }
+
+ inline Color3 lerp(const Color3& other, float a) const {
+ return (*this) + (other - *this) * a;
+
+ }
+
+ inline float sum() const {
+ return r + g + b;
+ }
+
+ inline float average() const {
+ return sum() / 3.0f;
+ }
+
+
+ /**
+ * Converts from HSV to RGB , note: toHSV(fromHSV(_hsv)) may not be _hsv, if it is at a grey point or black point.
+ * The components of _hsv should lie in the unit interval.
+ * @cite Alvy Ray Smith SIGGRAPH 1978 "Color Gamut Transform Pairs"
+ **/
+ static Color3 fromHSV(const Vector3& _hsv);
+ static Vector3 toHSV(const Color3& _rgb);
+
+ /** Duplicates the matlab jet colormap maps [0,1] --> (r,g,b) where blue is close to 0 and red is close to 1. */
+ static Color3 jetColorMap(const float& val);
+
+ /** Returns colors with maximum saturation and value @param hue [0, 1]*/
+ static Color3 rainbowColorMap(float hue);
+
+ std::string toString() const;
+
+ /** Random unit vector */
+ static Color3 random();
+
+ // Special values.
+ // Intentionally not inlined: see Matrix3::identity() for details.
+ static const Color3& red();
+ static const Color3& green();
+ static const Color3& blue();
+ static const Color3& purple();
+ static const Color3& cyan();
+ static const Color3& yellow();
+ static const Color3& brown();
+ static const Color3& orange();
+ static const Color3& black();
+ static const Color3& gray();
+ static const Color3& white();
+
+ static const Color3& zero();
+ static const Color3& one();
+
+ inline Color3 bgr() const {
+ return Color3(b, g, r);
+ }
+};
+
+inline G3D::Color3 operator* (float s, const G3D::Color3& c) {
+ return c * s;
+}
+
+inline G3D::Color3 operator* (G3D::Color1& s, const G3D::Color3& c) {
+ return c * s.value;
+}
+
+inline G3D::Color3 operator* (const G3D::Color3& c, G3D::Color1& s) {
+ return c * s.value;
+}
+
+
+//----------------------------------------------------------------------------
+inline Color3::Color3 () {
+}
+
+//----------------------------------------------------------------------------
+
+inline Color3::Color3(float fX, float fY, float fZ) {
+ r = fX;
+ g = fY;
+ b = fZ;
+}
+
+//----------------------------------------------------------------------------
+inline Color3::Color3(const float afCoordinate[3]) {
+ r = afCoordinate[0];
+ g = afCoordinate[1];
+ b = afCoordinate[2];
+}
+
+//----------------------------------------------------------------------------
+inline Color3::Color3 (const Color3& rkVector) {
+ r = rkVector.r;
+ g = rkVector.g;
+ b = rkVector.b;
+}
+
+//----------------------------------------------------------------------------
+inline float& Color3::operator[] (int i) {
+ return ((float*)this)[i];
+}
+
+//----------------------------------------------------------------------------
+
+inline const float& Color3::operator[] (int i) const {
+ return ((float*)this)[i];
+}
+
+//----------------------------------------------------------------------------
+
+inline bool Color3::fuzzyEq(const Color3& other) const {
+ return G3D::fuzzyEq((*this - other).squaredLength(), 0);
+}
+
+//----------------------------------------------------------------------------
+
+inline bool Color3::fuzzyNe(const Color3& other) const {
+ return G3D::fuzzyNe((*this - other).squaredLength(), 0);
+}
+
+
+//----------------------------------------------------------------------------
+inline Color3& Color3::operator= (const Color3& rkVector) {
+ r = rkVector.r;
+ g = rkVector.g;
+ b = rkVector.b;
+ return *this;
+}
+
+//----------------------------------------------------------------------------
+inline bool Color3::operator== (const Color3& rkVector) const {
+ return ( r == rkVector.r && g == rkVector.g && b == rkVector.b );
+}
+
+//----------------------------------------------------------------------------
+inline bool Color3::operator!= (const Color3& rkVector) const {
+ return ( r != rkVector.r || g != rkVector.g || b != rkVector.b );
+}
+
+//----------------------------------------------------------------------------
+inline Color3 Color3::operator+ (const Color3& rkVector) const {
+ return Color3(r + rkVector.r, g + rkVector.g, b + rkVector.b);
+}
+
+//----------------------------------------------------------------------------
+inline Color3 Color3::operator- (const Color3& rkVector) const {
+ return Color3(r -rkVector.r, g - rkVector.g, b - rkVector.b);
+}
+
+//----------------------------------------------------------------------------
+inline Color3 Color3::operator* (float fScalar) const {
+ return Color3(fScalar*r, fScalar*g, fScalar*b);
+}
+
+//----------------------------------------------------------------------------
+inline Color3 Color3::operator* (const Color3& rkVector) const {
+ return Color3(r * rkVector.r, g * rkVector.g, b * rkVector.b);
+}
+
+//----------------------------------------------------------------------------
+inline Color3 Color3::operator- () const {
+ return Color3( -r, -g, -b);
+}
+
+//----------------------------------------------------------------------------
+inline Color3& Color3::operator+= (const Color3& rkVector) {
+ r += rkVector.r;
+ g += rkVector.g;
+ b += rkVector.b;
+ return *this;
+}
+
+//----------------------------------------------------------------------------
+inline Color3& Color3::operator-= (const Color3& rkVector) {
+ r -= rkVector.r;
+ g -= rkVector.g;
+ b -= rkVector.b;
+ return *this;
+}
+
+//----------------------------------------------------------------------------
+inline Color3& Color3::operator*= (float fScalar) {
+ r *= fScalar;
+ g *= fScalar;
+ b *= fScalar;
+ return *this;
+}
+
+//----------------------------------------------------------------------------
+inline Color3& Color3::operator*= (const Color3& rkVector) {
+ r *= rkVector.r;
+ g *= rkVector.g;
+ b *= rkVector.b;
+ return *this;
+}
+//----------------------------------------------------------------------------
+inline float Color3::squaredLength () const {
+ return r*r + g*g + b*b;
+}
+
+//----------------------------------------------------------------------------
+inline float Color3::length () const {
+ return sqrtf(r*r + g*g + b*b);
+}
+
+//----------------------------------------------------------------------------
+inline Color3 Color3::direction () const {
+ float lenSquared = r * r + g * g + b * b;
+
+ if (lenSquared != 1.0f) {
+ return *this / sqrtf(lenSquared);
+ } else {
+ return *this;
+ }
+}
+
+//----------------------------------------------------------------------------
+inline float Color3::dot (const Color3& rkVector) const {
+ return r*rkVector.r + g*rkVector.g + b*rkVector.b;
+}
+
+//----------------------------------------------------------------------------
+inline Color3 Color3::cross (const Color3& rkVector) const {
+ return Color3(g*rkVector.b - b*rkVector.g, b*rkVector.r - r*rkVector.b,
+ r*rkVector.g - g*rkVector.r);
+}
+
+//----------------------------------------------------------------------------
+inline Color3 Color3::unitCross (const Color3& rkVector) const {
+ Color3 kCross(g*rkVector.b - b*rkVector.g, b*rkVector.r - r*rkVector.b,
+ r*rkVector.g - g*rkVector.r);
+ kCross.unitize();
+ return kCross;
+}
+} // namespace
+
+
+template <> struct HashTrait<G3D::Color3> {
+ static size_t hashCode(const G3D::Color3& key) {
+ return key.hashCode();
+ }
+};
+
+
+#endif
diff --git a/externals/g3dlite/G3D.lib/include/G3D/Color3uint8.h b/externals/g3dlite/G3D.lib/include/G3D/Color3uint8.h
new file mode 100644
index 00000000000..20e225a0734
--- /dev/null
+++ b/externals/g3dlite/G3D.lib/include/G3D/Color3uint8.h
@@ -0,0 +1,127 @@
+/**
+ @file Color3uint8.h
+
+ @maintainer Morgan McGuire, graphics3d.com
+
+ @created 2003-04-07
+ @edited 2006-06-24
+
+ Copyright 2000-2006, Morgan McGuire.
+ All rights reserved.
+ */
+
+#ifndef G3D_COLOR3UINT8_H
+#define G3D_COLOR3UINT8_H
+
+#include "G3D/platform.h"
+#include "G3D/g3dmath.h"
+
+namespace G3D {
+
+/**
+ Represents a Color3 as a packed integer. Convenient
+ for creating unsigned int vertex arrays. Used by
+ G3D::GImage as the underlying format.
+
+ <B>WARNING</B>: Integer color formats are different than
+ integer vertex formats. The color channels are automatically
+ scaled by 255 (because OpenGL automatically scales integer
+ colors back by this factor). So Color3(1,1,1) == Color3uint8(255,255,255)
+ but Vector3(1,1,1) == Vector3int16(1,1,1).
+ */
+
+#if defined(G3D_WIN32)
+ // Switch to tight alignment
+ #pragma pack(push, 1)
+#endif
+
+class Color3uint8 {
+private:
+ // Hidden operators
+ bool operator<(const Color3uint8&) const;
+ bool operator>(const Color3uint8&) const;
+ bool operator<=(const Color3uint8&) const;
+ bool operator>=(const Color3uint8&) const;
+
+public:
+ uint8 r;
+ uint8 g;
+ uint8 b;
+
+ Color3uint8() : r(0), g(0), b(0) {}
+
+ Color3uint8(const uint8 _r, const uint8 _g, const uint8 _b) : r(_r), g(_g), b(_b) {}
+
+ Color3uint8(const class Color3& c);
+
+ Color3uint8(class BinaryInput& bi);
+
+ inline static Color3uint8 fromARGB(uint32 i) {
+ Color3uint8 c;
+ c.r = (i >> 16) & 0xFF;
+ c.g = (i >> 8) & 0xFF;
+ c.b = i & 0xFF;
+ return c;
+ }
+
+ inline Color3uint8 bgr() const {
+ return Color3uint8(b, g, r);
+ }
+
+ /**
+ Returns the color packed into a uint32
+ (the upper byte is 0xFF)
+ */
+ inline uint32 asUInt32() const {
+ return (0xFF << 24) + ((uint32)r << 16) + ((uint32)g << 8) + b;
+ }
+
+ void serialize(class BinaryOutput& bo) const;
+
+ void deserialize(class BinaryInput& bi);
+
+ // access vector V as V[0] = V.r, V[1] = V.g, V[2] = V.b
+ //
+ // WARNING. These member functions rely on
+ // (1) Color3 not having virtual functions
+ // (2) the data packed in a 3*sizeof(uint8) memory block
+ G3D::uint8& operator[] (int i) const;
+ operator G3D::uint8* ();
+ operator const G3D::uint8* () const;
+
+ bool operator==(const Color3uint8& other) const {
+ return (other.r == r) && (other.g == g) && (other.b == b);
+ }
+
+ bool operator!=(const Color3uint8& other) const {
+ return (other.r != r) && (other.g != g) && (other.b != b);
+ }
+}
+
+#if defined(G3D_LINUX) || defined(G3D_OSX)
+ __attribute((aligned(1)))
+#endif
+
+;
+
+#ifdef G3D_WIN32
+ #pragma pack(pop)
+#endif
+
+
+inline G3D::uint8& Color3uint8::operator[] (int i) const {
+ debugAssert((unsigned int)i < 3);
+ return ((G3D::uint8*)this)[i];
+}
+
+//----------------------------------------------------------------------------
+inline Color3uint8::operator G3D::uint8* () {
+ return (G3D::uint8*)this;
+}
+
+inline Color3uint8::operator const G3D::uint8* () const {
+ return (G3D::uint8*)this;
+}
+}
+
+#endif
diff --git a/externals/g3dlite/G3D.lib/include/G3D/Color4.h b/externals/g3dlite/G3D.lib/include/G3D/Color4.h
new file mode 100644
index 00000000000..6b1b3b4c4bb
--- /dev/null
+++ b/externals/g3dlite/G3D.lib/include/G3D/Color4.h
@@ -0,0 +1,324 @@
+/**
+ @file Color4.h
+
+ Color class
+
+ @maintainer Morgan McGuire, matrix@graphics3d.com
+ @cite Portions based on Dave Eberly's Magic Software Library
+ at <A HREF="http://www.magic-software.com">http://www.magic-software.com</A>
+
+ @created 2002-06-25
+ @edited 2008-07-16
+
+ Copyright 2000-2008, Morgan McGuire.
+ All rights reserved.
+ */
+
+#ifndef G3D_COLOR4_H
+#define G3D_COLOR4_H
+
+#include "G3D/platform.h"
+#include "G3D/g3dmath.h"
+#include "G3D/Color3.h"
+#include <string>
+
+namespace G3D {
+
+/**
+ Do not subclass-- this implementation makes assumptions about the
+ memory layout.
+ */
+class Color4 {
+private:
+ // Hidden operators
+ bool operator<(const Color4&) const;
+ bool operator>(const Color4&) const;
+ bool operator<=(const Color4&) const;
+ bool operator>=(const Color4&) const;
+
+public:
+
+ /**
+ * Does not initialize fields.
+ */
+ Color4 ();
+
+ Color4(const Color3& c3, float a = 1.0);
+
+ Color4(const class Color4uint8& c);
+
+ Color4(class BinaryInput& bi);
+
+ Color4(const class Vector4& v);
+
+ /**
+ * Initialize from G3D::Reals.
+ */
+ Color4(float r, float g, float b, float a = 1.0);
+
+ /**
+ * Initialize from array of G3D::Reals.
+ */
+ Color4(float value[4]);
+
+ /**
+ * Initialize from another color.
+ */
+ Color4(const Color4& other);
+
+ void serialize(class BinaryOutput& bo) const;
+ void deserialize(class BinaryInput& bi);
+
+ /**
+ Initialize from an HTML-style color (e.g. 0xFFFF0000 == RED)
+ */
+ static Color4 fromARGB(uint32);
+
+ /**
+ * Channel values.
+ */
+ float r, g, b, a;
+
+ inline Color3 rgb() const {
+ return Color3(r, g, b);
+ }
+
+ // access vector V as V[0] = V.r, V[1] = V.g, V[2] = V.b, v[3] = V.a
+ //
+ // WARNING. These member functions rely on
+ // (1) Color4 not having virtual functions
+ // (2) the data packed in a 3*sizeof(float) memory block
+ float& operator[] (int i) const;
+ operator float* ();
+ operator const float* () const;
+
+ // assignment and comparison
+ Color4& operator= (const Color4& rkVector);
+ bool operator== (const Color4& rkVector) const;
+ bool operator!= (const Color4& rkVector) const;
+ size_t hashCode() const;
+
+ // arithmetic operations
+ Color4 operator+ (const Color4& rkVector) const;
+ Color4 operator- (const Color4& rkVector) const;
+ Color4 operator* (float fScalar) const;
+ Color4 operator/ (float fScalar) const;
+ Color4 operator- () const;
+ friend Color4 operator* (double fScalar, const Color4& rkVector);
+
+ // arithmetic updates
+ Color4& operator+= (const Color4& rkVector);
+ Color4& operator-= (const Color4& rkVector);
+ Color4& operator*= (float fScalar);
+ Color4& operator/= (float fScalar);
+
+ bool fuzzyEq(const Color4& other) const;
+ bool fuzzyNe(const Color4& other) const;
+
+ std::string toString() const;
+
+ inline Color4 max(const Color4& other) const {
+ return Color4(G3D::max(r, other.r), G3D::max(g, other.g), G3D::max(b, other.b), G3D::max(a, other.a));
+ }
+
+ inline Color4 min(const Color4& other) const {
+ return Color4(G3D::min(r, other.r), G3D::min(g, other.g), G3D::min(b, other.b), G3D::min(a, other.a));
+ }
+
+ /** r + g + b + a */
+ inline float sum() const {
+ return r + g + b + a;
+ }
+
+ inline Color4 lerp(const Color4& other, float a) const {
+ return (*this) + (other - *this) * a;
+
+ }
+
+ // Special values.
+ // Intentionally not inlined: see Matrix3::identity() for details.
+ static const Color4& zero();
+ static const Color4& clear();
+
+ static const Color4& inf();
+
+ inline Color3 bgr() const {
+ return Color3(b, g, r);
+ }
+};
+
+/**
+ Extends the c3 with alpha = 1.0
+ */
+Color4 operator*(const Color3& c3, const Color4& c4);
+
+
+inline Color4 operator*(const Color3& c3, const Color4& c4) {
+ return Color4(c3.r * c4.r, c3.g * c4.g, c3.b * c4.b, c4.a);
+}
+
+//----------------------------------------------------------------------------
+
+inline Color4::Color4 () {
+ // For efficiency in construction of large arrays of vectors, the
+ // default constructor does not initialize the vector.
+}
+
+//----------------------------------------------------------------------------
+
+inline Color4::Color4(const Color3& c3, float a) {
+ r = c3.r;
+ g = c3.g;
+ b = c3.b;
+ this->a = a;
+}
+
+//----------------------------------------------------------------------------
+
+inline Color4::Color4(
+ float r,
+ float g,
+ float b,
+ float a) :
+ r(r), g(g), b(b), a(a) {
+}
+
+//----------------------------------------------------------------------------
+inline Color4::Color4 (float afCoordinate[4]) {
+ r = afCoordinate[0];
+ g = afCoordinate[1];
+ b = afCoordinate[2];
+ a = afCoordinate[3];
+}
+
+//----------------------------------------------------------------------------
+
+inline Color4::Color4(
+ const Color4& other) {
+
+ r = other.r;
+ g = other.g;
+ b = other.b;
+ a = other.a;
+}
+
+//----------------------------------------------------------------------------
+
+inline float& Color4::operator[] (int i) const {
+ return ((float*)this)[i];
+}
+
+//----------------------------------------------------------------------------
+inline Color4::operator float* () {
+ return (float*)this;
+}
+
+inline Color4::operator const float* () const {
+ return (float*)this;
+}
+
+//----------------------------------------------------------------------------
+
+inline bool Color4::fuzzyEq(const Color4& other) const {
+ Color4 dif = (*this - other);
+ return G3D::fuzzyEq(dif.r * dif.r + dif.g * dif.g + dif.b * dif.b + dif.a * dif.a, 0);
+}
+
+//----------------------------------------------------------------------------
+
+inline bool Color4::fuzzyNe(const Color4& other) const {
+ Color4 dif = (*this - other);
+ return G3D::fuzzyNe(dif.r * dif.r + dif.g * dif.g + dif.b * dif.b + dif.a * dif.a, 0);
+}
+
+
+//----------------------------------------------------------------------------
+inline Color4& Color4::operator= (const Color4& other) {
+ r = other.r;
+ g = other.g;
+ b = other.b;
+ a = other.a;
+ return *this;
+}
+
+//----------------------------------------------------------------------------
+
+inline bool Color4::operator== (const Color4& other) const {
+ return ( r == other.r && g == other.g && b == other.b && a == other.a);
+}
+
+//----------------------------------------------------------------------------
+
+inline bool Color4::operator!= (const Color4& other) const {
+ return ( r != other.r || g != other.g || b != other.b || a != other.a);
+}
+
+//----------------------------------------------------------------------------
+inline Color4 Color4::operator+ (const Color4& other) const {
+ return Color4(r + other.r, g + other.g, b + other.b, a + other.a);
+}
+
+//----------------------------------------------------------------------------
+inline Color4 Color4::operator- (const Color4& other) const {
+ return Color4(r - other.r, g - other.g, b - other.b, a - other.a);
+}
+
+//----------------------------------------------------------------------------
+
+inline Color4 Color4::operator* (float fScalar) const {
+ return Color4(fScalar * r, fScalar * g, fScalar * b, fScalar * a);
+}
+
+//----------------------------------------------------------------------------
+
+inline Color4 Color4::operator- () const {
+ return Color4(-r, -g, -b, -a);
+}
+
+//----------------------------------------------------------------------------
+
+inline Color4 operator* (float fScalar, const Color4& other) {
+ return Color4(fScalar * other.r, fScalar * other.g,
+ fScalar * other.b, fScalar * other.a);
+}
+
+//----------------------------------------------------------------------------
+
+inline Color4& Color4::operator+= (const Color4& other) {
+ r += other.r;
+ g += other.g;
+ b += other.b;
+ a += other.a;
+ return *this;
+}
+
+//----------------------------------------------------------------------------
+
+inline Color4& Color4::operator-= (const Color4& other) {
+ r -= other.r;
+ g -= other.g;
+ b -= other.b;
+ a -= other.a;
+ return *this;
+}
+
+//----------------------------------------------------------------------------
+
+inline Color4& Color4::operator*= (float fScalar) {
+ r *= fScalar;
+ g *= fScalar;
+ b *= fScalar;
+ a *= fScalar;
+ return *this;
+}
+
+} // namespace
+
+template <>
+struct HashTrait<G3D::Color4> {
+ static size_t hashCode(const G3D::Color4& key) {
+ return key.hashCode();
+ }
+};
+
+#endif
diff --git a/externals/g3dlite/G3D.lib/include/G3D/Color4uint8.h b/externals/g3dlite/G3D.lib/include/G3D/Color4uint8.h
new file mode 100644
index 00000000000..7804a6e1e51
--- /dev/null
+++ b/externals/g3dlite/G3D.lib/include/G3D/Color4uint8.h
@@ -0,0 +1,133 @@
+/**
+ @file Color4uint8.h
+
+ @maintainer Morgan McGuire, graphics3d.com
+
+ @created 2003-04-07
+ @edited 2006-03-24
+
+ Copyright 2000-2006, Morgan McGuire.
+ All rights reserved.
+ */
+
+#ifndef COLOR4UINT8_H
+#define COLOR4UINT8_H
+
+#include "G3D/g3dmath.h"
+#include "G3D/platform.h"
+#include "G3D/Color3uint8.h"
+
+namespace G3D {
+
+/**
+ Represents a Color4 as a packed integer. Convenient
+ for creating unsigned int vertex arrays. Used by
+ G3D::GImage as the underlying format.
+
+ <B>WARNING</B>: Integer color formats are different than
+ integer vertex formats. The color channels are automatically
+ scaled by 255 (because OpenGL automatically scales integer
+ colors back by this factor). So Color4(1,1,1) == Color4uint8(255,255,255)
+ but Vector3(1,1,1) == Vector3int16(1,1,1).
+
+ */
+
+#ifdef G3D_WIN32
+ // Switch to tight alignment
+ #pragma pack(push, 1)
+#endif
+
+class Color4uint8 {
+private:
+ // Hidden operators
+ bool operator<(const Color4uint8&) const;
+ bool operator>(const Color4uint8&) const;
+ bool operator<=(const Color4uint8&) const;
+ bool operator>=(const Color4uint8&) const;
+
+public:
+ uint8 r;
+ uint8 g;
+ uint8 b;
+ uint8 a;
+
+ Color4uint8() : r(0), g(0), b(0), a(0) {}
+
+ Color4uint8(const class Color4& c);
+
+ Color4uint8(const uint8 _r, const uint8 _g, const uint8 _b, const uint8 _a) : r(_r), g(_g), b(_b), a(_a) {}
+
+ Color4uint8(const Color3uint8& c, const uint8 _a) : r(c.r), g(c.g), b(c.b), a(_a) {}
+
+ Color4uint8(class BinaryInput& bi);
+
+ inline static Color4uint8 fromARGB(uint32 i) {
+ Color4uint8 c;
+ c.a = (i >> 24) & 0xFF;
+ c.r = (i >> 16) & 0xFF;
+ c.g = (i >> 8) & 0xFF;
+ c.b = i & 0xFF;
+ return c;
+ }
+
+ inline uint32 asUInt32() const {
+ return ((uint32)a << 24) + ((uint32)r << 16) + ((uint32)g << 8) + b;
+ }
+
+ // access vector V as V[0] = V.r, V[1] = V.g, V[2] = V.b
+ //
+ // WARNING. These member functions rely on
+ // (1) Color4uint8 not having virtual functions
+ // (2) the data packed in a 3*sizeof(uint8) memory block
+ G3D::uint8& operator[] (int i) const;
+ operator G3D::uint8* ();
+ operator const G3D::uint8* () const;
+
+
+ inline Color3uint8 bgr() const {
+ return Color3uint8(b, g, r);
+ }
+
+ void serialize(class BinaryOutput& bo) const;
+
+ void deserialize(class BinaryInput& bi);
+
+ inline Color3uint8 rgb() const {
+ return Color3uint8(r, g, b);
+ }
+
+ bool operator==(const Color4uint8& other) const {
+ return *reinterpret_cast<const uint32*>(this) == *reinterpret_cast<const uint32*>(&other);
+ }
+
+ bool operator!=(const Color4uint8& other) const {
+ return *reinterpret_cast<const uint32*>(this) != *reinterpret_cast<const uint32*>(&other);
+ }
+
+}
+#if defined(G3D_LINUX) || defined(G3D_OSX)
+ __attribute((aligned(1)))
+#endif
+;
+
+#ifdef G3D_WIN32
+ #pragma pack(pop)
+#endif
+
+
+inline G3D::uint8& Color4uint8::operator[] (int i) const {
+ return ((G3D::uint8*)this)[i];
+}
+
+//----------------------------------------------------------------------------
+inline Color4uint8::operator G3D::uint8* () {
+ return (G3D::uint8*)this;
+}
+
+inline Color4uint8::operator const G3D::uint8* () const {
+ return (G3D::uint8*)this;
+}
+
+}
+
+#endif
diff --git a/externals/g3dlite/G3D.lib/include/G3D/Cone.h b/externals/g3dlite/G3D.lib/include/G3D/Cone.h
new file mode 100644
index 00000000000..c09b9b6846c
--- /dev/null
+++ b/externals/g3dlite/G3D.lib/include/G3D/Cone.h
@@ -0,0 +1,68 @@
+/**
+ @file Cone.h
+
+ Cone class
+
+ @maintainer Morgan McGuire, matrix@graphics3d.com
+ @cite Portions based on Dave Eberly's Magic Software Library at <A HREF="http://www.magic-software.com">http://www.magic-software.com</A>
+
+ @created 2001-06-02
+ @edited 2006-02-23
+
+ Copyright 2000-2006, Morgan McGuire.
+ All rights reserved.
+ */
+
+#ifndef G3D_CONE_H
+#define G3D_CONE_H
+
+#include "G3D/platform.h"
+#include "G3D/g3dmath.h"
+#include "G3D/Vector3.h"
+
+namespace G3D {
+
+/**
+ An infinite cone.
+ */
+class Cone {
+
+private:
+ Vector3 tip;
+ Vector3 direction;
+
+ /** Angle from the center line to the edge. */
+ float angle;
+
+public:
+
+ /**
+ @param angle Angle from the center line to the edge, in radians
+ */
+ Cone(const Vector3& tip, const Vector3& direction, float angle);
+
+ /**
+ Forms the smallest cone that contains the box. Undefined if
+ the tip is inside or on the box.
+ */
+ Cone(const Vector3& tip, const class Box& box);
+
+ virtual ~Cone() {}
+
+ /**
+ Returns true if the cone touches, intersects, or contains b.
+
+ If c.intersects(s) and c.intersects(Sphere(s.center, s.radius * 2)
+ then the sphere s is entirely within cone c.
+ */
+ bool intersects(const class Sphere& s) const;
+
+ /**
+ True if v is a point inside the cone.
+ */
+ bool contains(const class Vector3& v) const;
+};
+
+} // namespace
+
+#endif
diff --git a/externals/g3dlite/G3D.lib/include/G3D/ConvexPolyhedron.h b/externals/g3dlite/G3D.lib/include/G3D/ConvexPolyhedron.h
new file mode 100644
index 00000000000..6ae9ba136ff
--- /dev/null
+++ b/externals/g3dlite/G3D.lib/include/G3D/ConvexPolyhedron.h
@@ -0,0 +1,179 @@
+/**
+ @file ConvexPolyhedron.h
+
+ @maintainer Morgan McGuire, matrix@graphics3d.com
+
+ @created 2001-11-11
+ @edited 2006-04-10
+
+ Copyright 2000-2006, Morgan McGuire.
+ All rights reserved.
+ */
+
+#ifndef G3D_CONVEXPOLYHEDRON_H
+#define G3D_CONVEXPOLYHEDRON_H
+
+#include "G3D/platform.h"
+#include "G3D/Vector3.h"
+#include "G3D/Vector2.h"
+#include "G3D/CoordinateFrame.h"
+#include "G3D/Plane.h"
+#include "G3D/Line.h"
+#include "G3D/Array.h"
+
+namespace G3D {
+
+class DirectedEdge {
+public:
+ Vector3 start;
+ Vector3 stop;
+};
+
+class ConvexPolygon {
+private:
+
+ friend class ConvexPolyhedron;
+
+ Array<Vector3> _vertex;
+
+public:
+
+ ConvexPolygon() {}
+ ConvexPolygon(const Array<Vector3>& __vertex);
+ virtual ~ConvexPolygon() {}
+
+ /**
+ Counter clockwise winding order.
+ */
+ inline const Vector3& vertex(int i) const {
+ return _vertex[i];
+ }
+
+ inline void setVertex(int i, const Vector3& v) {
+ _vertex[i] = v;
+ }
+
+ /**
+ Zero vertices indicates an empty polygon (zero area).
+ */
+ inline int numVertices() const {
+ return _vertex.size();
+ }
+
+ inline void setNumVertices(int n) {
+ _vertex.resize(n);
+ }
+
+ /**
+ O(n) in the number of edges
+ */
+ bool isEmpty() const;
+
+ /**
+ Cuts the polygon at the plane. If the polygon is entirely above or below
+ the plane, one of the returned polygons will be empty.
+
+ @param above The part of the polygon above (on the side the
+ normal points to or in the plane) the plane
+ @param below The part of the polygon below the plane.
+ @param newEdge If a new edge was introduced, this is that edge (on the above portion; the below portion is the opposite winding.
+ */
+ void cut(const Plane& plane, ConvexPolygon &above, ConvexPolygon &below, DirectedEdge& newEdge);
+ void cut(const Plane& plane, ConvexPolygon &above, ConvexPolygon &below);
+
+ /**
+ When a cut plane grazes a vertex in the polygon, two near-identical vertices may be created.
+ The closeness of these two points can cause a number of problems, such as ConvexPolygon::normal()
+ returning an infinite vector. It should be noted, however, that not all applications are
+ sensitive to near-identical vertices.
+
+ removeDuplicateVertices() detects and eliminates redundant vertices.
+ */
+ void removeDuplicateVertices();
+
+ /**
+ O(n) in the number of edges
+ */
+ float getArea() const;
+
+ inline Vector3 normal() const {
+ debugAssert(_vertex.length() >= 3);
+ return (_vertex[1] - _vertex[0]).cross(_vertex[2] - _vertex[0]).direction();
+ }
+
+ /**
+ Returns the same polygon with inverse winding.
+ */
+ ConvexPolygon inverse() const;
+};
+
+
+
+class ConvexPolyhedron {
+public:
+ /**
+ Zero faces indicates an empty polyhedron
+ */
+ Array<ConvexPolygon> face;
+
+ ConvexPolyhedron() {}
+ ConvexPolyhedron(const Array<ConvexPolygon>& _face);
+
+ /**
+ O(n) in the number of edges
+ */
+ bool isEmpty() const;
+
+ /**
+ O(n) in the number of edges
+ */
+ float getVolume() const;
+
+ /**
+ Cuts the polyhedron at the plane. If the polyhedron is entirely above or below
+ the plane, one of the returned polyhedra will be empty.
+
+ @param above The part of the polyhedron above (on the side the
+ normal points to or in the plane) the plane
+ @param below The part of the polyhedron below the plane.
+ */
+ void cut(const Plane& plane, ConvexPolyhedron &above, ConvexPolyhedron &below);
+};
+
+/**
+
+ */
+class ConvexPolygon2D {
+private:
+
+ Array<Vector2> m_vertex;
+
+public:
+
+ ConvexPolygon2D() {}
+
+ /**
+ Points are counter-clockwise in a Y = down, X = right coordinate
+ system.
+
+ @param reverse If true, the points are reversed (i.e. winding direction is changed)
+ before the polygon is created.
+ */
+ ConvexPolygon2D(const Array<Vector2>& pts, bool reverse = false);
+
+ inline int numVertices() const {
+ return m_vertex.size();
+ }
+
+ inline const Vector2& vertex(int index) const {
+ debugAssert((index >= 0) && (index <= m_vertex.size()));
+ return m_vertex[index];
+ }
+
+ /** @param reverseWinding If true, the winding direction of the polygon is reversed for this test.*/
+ bool contains(const Vector2& p, bool reverseWinding = false) const;
+};
+
+
+} // namespace
+#endif
diff --git a/externals/g3dlite/G3D.lib/include/G3D/CoordinateFrame.h b/externals/g3dlite/G3D.lib/include/G3D/CoordinateFrame.h
new file mode 100644
index 00000000000..705c6c93ae2
--- /dev/null
+++ b/externals/g3dlite/G3D.lib/include/G3D/CoordinateFrame.h
@@ -0,0 +1,315 @@
+/**
+ @file CoordinateFrame.h
+
+ @maintainer Morgan McGuire, morgan@cs.williams.edu
+
+ @created 2001-03-04
+ @edited 2008-07-14
+
+ Copyright 2000-2008, Morgan McGuire.
+ All rights reserved.
+*/
+
+#ifndef G3D_COORDINATEFRAME_H
+#define G3D_COORDINATEFRAME_H
+
+#include "G3D/platform.h"
+#include "G3D/Vector3.h"
+#include "G3D/Vector4.h"
+#include "G3D/Matrix3.h"
+#include "G3D/Array.h"
+#include <math.h>
+#include <string>
+#include <stdio.h>
+#include <cstdarg>
+#include <assert.h>
+
+namespace G3D {
+
+/**
+ A rigid body RT (rotation-translation) transformation.
+
+CoordinateFrame abstracts a 4x4 matrix that maps object space to world space:
+
+ v_world = C * v_object
+
+CoordinateFrame::rotation is the upper 3x3 submatrix, CoordinateFrame::translation
+is the right 3x1 column. The 4th row is always [0 0 0 1], so it isn't stored.
+So you don't have to remember which way the multiplication and transformation work,
+it provides explicit toWorldSpace and toObjectSpace methods. Also, points, vectors
+(directions), and surface normals transform differently, so they have separate methods.
+
+Some helper functions transform whole primitives like boxes in and out of object space.
+
+Convert to Matrix4 using CoordinateFrame::toMatrix4. You <I>can</I> construct a CoordinateFrame
+from a Matrix4 using Matrix4::approxCoordinateFrame, however, because a Matrix4 is more
+general than a CoordinateFrame, some information may be lost.
+
+@sa G3D::UprightFrame, G3D::PhysicsFrame, G3D::Matrix4, G3D::Quat
+*/
+class CoordinateFrame {
+public:
+
+ /** Takes object space points to world space. */
+ Matrix3 rotation;
+
+ /** Takes object space points to world space. */
+ Vector3 translation;
+
+ inline bool operator==(const CoordinateFrame& other) const {
+ return (translation == other.translation) && (rotation == other.rotation);
+ }
+
+ inline bool operator!=(const CoordinateFrame& other) const {
+ return !(*this == other);
+ }
+
+ bool fuzzyEq(const CoordinateFrame& other) const;
+
+ bool fuzzyIsIdentity() const;
+
+ bool isIdentity() const;
+
+ /**
+ Initializes to the identity coordinate frame.
+ */
+ inline CoordinateFrame() :
+ rotation(Matrix3::identity()), translation(Vector3::zero()) {
+ }
+
+ CoordinateFrame(const Vector3& _translation) :
+ rotation(Matrix3::identity()), translation(_translation) {
+ }
+
+ CoordinateFrame(const Matrix3 &rotation, const Vector3 &translation) :
+ rotation(rotation), translation(translation) {
+ }
+
+ CoordinateFrame(const Matrix3 &rotation) :
+ rotation(rotation), translation(Vector3::zero()) {
+ }
+
+ CoordinateFrame(const class UprightFrame& f);
+
+ static CoordinateFrame fromXYZYPRRadians(float x, float y, float z, float yaw = 0.0f, float pitch = 0.0f, float roll = 0.0f);
+
+ /** Construct a coordinate frame from translation = (x,y,z) and
+ rotations (in that order) about Y, object space X, object space
+ Z. Note that because object-space axes are used, these are not
+ equivalent to Euler angles; they are known as Tait-Bryan
+ rotations and are more convenient for intuitive positioning.*/
+ static CoordinateFrame fromXYZYPRDegrees(float x, float y, float z, float yaw = 0.0f, float pitch = 0.0f, float roll = 0.0f);
+
+ CoordinateFrame(class BinaryInput& b);
+
+ void deserialize(class BinaryInput& b);
+
+ void serialize(class BinaryOutput& b) const;
+
+ CoordinateFrame(const CoordinateFrame &other) :
+ rotation(other.rotation), translation(other.translation) {}
+
+ /**
+ Computes the inverse of this coordinate frame.
+ */
+ inline CoordinateFrame inverse() const {
+ CoordinateFrame out;
+ out.rotation = rotation.transpose();
+ out.translation = -out.rotation * translation;
+ return out;
+ }
+
+ inline ~CoordinateFrame() {}
+
+ /** See also Matrix4::approxCoordinateFrame */
+ class Matrix4 toMatrix4() const;
+
+ void getXYZYPRRadians(float& x, float& y, float& z, float& yaw, float& pitch, float& roll) const;
+ void getXYZYPRDegrees(float& x, float& y, float& z, float& yaw, float& pitch, float& roll) const;
+
+
+ /**
+ Produces an XML serialization of this coordinate frame.
+ @deprecated
+ */
+ std::string toXML() const;
+
+ /**
+ Returns the heading of the lookVector as an angle in radians relative to
+ the world -z axis. That is, a counter-clockwise heading where north (-z)
+ is 0 and west (-x) is PI/2.
+
+ Note that the heading ignores the Y axis, so an inverted
+ object has an inverted heading.
+ */
+ inline float getHeading() const {
+ Vector3 look = rotation.getColumn(2);
+ float angle = -(float) atan2(-look.x, look.z);
+ return angle;
+ }
+
+ /**
+ Takes the coordinate frame into object space.
+ this->inverse() * c
+ */
+ inline CoordinateFrame toObjectSpace(const CoordinateFrame& c) const {
+ return this->inverse() * c;
+ }
+
+ inline Vector4 toObjectSpace(const Vector4& v) const {
+ return this->inverse().toWorldSpace(v);
+ }
+
+ inline Vector4 toWorldSpace(const Vector4& v) const {
+ return Vector4(rotation * Vector3(v.x, v.y, v.z) + translation * v.w, v.w);
+ }
+
+ /**
+ Transforms the point into world space.
+ */
+ inline Vector3 pointToWorldSpace(const Vector3& v) const {
+ return Vector3(
+ rotation[0][0] * v[0] + rotation[0][1] * v[1] + rotation[0][2] * v[2] + translation[0],
+ rotation[1][0] * v[0] + rotation[1][1] * v[1] + rotation[1][2] * v[2] + translation[1],
+ rotation[2][0] * v[0] + rotation[2][1] * v[1] + rotation[2][2] * v[2] + translation[2]);
+ }
+
+ /**
+ Transforms the point into object space. Assumes that the rotation matrix is orthonormal.
+ */
+ inline Vector3 pointToObjectSpace(const Vector3& v) const {
+ float p[3];
+ p[0] = v[0] - translation[0];
+ p[1] = v[1] - translation[1];
+ p[2] = v[2] - translation[2];
+ debugAssert(G3D::fuzzyEq(rotation.determinant(), 1.0f));
+ return Vector3(
+ rotation[0][0] * p[0] + rotation[1][0] * p[1] + rotation[2][0] * p[2],
+ rotation[0][1] * p[0] + rotation[1][1] * p[1] + rotation[2][1] * p[2],
+ rotation[0][2] * p[0] + rotation[1][2] * p[1] + rotation[2][2] * p[2]);
+ }
+
+ /**
+ Transforms the vector into world space (no translation).
+ */
+ inline Vector3 vectorToWorldSpace(const Vector3& v) const {
+ return rotation * v;
+ }
+
+ inline Vector3 normalToWorldSpace(const Vector3& v) const {
+ return rotation * v;
+ }
+
+ class Ray toObjectSpace(const Ray& r) const;
+
+ Ray toWorldSpace(const Ray& r) const;
+
+ /**
+ Transforms the vector into object space (no translation).
+ */
+ inline Vector3 vectorToObjectSpace(const Vector3 &v) const {
+ // Multiply on the left (same as rotation.transpose() * v)
+ return v * rotation;
+ }
+
+ inline Vector3 normalToObjectSpace(const Vector3 &v) const {
+ // Multiply on the left (same as rotation.transpose() * v)
+ return v * rotation;
+ }
+
+ void pointToWorldSpace(const Array<Vector3>& v, Array<Vector3>& vout) const;
+
+ void normalToWorldSpace(const Array<Vector3>& v, Array<Vector3>& vout) const;
+
+ void vectorToWorldSpace(const Array<Vector3>& v, Array<Vector3>& vout) const;
+
+ void pointToObjectSpace(const Array<Vector3>& v, Array<Vector3>& vout) const;
+
+ void normalToObjectSpace(const Array<Vector3>& v, Array<Vector3>& vout) const;
+
+ void vectorToObjectSpace(const Array<Vector3>& v, Array<Vector3>& vout) const;
+
+ class Box toWorldSpace(const class AABox& b) const;
+
+ class Box toWorldSpace(const class Box& b) const;
+
+ class Cylinder toWorldSpace(const class Cylinder& b) const;
+
+ class Capsule toWorldSpace(const class Capsule& b) const;
+
+ class Plane toWorldSpace(const class Plane& p) const;
+
+ class Sphere toWorldSpace(const class Sphere& b) const;
+
+ class Triangle toWorldSpace(const class Triangle& t) const;
+
+ class Box toObjectSpace(const AABox& b) const;
+
+ class Box toObjectSpace(const Box& b) const;
+
+ class Plane toObjectSpace(const Plane& p) const;
+
+ class Sphere toObjectSpace(const Sphere& b) const;
+
+ Triangle toObjectSpace(const Triangle& t) const;
+
+ /** Compose: create the transformation that is <I>other</I> followed by <I>this</I>.*/
+ CoordinateFrame operator*(const CoordinateFrame &other) const {
+ return CoordinateFrame(rotation * other.rotation,
+ pointToWorldSpace(other.translation));
+ }
+
+ CoordinateFrame operator+(const Vector3& v) const {
+ return CoordinateFrame(rotation, translation + v);
+ }
+
+ CoordinateFrame operator-(const Vector3& v) const {
+ return CoordinateFrame(rotation, translation - v);
+ }
+
+ void lookAt(const Vector3& target);
+
+ void lookAt(
+ const Vector3& target,
+ Vector3 up);
+
+ /** The direction this camera is looking (its negative z axis)*/
+ inline Vector3 lookVector() const {
+ return -rotation.getColumn(2);
+ }
+
+ /** Returns the ray starting at the camera origin travelling in direction CoordinateFrame::lookVector. */
+ class Ray lookRay() const;
+
+ /** Up direction for this camera (its y axis). */
+ inline Vector3 upVector() const {
+ return rotation.getColumn(1);
+ }
+
+ inline Vector3 rightVector() const {
+ return rotation.getColumn(0);
+ }
+
+ /**
+ If a viewer looks along the look vector, this is the viewer's "left".
+ Useful for strafing motions and building alternative coordinate frames.
+ */
+ inline Vector3 leftVector() const {
+ return -rotation.getColumn(0);
+ }
+
+ /**
+ Linearly interpolates between two coordinate frames, using
+ Quat::slerp for the rotations.
+ */
+ CoordinateFrame lerp(
+ const CoordinateFrame& other,
+ float alpha) const;
+
+};
+
+typedef CoordinateFrame CFrame;
+
+} // namespace
+
+#endif
diff --git a/externals/g3dlite/G3D.lib/include/G3D/Crypto.h b/externals/g3dlite/G3D.lib/include/G3D/Crypto.h
new file mode 100644
index 00000000000..f8266b8721b
--- /dev/null
+++ b/externals/g3dlite/G3D.lib/include/G3D/Crypto.h
@@ -0,0 +1,96 @@
+/**
+ @file Crypto.h
+
+ @maintainer Morgan McGuire, matrix@graphics3d.com
+
+
+ @created 2006-03-29
+ @edited 2006-04-06
+ */
+
+#ifndef G3D_CRYPTO_H
+#define G3D_CRYPTO_H
+
+#include "G3D/platform.h"
+#include "G3D/g3dmath.h"
+#include <string>
+
+namespace G3D {
+
+/** See G3D::Crypto::md5 */
+class MD5Hash {
+private:
+
+ uint8 value[16];
+
+public:
+
+ MD5Hash() {
+ for (int i = 0; i < 16; ++i) {
+ value[i] = 0;
+ }
+ }
+
+ explicit MD5Hash(class BinaryInput& b);
+
+ uint8& operator[](int i) {
+ return value[i];
+ }
+
+ const uint8& operator[](int i) const {
+ return value[i];
+ }
+
+ bool operator==(const MD5Hash& other) const {
+ bool match = true;
+ for (int i = 0; i < 16; ++i) {
+ match = match && (other.value[i] == value[i]);
+ }
+ return match;
+ }
+
+ inline bool operator!=(const MD5Hash& other) const {
+ return !(*this == other);
+ }
+
+ void deserialize(class BinaryInput& b);
+
+ void serialize(class BinaryOutput& b) const;
+};
+
+
+/** Cryptography and hashing helper functions */
+class Crypto {
+public:
+
+ /**
+ Computes the CRC32 value of a byte array. CRC32 is designed to be a hash
+ function that produces different values for similar strings.
+
+ This implementation is compatible with PKZIP and GZIP.
+
+ Based on http://www.gamedev.net/reference/programming/features/crc32/
+ */
+ static uint32 crc32(const void* bytes, size_t numBytes);
+
+ /**
+ Computes the MD5 hash (message digest) of a byte stream, as defined by
+ http://www.ietf.org/rfc/rfc1321.txt.
+
+ @cite Based on implementation by L. Peter Deutsch, ghost@aladdin.com
+ */
+ MD5Hash md5(const void* bytes, size_t numBytes);
+
+ /**
+ Returns the nth prime less than 2000 in constant time. The first prime has index
+ 0 and is the number 2.
+ */
+ static int smallPrime(int n);
+
+ /** Returns 1 + the largest value that can be passed to smallPrime. */
+ static int numSmallPrimes();
+};
+
+}
+
+#endif
diff --git a/externals/g3dlite/G3D.lib/include/G3D/Cylinder.h b/externals/g3dlite/G3D.lib/include/G3D/Cylinder.h
new file mode 100644
index 00000000000..c341d29a2b9
--- /dev/null
+++ b/externals/g3dlite/G3D.lib/include/G3D/Cylinder.h
@@ -0,0 +1,92 @@
+/**
+ @file Cylinder.h
+
+ @maintainer Morgan McGuire, matrix@graphics3d.com
+
+ @created 2003-02-07
+ @edited 2005-09-26
+
+ Copyright 2000-2005, Morgan McGuire.
+ All rights reserved.
+ */
+
+#ifndef G3D_Cylinder_H
+#define G3D_Cylinder_H
+
+#include "G3D/platform.h"
+#include "G3D/g3dmath.h"
+#include "G3D/Vector3.h"
+
+namespace G3D {
+
+class Line;
+class AABox;
+/**
+ Right cylinder
+ */
+class Cylinder {
+private:
+ Vector3 p1;
+ Vector3 p2;
+
+ float mRadius;
+
+public:
+
+ /** Uninitialized */
+ Cylinder();
+ Cylinder(class BinaryInput& b);
+ Cylinder(const Vector3& _p1, const Vector3& _p2, float _r);
+ void serialize(class BinaryOutput& b) const;
+ void deserialize(class BinaryInput& b);
+
+ /** The line down the center of the Cylinder */
+ Line axis() const;
+
+ /**
+ A reference frame in which the center of mass is at the origin and
+ the Y-axis is the cylinder's axis. If the cylinder is transformed, this reference frame
+ may freely rotate around its axis.*/
+ void getReferenceFrame(class CoordinateFrame& cframe) const;
+
+ /** Returns point 0 or 1 */
+ inline const Vector3& point(int i) const {
+ debugAssert(i >= 0 && i <= 1);
+ return (i == 0) ? p1 : p2;
+ }
+
+ /**
+ Returns true if the point is inside the Cylinder or on its surface.
+ */
+ bool contains(const Vector3& p) const;
+
+ float area() const;
+
+ float volume() const;
+
+ float radius() const;
+
+ /** Center of mass */
+ inline Vector3 center() const {
+ return (p1 + p2) / 2.0f;
+ }
+
+ inline float height() const {
+ return (p1 - p2).magnitude();
+ }
+
+ /**
+ Get close axis aligned bounding box.
+ With vertical world orientation, the top and bottom might not be very tight. */
+ void getBounds(AABox& out) const;
+
+ /** Random world space point with outward facing normal. */
+ void getRandomSurfacePoint(Vector3& P, Vector3& N) const;
+
+ /** Point selected uniformly at random over the volume. */
+ Vector3 randomInteriorPoint() const;
+};
+
+} // namespace
+
+#endif
diff --git a/externals/g3dlite/G3D.lib/include/G3D/Discovery.h b/externals/g3dlite/G3D.lib/include/G3D/Discovery.h
new file mode 100644
index 00000000000..acd1c2c01d0
--- /dev/null
+++ b/externals/g3dlite/G3D.lib/include/G3D/Discovery.h
@@ -0,0 +1,589 @@
+/**
+ @file Discovery.h
+
+ <H2>Discovery</H2>
+ Discovery is the process by which computers on a Local Area Network (LAN) find
+ one another. The Discovery API allows clients to make a list of servers running
+ the same application. The application typically presents this list to the user
+ so he can choose which server to connect to.
+ <P>
+ Features of the Discovery API:
+ <P>
+ Low network traffic
+ <BR> - Broadcasts mainly occur only when a new machine enters/leaves the network
+ <BR>Responsive
+ <BR> - Servers appear immediately after launching
+ <BR> - Servers disappear immedatiately after they shut down
+ <BR>Extensible
+ <BR> - Add your own game information (e.g. score, num players, map name)
+ <BR>Versioned
+ <BR> - Tracks incompatible servers so end-users know to upgrade their client/server
+
+ <H2>Using the Discovery API</H2>
+
+ Subclass DiscoveryAdvertisement to add fields describing a server running your
+ application. For a game, these might be the current map name and the number
+ of players.
+ <P>
+ On the client, create an instance of DiscoveryClient<your advertisement subclass>.
+ On the server, create an instance of DiscoveryServer and initialize it with an
+ instance of your advertisement subclass. From your main loop, call doNetwork()
+ on the client and server instances. When your server shuts down, invoke cleanup()
+ on it.
+
+ @maintainer Morgan McGuire, matrix@graphics3d.com
+
+ @created 2003-06-26
+ @edited 2008-11-24
+ */
+
+#ifndef G3D_DISCOVERY_H
+#define G3D_DISCOVERY_H
+
+#include "G3D/platform.h"
+#include "G3D/G3DGameUnits.h"
+#include "G3D/NetworkDevice.h"
+#include "G3D/Log.h"
+#include <time.h>
+
+/**
+ Different versions of G3D discovery protocols can't communicate with each other.
+ */
+#define G3D_DISCOVERY_PROTOCOL_NAME "G3D DISC"
+#define G3D_DISCOVERY_PROTOCOL_VERSION 1
+
+namespace G3D {
+
+/**
+ If a machine is running two different programs using discovery they
+ must have different ports. However different programs can share the
+ same ports if they run on the same LAN with different servers.
+
+ @deprecated See Discovery2::Settings
+ */
+class DiscoverySettings {
+public:
+
+ /**
+ Name of the program using discovery; used so that mutliple
+ programs can use the same discovery ports on the same network.
+ */
+ const char* appProtocolName;
+
+ /**
+ Version of the network protocol of the program using discovery.
+ Used so that discovery can identify incompatible versions of
+ the server.
+ */
+ int appProtocolVersion;
+
+ /**
+ Port on which the server broadcasts its identity. The client
+ and server must agree on this value.
+ */
+ uint16 serverBroadcastPort;
+
+ /**
+ Port on which the client broadcasts a server request. The client
+ and server must agree on this value.
+ */
+ uint16 clientBroadcastPort;
+
+ /**
+ Clients connect into this port using a reliable conduit
+ to receive the advertisement from a server. The client
+ doesn't look at this value; it uses whatever the server
+ sends it.
+ */
+ uint16 serverAdvertisementPort;
+
+ /**
+ You can use the default G3D discovery ports as long as no other
+ program with the same protocol name is using this port. You
+ <B>can</B> run two different G3D discovery programs on the same
+ two ports as long as they have different application protocol
+ strings.
+ */
+ DiscoverySettings(
+ const char* _appProtocolName,
+ int _appProtocolVersion,
+ uint16 _serverBroadcast = 6173,
+ uint16 _clientBroadcast = 6174,
+ uint16 _serverAdvertisementPort = 6175) :
+ appProtocolName(_appProtocolName),
+ appProtocolVersion(_appProtocolVersion),
+ serverBroadcastPort(_serverBroadcast),
+ clientBroadcastPort(_clientBroadcast),
+ serverAdvertisementPort(_serverAdvertisementPort) {}
+};
+
+/**
+ Make your own subclass of this advertisement. Add fields
+ (e.g. numPlayers, currentScore) to increase the amount
+ of information advertised.
+
+ Overrides must provide a default constructor.
+ @deprecated See Discovery2::Settings
+ */
+class DiscoveryAdvertisement {
+public:
+
+ /**
+ Address to connect to on the server for the actual game.
+ The IP portion is ignored (the client figures out the IP
+ address from the packet itself) but the port is essential.
+ Note that this port must not be the discovery port.
+ */
+ NetAddress address;
+
+ /**
+ (Only used on the client)
+ Time since this advertisement was updated.
+ */
+ RealTime lastUpdateTime;
+
+ /**
+ Overrides must call DiscoveryAdvertisement::serialize(b) first.
+ */
+ virtual void serialize(BinaryOutput& b) const;
+
+ /**
+ Overrides must call DiscoveryAdvertisement::deserialize(b) first.
+ */
+ virtual void deserialize(BinaryInput& b);
+
+ /**
+ An empty virtual destructor for virtual methods.
+ */
+ virtual ~DiscoveryAdvertisement() {}
+};
+
+
+/**
+ Sent by servers to describe their location.
+ */
+class DiscoveryServerAddressMessage {
+public:
+
+ /**
+ Not part of the message; these settings are used to determine
+ if the correct protocol is being used.
+ */
+ const DiscoverySettings* settings;
+
+
+ /**
+ Set to true if this server is running the correct protocol.
+ */
+ bool correctProtocol;
+
+ /**
+ This is set during the serialize process from the server's settings.
+ If different from the client's settings the discovery system will
+ classify this server as incompatible.
+ */
+ int serverProtocolVersion[2];
+
+ Array<NetAddress> address;
+
+ DiscoveryServerAddressMessage() {}
+ DiscoveryServerAddressMessage(const DiscoverySettings* s) : settings(s) {}
+
+ void serialize(BinaryOutput& b) const;
+
+ void deserialize(BinaryInput& b);
+};
+
+
+/**
+ Base class for DiscoveryClient and DiscoveryServer.
+ */
+class Discovery {
+public:
+
+ const DiscoverySettings* settings;
+
+ enum {
+ SERVER_SHUTDOWN_MESSAGE = 2,
+ SERVER_BROADCAST_MESSAGE = 3,
+ CLIENT_BROADCAST_MESSAGE = 4};
+
+ /**
+ Only called from subclasses.
+ */
+ virtual void init(
+ const DiscoverySettings* _settings) {
+ settings = _settings;
+ }
+
+ /**
+ An empty virtual destructor for virtual methods.
+ */
+ virtual ~Discovery() {}
+};
+
+/** @deprecated See Discovery2::Server*/
+class DiscoveryServer : private Discovery {
+private:
+
+ class ShutdownMessage {
+ public:
+ void serialize(BinaryOutput& b) const { (void)b; }
+
+ void deserialize(BinaryInput& b) { (void)b; }
+ };
+
+ /**
+ For broadcast.
+ */
+ LightweightConduitRef net;
+
+ /**
+ Listen for clients wanting to hear the advertisement over
+ a reliable connection.
+ */
+ NetListenerRef listener;
+
+ DiscoveryAdvertisement* advertisement;
+
+ /**
+ Broadcast across the lightweight conduit.
+ */
+ DiscoveryServerAddressMessage addressMessage;
+
+ /**
+ Servers periodically broadcast (unsolicited) in case
+ anyone missed the previous message.
+ */
+ RealTime lastBroadcast;
+
+ void sendAnnouncement() const;
+
+ void sendShutDown() const;
+
+public:
+
+ /**
+ You may update the advertisement (synchronously with calling doNetwork)
+ after it has been passed in. This allows a server to change the advertised
+ number of players or score for a game, for example.
+
+ You must set the port of the @a _advertisement G3D::DiscoveryAdvertisement::address
+ to the port which the G3D::NetListener for the actual program protocol (not discovery)
+ is running. That field how the client knows what address to connect to using
+ G3D::ReliableConduit or G3D::LightweightConduit to actually initiate communication.
+ */
+ virtual void init(
+ const DiscoverySettings* _settings,
+ DiscoveryAdvertisement* _advertisement);
+
+ /** Returns the broadcast address in use.*/
+ NetAddress broadcastAddress() const;
+
+ /**
+ Returns true if this discovery server has been initialized
+ and is functioning properly.
+ */
+ bool ok() const;
+
+ /**
+ Call periodically to let the server do its job.
+ */
+ void doNetwork();
+
+ /**
+ Broadcast a shutdown message.
+ */
+ void cleanup();
+};
+
+
+/**
+ Used by DiscoveryClient to report servers running a different version
+ of this application's protocol.
+ */
+class IncompatibleServerDescription {
+public:
+ NetAddress address;
+ int protocolVersion[2];
+ RealTime lastUpdateTime;
+
+ std::string toString() const;
+};
+
+
+/**
+ Only one DiscoveryClient can be active on a given port at a time on
+ a single computer.
+
+ AdType must be a subclass of DiscoveryAdvertisement.
+
+ @deprecated See Discovery2::Client
+ */
+template<class AdType>
+class DiscoveryClient : private Discovery {
+public:
+
+ /**
+ List of servers. Do not access on a second thread while in
+ doNetwork.
+ */
+ Array<AdType> serverList;
+
+ /**
+ List of servers running the same application but a different protocol.
+ It is useful to show these to users to help them recognize version
+ conflicts between client and server.
+ Do not access on a second thread while in doNetwork.
+ */
+ Array<IncompatibleServerDescription> incompatibleServerList;
+
+private:
+
+ class BroadcastMessage {
+ public:
+ void serialize(BinaryOutput& b) const {}
+
+ void deserialize(BinaryInput& b) {}
+ };
+
+ /**
+ The client periodically checks servers to make sure they are still up
+ and to update its information about them.
+ */
+ RealTime lastServerCheck;
+
+ LightweightConduitRef net;
+
+ /**
+ Returns an index in serverList of the server with the given address.
+ Returns -1 if there is none. Only checks IP addresses.
+ */
+ int findServerListIndex(const NetAddress& addr) const {
+ for (int i = 0; i < serverList.size(); ++i) {
+ if (addr.ip() == serverList[i].address.ip()) {
+ return i;
+ }
+ }
+
+ return -1;
+ }
+
+ /**
+ Returns true if this discovery client has been initialized
+ and is functioning properly.
+ */
+ bool ok() const {
+ return net->ok();
+ }
+
+ /**
+ Adds a server to the incompatible list if it is not already there.
+ */
+ void addToIncompatibleList(const NetAddress& addr, uint32 p0, uint32 p1) {
+ const RealTime now = System::time();
+
+ bool alreadyHere = false;
+
+ // Incorrect protocol; add to the incompatible list
+ for (int i = 0; i < incompatibleServerList.size(); ++i) {
+ IncompatibleServerDescription& server = incompatibleServerList[i];
+
+ if (server.address == addr) {
+ server.lastUpdateTime = now;
+ alreadyHere = true;
+ break;
+ }
+ }
+
+ if (! alreadyHere) {
+ IncompatibleServerDescription server;
+
+ server.lastUpdateTime = now;
+ server.address = addr;
+ server.protocolVersion[0] = p0;
+ server.protocolVersion[1] = p1;
+
+ incompatibleServerList.append(server);
+ }
+ }
+
+ /**
+ Connects to the specified server, reads its advertisement,
+ and adds it to the active server list. Returns true if the server
+ can be reached.
+ */
+ bool readAdvertisement(const NetAddress& address) {
+ std::string hostname = address.toString();
+
+ RealTime TIMEOUT = 2.0;
+
+ ReliableConduitRef server = ReliableConduit::create(address);
+
+ if (! server->ok()) {
+ return false;
+ }
+
+ AdType advertisement;
+
+ // Read the advertisement
+ RealTime stopWaiting = System::time() + TIMEOUT;
+ bool timedOut = false;
+
+ while (! server->messageWaiting() && ! timedOut && server->ok()) {
+ System::sleep(0.1);
+ timedOut = (System::time() > stopWaiting);
+ }
+
+ if (timedOut) {
+ Log::common()->printf("Discovery: Timed out while reading advertisment from %s\n",
+ hostname.c_str());
+ return false;
+ }
+
+
+ if (! server->ok()) {
+ Log::common()->printf("Discovery: Server %s dropped connection\n", hostname.c_str());
+ return false;
+ }
+
+ // Read the advertisement
+ debugAssert(server->messageWaiting());
+ if (! server->receive(advertisement)) {
+ Log::common()->printf("Discovery: Server %s failed to send advertisement\n", hostname.c_str());
+ return false;
+ }
+
+ // Update existing server info or create a new entry
+ int index = findServerListIndex(address);
+ if (index == -1) {
+ index = serverList.size();
+ serverList.resize(index + 1);
+ }
+
+ // Update element index
+ advertisement.address = address;
+ serverList[index] = advertisement;
+
+ return true;
+ }
+
+ /**
+ Remove this address from our list if we previously
+ had a server there.
+ */
+ void removeServer(const NetAddress& address) {
+ int index = findServerListIndex(address);
+ if (index > -1) {
+ serverList.fastRemove(index);
+ }
+ }
+
+ /**
+ Tries to connect to the server through the addresses in the array.
+ */
+ void addToServerList(const Array<NetAddress>& addressArray) {
+ // Try to connect to each address listed
+ for (int a = addressArray.size() - 1; a >= 0; --a) {
+ const NetAddress& address = addressArray[a];
+
+ if (readAdvertisement(address)) {
+ // We've connected to the server
+ break;
+ } else {
+ removeServer(address);
+ }
+ }
+ }
+
+ void checkRandomServer() {
+ if (serverList.size() >= 1) {
+ int index = iRandom(0, serverList.size() - 1);
+
+ Array<NetAddress> address;
+ address.append(serverList[index].address);
+
+ // Remove this server
+ serverList.fastRemove(index);
+
+ // Add it back with new info (or leave it removed if no response)
+ addToServerList(address);
+ }
+ }
+
+public:
+
+ void init(
+ const DiscoverySettings* _settings) {
+
+ Discovery::init(_settings);
+
+ lastServerCheck = System::time();
+
+ net = LightweightConduit::create(settings->serverBroadcastPort, true, true);
+
+ // Send announcement
+ NetAddress broadcast(NetworkDevice::instance()->broadcastAddressArray()[0],
+ settings->clientBroadcastPort);
+ BroadcastMessage tmp;
+ net->send(broadcast, CLIENT_BROADCAST_MESSAGE, tmp);
+ }
+
+ /** Shut down the discovery client. */
+ void cleanup() {
+ net = NULL;
+ }
+
+ /**
+ Call this regularly (several times per second) to
+ update the server list. Not threadsafe-- you must not touch
+ the server list while this is running. This will not block.
+ */
+ void doNetwork() {
+ if (net->messageWaiting()) {
+ NetAddress sender;
+
+ switch (net->waitingMessageType()) {
+ case SERVER_SHUTDOWN_MESSAGE:
+ // Remove the server
+ net->receive(sender);
+ removeServer(sender);
+ break;
+
+ case SERVER_BROADCAST_MESSAGE:
+ // Check the G3D protocol and the network protocol, then read the ad
+ DiscoveryServerAddressMessage msg(settings);
+ net->receive(sender, msg);
+
+ if (msg.correctProtocol && (msg.address.size() > 0)) {
+ // Add the actual return address as the first one to be tried.
+ msg.address.append(NetAddress(sender.ip(), msg.address[0].port()));
+
+ addToServerList(msg.address);
+
+ } else {
+
+ addToIncompatibleList(
+ sender,
+ msg.serverProtocolVersion[0],
+ msg.serverProtocolVersion[1]);
+ }
+ break;
+ }
+ }
+
+ // Periodically re-check servers in the list to see if they crashed
+ // (if they shut down, they should have broadcast a shut down message).
+ RealTime now = System::time();
+ const RealTime UPDATE_TIME_INTERVAL = 30;
+
+ if (now > lastServerCheck + UPDATE_TIME_INTERVAL) {
+ lastServerCheck = now;
+ checkRandomServer();
+ }
+ }
+};
+
+}
+
+#endif
diff --git a/externals/g3dlite/G3D.lib/include/G3D/EqualsTrait.h b/externals/g3dlite/G3D.lib/include/G3D/EqualsTrait.h
new file mode 100644
index 00000000000..acf17615b45
--- /dev/null
+++ b/externals/g3dlite/G3D.lib/include/G3D/EqualsTrait.h
@@ -0,0 +1,26 @@
+/**
+ @file EqualsTrait.h
+
+ @maintainer Morgan McGuire, morgan@cs.williams.edu
+ @created 2008-10-01
+ @edited 2008-10-01
+ Copyright 2000-2008, Morgan McGuire.
+ All rights reserved.
+ */
+
+#ifndef G3D_EQUALSTRAIT_H
+#define G3D_EQUALSTRAIT_H
+
+#include "G3D/platform.h"
+
+/** Default implementation of EqualsTrait.
+ @see G3D::Table for specialization requirements.
+*/
+template<typename Key> struct EqualsTrait {
+ static bool equals(const Key& a, const Key& b) {
+ return a == b;
+ }
+};
+
+#endif
+
diff --git a/externals/g3dlite/G3D.lib/include/G3D/G3D.h b/externals/g3dlite/G3D.lib/include/G3D/G3D.h
new file mode 100644
index 00000000000..42ab18e2c24
--- /dev/null
+++ b/externals/g3dlite/G3D.lib/include/G3D/G3D.h
@@ -0,0 +1,150 @@
+/**
+ @file graphics3D.h
+
+ This header includes all of the graphics3D libraries in
+ appropriate namespaces.
+
+ @maintainer Morgan McGuire, matrix@graphics3d.com
+
+ @created 2001-08-25
+ @edited 2008-11-01
+
+ Copyright 2000-2008, Morgan McGuire.
+ All rights reserved.
+*/
+
+#ifndef G3D_GRAPHICS3D_H
+#define G3D_GRAPHICS3D_H
+
+#define NOMINMAX 1
+#ifdef min
+ #undef min
+#endif
+#ifdef max
+ #undef max
+#endif
+
+#include "G3D/platform.h"
+#include "G3D/Array.h"
+#include "G3D/Queue.h"
+#include "G3D/Crypto.h"
+#include "G3D/format.h"
+#include "G3D/Vector2.h"
+#include "G3D/Vector3.h"
+#include "G3D/Vector4.h"
+#include "G3D/Color1.h"
+#include "G3D/Color3.h"
+#include "G3D/Color4.h"
+#include "G3D/Matrix2.h"
+#include "G3D/Matrix3.h"
+#include "G3D/Matrix4.h"
+#include "G3D/CoordinateFrame.h"
+#include "G3D/PhysicsFrame.h"
+#include "G3D/Plane.h"
+#include "G3D/Line.h"
+#include "G3D/Ray.h"
+#include "G3D/Sphere.h"
+#include "G3D/Box.h"
+#include "G3D/AABox.h"
+#include "G3D/WrapMode.h"
+#include "G3D/Cone.h"
+#include "G3D/Quat.h"
+#include "G3D/stringutils.h"
+#include "G3D/prompt.h"
+#include "G3D/Table.h"
+#include "G3D/Set.h"
+#include "G3D/GUniqueID.h"
+#include "G3D/BinaryFormat.h"
+#include "G3D/BinaryInput.h"
+#include "G3D/BinaryOutput.h"
+#include "G3D/debug.h"
+#include "G3D/G3DGameUnits.h"
+#include "G3D/g3dmath.h"
+#include "G3D/uint128.h"
+#include "G3D/fileutils.h"
+#include "G3D/ReferenceCount.h"
+
+template<class T> struct HashTrait< G3D::ReferenceCountedPointer<T> > {
+ static size_t hashCode(G3D::ReferenceCountedPointer<T> key) { return reinterpret_cast<size_t>( key.pointer() ); }
+};
+
+#include "G3D/GImage.h"
+#include "G3D/CollisionDetection.h"
+#include "G3D/Log.h"
+#include "G3D/serialize.h"
+#include "G3D/TextInput.h"
+#include "G3D/NetAddress.h"
+#include "G3D/NetworkDevice.h"
+#include "G3D/System.h"
+#include "G3D/splinefunc.h"
+#include "G3D/Spline.h"
+#include "G3D/UprightFrame.h"
+#include "G3D/LineSegment.h"
+#include "G3D/Capsule.h"
+#include "G3D/Cylinder.h"
+#include "G3D/Triangle.h"
+#include "G3D/Color3uint8.h"
+#include "G3D/Color4uint8.h"
+#include "G3D/Vector2int16.h"
+#include "G3D/Vector3int16.h"
+#include "G3D/Vector3int32.h"
+#include "G3D/Vector4int8.h"
+#include "G3D/ConvexPolyhedron.h"
+#include "G3D/Discovery.h"
+#include "G3D/MeshAlg.h"
+#include "G3D/vectorMath.h"
+#include "G3D/Rect2D.h"
+#include "G3D/GCamera.h"
+#include "G3D/GLight.h"
+#include "G3D/AABSPTree.h"
+#include "G3D/PointAABSPTree.h"
+#include "G3D/TextOutput.h"
+#include "G3D/MeshBuilder.h"
+#include "G3D/Stopwatch.h"
+#include "G3D/AtomicInt32.h"
+#include "G3D/GThread.h"
+#include "G3D/ThreadSet.h"
+#include "G3D/RegistryUtil.h"
+#include "G3D/AnyVal.h"
+#include "G3D/PointHashGrid.h"
+#include "G3D/Map2D.h"
+#include "G3D/Image1.h"
+#include "G3D/Image1uint8.h"
+#include "G3D/Image3.h"
+#include "G3D/Image3uint8.h"
+#include "G3D/Image4.h"
+#include "G3D/Image4uint8.h"
+#include "G3D/filter.h"
+#include "G3D/WeakCache.h"
+#include "G3D/Pointer.h"
+#include "G3D/Matrix.h"
+#include "G3D/ImageFormat.h"
+
+#ifdef _MSC_VER
+# pragma comment(lib, "zlib")
+# pragma comment(lib, "ws2_32")
+# pragma comment(lib, "winmm")
+# pragma comment(lib, "imagehlp")
+# pragma comment(lib, "gdi32")
+# pragma comment(lib, "user32")
+# pragma comment(lib, "kernel32")
+# pragma comment(lib, "version")
+# pragma comment(lib, "advapi32")
+# pragma comment(lib, "png")
+# pragma comment(lib, "jpeg")
+# pragma comment(lib, "zip")
+# ifdef _DEBUG
+ // Don't link against G3D when building G3D itself.
+# ifndef G3D_BUILDING_LIBRARY_DLL
+# pragma comment(lib, "G3Dd.lib")
+# endif
+# else
+ // Don't link against G3D when building G3D itself.
+# ifndef G3D_BUILDING_LIBRARY_DLL
+# pragma comment(lib, "G3D.lib")
+# endif
+# endif
+#endif
+
+#endif
+
diff --git a/externals/g3dlite/G3D.lib/include/G3D/G3DAll.h b/externals/g3dlite/G3D.lib/include/G3D/G3DAll.h
new file mode 100644
index 00000000000..feba3d6d390
--- /dev/null
+++ b/externals/g3dlite/G3D.lib/include/G3D/G3DAll.h
@@ -0,0 +1,26 @@
+/**
+ @file G3DAll.h
+
+ Includes all G3D and GLG3D files and uses the G3D namespace.
+
+ This requires OpenGL and SDL headers. If you don't want all of this,
+ #include <G3D.h> separately.
+
+ @maintainer Morgan McGuire, matrix@graphics3d.com
+
+ @created 2002-01-01
+ @edited 2006-08-13
+
+ Copyright 2000-2006, Morgan McGuire.
+ All rights reserved.
+ */
+
+#ifndef G3D_G3DALL_H
+#define G3D_G3DALL_H
+
+#include "G3D/G3D.h"
+#include "GLG3D/GLG3D.h"
+
+using namespace G3D;
+
+#endif
diff --git a/externals/g3dlite/G3D.lib/include/G3D/G3DGameUnits.h b/externals/g3dlite/G3D.lib/include/G3D/G3DGameUnits.h
new file mode 100644
index 00000000000..bc4c873f290
--- /dev/null
+++ b/externals/g3dlite/G3D.lib/include/G3D/G3DGameUnits.h
@@ -0,0 +1,44 @@
+/**
+ @file G3DGameUnits.h
+
+ @maintainer Morgan McGuire, matrix@graphics3d.com
+ @created 2002-10-05
+ @edited 2006-11-10
+ */
+
+#ifndef G3D_GAMEUNITS_H
+#define G3D_GAMEUNITS_H
+
+#include "G3D/platform.h"
+
+namespace G3D {
+/**
+ Time, in seconds.
+ */
+typedef double GameTime;
+typedef double SimTime;
+
+/**
+ Actual wall clock time in seconds.
+ */
+typedef double RealTime;
+
+enum AMPM {AM, PM};
+
+enum {SECOND=1, MINUTE=60, HOUR = 60*60, DAY=24*60*60, SUNRISE=24*60*60/4, SUNSET=24*60*60*3/4, MIDNIGHT=0, METER=1, KILOMETER=1000};
+
+#define CENTIMETER (0.01)
+#define DECIMETER (0.1)
+
+/**
+ Converts a 12 hour clock time into the number of seconds since
+ midnight. Note that 12:00 PM is noon and 12:00 AM is midnight.
+
+ Example: <CODE>toSeconds(10, 00, AM)</CODE>
+ */
+SimTime toSeconds(int hour, int minute, double seconds, AMPM ap);
+SimTime toSeconds(int hour, int minute, AMPM ap);
+
+}
+
+#endif
diff --git a/externals/g3dlite/G3D.lib/include/G3D/GCamera.h b/externals/g3dlite/G3D.lib/include/G3D/GCamera.h
new file mode 100644
index 00000000000..4dfa500883b
--- /dev/null
+++ b/externals/g3dlite/G3D.lib/include/G3D/GCamera.h
@@ -0,0 +1,294 @@
+/**
+ @file GCamera.h
+
+ @maintainer Morgan McGuire, matrix@graphics3d.com
+
+ @created 2005-07-20
+ @edited 2007-07-24
+*/
+
+#ifndef G3D_GCamera_H
+#define G3D_GCamera_H
+
+#include "G3D/platform.h"
+#include "G3D/CoordinateFrame.h"
+#include "G3D/Vector3.h"
+#include "G3D/Plane.h"
+#include "G3D/debugAssert.h"
+
+namespace G3D {
+
+class Matrix4;
+class Rect2D;
+
+/**
+ Abstraction of a pinhole camera.
+
+ The area a camera sees is called a frustum. It is bounded by the near plane, the far plane, and the sides
+ of the view frame projected into the scene. It has the shape of a pyramid with the top cut off.
+
+ Cameras can project points from 3D to 2D. The "unit" projection matches OpenGL. It maps the entire view frustum
+ to a cube of unit radius (i.e., edges of length 2) centered at the origin. The non-unit projection then maps
+ that cube to the specified pixel viewport in X and Y and the range [0, 1] in Z. The projection is reversable
+ as long as the projected Z value is known.
+
+ All viewport arguments are the pixel bounds of the viewport-- e.g.,
+ RenderDevice::viewport().
+ */
+class GCamera {
+
+public:
+ /**
+ Stores the direction of the field of view
+ */
+ enum FOVDirection {HORIZONTAL, VERTICAL};
+
+private:
+
+
+ /** field of view (in radians) */
+ float m_fieldOfView;
+
+ /** Clipping plane, *not* imaging plane. Negative numbers. */
+ float m_nearPlaneZ;
+
+ /** Negative */
+ float m_farPlaneZ;
+
+ /** Stores the camera's location and orientation */
+ CoordinateFrame m_cframe;
+
+ /** Horizontal or Vertical */
+ FOVDirection m_direction;
+
+public:
+
+ class Frustum {
+ public:
+ class Face {
+ public:
+ /** Counter clockwise indices into vertexPos */
+ int vertexIndex[4];
+
+ /** The plane containing the face. */
+ Plane plane;
+ };
+
+ /** The vertices, in homogeneous space. If w == 0,
+ a vertex is at infinity. */
+ Array<Vector4> vertexPos;
+
+ /** The faces in the frustum. When the
+ far plane is at infinity, there are 5 faces,
+ otherwise there are 6. The faces are in the order
+ N,R,L,B,T,[F].
+ */
+ Array<Face> faceArray;
+ };
+
+ GCamera();
+
+ virtual ~GCamera();
+
+ /** Returns the current coordinate frame */
+ const CoordinateFrame& coordinateFrame() const {
+ return m_cframe;
+ }
+
+ /** Sets c to the camera's coordinate frame */
+ void getCoordinateFrame(CoordinateFrame& c) const;
+
+ /** Sets a new coordinate frame for the camera */
+ void setCoordinateFrame(const CoordinateFrame& c);
+
+ /** Sets P equal to the camera's projection matrix */
+ void getProjectUnitMatrix(const Rect2D& viewport, Matrix4& P) const;
+
+ /** Converts projected points from OpenGL standards
+ (-1, 1) to normal 3D coordinate standards (0, 1) */
+ Vector3 convertFromUnitToNormal(const Vector3& in, const Rect2D& viewport) const;
+
+ /**
+ Sets the vertical field of view, in radians. The
+ initial angle is toRadians(55). Must specify the direction of the angle
+ */
+ void setFieldOfView(float angle, FOVDirection direction);
+
+ /** Returns the current field of view angle and direction */
+ inline void getFieldOfView(float& angle, FOVDirection& direction) const {
+ angle = m_fieldOfView;
+ direction = m_direction;
+ }
+
+ /**
+ Projects a world space point onto a width x height screen. The
+ returned coordinate uses pixmap addressing: x = right and y =
+ down. The resulting z value is 0 at the near plane, 1 at the far plane,
+ and is a linear compression of unit cube projection.
+
+ If the point is behind the camera, Vector3::inf() is returned.
+ */
+ Vector3 project(const G3D::Vector3& point,
+ const class Rect2D& viewport) const;
+
+ /**
+ Projects a world space point onto a unit cube. The resulting
+ x,y,z values range between -1 and 1, where z is -1
+ at the near plane and 1 at the far plane and varies hyperbolically in between.
+
+ If the point is behind the camera, Vector3::inf() is returned.
+ */
+ Vector3 projectUnit(const G3D::Vector3& point,
+ const class Rect2D& viewport) const;
+
+ /**
+ Gives the world-space coordinates of screen space point v, where
+ v.x is in pixels from the left, v.y is in pixels from
+ the top, and v.z is on the range 0 (near plane) to 1 (far plane).
+ */
+ Vector3 unproject(const Vector3& v, const Rect2D& viewport) const;
+
+ /**
+ Gives the world-space coordinates of unit cube point v, where
+ v varies from -1 to 1 on all axes. The unproject first
+ transforms the point into a pixel location for the viewport, then calls unproject
+ */
+ Vector3 unprojectUnit(const Vector3& v, const Rect2D& viewport) const;
+
+ /**
+ Returns the pixel area covered by a shape of the given
+ world space area at the given z value (z must be negative).
+ */
+ float worldToScreenSpaceArea(float area, float z, const class Rect2D& viewport) const;
+
+ /**
+ Returns the world space 3D viewport corners. These
+ are at the near clipping plane. The corners are constructed
+ from the nearPlaneZ, viewportWidth, and viewportHeight.
+ "left" and "right" are from the GCamera's perspective.
+ */
+ void getNearViewportCorners(const class Rect2D& viewport,
+ Vector3& outUR, Vector3& outUL,
+ Vector3& outLL, Vector3& outLR) const;
+
+ /**
+ Returns the world space 3D viewport corners. These
+ are at the Far clipping plane. The corners are constructed
+ from the nearPlaneZ, farPlaneZ, viewportWidth, and viewportHeight.
+ "left" and "right" are from the GCamera's perspective.
+ */
+ void getFarViewportCorners(const class Rect2D& viewport,
+ Vector3& outUR, Vector3& outUL,
+ Vector3& outLL, Vector3& outLR) const;
+
+ /**
+ Returns the image plane depth, assumes imagePlane
+ is the same as the near clipping plane.
+ returns a positive number.
+ */
+ float imagePlaneDepth() const;
+
+ /**
+ Returns the world space ray passing through the center of pixel
+ (x, y) on the image plane. The pixel x and y axes are opposite
+ the 3D object space axes: (0,0) is the upper left corner of the screen.
+ They are in viewport coordinates, not screen coordinates.
+
+ The ray origin is at the origin. To start it at the image plane,
+ move it forward by imagePlaneDepth/ray.direction.z
+
+ Integer (x, y) values correspond to
+ the upper left corners of pixels. If you want to cast rays
+ through pixel centers, add 0.5 to x and y.
+ */
+ Ray worldRay(
+ float x,
+ float y,
+ const class Rect2D& viewport) const;
+
+ /**
+ Returns a negative z-value.
+ */
+ inline float nearPlaneZ() const {
+ return m_nearPlaneZ;
+ }
+
+ /**
+ Returns a negative z-value.
+ */
+ inline float farPlaneZ() const {
+ return m_farPlaneZ;
+ }
+
+ /**
+ Sets a new value for the far clipping plane
+ Expects a negative value
+ */
+ inline void setFarPlaneZ(float z) {
+ debugAssert(z < 0);
+ m_farPlaneZ = z;
+ }
+
+ /**
+ Sets a new value for the near clipping plane
+ Expects a negative value
+ */
+ inline void setNearPlaneZ(float z) {
+ debugAssert(z < 0);
+ m_nearPlaneZ = z;
+ }
+
+ /**
+ Returns the camera space width of the viewport at the near plane.
+ */
+ float viewportWidth(const class Rect2D& viewport) const;
+
+ /**
+ Returns the camera space height of the viewport at the near plane.
+ */
+ float viewportHeight(const class Rect2D& viewport) const;
+
+ void setPosition(const Vector3& t);
+
+ /** Rotate the camera in place to look at the target. Does not
+ persistently look at that location when the camera moves;
+ i.e., if you move the camera and still want it to look at the
+ old target, you must call lookAt again after moving the
+ camera.)*/
+ void lookAt(const Vector3& position, const Vector3& up = Vector3::unitY());
+
+ /**
+ Returns the clipping planes of the frustum, in world space.
+ The planes have normals facing <B>into</B> the view frustum.
+
+ The plane order is guaranteed to be:
+ Near, Right, Left, Top, Bottom, [Far]
+
+ If the far plane is at infinity, the resulting array will have
+ 5 planes, otherwise there will be 6.
+
+ The viewport is used only to determine the aspect ratio of the screen; the
+ absolute dimensions and xy values don't matter.
+ */
+ void getClipPlanes
+ (
+ const Rect2D& viewport,
+ Array<Plane>& outClip) const;
+
+ /**
+ Returns the world space view frustum, which is a truncated pyramid describing
+ the volume of space seen by this camera.
+ */
+ void frustum(const Rect2D& viewport, GCamera::Frustum& f) const;
+
+ GCamera::Frustum frustum(const Rect2D& viewport) const;
+
+ /** Read and Write camera parameters */
+ void serialize(class BinaryOutput& bo) const;
+ void deserialize(class BinaryInput& bi);
+
+};
+
+} // namespace G3D
+
+#endif
diff --git a/externals/g3dlite/G3D.lib/include/G3D/GImage.h b/externals/g3dlite/G3D.lib/include/G3D/GImage.h
new file mode 100644
index 00000000000..12003514d6a
--- /dev/null
+++ b/externals/g3dlite/G3D.lib/include/G3D/GImage.h
@@ -0,0 +1,550 @@
+/**
+ @file GImage.h
+
+ See G3D::GImage for details.
+
+ @cite JPEG compress/decompressor is the <A HREF="http://www.ijg.org/files/">IJG library</A>, used in accordance with their license.
+ @cite JPG code by John Chisholm, using the IJG Library
+ @cite TGA code by Morgan McGuire
+ @cite BMP code by John Chisholm, based on code by Edward "CGameProgrammer" Resnick <A HREF="mailto:cgp@gdnmail.net">mailto:cgp@gdnmail.net</A> at <A HREF="ftp://ftp.flipcode.com/cotd/LoadPicture.txt">ftp://ftp.flipcode.com/cotd/LoadPicture.txt</A>
+ @cite PCX format described in the ZSOFT PCX manual http://www.nist.fss.ru/hr/doc/spec/pcx.htm#2
+ @cite PNG compress/decompressor is the <A HREF="http://www.libpng.org/pub/png/libpng.html">libpng library</A>, used in accordance with their license.
+ @cite PPM code by Morgan McGuire based on http://netpbm.sourceforge.net/doc/ppm.html
+
+ @maintainer Morgan McGuire, morgan@graphics3d.com
+ @created 2002-05-27
+ @edited 2007-01-31
+
+ Copyright 2000-2007, Morgan McGuire.
+ All rights reserved.
+
+ */
+
+#ifndef G3D_GIMAGE_H
+#define G3D_GIMAGE_H
+
+#include "G3D/platform.h"
+#include <string>
+#include "G3D/Array.h"
+#include "G3D/g3dmath.h"
+#include "G3D/stringutils.h"
+#include "G3D/Color1uint8.h"
+#include "G3D/Color3uint8.h"
+#include "G3D/Color4uint8.h"
+
+namespace G3D {
+class BinaryInput;
+class BinaryOutput;
+/**
+ Interface to image compression & file formats.
+
+ Supported formats (decode and encode): Color JPEG, PNG, (Uncompressed)TGA 24, (Uncompressed)TGA 32, BMP 1, BMP 4, BMP 8, BMP 24, PPM (P6), and PPM ASCII (P1, P2, P3).
+ 8-bit paletted PCX, 24-bit PCX, and ICO are supported for decoding only.
+
+ Sample usage:
+
+ <PRE>
+ #include "graphics3D.h"
+
+ // Loading from disk:
+ G3D::GImage im1 = G3D::GImage("test.jpg");
+
+ // Loading from memory:
+ G3D::GImage im2 = G3D::GImage(data, length);
+
+ // im.pixel is a pointer to RGB color data. If you want
+ // an alpha channel, call RGBtoRGBA or RGBtoARGB for
+ // conversion.
+
+ // Saving to memory:
+ G3D::GImage im3 = G3D::GImage(width, height);
+ // (Set the pixels of im3...)
+ uint8* data2;
+ int len2;
+ im3.encode(G3D::GImage::JPEG, data2, len2);
+
+ // Saving to disk
+ im3.save("out.jpg");
+ </PRE>
+
+ The free Image Magick Magick Wand API
+ (http://www.imagemagick.org/www/api/magick_wand.html) provides a more powerful
+ API for image manipulation and wider set of image load/save formats. It is
+ recommended over GImage (we don't include it directly in G3D because their license
+ is more restrictive than the BSD one).
+
+ */
+class GImage {
+private:
+ uint8* _byte;
+
+public:
+
+ class Error {
+ public:
+ Error(
+ const std::string& reason,
+ const std::string& filename = "") :
+ reason(reason), filename(filename) {}
+
+ std::string reason;
+ std::string filename;
+ };
+
+ enum Format {JPEG, BMP, TGA, PCX, ICO, PNG, PPM_ASCII, PPM, AUTODETECT, UNKNOWN};
+
+ int width;
+ int height;
+
+ /**
+ The number of channels; either 3 (RGB) or 4 (RGBA)
+ */
+ int channels;
+
+ inline const uint8* byte() const {
+ return _byte;
+ }
+
+ /** Returns a pointer to the upper left pixel
+ as Color3uint8.
+ */
+ inline const Color3uint8* pixel3() const {
+ debugAssertM(channels == 3, format("Tried to call GImage::pixel3 on an image with %d channels", channels));
+ return (Color3uint8*)_byte;
+ }
+
+ /** Returns a pointer to the upper left pixel
+ as Color4uint8.
+ */
+ inline const Color4uint8* pixel4() const {
+ debugAssertM(channels == 4, format("Tried to call GImage::pixel4 on an image with %d channels", channels));
+ return (Color4uint8*)_byte;
+ }
+
+ inline const Color1uint8* pixel1() const {
+ debugAssertM(channels == 1, format("Tried to call GImage::pixel1 on an image with %d channels", channels));
+ return (Color1uint8*)_byte;
+ }
+
+ inline Color1uint8* pixel1() {
+ debugAssertM(channels == 1, format("Tried to call GImage::pixel1 on an image with %d channels", channels));
+ return (Color1uint8*)_byte;
+ }
+
+ /** Returns the pixel at (x, y), where (0,0) is the upper left. */
+ inline const Color1uint8& pixel1(int x, int y) const {
+ debugAssert(x >= 0 && x < width);
+ debugAssert(y >= 0 && y < height);
+ return pixel1()[x + y * width];
+ }
+
+ /** Returns the pixel at (x, y), where (0,0) is the upper left. */
+ inline Color1uint8& pixel1(int x, int y) {
+ debugAssert(x >= 0 && x < width);
+ debugAssert(y >= 0 && y < height);
+ return pixel1()[x + y * width];
+ }
+
+ /** Returns the pixel at (x, y), where (0,0) is the upper left. */
+ inline const Color3uint8& pixel3(int x, int y) const {
+ debugAssert(x >= 0 && x < width);
+ debugAssert(y >= 0 && y < height);
+ return pixel3()[x + y * width];
+ }
+
+ inline Color3uint8& pixel3(int x, int y) {
+ debugAssert(x >= 0 && x < width);
+ debugAssert(y >= 0 && y < height);
+ return pixel3()[x + y * width];
+ }
+
+ /** Returns the pixel at (x, y), where (0,0) is the upper left. */
+ inline const Color4uint8& pixel4(int x, int y) const {
+ debugAssert(x >= 0 && x < width);
+ debugAssert(y >= 0 && y < height);
+ return pixel4()[x + y * width];
+ }
+
+ inline Color4uint8& pixel4(int x, int y) {
+ debugAssert(x >= 0 && x < width);
+ debugAssert(y >= 0 && y < height);
+ return pixel4()[x + y * width];
+ }
+
+ inline uint8* byte() {
+ return _byte;
+ }
+
+ inline Color3uint8* pixel3() {
+ debugAssert(channels == 3);
+ return (Color3uint8*)_byte;
+ }
+
+ inline Color4uint8* pixel4() {
+ debugAssert(channels == 4);
+ return (Color4uint8*)_byte;
+ }
+
+private:
+
+ void encodeBMP(
+ BinaryOutput& out) const;
+
+ /**
+ The TGA file will be either 24- or 32-bit depending
+ on the number of channels.
+ */
+ void encodeTGA(
+ BinaryOutput& out) const;
+
+ /**
+ Converts this image into a JPEG
+ */
+ void encodeJPEG(
+ BinaryOutput& out) const;
+
+ /**
+ Converts this image into a JPEG
+ */
+ void encodePNG(
+ BinaryOutput& out) const;
+
+ void encodePPM(
+ BinaryOutput& out) const;
+
+ void encodePPMASCII(
+ BinaryOutput& out) const;
+
+ void decodeTGA(
+ BinaryInput& input);
+
+ void decodeBMP(
+ BinaryInput& input);
+
+ void decodeJPEG(
+ BinaryInput& input);
+
+ void decodePCX(
+ BinaryInput& input);
+
+ void decodeICO(
+ BinaryInput& input);
+
+ void decodePNG(
+ BinaryInput& input);
+
+ void decodePPM(
+ BinaryInput& input);
+
+ void decodePPMASCII(
+ BinaryInput& input);
+
+ /**
+ Given [maybe] a filename, memory buffer, and [maybe] a format,
+ returns the most likely format of this file.
+ */
+ static Format resolveFormat(
+ const std::string& filename,
+ const uint8* data,
+ int dataLen,
+ Format maybeFormat);
+
+ void _copy(
+ const GImage& other);
+
+public:
+ static Format resolveFormat(const std::string& filename);
+
+ GImage() {
+ width = height = channels = 0;
+ _byte = NULL;
+ }
+
+ /**
+ Load an encoded image from disk and decode it.
+ Throws GImage::Error if something goes wrong.
+ */
+ GImage(
+ const std::string& filename,
+ Format format = AUTODETECT);
+
+ /**
+ Decodes an image stored in a buffer.
+ */
+ GImage(
+ const unsigned char*data,
+ int length,
+ Format format = AUTODETECT);
+
+ /**
+ Create an empty image of the given size.
+ */
+ GImage(
+ int width,
+ int height,
+ int channels = 3);
+
+ GImage(
+ const GImage& other);
+
+ GImage& operator=(const GImage& other);
+
+ /**
+ Returns a new GImage that has 4 channels. RGB is
+ taken from this GImage and the alpha from the red
+ channel of the supplied image. The new GImage is passed
+ as a reference parameter for speed.
+ */
+ void insertRedAsAlpha(const GImage& alpha, GImage& output) const;
+
+ GImage G3D_DEPRECATED insertRedAsAlpha(const GImage& alpha) const;
+
+ /**
+ Returns a new GImage with 3 channels, removing
+ the alpha channel if there is one. The new GImage
+ is passed as a reference parameter for speed.
+ */
+ void stripAlpha(GImage& output) const;
+
+ GImage G3D_DEPRECATED stripAlpha() const;
+
+ /**
+ Loads an image from disk (clearing the old one first).
+ */
+ void load(
+ const std::string& filename,
+ Format format = AUTODETECT);
+
+ /**
+ Frees memory and resets to a 0x0 image.
+ */
+ void clear();
+
+ /**
+ Deallocates the pixels.
+ */
+ virtual ~GImage();
+
+ /**
+ Resizes the internal buffer to (width x height) with the
+ number of channels specified. All data is set to 0 (black).
+ */
+ void resize(int width, int height, int channels);
+
+
+ /**
+ Copies src sub-image data into dest at a certain offset.
+ The dest variable must already contain an image that is large
+ enough to contain the src sub-image at the specified offset.
+ Returns true on success and false if the src sub-image cannot
+ completely fit within dest at the specified offset. Both
+ src and dest must have the same number of channels.
+ */
+ static bool pasteSubImage(GImage & dest, const GImage & src,
+ int destX, int destY, int srcX, int srcY, int srcWidth, int srcHeight);
+
+ /**
+ creates dest from src sub-image data.
+ Returns true on success and false if the src sub-image
+ is not within src.
+ */
+ static bool copySubImage(GImage & dest, const GImage & src,
+ int srcX, int srcY, int srcWidth, int srcHeight);
+
+ void convertToRGBA();
+
+ void convertToRGB();
+
+ /** Averages color channels if they exist */
+ void convertToL8();
+
+ /**
+ Returns true if format is supported. Format
+ should be an extension string (e.g. "BMP").
+ */
+ static bool supportedFormat(
+ const std::string& format);
+
+ /**
+ Converts a string to an enum, returns UNKNOWN if not recognized.
+ */
+ static Format stringToFormat(
+ const std::string& format);
+
+ /**
+ Encode and save to disk.
+ */
+ void save(
+ const std::string& filename,
+ Format format = AUTODETECT) const;
+
+ /**
+ The caller must delete the returned buffer.
+ */
+ void encode(
+ Format format,
+ uint8*& outData,
+ int& outLength) const;
+
+ /**
+ Does not commit the BinaryOutput when done.
+ */
+ void encode(
+ Format format,
+ BinaryOutput& out) const;
+
+ /**
+ Decodes the buffer into this image.
+ @format Must be the correct format.
+ */
+ void decode(
+ BinaryInput& input,
+ Format format);
+
+ /** Returns the size of this object in bytes */
+ int sizeInMemory() const;
+
+
+ /** Ok for in == out */
+ static void R8G8B8_to_Y8U8V8(int width, int height, const uint8* in, uint8* out);
+
+ /** Ok for in == out */
+ static void Y8U8V8_to_R8G8B8(int width, int height, const uint8* in, uint8* out);
+
+ /**
+ @param in RGB buffer of numPixels * 3 bytes
+ @param out Buffer of numPixels * 4 bytes
+ @param numPixels Number of RGB pixels to convert
+ */
+ static void RGBtoRGBA(
+ const uint8* in,
+ uint8* out,
+ int numPixels);
+
+ static void RGBtoARGB(
+ const uint8* in,
+ uint8* out,
+ int numPixels);
+
+ /** Safe for in == out */
+ static void RGBtoBGR(
+ const uint8* in,
+ uint8* out,
+ int numPixels);
+
+ /**
+ Win32 32-bit HDC format.
+ */
+ static void RGBtoBGRA(
+ const uint8* in,
+ uint8* out,
+ int numPixels);
+
+ static void RGBAtoRGB(
+ const uint8* in,
+ uint8* out,
+ int numPixels);
+ /**
+ Uses the red channel of the second image as an alpha channel.
+ */
+ static void RGBxRGBtoRGBA(
+ const uint8* colorRGB,
+ const uint8* alphaRGB,
+ uint8* out,
+ int numPixels);
+
+ /**
+ Flips the image along the vertical axis.
+ Safe for in == out.
+ */
+ static void flipRGBVertical(
+ const uint8* in,
+ uint8* out,
+ int width,
+ int height);
+
+ static void flipRGBAVertical(
+ const uint8* in,
+ uint8* out,
+ int width,
+ int height);
+
+ /**
+ Given a tangent space bump map, computes a new image where the
+ RGB channels are a tangent space normal map and the alpha channel
+ is the original bump map. Assumes the input image is tileable.
+
+ In the resulting image, x = red = tangent, y = green = binormal, and z = blue = normal.
+
+ Particularly useful as part of the idiom:
+ <PRE>
+ GImage normal;
+ computeNormalMap(GImage(filename), normal);
+ return Texture::fromGImage(filename, normal);
+ </PRE>
+
+ @param lowPassBump If true, a 9x9 filter of 1's is used to low-pass filter the elevations,
+ which produces better results for parallax mapping.
+
+ @param scaleHeightByNz After computing normals, scale the height by |N.z|, a trick that
+ reduces texture swim in steep areas for parallax mapping.
+
+ @param whiteHeightInPixels How high should the brightest input value be considered for purposes
+ of normal computation, compared to the horizontal and vertical size of a pixel.
+ A value of 255 means that a 255 x 255 bump image with a full black-to-white gradient will
+ produce a 45-degree ramp (this also results in "cubic" voxels).
+ A special (default) value of -1 means scale the effective white height so that it is equal
+ to the larger spatial dimension.
+ */
+ static void computeNormalMap(
+ const class GImage& bump,
+ class GImage& normal,
+ float whiteHeightInPixels = -1.0f,
+ bool lowPassBump = false,
+ bool scaleHeightByNz = false);
+
+ static void computeNormalMap(
+ int width,
+ int height,
+ int channels,
+ const uint8* src,
+ GImage& normal,
+ float whiteHeightInPixels,
+ bool lowPassBump,
+ bool scaleHeightByNz);
+
+ /**
+ Bayer demosaicing using the filter proposed in
+
+ HIGH-QUALITY LINEAR INTERPOLATION FOR DEMOSAICING OF BAYER-PATTERNED COLOR IMAGES
+ Henrique S. Malvar, Li-wei He, and Ross Cutler
+
+ The filter wraps at the image boundaries.
+
+ Assumes in != out.
+ */
+ static void BAYER_G8B8_R8G8_to_R8G8B8_MHC(int w, int h, const uint8* in, uint8* _out);
+ static void BAYER_G8R8_B8G8_to_R8G8B8_MHC(int w, int h, const uint8* in, uint8* _out);
+ static void BAYER_R8G8_G8B8_to_R8G8B8_MHC(int w, int h, const uint8* in, uint8* _out);
+ static void BAYER_B8G8_G8R8_to_R8G8B8_MHC(int w, int h, const uint8* in, uint8* _out);
+
+ /** Fast conversion; the output has 1/2 the size of the input in each direction. Assumes in != out.
+ See G3D::BAYER_G8B8_R8G8_to_R8G8B8_MHC for a much better result. */
+ static void BAYER_G8B8_R8G8_to_Quarter_R8G8B8(int inWidth, int inHeight, const uint8* in, uint8* out);
+
+ /** Attempt to undo fast conversion of G3D::BAYER_G8B8_R8G8_to_Quarter_R8G8B8;
+ the green channel will lose data. Assumes in != out
+ The input should have size 3 * inWidth * inHeight. The output should have size
+ 2 * inWidth * 2 * inHeight.
+ */
+ static void Quarter_R8G8B8_to_BAYER_G8B8_R8G8(int inWidth, int inHeight, const uint8* in, uint8* out);
+
+ /** Overwrites every pixel with one of the two colors in a checkerboard pattern.
+ The fields used from the two colors depend on the current number of channels in @a im.
+ */
+ static void makeCheckerboard(GImage& im, int checkerSize = 1, const Color4uint8& color1 = Color4uint8(255,255,255,255), const Color4uint8& color2 = Color4uint8(0,0,0,255));
+};
+
+}
+
+#endif
diff --git a/externals/g3dlite/G3D.lib/include/G3D/GLight.h b/externals/g3dlite/G3D.lib/include/G3D/GLight.h
new file mode 100644
index 00000000000..b8fa7b54261
--- /dev/null
+++ b/externals/g3dlite/G3D.lib/include/G3D/GLight.h
@@ -0,0 +1,72 @@
+/**
+ @file GLight.h
+
+ @maintainer Morgan McGuire, matrix@graphics3d.com
+
+ @created 2003-11-12
+ @edited 2006-02-08
+*/
+
+#ifndef G3D_GLIGHT_H
+#define G3D_GLIGHT_H
+
+#include "G3D/platform.h"
+#include "G3D/Vector4.h"
+#include "G3D/Vector3.h"
+#include "G3D/Color4.h"
+
+namespace G3D {
+
+/**
+ A light representation that closely follows the OpenGL light format.
+ */
+class GLight {
+public:
+ /** World space position (for a directional light, w = 0 */
+ Vector4 position;
+
+ /** Direction in which the light faces, if a spot light. This is the "look vector" of the light source. */
+ Vector3 spotDirection;
+
+ /** In <B>degrees</B>. 180 = no cutoff (point/dir) >90 = spot light */
+ float spotCutoff;
+
+ /** Constant, linear, quadratic */
+ float attenuation[3];
+
+ /** May be outside the range [0, 1] */
+ Color3 color;
+
+ /** If false, this light is ignored */
+ bool enabled;
+
+ /** If false, this light does not create specular highlights (useful when using negative lights). */
+ bool specular;
+
+ /** If false, this light does not create diffuse illumination (useful when rendering a specular-only pass). */
+ bool diffuse;
+
+ GLight();
+
+ /** @param toLight will be normalized */
+ static GLight directional(const Vector3& toLight, const Color3& color, bool specular = true, bool diffuse = true);
+
+ static GLight point(const Vector3& pos, const Color3& color, float constAtt = 1, float linAtt = 0, float quadAtt = 0.5f, bool specular = true, bool diffuse = true);
+
+ /** @param pointDirection Will be normalized. Points in the direction that light propagates.
+ @param cutOffAngleDegrees Must be on the range [0, 90]. This is the angle from the point direction
+ to the edge of the light cone.
+ */
+ static GLight spot(const Vector3& pos, const Vector3& pointDirection, float cutOffAngleDegrees, const Color3& color, float constAtt = 1, float linAtt = 0, float quadAtt = 0, bool specular = true, bool diffuse = true);
+
+ /** Returns the sphere within which this light has some noticable effect. May be infinite.
+ @param cutoff The value at which the light intensity is considered negligible. */
+ class Sphere effectSphere(float cutoff = 30.0f / 255) const;
+
+ bool operator==(const GLight& other) const;
+ bool operator!=(const GLight& other) const;
+};
+
+} // namespace
+#endif
+
diff --git a/externals/g3dlite/G3D.lib/include/G3D/GThread.h b/externals/g3dlite/G3D.lib/include/G3D/GThread.h
new file mode 100644
index 00000000000..63f1c235bda
--- /dev/null
+++ b/externals/g3dlite/G3D.lib/include/G3D/GThread.h
@@ -0,0 +1,169 @@
+/**
+ @file GThread.h
+
+ @created 2005-09-22
+ @edited 2007-01-31
+
+ */
+
+#ifndef G3D_GTHREAD_H
+#define G3D_GTHREAD_H
+
+#include "G3D/platform.h"
+#include "G3D/ReferenceCount.h"
+#include <string>
+
+#ifndef G3D_WIN32
+# include <pthread.h>
+# include <signal.h>
+#endif
+
+
+namespace G3D {
+
+typedef ReferenceCountedPointer<class GThread> GThreadRef;
+
+/**
+ Platform independent thread implementation. You can either subclass and
+ override GThread::threadMain or call the create method with a method.
+
+ Beware of reference counting and threads. If circular references exist between
+ GThread subclasses then neither class will ever be deallocated. Also,
+ dropping all pointers (and causing deallocation) of a GThread does NOT
+ stop the underlying process.
+
+ @sa G3D::GMutex, G3D::AtomicInt32
+*/
+class GThread : public ReferenceCountedObject {
+private:
+ // "Status" is a reserved work on FreeBSD
+ enum GStatus {STATUS_CREATED, STATUS_STARTED, STATUS_RUNNING, STATUS_COMPLETED};
+
+ // Not implemented on purpose, don't use
+ GThread(const GThread &);
+ GThread& operator=(const GThread&);
+ bool operator==(const GThread&);
+
+#ifdef G3D_WIN32
+ static DWORD WINAPI internalThreadProc(LPVOID param);
+#else
+ static void* internalThreadProc(void* param);
+#endif //G3D_WIN32
+
+ volatile GStatus m_status;
+
+ // Thread handle to hold HANDLE and pthread_t
+#ifdef G3D_WIN32
+ HANDLE m_handle;
+ HANDLE m_event;
+#else
+ pthread_t m_handle;
+#endif //G3D_WIN32
+
+ std::string m_name;
+
+public:
+
+ GThread(const std::string& name);
+
+ virtual ~GThread();
+
+ /** Constructs a basic GThread without requiring a subclass.
+
+ @param proc The global or static function for the threadMain() */
+ static GThreadRef create(const std::string& name, void (*proc)(void*), void* param);
+
+ /** @deprecated use overload that accepts void* param */
+ static GThreadRef create(const std::string& name, void (*proc)());
+
+ /** Starts the thread and executes threadMain(). Returns false if
+ the thread failed to start (either because it was already started
+ or because the OS refused).*/
+ bool start();
+
+ /** Terminates the thread without notifying or
+ waiting for a cancelation point. */
+ void terminate();
+
+ /**
+ Returns true if threadMain is currently executing. This will
+ only be set when the thread is actually running and might not
+ be set when start() returns. */
+ bool running() const;
+
+ /** True after start() has been called, even through the thread
+ may have already completed(), or be currently running().*/
+ bool started() const;
+
+ /** Returns true if the thread has exited. */
+ bool completed() const;
+
+ /** Waits for the thread to finish executing. */
+ void waitForCompletion();
+
+ /** Returns thread name */
+ inline const std::string& name() {
+ return m_name;
+ }
+
+ /** Overriden by the thread implementor */
+ virtual void threadMain() = 0;
+};
+
+
+/**
+ Mutual exclusion lock used for synchronization.
+ @sa G3D::GThread, G3D::AtomicInt32
+*/
+class GMutex {
+private:
+# ifdef G3D_WIN32
+ CRITICAL_SECTION m_handle;
+# else
+ pthread_mutex_t m_handle;
+# endif
+
+ // Not implemented on purpose, don't use
+ GMutex(const GMutex &mlock);
+ GMutex &operator=(const GMutex &);
+ bool operator==(const GMutex&);
+
+public:
+ GMutex();
+ ~GMutex();
+
+ /** Locks the mutex or blocks until available. */
+ void lock();
+
+ /** Unlocks the mutex. */
+ void unlock();
+};
+
+
+/**
+ Automatically locks while in scope.
+*/
+class GMutexLock {
+private:
+ GMutex* m;
+
+ // Not implemented on purpose, don't use
+ GMutexLock(const GMutexLock &mlock);
+ GMutexLock &operator=(const GMutexLock &);
+ bool operator==(const GMutexLock&);
+
+public:
+ GMutexLock(GMutex* mutex) {
+ m = mutex;
+ m->lock();
+ }
+
+ ~GMutexLock() {
+ m->unlock();
+ }
+};
+
+
+} // namespace G3D
+
+#endif //G3D_GTHREAD_H
diff --git a/externals/g3dlite/G3D.lib/include/G3D/GUniqueID.h b/externals/g3dlite/G3D.lib/include/G3D/GUniqueID.h
new file mode 100644
index 00000000000..44ec6e03405
--- /dev/null
+++ b/externals/g3dlite/G3D.lib/include/G3D/GUniqueID.h
@@ -0,0 +1,69 @@
+/**
+ @file GUniqueID.h
+ @author Morgan McGuire, morgan@cs.williams.edu
+ */
+#ifndef G3D_GUNIQUEID_H
+#define G3D_GUNIQUEID_H
+
+#include "G3D/platform.h"
+#include "G3D/g3dmath.h"
+#include "G3D/Table.h"
+
+namespace G3D {
+
+/** Globally unique identifiers. The probability of two different
+ programs generating the same value from UniqueID::create is
+ vanishingly small.
+
+ UniqueIDs optionally contain a 10-bit application specific tag
+ that distinguishes their type.
+*/
+class GUniqueID {
+private:
+
+ uint64 id;
+
+public:
+
+ GUniqueID() : id(0) {}
+
+ bool uninitialized() const {
+ return id == 0;
+ }
+
+ uint16 tag() const {
+ return id >> 54;
+ }
+
+ operator uint64() const {
+ return id;
+ }
+
+ bool operator==(const GUniqueID& other) const {
+ return id == other.id;
+ }
+
+ bool operator!=(const GUniqueID& other) const {
+ return id != other.id;
+ }
+
+ void serialize(class BinaryOutput& b) const;
+
+ void deserialize(class BinaryInput& b);
+
+ void serialize(class TextOutput& t) const;
+
+ void deserialize(class TextInput& t);
+
+ /** Create a new ID */
+ static GUniqueID create(uint16 tag = 0);
+};
+
+} // G3D
+
+/** For Table and Set */
+template<> struct HashTrait<class G3D::GUniqueID> {
+ static size_t hashCode(G3D::GUniqueID id) { return (size_t)(G3D::uint64)id; }
+};
+
+#endif
diff --git a/externals/g3dlite/G3D.lib/include/G3D/HashTrait.h b/externals/g3dlite/G3D.lib/include/G3D/HashTrait.h
new file mode 100644
index 00000000000..702903ff09b
--- /dev/null
+++ b/externals/g3dlite/G3D.lib/include/G3D/HashTrait.h
@@ -0,0 +1,63 @@
+/**
+ @file HashTrait.h
+
+ @maintainer Morgan McGuire, morgan@cs.williams.edu
+ @created 2008-10-01
+ @edited 2008-10-01
+ Copyright 2000-2008, Morgan McGuire.
+ All rights reserved.
+ */
+
+#ifndef G3D_HASHTRAIT_H
+#define G3D_HASHTRAIT_H
+
+#include "G3D/platform.h"
+#include "G3D/Crypto.h"
+#include "G3D/g3dmath.h"
+#include "G3D/uint128.h"
+
+/** Must be specialized for custom types.
+ @see G3D::Table for specialization requirements.
+*/
+template <typename T> struct HashTrait{};
+
+template <typename T> struct HashTrait<T*> {
+ static size_t hashCode(const void* k) { return reinterpret_cast<size_t>(k); }
+};
+
+template <> struct HashTrait <int> {
+ static size_t hashCode(int k) { return static_cast<size_t>(k); }
+};
+
+template <> struct HashTrait <G3D::uint32> {
+ static size_t hashCode(G3D::uint32 k) { return static_cast<size_t>(k); }
+};
+
+template <> struct HashTrait <G3D::uint64> {
+ static size_t hashCode(G3D::uint64 k) { return static_cast<size_t>(k); }
+};
+
+template <> struct HashTrait <std::string> {
+ static size_t hashCode(const std::string& k) { return static_cast<size_t>(G3D::Crypto::crc32(k.c_str(), k.size())); }
+};
+
+template <> struct HashTrait<G3D::uint128> {
+ // Use the FNV-1 hash (http://isthe.com/chongo/tech/comp/fnv/#FNV-1).
+ static size_t hashCode(G3D::uint128 key) {
+ static const G3D::uint128 FNV_PRIME_128(1 << 24, 0x159);
+ static const G3D::uint128 FNV_OFFSET_128(0xCF470AAC6CB293D2LL, 0xF52F88BF32307F8FLL);
+
+ G3D::uint128 hash = FNV_OFFSET_128;
+ G3D::uint128 mask(0, 0xFF);
+ for (int i = 0; i < 16; ++i) {
+ hash *= FNV_PRIME_128;
+ hash ^= (mask & key);
+ key >>= 8;
+ }
+
+ G3D::uint64 foldedHash = hash.hi ^ hash.lo;
+ return static_cast<size_t>((foldedHash >> 32) ^ (foldedHash & 0xFFFFFFFF));
+ }
+};
+
+#endif
diff --git a/externals/g3dlite/G3D.lib/include/G3D/Image1.h b/externals/g3dlite/G3D.lib/include/G3D/Image1.h
new file mode 100644
index 00000000000..f0850710d2c
--- /dev/null
+++ b/externals/g3dlite/G3D.lib/include/G3D/Image1.h
@@ -0,0 +1,79 @@
+/**
+ @file Image1.h
+
+ @maintainer Morgan McGuire, matrix@graphics3d.com
+
+ @created 2007-01-31
+ @edited 2007-01-31
+*/
+
+
+#ifndef G3D_IMAGE1_H
+#define G3D_IMAGE1_H
+
+#include "G3D/platform.h"
+#include "G3D/Map2D.h"
+#include "G3D/Color1.h"
+#include "G3D/GImage.h"
+
+namespace G3D {
+
+typedef ReferenceCountedPointer<class Image1> Image1Ref;
+
+/**
+ Luminance image with 32-bit floating point storage.
+
+ See also G3D::Image1uint8, G3D::GImage.
+ */
+class Image1 : public Map2D<Color1, Color1> {
+public:
+
+ typedef Image1 Type;
+ typedef Image1Ref Ref;
+
+protected:
+
+ Image1(int w, int h, WrapMode wrap);
+
+ void copyGImage(const class GImage& im);
+ void copyArray(const Color1* src, int w, int h);
+ void copyArray(const Color3* src, int w, int h);
+ void copyArray(const Color4* src, int w, int h);
+ void copyArray(const Color1uint8* src, int w, int h);
+ void copyArray(const Color3uint8* src, int w, int h);
+ void copyArray(const Color4uint8* src, int w, int h);
+
+public:
+
+ const class ImageFormat* format() const;
+
+ /** Creates an all-zero width x height image. */
+ static Ref createEmpty(int width, int height, WrapMode wrap = WrapMode::ERROR);
+
+ /** Creates a 0 x 0 image. */
+ static Ref createEmpty(WrapMode wrap = WrapMode::ERROR);
+
+ static Ref fromFile(const std::string& filename, WrapMode wrap = WrapMode::ERROR, GImage::Format fmt = GImage::AUTODETECT);
+
+ static Ref fromArray(const class Color1uint8* ptr, int width, int height, WrapMode wrap = WrapMode::ERROR);
+ static Ref fromArray(const class Color3uint8* ptr, int width, int height, WrapMode wrap = WrapMode::ERROR);
+ static Ref fromArray(const class Color4uint8* ptr, int width, int height, WrapMode wrap = WrapMode::ERROR);
+ static Ref fromArray(const class Color1* ptr, int width, int height, WrapMode wrap = WrapMode::ERROR);
+ static Ref fromArray(const class Color3* ptr, int width, int height, WrapMode wrap = WrapMode::ERROR);
+ static Ref fromArray(const class Color4* ptr, int width, int height, WrapMode wrap = WrapMode::ERROR);
+
+ static Ref fromImage1uint8(const ReferenceCountedPointer<class Image1uint8>& im);
+
+ static Ref fromGImage(const class GImage& im, WrapMode wrap = WrapMode::ERROR);
+
+ /** Loads from any of the file formats supported by G3D::GImage. If there is an alpha channel on the input,
+ it is stripped. */
+ void load(const std::string& filename, GImage::Format fmt = GImage::AUTODETECT);
+
+ /** Saves in any of the formats supported by G3D::GImage. */
+ void save(const std::string& filename, GImage::Format fmt = GImage::AUTODETECT);
+};
+
+} // G3D
+
+#endif
diff --git a/externals/g3dlite/G3D.lib/include/G3D/Image1uint8.h b/externals/g3dlite/G3D.lib/include/G3D/Image1uint8.h
new file mode 100644
index 00000000000..7225ca35db8
--- /dev/null
+++ b/externals/g3dlite/G3D.lib/include/G3D/Image1uint8.h
@@ -0,0 +1,80 @@
+/**
+ @file Image1uint8.h
+
+ @maintainer Morgan McGuire, matrix@graphics3d.com
+
+ @created 2007-01-31
+ @edited 2007-01-31
+*/
+
+#ifndef G3D_IMAGE1UINT8_H
+#define G3D_IMAGE1UINT8_H
+
+#include "G3D/platform.h"
+#include "G3D/Map2D.h"
+#include "G3D/Color1uint8.h"
+#include "G3D/Color1.h"
+#include "G3D/GImage.h"
+
+namespace G3D {
+
+typedef ReferenceCountedPointer<class Image1uint8> Image1uint8Ref;
+
+/**
+ Compact storage for luminance 8-bit images.
+
+ See also G3D::Image3, G3D::GImage
+ */
+class Image1uint8 : public Map2D<Color1uint8, Color1> {
+public:
+
+ typedef Image1uint8 Type;
+ typedef Image1uint8Ref Ref;
+
+protected:
+
+ Image1uint8(int w, int h, WrapMode wrap);
+
+ void copyGImage(const class GImage& im);
+ void copyArray(const Color1* src, int w, int h);
+ void copyArray(const Color3* src, int w, int h);
+ void copyArray(const Color4* src, int w, int h);
+ void copyArray(const Color1uint8* src, int w, int h);
+ void copyArray(const Color3uint8* src, int w, int h);
+ void copyArray(const Color4uint8* src, int w, int h);
+
+public:
+
+ const class ImageFormat* format() const;
+
+ /** Creates an all-zero width x height image. */
+ static Ref createEmpty(int width, int height, WrapMode wrap = WrapMode::ERROR);
+
+ /** Creates a 0 x 0 image. */
+ static Ref createEmpty(WrapMode wrap = WrapMode::ERROR);
+
+ static Ref fromFile(const std::string& filename, WrapMode wrap = WrapMode::ERROR, GImage::Format fmt = GImage::AUTODETECT);
+
+ static Ref fromGImage(const class GImage& im, WrapMode wrap = WrapMode::ERROR);
+
+ static Ref fromArray(const class Color1uint8* ptr, int width, int height, WrapMode wrap = WrapMode::ERROR);
+ static Ref fromArray(const class Color3uint8* ptr, int width, int height, WrapMode wrap = WrapMode::ERROR);
+ static Ref fromArray(const class Color4uint8* ptr, int width, int height, WrapMode wrap = WrapMode::ERROR);
+ static Ref fromArray(const class Color1* ptr, int width, int height, WrapMode wrap = WrapMode::ERROR);
+ static Ref fromArray(const class Color3* ptr, int width, int height, WrapMode wrap = WrapMode::ERROR);
+ static Ref fromArray(const class Color4* ptr, int width, int height, WrapMode wrap = WrapMode::ERROR);
+
+ static Ref fromImage1(const ReferenceCountedPointer<class Image1>& im);
+ static Ref fromImage3uint8(const ReferenceCountedPointer<class Image3uint8>& im);
+
+ /** Loads from any of the file formats supported by G3D::GImage. If there is an alpha channel on the input,
+ it is stripped. */
+ void load(const std::string& filename, GImage::Format fmt = GImage::AUTODETECT);
+
+ /** Saves in any of the formats supported by G3D::GImage. */
+ void save(const std::string& filename, GImage::Format fmt = GImage::AUTODETECT);
+};
+
+} // G3D
+
+#endif
diff --git a/externals/g3dlite/G3D.lib/include/G3D/Image3.h b/externals/g3dlite/G3D.lib/include/G3D/Image3.h
new file mode 100644
index 00000000000..c67669c1c5e
--- /dev/null
+++ b/externals/g3dlite/G3D.lib/include/G3D/Image3.h
@@ -0,0 +1,79 @@
+/**
+ @file Image3.h
+
+ @maintainer Morgan McGuire, matrix@graphics3d.com
+
+ @created 2007-01-31
+ @edited 2007-01-31
+*/
+
+
+#ifndef G3D_IMAGE3_H
+#define G3D_IMAGE3_H
+
+#include "G3D/platform.h"
+#include "G3D/Map2D.h"
+#include "G3D/Color3.h"
+#include "G3D/GImage.h"
+
+namespace G3D {
+
+typedef ReferenceCountedPointer<class Image3> Image3Ref;
+
+/**
+ RGB image with 32-bit floating point storage for each channel.
+
+ See also G3D::Image3uint8, G3D::GImage.
+ */
+class Image3 : public Map2D<Color3, Color3> {
+public:
+
+ typedef Image3 Type;
+ typedef Image3Ref Ref;
+
+protected:
+
+ Image3(int w, int h, WrapMode wrap);
+
+ void copyGImage(const class GImage& im);
+ void copyArray(const Color1* src, int w, int h);
+ void copyArray(const Color3* src, int w, int h);
+ void copyArray(const Color4* src, int w, int h);
+ void copyArray(const Color1uint8* src, int w, int h);
+ void copyArray(const Color3uint8* src, int w, int h);
+ void copyArray(const Color4uint8* src, int w, int h);
+
+public:
+
+ const class ImageFormat* format() const;
+
+ /** Creates an all-zero width x height image. */
+ static Ref createEmpty(int width, int height, WrapMode wrap = WrapMode::ERROR);
+
+ /** Creates a 0 x 0 image. */
+ static Ref createEmpty(WrapMode wrap = WrapMode::ERROR);
+
+ static Ref fromFile(const std::string& filename, WrapMode wrap = WrapMode::ERROR, GImage::Format fmt = GImage::AUTODETECT);
+
+ static Ref fromArray(const class Color1uint8* ptr, int width, int height, WrapMode wrap = WrapMode::ERROR);
+ static Ref fromArray(const class Color3uint8* ptr, int width, int height, WrapMode wrap = WrapMode::ERROR);
+ static Ref fromArray(const class Color4uint8* ptr, int width, int height, WrapMode wrap = WrapMode::ERROR);
+ static Ref fromArray(const class Color1* ptr, int width, int height, WrapMode wrap = WrapMode::ERROR);
+ static Ref fromArray(const class Color3* ptr, int width, int height, WrapMode wrap = WrapMode::ERROR);
+ static Ref fromArray(const class Color4* ptr, int width, int height, WrapMode wrap = WrapMode::ERROR);
+
+ static Ref fromImage3uint8(const ReferenceCountedPointer<class Image3uint8>& im);
+
+ static Ref fromGImage(const class GImage& im, WrapMode wrap = WrapMode::ERROR);
+
+ /** Loads from any of the file formats supported by G3D::GImage. If there is an alpha channel on the input,
+ it is stripped. */
+ void load(const std::string& filename, GImage::Format fmt = GImage::AUTODETECT);
+
+ /** Saves in any of the formats supported by G3D::GImage. */
+ void save(const std::string& filename, GImage::Format fmt = GImage::AUTODETECT);
+};
+
+} // G3D
+
+#endif
diff --git a/externals/g3dlite/G3D.lib/include/G3D/Image3uint8.h b/externals/g3dlite/G3D.lib/include/G3D/Image3uint8.h
new file mode 100644
index 00000000000..9ee1ef6678b
--- /dev/null
+++ b/externals/g3dlite/G3D.lib/include/G3D/Image3uint8.h
@@ -0,0 +1,85 @@
+/**
+ @file Image3uint8.h
+
+ @maintainer Morgan McGuire, matrix@graphics3d.com
+
+ @created 2007-01-31
+ @edited 2007-01-31
+*/
+
+#ifndef G3D_IMAGE3UINT8_H
+#define G3D_IMAGE3UINT8_H
+
+#include "G3D/platform.h"
+#include "G3D/Map2D.h"
+#include "G3D/Color3uint8.h"
+#include "G3D/Color3.h"
+#include "G3D/GImage.h"
+
+namespace G3D {
+
+typedef ReferenceCountedPointer<class Image3uint8> Image3uint8Ref;
+
+/**
+ Compact storage for RGB 8-bit per channel images.
+
+ See also G3D::Image3, G3D::GImage
+ */
+class Image3uint8 : public Map2D<Color3uint8, Color3> {
+public:
+
+ typedef Image3uint8 Type;
+ typedef Image3uint8Ref Ref;
+
+protected:
+
+ Image3uint8(int w, int h, WrapMode wrap);
+
+ void copyGImage(const class GImage& im);
+ void copyArray(const Color1* src, int w, int h);
+ void copyArray(const Color3* src, int w, int h);
+ void copyArray(const Color4* src, int w, int h);
+ void copyArray(const Color1uint8* src, int w, int h);
+ void copyArray(const Color3uint8* src, int w, int h);
+ void copyArray(const Color4uint8* src, int w, int h);
+
+public:
+
+ const class ImageFormat* format() const;
+
+ /** Creates an all-zero width x height image. */
+ static Ref createEmpty(int width, int height, WrapMode wrap = WrapMode::ERROR);
+
+
+ /** Creates a 0 x 0 image. */
+ static Ref createEmpty(WrapMode wrap = WrapMode::ERROR);
+
+
+ static Ref fromFile(const std::string& filename, WrapMode wrap = WrapMode::ERROR, GImage::Format fmt = GImage::AUTODETECT);
+
+ static Ref fromGImage(const class GImage& im, WrapMode wrap = WrapMode::ERROR);
+
+ static Ref fromArray(const class Color1uint8* ptr, int width, int height, WrapMode wrap = WrapMode::ERROR);
+ static Ref fromArray(const class Color3uint8* ptr, int width, int height, WrapMode wrap = WrapMode::ERROR);
+ static Ref fromArray(const class Color4uint8* ptr, int width, int height, WrapMode wrap = WrapMode::ERROR);
+ static Ref fromArray(const class Color1* ptr, int width, int height, WrapMode wrap = WrapMode::ERROR);
+ static Ref fromArray(const class Color3* ptr, int width, int height, WrapMode wrap = WrapMode::ERROR);
+ static Ref fromArray(const class Color4* ptr, int width, int height, WrapMode wrap = WrapMode::ERROR);
+
+ static Ref fromImage3(const ReferenceCountedPointer<class Image3>& im);
+ static Ref fromImage1uint8(const ReferenceCountedPointer<class Image1uint8>& im);
+
+ /** Loads from any of the file formats supported by G3D::GImage. If there is an alpha channel on the input,
+ it is stripped. */
+ void load(const std::string& filename, GImage::Format fmt = GImage::AUTODETECT);
+
+ /** Saves in any of the formats supported by G3D::GImage. */
+ void save(const std::string& filename, GImage::Format fmt = GImage::AUTODETECT);
+
+ /** Extracts color channel 0 <= c <= 2 and returns it as a new monochrome image. */
+ ReferenceCountedPointer<class Image1uint8> getChannel(int c) const;
+};
+
+} // G3D
+
+#endif
diff --git a/externals/g3dlite/G3D.lib/include/G3D/Image4.h b/externals/g3dlite/G3D.lib/include/G3D/Image4.h
new file mode 100644
index 00000000000..04df43f9527
--- /dev/null
+++ b/externals/g3dlite/G3D.lib/include/G3D/Image4.h
@@ -0,0 +1,80 @@
+/**
+ @file Image4.h
+
+ @maintainer Morgan McGuire, matrix@graphics3d.com
+
+ @created 2007-01-31
+ @edited 2007-01-31
+*/
+
+
+#ifndef G3D_IMAGE4_H
+#define G3D_IMAGE4_H
+
+#include "G3D/platform.h"
+#include "G3D/Map2D.h"
+#include "G3D/Color4.h"
+#include "G3D/GImage.h"
+
+namespace G3D {
+
+typedef ReferenceCountedPointer<class Image4> Image4Ref;
+
+/**
+ RGBA image with 32-bit floating point storage for each channel.
+
+ Whenever a method needs to convert from RGB to ARGB, A=1 is assumed.
+
+ See also G3D::Image4uint8, G3D::GImage.
+ */
+class Image4 : public Map2D<Color4, Color4> {
+public:
+
+ typedef Image4 Type;
+ typedef Image4Ref Ref;
+
+protected:
+
+ Image4(int w, int h, WrapMode wrap);
+
+ void copyGImage(const class GImage& im);
+ void copyArray(const Color1* src, int w, int h);
+ void copyArray(const Color3* src, int w, int h);
+ void copyArray(const Color4* src, int w, int h);
+ void copyArray(const Color1uint8* src, int w, int h);
+ void copyArray(const Color3uint8* src, int w, int h);
+ void copyArray(const Color4uint8* src, int w, int h);
+
+public:
+
+ const class ImageFormat* format() const;
+
+ /** Creates an all-zero width x height image. */
+ static Ref createEmpty(int width, int height, WrapMode wrap = WrapMode::ERROR);
+
+ /** Creates a 0 x 0 image. */
+ static Ref createEmpty(WrapMode wrap = WrapMode::ERROR);
+
+ static Ref fromFile(const std::string& filename, WrapMode wrap = WrapMode::ERROR, GImage::Format fmt = GImage::AUTODETECT);
+
+ static Ref fromArray(const class Color1uint8* ptr, int width, int height, WrapMode wrap = WrapMode::ERROR);
+ static Ref fromArray(const class Color3uint8* ptr, int width, int height, WrapMode wrap = WrapMode::ERROR);
+ static Ref fromArray(const class Color4uint8* ptr, int width, int height, WrapMode wrap = WrapMode::ERROR);
+ static Ref fromArray(const class Color1* ptr, int width, int height, WrapMode wrap = WrapMode::ERROR);
+ static Ref fromArray(const class Color3* ptr, int width, int height, WrapMode wrap = WrapMode::ERROR);
+ static Ref fromArray(const class Color4* ptr, int width, int height, WrapMode wrap = WrapMode::ERROR);
+
+ static Ref fromImage4uint8(const ReferenceCountedPointer<class Image4uint8>& im);
+
+ static Ref fromGImage(const class GImage& im, WrapMode wrap = WrapMode::ERROR);
+
+ /** Loads from any of the file formats supported by G3D::GImage. */
+ void load(const std::string& filename, GImage::Format fmt = GImage::AUTODETECT);
+
+ /** Saves in any of the formats supported by G3D::GImage. */
+ void save(const std::string& filename, GImage::Format fmt = GImage::AUTODETECT);
+};
+
+} // G3D
+
+#endif
diff --git a/externals/g3dlite/G3D.lib/include/G3D/Image4uint8.h b/externals/g3dlite/G3D.lib/include/G3D/Image4uint8.h
new file mode 100644
index 00000000000..11daf97c83c
--- /dev/null
+++ b/externals/g3dlite/G3D.lib/include/G3D/Image4uint8.h
@@ -0,0 +1,85 @@
+/**
+ @file Image4uint8.h
+
+ @maintainer Morgan McGuire, matrix@graphics3d.com
+
+ @created 2007-01-31
+ @edited 2007-01-31
+*/
+
+#ifndef G3D_IMAGE4UINT8_H
+#define G3D_IMAGE4UINT8_H
+
+#include "G3D/platform.h"
+#include "G3D/Map2D.h"
+#include "G3D/Color4uint8.h"
+#include "G3D/Color4.h"
+#include "G3D/GImage.h"
+#include "G3D/Image1uint8.h"
+
+namespace G3D {
+
+typedef ReferenceCountedPointer<class Image4uint8> Image4uint8Ref;
+
+/**
+ Compact storage for RGBA 8-bit per channel images.
+
+ See also G3D::Image4, G3D::GImage
+ */
+class Image4uint8 : public Map2D<Color4uint8, Color4> {
+public:
+
+ typedef Image4uint8 Type;
+ typedef Image4uint8Ref Ref;
+
+protected:
+
+ Image4uint8(int w, int h, WrapMode wrap);
+
+ void copyGImage(const class GImage& im);
+ void copyArray(const Color1* src, int w, int h);
+ void copyArray(const Color3* src, int w, int h);
+ void copyArray(const Color4* src, int w, int h);
+ void copyArray(const Color1uint8* src, int w, int h);
+ void copyArray(const Color3uint8* src, int w, int h);
+ void copyArray(const Color4uint8* src, int w, int h);
+
+public:
+
+ const class ImageFormat* format() const;
+
+ /** Creates an all-zero width x height image. */
+ static Ref createEmpty(int width, int height, WrapMode wrap = WrapMode::ERROR);
+
+
+ /** Creates a 0 x 0 image. */
+ static Ref createEmpty(WrapMode wrap = WrapMode::ERROR);
+
+
+ static Ref fromFile(const std::string& filename, WrapMode wrap = WrapMode::ERROR, GImage::Format fmt = GImage::AUTODETECT);
+
+ static Ref fromGImage(const class GImage& im, WrapMode wrap = WrapMode::ERROR);
+
+ static Ref fromArray(const class Color1uint8* ptr, int width, int height, WrapMode wrap = WrapMode::ERROR);
+ static Ref fromArray(const class Color3uint8* ptr, int width, int height, WrapMode wrap = WrapMode::ERROR);
+ static Ref fromArray(const class Color4uint8* ptr, int width, int height, WrapMode wrap = WrapMode::ERROR);
+ static Ref fromArray(const class Color1* ptr, int width, int height, WrapMode wrap = WrapMode::ERROR);
+ static Ref fromArray(const class Color3* ptr, int width, int height, WrapMode wrap = WrapMode::ERROR);
+ static Ref fromArray(const class Color4* ptr, int width, int height, WrapMode wrap = WrapMode::ERROR);
+
+ static Ref fromImage4(const ReferenceCountedPointer<class Image4>& im);
+
+ /** Loads from any of the file formats supported by G3D::GImage. If there is an alpha channel on the input,
+ it is stripped. */
+ void load(const std::string& filename, GImage::Format fmt = GImage::AUTODETECT);
+
+ /** Saves in any of the formats supported by G3D::GImage. */
+ void save(const std::string& filename, GImage::Format fmt = GImage::AUTODETECT);
+
+ /** Extracts color channel 0 <= c <= 3 and returns it as a new monochrome image. */
+ ReferenceCountedPointer<class Image1uint8> getChannel(int c) const;
+};
+
+} // G3D
+
+#endif
diff --git a/externals/g3dlite/G3D.lib/include/G3D/ImageFormat.h b/externals/g3dlite/G3D.lib/include/G3D/ImageFormat.h
new file mode 100644
index 00000000000..a1334fb1a5a
--- /dev/null
+++ b/externals/g3dlite/G3D.lib/include/G3D/ImageFormat.h
@@ -0,0 +1,362 @@
+/**
+ @file ImageFormat.h
+
+ @maintainer Morgan McGuire, matrix@graphics3d.com
+
+ @created 2003-05-23
+ @edited 2008-07-17
+*/
+
+#ifndef GLG3D_ImageFormat_H
+#define GLG3D_ImageFormat_H
+
+#include "G3D/platform.h"
+#include "G3D/Table.h"
+#include "G3D/enumclass.h"
+
+namespace G3D {
+
+/** Information about common image formats.
+ Don't construct these; use the methods provided.
+
+ For most formats, the number indicates the number of bits per channel and a suffix of "F" indicates
+ floating point. This does not hold for the YUV and DXT formats.*/
+class ImageFormat {
+public:
+
+ // Must update ImageFormat::name() when this enum changes.
+ enum Code {
+ CODE_NONE = -1,
+ CODE_L8,
+ CODE_L16,
+ CODE_L16F,
+ CODE_L32F,
+
+ CODE_A8,
+ CODE_A16,
+ CODE_A16F,
+ CODE_A32F,
+
+ CODE_LA4,
+ CODE_LA8,
+ CODE_LA16,
+ CODE_LA16F,
+ CODE_LA32F,
+
+ CODE_RGB5,
+ CODE_RGB5A1,
+ CODE_RGB8,
+ CODE_RGB10,
+ CODE_RGB10A2,
+ CODE_RGB16,
+ CODE_RGB16F,
+ CODE_RGB32F,
+
+ CODE_ARGB8,
+ CODE_BGR8,
+
+ CODE_RGBA8,
+ CODE_RGBA16,
+ CODE_RGBA16F,
+ CODE_RGBA32F,
+
+ CODE_BAYER_RGGB8,
+ CODE_BAYER_GRBG8,
+ CODE_BAYER_GBRG8,
+ CODE_BAYER_BGGR8,
+ CODE_BAYER_RGGB32F,
+ CODE_BAYER_GRBG32F,
+ CODE_BAYER_GBRG32F,
+ CODE_BAYER_BGGR32F,
+
+ CODE_HSV8,
+ CODE_HSV32F,
+
+ CODE_YUV420_PLANAR,
+ CODE_YUV422,
+ CODE_YUV444,
+
+ CODE_RGB_DXT1,
+ CODE_RGBA_DXT1,
+ CODE_RGBA_DXT3,
+ CODE_RGBA_DXT5,
+
+ CODE_DEPTH16,
+ CODE_DEPTH24,
+ CODE_DEPTH32,
+ CODE_DEPTH32F,
+
+ CODE_STENCIL1,
+ CODE_STENCIL4,
+ CODE_STENCIL8,
+ CODE_STENCIL16,
+
+ CODE_DEPTH24_STENCIL8,
+
+ CODE_NUM
+ };
+
+ enum ColorSpace {
+ COLOR_SPACE_NONE,
+ COLOR_SPACE_RGB,
+ COLOR_SPACE_HSV,
+ COLOR_SPACE_YUV
+ };
+
+ enum BayerPattern {
+ BAYER_PATTERN_NONE,
+ BAYER_PATTERN_RGGB,
+ BAYER_PATTERN_GRBG,
+ BAYER_PATTERN_GBRG,
+ BAYER_PATTERN_BGGR
+ };
+
+ /** Number of channels (1 for a depth texture). */
+ int numComponents;
+ bool compressed;
+
+ /** Useful for serializing. */
+ Code code;
+
+ ColorSpace colorSpace;
+
+ /** If this is a Bayer format, what is the pattern. */
+ BayerPattern bayerPattern;
+
+ /** The OpenGL format equivalent to this one, e.g, GL_RGB8 Zero if there is no equivalent. This is actually a GLenum */
+ int openGLFormat;
+
+ /** The OpenGL base format equivalent to this one (e.g., GL_RGB, GL_ALPHA). Zero if there is no equivalent. */
+ int openGLBaseFormat;
+
+ int luminanceBits;
+
+ /** Number of bits per pixel storage for alpha values; Zero for compressed textures and non-RGB. */
+ int alphaBits;
+
+ /** Number of bits per pixel storage for red values; Zero for compressed textures and non-RGB. */
+ int redBits;
+
+ /** Number of bits per pixel storage for green values; Zero for compressed textures and non-RGB. */
+ int greenBits;
+
+ /** Number of bits per pixel storage for blue values; Zero for compressed textures and non-RGB. */
+ int blueBits;
+
+ /** Number of bits per pixel */
+ int stencilBits;
+
+ /** Number of depth bits (for depth textures; e.g. shadow maps) */
+ int depthBits;
+
+ /** Amount of CPU memory per pixel when packed into an array, discounting any end-of-row padding. */
+ int cpuBitsPerPixel;
+
+ /** Amount of CPU memory per pixel when packed into an array, discounting any end-of-row padding. @deprecated Use cpuBitsPerPixel*/
+ int packedBitsPerTexel;
+
+ /**
+ Amount of GPU memory per pixel on most graphics cards, for formats supported by OpenGL. This is
+ only an estimate--the actual amount of memory may be different on your actual card.
+
+ This may be greater than the sum of the per-channel bits
+ because graphics cards need to pad to the nearest 1, 2, or
+ 4 bytes.
+ */
+ int openGLBitsPerPixel;
+
+ /** @deprecated Use openGLBitsPerPixel */
+ int hardwareBitsPerTexel;
+
+ /** The OpenGL bytes format of the data buffer used with this texture format, e.g., GL_UNSIGNED_BYTE */
+ int openGLDataFormat;
+
+ /** True if there is no alpha channel for this texture. */
+ bool opaque;
+
+ /** True if the bit depths specified are for float formats. */
+ bool floatingPoint;
+
+ /** Human readable name of this texture.*/
+ std::string name() const;
+
+private:
+
+ ImageFormat(
+ int numComponents,
+ bool compressed,
+ int glFormat,
+ int glBaseFormat,
+ int luminanceBits,
+ int alphaBits,
+ int redBits,
+ int greenBits,
+ int blueBits,
+ int depthBits,
+ int stencilBits,
+ int hardwareBitsPerTexel,
+ int packedBitsPerTexel,
+ int glDataFormat,
+ bool opaque,
+ bool floatingPoint,
+ Code code,
+ ColorSpace colorSpace,
+ BayerPattern bayerPattern = BAYER_PATTERN_NONE);
+
+public:
+
+ static const ImageFormat* L8();
+
+ static const ImageFormat* L16();
+
+ static const ImageFormat* L16F();
+
+ static const ImageFormat* L32F();
+
+ static const ImageFormat* A8();
+
+ static const ImageFormat* A16();
+
+ static const ImageFormat* A16F();
+
+ static const ImageFormat* A32F();
+
+ static const ImageFormat* LA4();
+
+ static const ImageFormat* LA8();
+
+ static const ImageFormat* LA16();
+
+ static const ImageFormat* LA16F();
+
+ static const ImageFormat* LA32F();
+
+ static const ImageFormat* BGR8();
+
+ static const ImageFormat* RGB5();
+
+ static const ImageFormat* RGB5A1();
+
+ static const ImageFormat* RGB8();
+
+ static const ImageFormat* RGB10();
+
+ static const ImageFormat* RGB10A2();
+
+ static const ImageFormat* RGB16();
+
+ static const ImageFormat* RGB16F();
+
+ static const ImageFormat* RGB32F();
+
+ static const ImageFormat* RGBA8();
+
+ static const ImageFormat* RGBA16();
+
+ static const ImageFormat* RGBA16F();
+
+ static const ImageFormat* RGBA32F();
+
+ static const ImageFormat* RGB_DXT1();
+
+ static const ImageFormat* RGBA_DXT1();
+
+ static const ImageFormat* RGBA_DXT3();
+
+ static const ImageFormat* RGBA_DXT5();
+
+ static const ImageFormat* DEPTH16();
+
+ static const ImageFormat* DEPTH24();
+
+ static const ImageFormat* DEPTH32();
+
+ static const ImageFormat* DEPTH32F();
+
+ static const ImageFormat* STENCIL1();
+
+ static const ImageFormat* STENCIL4();
+
+ static const ImageFormat* STENCIL8();
+
+ static const ImageFormat* STENCIL16();
+
+ static const ImageFormat* DEPTH24_STENCIL8();
+
+ static const ImageFormat* YUV420_PLANAR();
+
+ static const ImageFormat* YUV422();
+
+ static const ImageFormat* YUV444();
+
+ /**
+ NULL pointer; indicates that the G3D::Texture class should choose
+ either RGBA8 or RGB8 depending on the presence of an alpha channel
+ in the input.
+ */
+ static const ImageFormat* AUTO() { return NULL; }
+
+ /** Returns DEPTH16, DEPTH24, or DEPTH32 according to the bits
+ specified. You can use "glGetInteger(GL_DEPTH_BITS)" to match
+ the screen's format.*/
+ static const ImageFormat* depth(int depthBits = 24);
+
+ /** Returns STENCIL1, STENCIL4, STENCIL8 or STENCIL16 according to the bits
+ specified. You can use "glGetInteger(GL_STENCIL_BITS)" to match
+ the screen's format.*/
+ static const ImageFormat* stencil(int bits = 8);
+
+ /** Returns the matching ImageFormat* identified by the Code. May return NULL
+ if this format's code is reserved but not yet implemented by G3D. */
+ static const ImageFormat* fromCode(ImageFormat::Code code);
+
+
+
+ /** For use with ImageFormat::convert. */
+ class BayerAlgorithm {
+ public:
+ enum Value {
+ NEAREST,
+ BILINEAR,
+ mhc,
+ HIGH_QUALITY = mhc
+ };
+ private:
+
+ Value value;
+
+ public:
+
+ G3D_DECLARE_ENUM_CLASS_METHODS(BayerAlgorithm);
+ };
+
+ /** Converts between arbitrary formats on the CPU. Not all format conversions are supported or directly supported.
+ Formats without direct conversions will attempt to convert through RGBA first.
+
+ A conversion routine might only support source or destination padding or y inversion or none.
+ If support is needed and not available in any of the direct conversion routines, then no conversion is done.
+
+ YUV422 expects data in YUY2 format (Y, U, Y2, v). Most YUV formats require width and heights that are multiples of 2.
+
+ Returns true if a conversion was available, false if none occurred.
+ */
+ static bool convert(const Array<const void*>& srcBytes, int srcWidth, int srcHeight, const ImageFormat* srcFormat, int srcRowPadBits,
+ const Array<void*>& dstBytes, const ImageFormat* dstFormat, int dstRowPadBits,
+ bool invertY = false, BayerAlgorithm bayerAlg = BayerAlgorithm::HIGH_QUALITY);
+
+ /* Checks if a conversion between two formats is available. */
+ static bool conversionAvailable(const ImageFormat* srcFormat, int srcRowPadBits, const ImageFormat* dstFormat, int dstRowPadBits, bool invertY = false);
+};
+
+typedef ImageFormat TextureFormat;
+
+}
+
+template <>
+struct HashTrait<const G3D::ImageFormat*> {
+ static size_t hashCode(const G3D::ImageFormat* key) { return reinterpret_cast<size_t>(key); }
+};
+
+
+
+#endif
diff --git a/externals/g3dlite/G3D.lib/include/G3D/Line.h b/externals/g3dlite/G3D.lib/include/G3D/Line.h
new file mode 100644
index 00000000000..ff6dc8d08e7
--- /dev/null
+++ b/externals/g3dlite/G3D.lib/include/G3D/Line.h
@@ -0,0 +1,105 @@
+/**
+ @file Line.h
+
+ Line class
+
+ @maintainer Morgan McGuire, matrix@graphics3d.com
+
+ @created 2001-06-02
+ @edited 2006-02-28
+ */
+
+#ifndef G3D_LINE_H
+#define G3D_LINE_H
+
+#include "G3D/platform.h"
+#include "G3D/Vector3.h"
+
+namespace G3D {
+
+class Plane;
+
+/**
+ An infinite 3D line.
+ */
+class Line {
+protected:
+
+ Vector3 _point;
+ Vector3 _direction;
+
+ Line(const Vector3& point, const Vector3& direction) {
+ _point = point;
+ _direction = direction.direction();
+ }
+
+public:
+
+ /** Undefined (provided for creating Array<Line> only) */
+ inline Line() {}
+
+ Line(class BinaryInput& b);
+
+ void serialize(class BinaryOutput& b) const;
+
+ void deserialize(class BinaryInput& b);
+
+ virtual ~Line() {}
+
+ /**
+ Constructs a line from two (not equal) points.
+ */
+ static Line fromTwoPoints(const Vector3 &point1, const Vector3 &point2) {
+ return Line(point1, point2 - point1);
+ }
+
+ /**
+ Creates a line from a point and a (nonzero) direction.
+ */
+ static Line fromPointAndDirection(const Vector3& point, const Vector3& direction) {
+ return Line(point, direction);
+ }
+
+ /**
+ Returns the closest point on the line to point.
+ */
+ Vector3 closestPoint(const Vector3& pt) const;
+
+ /**
+ Returns the distance between point and the line
+ */
+ double distance(const Vector3& point) const {
+ return (closestPoint(point) - point).magnitude();
+ }
+
+ /** Returns a point on the line */
+ Vector3 point() const;
+
+ /** Returns the direction (or negative direction) of the line */
+ Vector3 direction() const;
+
+ /**
+ Returns the point where the line and plane intersect. If there
+ is no intersection, returns a point at infinity.
+ */
+ Vector3 intersection(const Plane &plane) const;
+
+
+ /** Finds the closest point to the two lines.
+
+ @param minDist Returns the minimum distance between the lines.
+
+ @cite http://objectmix.com/graphics/133793-coordinates-closest-points-pair-skew-lines.html
+ */
+ Vector3 closestPoint(const Line& B, float& minDist) const;
+
+ inline Vector3 closestPoint(const Line& B) const {
+ float m;
+ return closestPoint(B, m);
+ }
+};
+
+};// namespace
+
+
+#endif
diff --git a/externals/g3dlite/G3D.lib/include/G3D/LineSegment.h b/externals/g3dlite/G3D.lib/include/G3D/LineSegment.h
new file mode 100644
index 00000000000..092b0c9ca6d
--- /dev/null
+++ b/externals/g3dlite/G3D.lib/include/G3D/LineSegment.h
@@ -0,0 +1,115 @@
+/**
+ @file LineSegment.h
+
+ @maintainer Morgan McGuire, matrix@graphics3d.com
+
+ @created 2003-02-08
+ @edited 2008-02-02
+ */
+
+#ifndef G3D_LINESEGMENT_H
+#define G3D_LINESEGMENT_H
+
+#include "G3D/platform.h"
+#include "G3D/Vector3.h"
+
+namespace G3D {
+
+/**
+ An finite segment of an infinite 3D line.
+ */
+class LineSegment {
+protected:
+
+ Vector3 _point;
+
+ /** Not normalized */
+ Vector3 direction;
+
+ LineSegment(const Vector3& __point, const Vector3& _direction) : _point(__point), direction(_direction) {
+ }
+
+public:
+
+ inline LineSegment() : _point(Vector3::zero()), direction(Vector3::zero()) {}
+
+ LineSegment(class BinaryInput& b);
+
+ void serialize(class BinaryOutput& b) const;
+
+ void deserialize(class BinaryInput& b);
+
+ virtual ~LineSegment() {}
+
+ /**
+ * Constructs a line from two (not equal) points.
+ */
+ static LineSegment fromTwoPoints(const Vector3 &point1, const Vector3 &point2) {
+ return LineSegment(point1, point2 - point1);
+ }
+
+ /** Call with 0 or 1 */
+ Vector3 point(int i) const;
+
+ inline float length() const {
+ return direction.magnitude();
+ }
+
+ /**
+ * Returns the closest point on the line segment to point.
+ */
+ Vector3 closestPoint(const Vector3 &point) const;
+
+ /**
+ Returns the distance between point and the line
+ */
+ double distance(const Vector3& p) const {
+ return (closestPoint(p) - p).magnitude();
+ }
+
+ double distanceSquared(const Vector3& p) const {
+ return (closestPoint(p) - p).squaredMagnitude();
+ }
+
+ /** Returns true if some part of this segment is inside the sphere */
+ bool intersectsSolidSphere(const class Sphere& s) const;
+
+ Vector3 randomPoint() const;
+
+};
+
+
+class LineSegment2D {
+private:
+
+ Vector2 m_origin;
+
+ /** Not normalized */
+ Vector2 m_direction;
+
+ /** Length of m_direction */
+ float m_length;
+
+public:
+
+ LineSegment2D() {}
+
+ static LineSegment2D fromTwoPoints(const Vector2& p0, const Vector2& p1);
+
+ /** Returns the intersection of these segements (including
+ testing endpoints), or Vector2::inf() if they do not intersect. */
+ Vector2 intersection(const LineSegment2D& other) const;
+
+ Vector2 point(int i) const;
+
+ Vector2 closestPoint(const Vector2& Q) const;
+
+ float distance(const Vector2& p) const;
+
+ float length() const;
+};
+
+} // namespace
+
+
+#endif
diff --git a/externals/g3dlite/G3D.lib/include/G3D/Log.h b/externals/g3dlite/G3D.lib/include/G3D/Log.h
new file mode 100644
index 00000000000..19b4b65c82d
--- /dev/null
+++ b/externals/g3dlite/G3D.lib/include/G3D/Log.h
@@ -0,0 +1,109 @@
+/**
+ @file Log.h
+
+ @maintainer Morgan McGuire, morgan@graphics3d.com
+ @cite Backtrace by Aaron Orenstein
+ @created 2001-08-04
+ @edited 2005-11-04
+ */
+
+#ifndef G3D_LOG_H
+#define G3D_LOG_H
+
+#include <stdio.h>
+#include <string>
+#include "G3D/platform.h"
+
+#ifndef G3D_WIN32
+ #include <stdarg.h>
+#endif
+
+namespace G3D {
+
+/** Prints to the common system log, log.txt, which is usually
+ in the working directory of the program. If your disk is
+ not writable or is slow, it will attempt to write to "c:/tmp/log.txt" or
+ "c:/temp/log.txt" on Windows systems instead.
+
+ Unlike printf or debugPrintf,
+ this function guarantees that all output is committed before it returns.
+ This is very useful for debugging a crash, which might hide the last few
+ buffered print statements otherwise.
+
+ Many G3D routines write useful warnings and debugging information to the
+ system log, which makes it a good first place to go when tracking down
+ a problem.
+ */
+void logPrintf(const char* fmt, ...);
+
+/**
+ System log for debugging purposes. The first log opened
+ is the "common log" and can be accessed with the static
+ method common(). If you access common() and a common log
+ does not yet exist, one is created for you.
+ */
+class Log {
+private:
+
+ /**
+ Log messages go here.
+ */
+ FILE* logFile;
+
+ std::string filename;
+
+ static Log* commonLog;
+
+ int stripFromStackBottom;
+
+ /**
+ Prints the time & stack trace.
+ */
+ void printHeader();
+
+public:
+
+ /**
+ @param stripFromStackBottom Number of call stacks to strip from the
+ bottom of the stack when printing a trace. Useful for hiding
+ routines like "main" and "WinMain". If the specified file cannot
+ be opened for some reason, tries to open "c:/tmp/log.txt" or
+ "c:/temp/log.txt" instead.
+ */
+ Log(const std::string& filename = "log.txt",
+ int stripFromStackBottom = 0);
+
+ virtual ~Log();
+
+ /**
+ Returns the handle to the file log.
+ */
+ FILE* getFile() const;
+
+ /**
+ Marks the beginning of a logfile section.
+ */
+ void section(const std::string& s);
+
+ /**
+ Given arguments like printf, writes characters to the debug text overlay.
+ */
+ // We want G3D_CHECK_PRINTF_ARGS here, but that conflicts with the
+ // overload.
+ void __cdecl printf(const char* fmt, ...) G3D_CHECK_PRINTF_METHOD_ARGS;
+
+ void __cdecl vprintf(const char*, va_list argPtr) G3D_CHECK_VPRINTF_METHOD_ARGS;
+
+ static Log* common();
+
+ static std::string getCommonLogFilename();
+
+ void print(const std::string& s);
+
+
+ void println(const std::string& s);
+};
+
+}
+
+#endif
diff --git a/externals/g3dlite/G3D.lib/include/G3D/Map2D.h b/externals/g3dlite/G3D.lib/include/G3D/Map2D.h
new file mode 100644
index 00000000000..48e2e957a5f
--- /dev/null
+++ b/externals/g3dlite/G3D.lib/include/G3D/Map2D.h
@@ -0,0 +1,665 @@
+/**
+ @file Map2D.h
+
+ More flexible support than provided by G3D::GImage.
+
+ @maintainer Morgan McGuire, morgan@cs.brown.edu
+ @created 2004-10-10
+ @edited 2007-07-18
+ */
+#ifndef G3D_MAP2D_H
+#define G3D_MAP2D_H
+
+#include "G3D/platform.h"
+#include "G3D/g3dmath.h"
+#include "G3D/Array.h"
+#include "G3D/Vector2int16.h"
+#include "G3D/ReferenceCount.h"
+#include "G3D/AtomicInt32.h"
+#include "G3D/GThread.h"
+#include "G3D/Rect2D.h"
+#include "G3D/WrapMode.h"
+
+#include <string>
+
+namespace G3D {
+namespace _internal {
+
+/** The default compute type for a type is the type itself. */
+template<typename Storage> class _GetComputeType {
+public:
+ typedef Storage Type;
+};
+
+} // _internal
+} // G3D
+
+// This weird syntax is needed to support VC6, which doesn't
+// properly implement template overloading.
+#define DECLARE_COMPUTE_TYPE(StorageType, ComputeType) \
+namespace G3D { \
+ namespace _internal { \
+ template<> class _GetComputeType < StorageType > { \
+ public: \
+ typedef ComputeType Type; \
+ }; \
+ } \
+}
+
+DECLARE_COMPUTE_TYPE( float32, float64)
+DECLARE_COMPUTE_TYPE( float64, float64)
+
+DECLARE_COMPUTE_TYPE( int8, float32)
+DECLARE_COMPUTE_TYPE( int16, float32)
+DECLARE_COMPUTE_TYPE( int32, float64)
+DECLARE_COMPUTE_TYPE( int64, float64)
+
+DECLARE_COMPUTE_TYPE( uint8, float32)
+DECLARE_COMPUTE_TYPE( uint16, float32)
+DECLARE_COMPUTE_TYPE( uint32, float64)
+DECLARE_COMPUTE_TYPE( uint64, float64)
+
+DECLARE_COMPUTE_TYPE( Vector2, Vector2)
+DECLARE_COMPUTE_TYPE( Vector2int16, Vector2)
+
+DECLARE_COMPUTE_TYPE( Vector3, Vector3)
+DECLARE_COMPUTE_TYPE( Vector3int16, Vector3)
+
+DECLARE_COMPUTE_TYPE( Vector4, Vector4)
+
+DECLARE_COMPUTE_TYPE( Color3, Color3)
+DECLARE_COMPUTE_TYPE( Color3uint8, Color3)
+
+DECLARE_COMPUTE_TYPE( Color4, Color4)
+DECLARE_COMPUTE_TYPE( Color4uint8, Color4)
+#undef DECLARE_COMPUTE_TYPE
+
+namespace G3D {
+
+/**
+ Map of values across a discrete 2D plane. Can be thought of as a generic class for 2D images,
+ allowing flexibility as to pixel format and convenient methods.
+ In fact, the "pixels" can be any values
+ on a grid that can be sensibly interpolated--RGB colors, scalars, 4D vectors, and so on.
+
+ Other "image" classes in G3D:
+
+ G3D::GImage - Supports file formats, fast, Color3uint8 and Color4uint8 formats. No interpolation.
+
+ G3D::Texture::Ref - Represents image on the graphics card (not directly readable on the CPU). Supports 2D, 3D, and a variety of interpolation methods, loads file formats.
+
+ G3D::Image3 - A subclass of Map2D<Color3> that supports image loading and saving and conversion to Texture.
+
+ G3D::Image4 - A subclass of Map2D<Color4> that supports image loading and saving and conversion to Texture.
+
+ G3D::Image3uint8 - A subclass of Map2D<Color3uint8> that supports image loading and saving and conversion to Texture.
+
+ G3D::Image4uint8 - A subclass of Map2D<Color4uint8> that supports image loading and saving and conversion to Texture.
+
+ There are two type parameters-- the first (@ Storage) is the type
+ used to store the "pixel" values efficiently and
+ the second (@a Compute) is
+ the type operated on by computation. The Compute::Compute(Storage&) constructor
+ is used to convert between storage and computation types.
+ @a Storage is often an integer version of @a Compute, for example
+ <code>Map2D<double, uint8></code>. By default, the computation type is:
+
+ <pre>
+ Storage Computation
+
+ uint8 float32
+ uint16 float32
+ uint32 float64
+ uint64 float64
+
+ int8 float32
+ int16 float32
+ int32 float64
+ int64 float64
+
+ float32 float64
+ float64 float64
+
+ Vector2 Vector2
+ Vector2int16 Vector2
+
+ Vector3 Vector3
+ Vector3int16 Vector3
+
+ Vector4 Vector4
+
+ Color3 Color3
+ Color3uint8 Color3
+
+ Color4 Color4
+ Color4uint8 Color4
+ </pre>
+ Any other storage type defaults to itself as the computation type.
+
+ The computation type can be any that
+ supports lerp, +, -, *, /, and an empty constructor.
+
+ Assign value:
+
+ <code>im->set(x, y, 7);</code> or
+ <code>im->get(x, y) = 7;</code>
+
+ Read value:
+
+ <code>int c = im(x, y);</code>
+
+ Can also sample with nearest neighbor, bilinear, and bicubic
+ interpolation.
+
+ Sampling follows OpenGL conventions, where
+ pixel values represent grid points and (0.5, 0.5) is half-way
+ between two vertical and two horizontal grid points.
+ To draw an image of dimensions w x h with nearest neighbor
+ sampling, render pixels from [0, 0] to [w - 1, h - 1].
+
+ Under the WrapMode::CLAMP wrap mode, the value of bilinear interpolation
+ becomes constant outside [1, w - 2] horizontally. Nearest neighbor
+ interpolation is constant outside [0, w - 1] and bicubic outside
+ [3, w - 4]. The class does not offer quadratic interpolation because
+ the interpolation filter could not center over a pixel.
+
+ @author Morgan McGuire, morgan@cs.williams.edu
+ */
+template< typename Storage,
+typename Compute = typename G3D::_internal::_GetComputeType<Storage>::Type>
+class Map2D : public ReferenceCountedObject {
+
+//
+// It doesn't make sense to automatically convert from Compute back to Storage
+// because the rounding rule (and scaling) is application dependent.
+// Thus the interpolation methods all return type Compute.
+//
+
+public:
+
+ typedef Storage StorageType;
+ typedef Compute ComputeType;
+ typedef Map2D<Storage, Compute> Type;
+ typedef ReferenceCountedPointer<Map2D> Ref;
+
+protected:
+
+ Storage ZERO;
+
+ /** Width, in pixels. */
+ uint32 w;
+
+ /** Height, in pixels. */
+ uint32 h;
+
+ WrapMode _wrapMode;
+
+ /** 0 if no mutating method has been invoked
+ since the last call to setChanged(); */
+ AtomicInt32 m_changed;
+
+ Array<Storage> data;
+
+ /** Handles the exceptional cases from get */
+ const Storage& slowGet(int x, int y, WrapMode wrap) {
+ switch (wrap) {
+ case WrapMode::CLAMP:
+ return fastGet(iClamp(x, 0, w - 1), iClamp(y, 0, h - 1));
+
+ case WrapMode::TILE:
+ return fastGet(iWrap(x, w), iWrap(y, h));
+
+ case WrapMode::ZERO:
+ return ZERO;
+
+ case WrapMode::ERROR:
+ alwaysAssertM(((uint32)x < w) && ((uint32)y < h),
+ format("Index out of bounds: (%d, %d), w = %d, h = %d",
+ x, y, w, h));
+
+ // intentionally fall through
+ case WrapMode::IGNORE:
+ // intentionally fall through
+ default:
+ {
+ static Storage temp;
+ return temp;
+ }
+ }
+ }
+
+public:
+
+ /** Unsafe access to the underlying data structure with no wrapping support; requires that (x, y) is in bounds. */
+ inline const Storage& fastGet(int x, int y) const {
+ debugAssert(((uint32)x < w) && ((uint32)y < h));
+ return data[x + y * w];
+ }
+
+ /** Unsafe access to the underlying data structure with no wrapping support; requires that (x, y) is in bounds. */
+ inline void fastSet(int x, int y, const Storage& v) {
+ debugAssert(((uint32)x < w) && ((uint32)y < h));
+ data[x + y * w] = v;
+ }
+
+protected:
+
+ /** Given four control points and a value on the range [0, 1)
+ evaluates the Catmull-rom spline between the times of the
+ middle two control points */
+ Compute bicubic(const Compute* ctrl, double s) const {
+
+ // f = B * S * ctrl'
+
+ // B matrix: Catmull-Rom spline basis
+ static const double B[4][4] = {
+ { 0.0, -0.5, 1.0, -0.5},
+ { 1.0, 0.0, -2.5, 1.5},
+ { 0.0, 0.5, 2.0, -1.5},
+ { 0.0, 0.0, -0.5, 0.5}};
+
+ // S: Powers of the fraction
+ double S[4];
+ double s2 = s * s;
+ S[0] = 1.0;
+ S[1] = s;
+ S[2] = s2;
+ S[3] = s2 * s;
+
+ Compute sum(ZERO);
+
+ for (int c = 0; c < 4; ++c) {
+ double coeff = 0.0;
+ for (int power = 0; power < 4; ++power) {
+ coeff += B[c][power] * S[power];
+ }
+ sum += ctrl[c] * coeff;
+ }
+
+ return sum;
+ }
+
+
+ Map2D(int w, int h, WrapMode wrap) : w(0), h(0), _wrapMode(wrap), m_changed(1) {
+ ZERO = Storage(Compute(Storage()) * 0);
+ resize(w, h);
+ }
+
+public:
+
+ /**
+ Although Map2D is not threadsafe (except for the setChanged() method),
+ you can use this mutex to create your own threadsafe access to a Map2D.
+ Not used by the default implementation.
+ */
+ GMutex mutex;
+
+ static Ref create(int w = 0, int h = 0, WrapMode wrap = WrapMode::ERROR) {
+ return new Map2D(w, h, wrap);
+ }
+
+ /** Resizes without clearing, leaving garbage.
+ */
+ void resize(uint32 newW, uint32 newH) {
+ if ((newW != w) || (newH != h)) {
+ w = newW;
+ h = newH;
+ data.resize(w * h);
+ setChanged(true);
+ }
+ }
+
+ /**
+ Returns true if this map has been written to since the last call to setChanged(false).
+ This is useful if you are caching a texture map other value that must be recomputed
+ whenever this changes.
+ */
+ bool changed() {
+ return m_changed.value() != 0;
+ }
+
+ /** Set/unset the changed flag. */
+ void setChanged(bool c) {
+ m_changed = c ? 1 : 0;
+ }
+
+ /** Returns a pointer to the underlying row-major data. There is no padding at the end of the row.
+ Be careful--this will be reallocated during a resize. You should call setChanged(true) if you mutate the array.*/
+ Storage* getCArray() {
+ return data.getCArray();
+ }
+
+
+ const Storage* getCArray() const {
+ return data.getCArray();
+ }
+
+
+ /** Row-major array. You should call setChanged(true) if you mutate the array. */
+ Array<Storage>& getArray() {
+ return data;
+ }
+
+
+ const Array<Storage>& getArray() const {
+ return data;
+ }
+
+ /** is (x, y) strictly within the image bounds, or will it trigger some kind of wrap mode */
+ inline bool inBounds(int x, int y) const {
+ return (((uint32)x < w) && ((uint32)y < h));
+ }
+
+ /** is (x, y) strictly within the image bounds, or will it trigger some kind of wrap mode */
+ inline bool inBounds(const Vector2int16& v) const {
+ return inBounds(v.x, v.y);
+ }
+
+ /** Get the value at (x, y).
+
+ Note that the type of image->get(x, y) is
+ the storage type, not the computation
+ type. If the constructor promoting Storage to Compute rescales values
+ (as, for example Color3(Color3uint8&) does), this will not match the value
+ returned by Map2D::nearest.
+ */
+ inline const Storage& get(int x, int y, WrapMode wrap) const {
+ if (((uint32)x < w) && ((uint32)y < h)) {
+ return data[x + y * w];
+ } else {
+ // Remove the const to allow a slowGet on this object
+ // (we're returning a const reference so this is ok)
+ return const_cast<Type*>(this)->slowGet(x, y, wrap);
+ }
+# ifndef G3D_WIN32
+ // gcc gives a useless warning that the above code might reach the end of the function;
+ // we use this line to supress the warning.
+ return ZERO;
+# endif
+ }
+
+ inline const Storage& get(int x, int y) const {
+ return get(x, y, _wrapMode);
+ }
+
+ inline const Storage& get(const Vector2int16& p) const {
+ return get(p.x, p.y, _wrapMode);
+ }
+
+ inline const Storage& get(const Vector2int16& p, WrapMode wrap) const {
+ return get(p.x, p.y, wrap);
+ }
+
+ inline Storage& get(int x, int y, WrapMode wrap) {
+ return const_cast<Storage&>(const_cast<const Type*>(this)->get(x, y, wrap));
+# ifndef G3D_WIN32
+ // gcc gives a useless warning that the above code might reach the end of the function;
+ // we use this line to supress the warning.
+ return ZERO;
+# endif
+ }
+
+ inline Storage& get(int x, int y) {
+ return const_cast<Storage&>(const_cast<const Type*>(this)->get(x, y));
+# ifndef G3D_WIN32
+ // gcc gives a useless warning that the above code might reach the end of the function;
+ // we use this line to supress the warning.
+ return ZERO;
+# endif
+ }
+
+ inline Storage& get(const Vector2int16& p) {
+ return get(p.x, p.y);
+ }
+
+ /** Sets the changed flag to true */
+ inline void set(const Vector2int16& p, const Storage& v) {
+ set(p.x, p.y, v);
+ }
+
+ /** Sets the changed flag to true */
+ void set(int x, int y, const Storage& v, WrapMode wrap) {
+ setChanged(true);
+ if (((uint32)x < w) && ((uint32)y < h)) {
+ // In bounds, wrapping isn't an issue.
+ data[x + y * w] = v;
+ } else {
+ const_cast<Storage&>(slowGet(x, y, wrap)) = v;
+ }
+ }
+
+ void set(int x, int y, const Storage& v) {
+ set(x, y, v, _wrapMode);
+ }
+
+
+ void setAll(const Storage& v) {
+ for(int i = 0; i < data.size(); ++i) {
+ data[i] = v;
+ }
+ setChanged(true);
+ }
+
+ /** flips if @a flip is true*/
+ void maybeFlipVertical(bool flip) {
+ if (flip) {
+ flipVertical();
+ }
+ }
+
+ virtual void flipVertical() {
+ int halfHeight = h/2;
+ Storage* d = data.getCArray();
+ for (int y = 0; y < halfHeight; ++y) {
+ int o1 = y * w;
+ int o2 = (h - y - 1) * w;
+ for (int x = 0; x < (int)w; ++x) {
+ int i1 = o1 + x;
+ int i2 = o2 + x;
+ Storage temp = d[i1];
+ d[i1] = d[i2];
+ d[i2] = temp;
+ }
+ }
+ setChanged(true);
+ }
+
+ virtual void flipHorizontal() {
+ int halfWidth = w / 2;
+ Storage* d = data.getCArray();
+ for (int x = 0; x < halfWidth; ++x) {
+ for (int y = 0; y < (int)h; ++y) {
+ int i1 = y * w + x;
+ int i2 = y * w + (w - x - 1);
+ Storage temp = d[i1];
+ d[i1] = d[i2];
+ d[i2] = temp;
+ }
+ }
+ setChanged(true);
+ }
+
+ /**
+ Crops this map so that it only contains pixels between (x, y) and (x + w - 1, y + h - 1) inclusive.
+ */
+ virtual void crop(int newX, int newY, int newW, int newH) {
+ alwaysAssertM(newX + newW <= (int)w, "Cannot grow when cropping");
+ alwaysAssertM(newY + newH <= (int)h, "Cannot grow when cropping");
+ alwaysAssertM(newX >= 0 && newY >= 0, "Origin out of bounds.");
+
+ // Always safe to copy towards the upper left, provided
+ // that we're iterating towards the lower right. This lets us avoid
+ // reallocating the underlying array.
+ for (int y = 0; y < newH; ++y) {
+ for (int x = 0; x < newW; ++x) {
+ data[x + y * newW] = data[(x + newX) + (y + newY) * w];
+ }
+ }
+
+ resize(newW, newH);
+ }
+
+ /** iRounds to the nearest x0 and y0. */
+ virtual void crop(const Rect2D& rect) {
+ crop(iRound(rect.x0()), iRound(rect.y0()), iRound(rect.x1()) - iRound(rect.x0()), iRound(rect.y1()) - iRound(rect.y0()));
+ }
+
+ /** Returns the nearest neighbor. Pixel values are considered
+ to be at the upper left corner, so <code>image->nearest(x, y) == image(x, y)</code>
+ */
+ inline Compute nearest(float x, float y, WrapMode wrap) const {
+ return Compute(get(iRound(x), iRound(y), wrap));
+ }
+
+ inline Compute nearest(float x, float y) const {
+ return nearest(x, y, _wrapMode);
+ }
+
+ inline Compute nearest(const Vector2& p) const {
+ return nearest(p.x, p.y);
+ }
+
+ /** Returns the average value of all elements of the map */
+ Compute average() const {
+ if ((w == 0) || (h == 0)) {
+ return ZERO;
+ }
+
+ // To avoid overflows, compute the average of row averages
+
+ Compute rowSum = ZERO;
+ for (unsigned int y = 0; y < h; ++y) {
+ Compute sum = ZERO;
+ int offset = y * w;
+ for (unsigned int x = 0; x < w; ++x) {
+ sum += Compute(data[offset + x]);
+ }
+ rowSum += sum * (1.0f / w);
+ }
+
+ return rowSum * (1.0f / h);
+ }
+
+ /**
+ Needs to access elements from (floor(x), floor(y))
+ to (floor(x) + 1, floor(y) + 1) and will use
+ the wrap mode appropriately (possibly generating
+ out of bounds errors).
+
+ Guaranteed to match nearest(x, y) at integers. */
+ Compute bilinear(float x, float y, WrapMode wrap) const {
+ int i = iFloor(x);
+ int j = iFloor(y);
+
+ float fX = x - i;
+ float fY = y - j;
+
+ // Horizontal interpolation, first row
+ Compute t0(get(i, j, wrap));
+ Compute t1(get(i + 1, j, wrap));
+ Compute A = lerp(t0, t1, fX);
+
+ // Horizontal interpolation, second row
+ Compute t2(get(i, j + 1, wrap));
+ Compute t3(get(i + 1, j + 1, wrap));
+ Compute B = lerp(t2, t3, fX);
+
+ // Vertical interpolation
+ return lerp(A, B, fY);
+ }
+
+ Compute bilinear(float x, float y) const {
+ return bilinear(x, y, _wrapMode);
+ }
+
+ inline Compute bilinear(const Vector2& p) const {
+ return bilinear(p.x, p.y, _wrapMode);
+ }
+
+ inline Compute bilinear(const Vector2& p, WrapMode wrap) const {
+ return bilinear(p.x, p.y, wrap);
+ }
+
+ /**
+ Uses Catmull-Rom splines to interpolate between grid
+ values. Guaranteed to match nearest(x, y) at integers.
+ */
+ Compute bicubic(float x, float y, WrapMode wrap) const {
+ int i = iFloor(x);
+ int j = iFloor(y);
+ float fX = x - i;
+ float fY = y - j;
+
+ // 'static' prevents constructors from being called
+ // every time through this loop.
+ static Compute vsample[4];
+ for (int v = 0; v < 4; ++v) {
+
+ // Horizontal interpolation
+ static Compute hsample[4];
+ for (int u = 0; u < 4; ++u) {
+ hsample[u] = Compute(get(i + u - 1, j + v - 1, wrap));
+ }
+
+ vsample[v] = bicubic(hsample, fX);
+ }
+
+ // Vertical interpolation
+ return bicubic(vsample, fY);
+ }
+
+ Compute bicubic(float x, float y) const {
+ return bicubic(x, y, _wrapMode);
+ }
+
+ inline Compute bicubic(const Vector2& p, WrapMode wrap) const {
+ return bicubic(p.x, p.y, wrap);
+ }
+
+ inline Compute bicubic(const Vector2& p) const {
+ return bicubic(p.x, p.y, _wrapMode);
+ }
+
+ /** Pixel width */
+ inline int32 width() const {
+ return (int32)w;
+ }
+
+
+ /** Pixel height */
+ inline int32 height() const {
+ return (int32)h;
+ }
+
+
+ /** Dimensions in pixels */
+ Vector2int16 size() const {
+ return Vector2int16(w, h);
+ }
+
+ /** Rectangle from (0, 0) to (w, h) */
+ Rect2D rect2DBounds() const {
+ return Rect2D::xywh(0, 0, w, h);
+ }
+
+ /** Number of bytes occupied by the image data and this structure */
+ size_t sizeInMemory() const {
+ return data.size() * sizeof(Storage) + sizeof(*this);
+ }
+
+
+ WrapMode wrapMode() const {
+ return _wrapMode;
+ }
+
+
+ void setWrapMode(WrapMode m) {
+ _wrapMode = m;
+ }
+};
+
+
+
+}
+
+#endif // G3D_IMAGE_H
diff --git a/externals/g3dlite/G3D.lib/include/G3D/Matrix.h b/externals/g3dlite/G3D.lib/include/G3D/Matrix.h
new file mode 100644
index 00000000000..481af940324
--- /dev/null
+++ b/externals/g3dlite/G3D.lib/include/G3D/Matrix.h
@@ -0,0 +1,634 @@
+/**
+ @file Matrix.h
+ @author Morgan McGuire, morgan@cs.williams.edu
+
+ @created 2005-10-23
+ @edited 2007-07-18
+ */
+
+#ifndef G3D_MATRIX_H
+#define G3D_MATRIX_H
+
+#include "G3D/g3dmath.h"
+#include "G3D/Vector3.h"
+#include "G3D/Vector4.h"
+#include "G3D/Matrix3.h"
+#include "G3D/Matrix4.h"
+#include "G3D/ReferenceCount.h"
+
+namespace G3D {
+
+/**
+ N x M matrix.
+
+ The actual data is tracked internally by a reference counted pointer;
+ it is efficient to pass and assign Matrix objects because no data is actually copied.
+ This avoids the headache of pointers and allows natural math notation:
+
+ <PRE>
+ Matrix A, B, C;
+ // ...
+
+ C = A * f(B);
+ C = C.inverse();
+
+ A = Matrix::identity(4);
+ C = A;
+ C.set(0, 0, 2.0); // Triggers a copy of the data so that A remains unchanged.
+
+ // etc.
+
+ </PRE>
+
+ The Matrix::debugNumCopyOps and Matrix::debugNumAllocOps counters
+ increment every time an operation forces the copy and allocation of matrices. You
+ can use these to detect slow operations when efficiency is a major concern.
+
+ Some methods accept an output argument instead of returning a value. For example,
+ <CODE>A = B.transpose()</CODE> can also be invoked as <CODE>B.transpose(A)</CODE>.
+ The latter may be more efficient, since Matrix may be able to re-use the storage of
+ A (if it has approximatly the right size and isn't currently shared with another matrix).
+
+ @sa G3D::Matrix3, G3D::Matrix4, G3D::Vector2, G3D::Vector3, G3D::Vector4, G3D::CoordinateFrame
+
+ @beta
+ */
+class Matrix {
+public:
+ /**
+ Internal precision. Currently float, but this may become a templated class in the future
+ to allow operations like Matrix<double> and Matrix<ComplexFloat>.
+
+ Not necessarily a plain-old-data type (e.g., could ComplexFloat), but must be something
+ with no constructor, that can be safely memcpyd, and that has a bit pattern of all zeros
+ when zero.*/
+ typedef float T;
+
+ /** Incremented every time the elements of a matrix are copied. Useful for profiling your
+ own code that uses Matrix to determine when it is slow due to copying.*/
+ static int debugNumCopyOps;
+
+ /** Incremented every time a new matrix object is allocated. Useful for profiling your
+ own code that uses Matrix to determine when it is slow due to allocation.*/
+ static int debugNumAllocOps;
+
+private:
+public:
+
+ /** Used internally by Matrix.
+
+ Does not throw exceptions-- assumes the caller has taken care of
+ argument checking. */
+ class Impl : public ReferenceCountedObject {
+ public:
+
+ static void* operator new(size_t size) {
+ return System::malloc(size);
+ }
+
+ static void operator delete(void* p) {
+ System::free(p);
+ }
+
+ ~Impl();
+
+ private:
+ friend class Matrix;
+
+ /** elt[r][c] = the element. Pointers into data.*/
+ T** elt;
+
+ /** Row major data for the entire matrix. */
+ T* data;
+
+ /** The number of rows */
+ int R;
+
+ /** The number of columns */
+ int C;
+
+ int dataSize;
+
+ /** If R*C is much larger or smaller than the current, deletes all previous data
+ and resets to random data. Otherwise re-uses existing memory and just resets
+ R, C, and the row pointers. */
+ void setSize(int newRows, int newCols);
+
+ inline Impl() : elt(NULL), data(NULL), R(0), C(0), dataSize(0) {}
+
+ Impl(const Matrix3& M);
+
+ Impl(const Matrix4& M);
+
+ inline Impl(int r, int c) : elt(NULL), data(NULL), R(0), C(0), dataSize(0) {
+ setSize(r, c);
+ }
+
+ Impl& operator=(const Impl& m);
+
+ inline Impl(const Impl& B) : elt(NULL), data(NULL), R(0), C(0), dataSize(0) {
+ // Use the assignment operator
+ *this = B;
+ }
+
+ void setZero();
+
+ inline void set(int r, int c, T v) {
+ debugAssert(r < R);
+ debugAssert(c < C);
+ elt[r][c] = v;
+ }
+
+ inline const T& get(int r, int c) const {
+ debugAssert(r < R);
+ debugAssert(c < C);
+ return elt[r][c];
+ }
+
+ /** Multiplies this by B and puts the result in out. */
+ void mul(const Impl& B, Impl& out) const;
+
+ /** Ok if out == this or out == B */
+ void add(const Impl& B, Impl& out) const;
+
+ /** Ok if out == this or out == B */
+ void add(T B, Impl& out) const;
+
+ /** Ok if out == this or out == B */
+ void sub(const Impl& B, Impl& out) const;
+
+ /** Ok if out == this or out == B */
+ void sub(T B, Impl& out) const;
+
+ /** B - this */
+ void lsub(T B, Impl& out) const;
+
+ /** Ok if out == this or out == B */
+ void arrayMul(const Impl& B, Impl& out) const;
+
+ /** Ok if out == this or out == B */
+ void mul(T B, Impl& out) const;
+
+ /** Ok if out == this or out == B */
+ void arrayDiv(const Impl& B, Impl& out) const;
+
+ /** Ok if out == this or out == B */
+ void div(T B, Impl& out) const;
+
+ void negate(Impl& out) const;
+
+ /** Slow way of computing an inverse; for reference */
+ void inverseViaAdjoint(Impl& out) const;
+
+ /** Use Gaussian elimination with pivots to solve for the inverse destructively in place. */
+ void inverseInPlaceGaussJordan();
+
+ void adjoint(Impl& out) const;
+
+ /** Matrix of all cofactors */
+ void cofactor(Impl& out) const;
+
+ /**
+ Cofactor [r][c] is defined as C[r][c] = -1 ^(r+c) * det(A[r][c]),
+ where A[r][c] is the (R-1)x(C-1) matrix formed by removing row r and
+ column c from the original matrix.
+ */
+ T cofactor(int r, int c) const;
+
+ /** Ok if out == this or out == B */
+ void transpose(Impl& out) const;
+
+ T determinant() const;
+
+ /** Determinant computed without the given row and column */
+ T determinant(int r, int c) const;
+
+ void arrayLog(Impl& out) const;
+
+ void arrayExp(Impl& out) const;
+
+ void arraySqrt(Impl& out) const;
+
+ void arrayCos(Impl& out) const;
+
+ void arraySin(Impl& out) const;
+
+ void swapRows(int r0, int r1);
+
+ void swapAndNegateCols(int c0, int c1);
+
+ void mulRow(int r, const T& v);
+
+ void abs(Impl& out) const;
+
+ /** Makes a (R-1)x(C-1) copy of this matrix */
+ void withoutRowAndCol(int excludeRow, int excludeCol, Impl& out) const;
+
+ bool anyNonZero() const;
+
+ bool allNonZero() const;
+
+ void setRow(int r, const T* vals);
+
+ void setCol(int c, const T* vals);
+ };
+private:
+
+ typedef ReferenceCountedPointer<Impl> ImplRef;
+
+ ImplRef impl;
+
+ inline Matrix(ImplRef i) : impl(i) {}
+ inline Matrix(Impl* i) : impl(ImplRef(i)) {}
+
+ /** Used by SVD */
+ class SortRank {
+ public:
+ T value;
+ int col;
+
+ inline bool operator>(const SortRank& x) const {
+ return x.value > value;
+ }
+
+ inline bool operator<(const SortRank& x) const {
+ return x.value < value;
+ }
+
+ inline bool operator>=(const SortRank& x) const {
+ return x.value >= value;
+ }
+
+ inline bool operator<=(const SortRank& x) const {
+ return x.value <= value;
+ }
+
+ inline bool operator==(const SortRank& x) const {
+ return x.value == value;
+ }
+
+ inline bool operator!=(const SortRank& x) const {
+ return x.value != value;
+ }
+ };
+
+ Matrix vectorPseudoInverse() const;
+ Matrix partitionPseudoInverse() const;
+ Matrix colPartPseudoInverse() const;
+ Matrix rowPartPseudoInverse() const;
+
+ Matrix col2PseudoInverse(const Matrix& B) const;
+ Matrix col3PseudoInverse(const Matrix& B) const;
+ Matrix col4PseudoInverse(const Matrix& B) const;
+ Matrix row2PseudoInverse(const Matrix& B) const;
+ Matrix row3PseudoInverse(const Matrix& B) const;
+ Matrix row4PseudoInverse(const Matrix& B) const;
+
+public:
+
+ Matrix() : impl(new Impl(0, 0)) {}
+
+ Matrix(const Matrix3& M) : impl(new Impl(M)) {}
+
+ Matrix(const Matrix4& M) : impl(new Impl(M)) {}
+
+ template<class S>
+ static Matrix fromDiagonal(const Array<S>& d) {
+ Matrix D = zero(d.length(), d.length());
+ for (int i = 0; i < d.length(); ++i) {
+ D.set(i, i, d[i]);
+ }
+ return D;
+ }
+
+ static Matrix fromDiagonal(const Matrix& d);
+
+ /** Returns a new matrix that is all zero. */
+ Matrix(int R, int C) : impl(new Impl(R, C)) {
+ impl->setZero();
+ }
+
+ /** Returns a new matrix that is all zero. */
+ static Matrix zero(int R, int C);
+
+ /** Returns a new matrix that is all one. */
+ static Matrix one(int R, int C);
+
+ /** Returns a new identity matrix */
+ static Matrix identity(int N);
+
+ /** Uniformly distributed values between zero and one. */
+ static Matrix random(int R, int C);
+
+ /** The number of rows */
+ inline int rows() const {
+ return impl->R;
+ }
+
+ /** Number of columns */
+ inline int cols() const {
+ return impl->C;
+ }
+
+ /** Generally more efficient than A * B */
+ Matrix& operator*=(const T& B);
+
+ /** Generally more efficient than A / B */
+ Matrix& operator/=(const T& B);
+
+ /** Generally more efficient than A + B */
+ Matrix& operator+=(const T& B);
+
+ /** Generally more efficient than A - B */
+ Matrix& operator-=(const T& B);
+
+ /** No performance advantage over A * B because
+ matrix multiplication requires intermediate
+ storage. */
+ Matrix& operator*=(const Matrix& B);
+
+ /** Generally more efficient than A + B */
+ Matrix& operator+=(const Matrix& B);
+
+ /** Generally more efficient than A - B */
+ Matrix& operator-=(const Matrix& B);
+
+ /** Returns a new matrix that is a subset of this one,
+ from r1:r2 to c1:c2, inclusive.*/
+ Matrix subMatrix(int r1, int r2, int c1, int c2) const;
+
+ /** Matrix multiplication. To perform element-by-element multiplication,
+ see arrayMul. */
+ inline Matrix operator*(const Matrix& B) const {
+ Matrix C(impl->R, B.impl->C);
+ impl->mul(*B.impl, *C.impl);
+ return C;
+ }
+
+ /** See also A *= B, which is more efficient in many cases */
+ inline Matrix operator*(const T& B) const {
+ Matrix C(impl->R, impl->C);
+ impl->mul(B, *C.impl);
+ return C;
+ }
+
+ /** See also A += B, which is more efficient in many cases */
+ inline Matrix operator+(const Matrix& B) const {
+ Matrix C(impl->R, impl->C);
+ impl->add(*B.impl, *C.impl);
+ return C;
+ }
+
+ /** See also A -= B, which is more efficient in many cases */
+ inline Matrix operator-(const Matrix& B) const {
+ Matrix C(impl->R, impl->C);
+ impl->sub(*B.impl, *C.impl);
+ return C;
+ }
+
+ /** See also A += B, which is more efficient in many cases */
+ inline Matrix operator+(const T& v) const {
+ Matrix C(impl->R, impl->C);
+ impl->add(v, *C.impl);
+ return C;
+ }
+
+ /** See also A -= B, which is more efficient in many cases */
+ inline Matrix operator-(const T& v) const {
+ Matrix C(impl->R, impl->C);
+ impl->sub(v, *C.impl);
+ return C;
+ }
+
+
+ Matrix operator>(const T& scalar) const;
+
+ Matrix operator<(const T& scalar) const;
+
+ Matrix operator>=(const T& scalar) const;
+
+ Matrix operator<=(const T& scalar) const;
+
+ Matrix operator==(const T& scalar) const;
+
+ Matrix operator!=(const T& scalar) const;
+
+ /** scalar B - this */
+ inline Matrix lsub(const T& B) const {
+ Matrix C(impl->R, impl->C);
+ impl->lsub(B, *C.impl);
+ return C;
+ }
+
+ inline Matrix arrayMul(const Matrix& B) const {
+ Matrix C(impl->R, impl->C);
+ impl->arrayMul(*B.impl, *C.impl);
+ return C;
+ }
+
+ Matrix3 toMatrix3() const;
+
+ Matrix4 toMatrix4() const;
+
+ Vector2 toVector2() const;
+
+ Vector3 toVector3() const;
+
+ Vector4 toVector4() const;
+
+ /** Mutates this */
+ void arrayMulInPlace(const Matrix& B);
+
+ /** Mutates this */
+ void arrayDivInPlace(const Matrix& B);
+
+ // Declares an array unary method and its explicit-argument counterpart
+# define DECLARE_METHODS_1(method)\
+ inline Matrix method() const {\
+ Matrix C(impl->R, impl->C);\
+ impl->method(*C.impl);\
+ return C;\
+ }\
+ void method(Matrix& out) const;
+
+
+ DECLARE_METHODS_1(abs)
+ DECLARE_METHODS_1(arrayLog)
+ DECLARE_METHODS_1(arrayExp)
+ DECLARE_METHODS_1(arraySqrt)
+ DECLARE_METHODS_1(arrayCos)
+ DECLARE_METHODS_1(arraySin)
+ DECLARE_METHODS_1(negate)
+
+# undef DECLARE_METHODS_1
+
+ inline Matrix operator-() const {
+ return negate();
+ }
+
+ /**
+ A<SUP>-1</SUP> computed using the Gauss-Jordan algorithm,
+ for square matrices.
+ Run time is <I>O(R<sup>3</sup>)</I>, where <I>R</i> is the
+ number of rows.
+ */
+ inline Matrix inverse() const {
+ Impl* A = new Impl(*impl);
+ A->inverseInPlaceGaussJordan();
+ return Matrix(A);
+ }
+
+ inline T determinant() const {
+ return impl->determinant();
+ }
+
+ /**
+ A<SUP>T</SUP>
+ */
+ inline Matrix transpose() const {
+ Impl* A = new Impl(cols(), rows());
+ impl->transpose(*A);
+ return Matrix(A);
+ }
+
+ /** Transpose in place; more efficient than transpose */
+ void transpose(Matrix& out) const;
+
+ inline Matrix adjoint() const {
+ Impl* A = new Impl(cols(), rows());
+ impl->adjoint(*A);
+ return Matrix(A);
+ }
+
+ /**
+ (A<SUP>T</SUP>A)<SUP>-1</SUP>A<SUP>T</SUP>) computed
+ using SVD.
+
+ @param tolerance Use -1 for automatic tolerance.
+ */
+ Matrix pseudoInverse(float tolerance = -1) const;
+
+ /** Called from pseudoInverse when the matrix has size > 4 along some dimension.*/
+ Matrix svdPseudoInverse(float tolerance = -1) const;
+
+ /**
+ (A<SUP>T</SUP>A)<SUP>-1</SUP>A<SUP>T</SUP>) computed
+ using Gauss-Jordan elimination.
+ */
+ inline Matrix gaussJordanPseudoInverse() const {
+ Matrix trans = transpose();
+ return (trans * (*this)).inverse() * trans;
+ }
+
+ /** Singular value decomposition. Factors into three matrices
+ such that @a this = @a U * fromDiagonal(@a d) * @a V.transpose().
+
+ The matrix must have at least as many rows as columns.
+
+ Run time is <I>O(C<sup>2</sup>*R)</I>.
+
+ @param sort If true (default), the singular values
+ are arranged so that D is sorted from largest to smallest.
+ */
+ void svd(Matrix& U, Array<T>& d, Matrix& V, bool sort = true) const;
+
+ void set(int r, int c, T v);
+
+ void setCol(int c, const Matrix& vec);
+
+ void setRow(int r, const Matrix& vec);
+
+ Matrix col(int c) const;
+
+ Matrix row(int r) const;
+
+ T get(int r, int c) const;
+
+ Vector2int16 size() const {
+ return Vector2int16(rows(), cols());
+ }
+
+ int numElements() const {
+ return rows() * cols();
+ }
+
+ void swapRows(int r0, int r1);
+
+ /** Swaps columns c0 and c1 and negates both */
+ void swapAndNegateCols(int c0, int c1);
+
+ void mulRow(int r, const T& v);
+
+ /** Returns true if any element is non-zero */
+ bool anyNonZero() const;
+
+ /** Returns true if all elements are non-zero */
+ bool allNonZero() const;
+
+ inline bool allZero() const {
+ return !anyNonZero();
+ }
+
+ inline bool anyZero() const {
+ return !allNonZero();
+ }
+
+ /** Serializes in Matlab source format */
+ void serialize(TextOutput& t) const;
+
+ std::string toString(const std::string& name) const;
+
+ std::string toString() const {
+ static const std::string name = "";
+ return toString(name);
+ }
+
+ /** 2-norm squared: sum(squares). (i.e., dot product with itself) */
+ double normSquared() const;
+
+ /** 2-norm (sqrt(sum(squares)) */
+ double norm() const;
+
+ /**
+ Low-level SVD functionality. Useful for applications that do not want
+ to construct a Matrix but need to perform the SVD operation.
+
+ this = U * D * V'
+
+ Assumes that rows >= cols
+
+ @return NULL on success, a string describing the error on failure.
+ @param U rows x cols matrix to be decomposed, gets overwritten with U, a rows x cols matrix with orthogonal columns.
+ @param D vector of singular values of a (diagonal of the D matrix). Length cols.
+ @param V returns the right orthonormal transformation matrix, size cols x cols
+
+ @cite Based on Dianne Cook's implementation, which is adapted from
+ svdecomp.c in XLISP-STAT 2.1, which is code from Numerical Recipes
+ adapted by Luke Tierney and David Betz. The Numerical Recipes code
+ is adapted from Forsythe et al, who based their code on Golub and
+ Reinsch's original implementation.
+ */
+ static const char* svdCore(float** U, int rows, int cols, float* D, float** V);
+
+};
+
+}
+
+inline G3D::Matrix operator-(const G3D::Matrix::T& v, const G3D::Matrix& M) {
+ return M.lsub(v);
+}
+
+inline G3D::Matrix operator*(const G3D::Matrix::T& v, const G3D::Matrix& M) {
+ return M * v;
+}
+
+inline G3D::Matrix operator+(const G3D::Matrix::T& v, const G3D::Matrix& M) {
+ return M + v;
+}
+
+inline G3D::Matrix abs(const G3D::Matrix& M) {
+ return M.abs();
+}
+
+#endif
+
diff --git a/externals/g3dlite/G3D.lib/include/G3D/Matrix2.h b/externals/g3dlite/G3D.lib/include/G3D/Matrix2.h
new file mode 100644
index 00000000000..eaf4aefa220
--- /dev/null
+++ b/externals/g3dlite/G3D.lib/include/G3D/Matrix2.h
@@ -0,0 +1,69 @@
+#ifndef G3D_MATRIX2_H
+#define G3D_MATRIX2_H
+
+#include "G3D/platform.h"
+#include "G3D/Vector2.h"
+
+namespace G3D {
+
+/** @beta */
+class Matrix2 {
+private:
+
+ float data[2][2];
+
+public:
+
+ inline Matrix2() {
+ data[0][0] = 1.0f; data[0][1] = 0.0f;
+ data[1][0] = 0.0f; data[1][1] = 1.0f;
+ }
+
+ inline Matrix2(float v00, float v01, float v10, float v11) {
+ data[0][0] = v00; data[0][1] = v01;
+ data[1][0] = v10; data[1][1] = v11;
+ }
+
+ inline Vector2 operator*(const Vector2& v) const {
+ return Vector2(data[0][0] * v[0] + data[0][1] * v[1],
+ data[1][0] * v[0] + data[1][1] * v[1]);
+ }
+
+ inline Matrix2 inverse() const {
+ return Matrix2(data[0][0], data[1][0],
+ data[0][1], data[1][1]) * (1.0f / determinant());
+ }
+
+ inline Matrix2 transpose() const {
+ return Matrix2(data[0][0], data[1][0],
+ data[0][1], data[1][1]);
+ }
+
+ inline float determinant() const {
+ return data[0][0] * data[1][1] - data[0][1] * data[1][0];
+ }
+
+ inline Matrix2 operator*(float f) const {
+ return Matrix2(data[0][0] * f, data[0][1] * f,
+ data[1][0] * f, data[1][1] * f);
+ }
+
+ inline Matrix2 operator/(float f) const {
+ return Matrix2(data[0][0] / f, data[0][1] / f,
+ data[1][0] / f, data[1][1] / f);
+ }
+
+ inline float* operator[](int i) {
+ debugAssert(i >= 0 && i <= 2);
+ return data[i];
+ }
+
+ inline const float* operator[](int i) const {
+ debugAssert(i >= 0 && i <= 1);
+ return data[i];
+ }
+};
+
+}
+
+#endif
diff --git a/externals/g3dlite/G3D.lib/include/G3D/Matrix3.h b/externals/g3dlite/G3D.lib/include/G3D/Matrix3.h
new file mode 100644
index 00000000000..ad09cd3860f
--- /dev/null
+++ b/externals/g3dlite/G3D.lib/include/G3D/Matrix3.h
@@ -0,0 +1,323 @@
+/**
+ @file Matrix3.h
+
+ 3x3 matrix class
+
+ @maintainer Morgan McGuire, matrix@graphics3d.com
+
+ @cite Portions based on Dave Eberly's Magic Software Library at <A HREF="http://www.magic-software.com">http://www.magic-software.com</A>
+
+ @created 2001-06-02
+ @edited 2006-04-05
+ */
+
+#ifndef G3D_MATRIX3_H
+#define G3D_MATRIX3_H
+
+#include "G3D/platform.h"
+#include "G3D/Vector3.h"
+#include "G3D/Vector4.h"
+#include "G3D/debugAssert.h"
+
+#include <cstring>
+
+namespace G3D {
+
+/**
+ 3x3 matrix. Do not subclass.
+ */
+class Matrix3 {
+private:
+
+ float elt[3][3];
+
+ // Hidden operators
+ bool operator<(const Matrix3&) const;
+ bool operator>(const Matrix3&) const;
+ bool operator<=(const Matrix3&) const;
+ bool operator>=(const Matrix3&) const;
+
+public:
+
+ /** Initial values are undefined for performance. See also
+ Matrix3::zero(), Matrix3::identity(), Matrix3::fromAxisAngle, etc.*/
+ inline Matrix3() {}
+
+ Matrix3 (class BinaryInput& b);
+ Matrix3 (const float aafEntry[3][3]);
+ Matrix3 (const Matrix3& rkMatrix);
+ Matrix3 (float fEntry00, float fEntry01, float fEntry02,
+ float fEntry10, float fEntry11, float fEntry12,
+ float fEntry20, float fEntry21, float fEntry22);
+
+ bool fuzzyEq(const Matrix3& b) const;
+
+ /** Constructs a matrix from a quaternion.
+ @cite Graphics Gems II, p. 351--354
+ @cite Implementation from Watt and Watt, pg 362*/
+ Matrix3(const class Quat& q);
+
+ void serialize(class BinaryOutput& b) const;
+ void deserialize(class BinaryInput& b);
+
+ /**
+ Sets all elements.
+ */
+ void set(float fEntry00, float fEntry01, float fEntry02,
+ float fEntry10, float fEntry11, float fEntry12,
+ float fEntry20, float fEntry21, float fEntry22);
+
+ /**
+ * member access, allows use of construct mat[r][c]
+ */
+ inline float* operator[] (int iRow) {
+ debugAssert(iRow >= 0);
+ debugAssert(iRow < 3);
+ return (float*)&elt[iRow][0];
+ }
+
+ inline const float* operator[] (int iRow) const {
+ debugAssert(iRow >= 0);
+ debugAssert(iRow < 3);
+ return (const float*)&elt[iRow][0];
+ }
+
+ inline operator float* () {
+ return (float*)&elt[0][0];
+ }
+
+ inline operator const float* () const{
+ return (const float*)&elt[0][0];
+ }
+
+ /** @deprecated */
+ Vector3 getColumn (int iCol) const;
+ /** @deprecated */
+ Vector3 getRow (int iRow) const;
+
+ Vector3 column(int c) const;
+ const Vector3& row(int r) const;
+
+ void setColumn(int iCol, const Vector3 &vector);
+ void setRow(int iRow, const Vector3 &vector);
+
+ // assignment and comparison
+ inline Matrix3& operator= (const Matrix3& rkMatrix) {
+ memcpy(elt, rkMatrix.elt, 9 * sizeof(float));
+ return *this;
+ }
+
+ bool operator== (const Matrix3& rkMatrix) const;
+ bool operator!= (const Matrix3& rkMatrix) const;
+
+ // arithmetic operations
+ Matrix3 operator+ (const Matrix3& rkMatrix) const;
+ Matrix3 operator- (const Matrix3& rkMatrix) const;
+ /** Matrix-matrix multiply */
+ Matrix3 operator* (const Matrix3& rkMatrix) const;
+ Matrix3 operator- () const;
+
+ Matrix3& operator+= (const Matrix3& rkMatrix);
+ Matrix3& operator-= (const Matrix3& rkMatrix);
+ Matrix3& operator*= (const Matrix3& rkMatrix);
+
+ /**
+ * matrix * vector [3x3 * 3x1 = 3x1]
+ */
+ inline Vector3 operator* (const Vector3& v) const {
+ Vector3 kProd;
+
+ for (int r = 0; r < 3; ++r) {
+ kProd[r] =
+ elt[r][0] * v[0] +
+ elt[r][1] * v[1] +
+ elt[r][2] * v[2];
+ }
+
+ return kProd;
+ }
+
+
+ /**
+ * vector * matrix [1x3 * 3x3 = 1x3]
+ */
+ friend Vector3 operator* (const Vector3& rkVector,
+ const Matrix3& rkMatrix);
+
+ /**
+ * matrix * scalar
+ */
+ Matrix3 operator* (float fScalar) const;
+
+ /** scalar * matrix */
+ friend Matrix3 operator* (double fScalar, const Matrix3& rkMatrix);
+ friend Matrix3 operator* (float fScalar, const Matrix3& rkMatrix);
+ friend Matrix3 operator* (int fScalar, const Matrix3& rkMatrix);
+
+private:
+ /** Multiplication where out != A and out != B */
+ static void _mul(const Matrix3& A, const Matrix3& B, Matrix3& out);
+public:
+
+ /** Optimized implementation of out = A * B. It is safe (but slow) to call
+ with A, B, and out possibly pointer equal to one another.*/
+ // This is a static method so that it is not ambiguous whether "this"
+ // is an input or output argument.
+ inline static void mul(const Matrix3& A, const Matrix3& B, Matrix3& out) {
+ if ((&out == &A) || (&out == &B)) {
+ // We need a temporary anyway, so revert to the stack method.
+ out = A * B;
+ } else {
+ // Optimized in-place multiplication.
+ _mul(A, B, out);
+ }
+ }
+
+private:
+ static void _transpose(const Matrix3& A, Matrix3& out);
+public:
+
+ /** Optimized implementation of out = A.transpose(). It is safe (but slow) to call
+ with A and out possibly pointer equal to one another.
+
+ Note that <CODE>A.transpose() * v</CODE> can be computed
+ more efficiently as <CODE>v * A</CODE>.
+ */
+ inline static void transpose(const Matrix3& A, Matrix3& out) {
+ if (&A == &out) {
+ out = A.transpose();
+ } else {
+ _transpose(A, out);
+ }
+ }
+
+ /** Returns true if the rows and column L2 norms are 1.0 and the rows are orthogonal. */
+ bool isOrthonormal() const;
+
+ Matrix3 transpose () const;
+ bool inverse (Matrix3& rkInverse, float fTolerance = 1e-06) const;
+ Matrix3 inverse (float fTolerance = 1e-06) const;
+ float determinant () const;
+
+ /** singular value decomposition */
+ void singularValueDecomposition (Matrix3& rkL, Vector3& rkS,
+ Matrix3& rkR) const;
+ /** singular value decomposition */
+ void singularValueComposition (const Matrix3& rkL,
+ const Vector3& rkS, const Matrix3& rkR);
+
+ /** Gram-Schmidt orthonormalization (applied to columns of rotation matrix) */
+ void orthonormalize();
+
+ /** orthogonal Q, diagonal D, upper triangular U stored as (u01,u02,u12) */
+ void qDUDecomposition (Matrix3& rkQ, Vector3& rkD,
+ Vector3& rkU) const;
+
+ float spectralNorm () const;
+
+ /** matrix must be orthonormal */
+ void toAxisAngle(Vector3& rkAxis, float& rfRadians) const;
+
+ static Matrix3 fromDiagonal(const Vector3& d) {
+ return Matrix3(d.x, 0, 0,
+ 0, d.y, 0,
+ 0, 0, d.z);
+ }
+
+ static Matrix3 fromAxisAngle(const Vector3& rkAxis, float fRadians);
+
+ /**
+ * The matrix must be orthonormal. The decomposition is yaw*pitch*roll
+ * where yaw is rotation about the Up vector, pitch is rotation about the
+ * right axis, and roll is rotation about the Direction axis.
+ */
+ bool toEulerAnglesXYZ (float& rfYAngle, float& rfPAngle,
+ float& rfRAngle) const;
+ bool toEulerAnglesXZY (float& rfYAngle, float& rfPAngle,
+ float& rfRAngle) const;
+ bool toEulerAnglesYXZ (float& rfYAngle, float& rfPAngle,
+ float& rfRAngle) const;
+ bool toEulerAnglesYZX (float& rfYAngle, float& rfPAngle,
+ float& rfRAngle) const;
+ bool toEulerAnglesZXY (float& rfYAngle, float& rfPAngle,
+ float& rfRAngle) const;
+ bool toEulerAnglesZYX (float& rfYAngle, float& rfPAngle,
+ float& rfRAngle) const;
+ static Matrix3 fromEulerAnglesXYZ (float fYAngle, float fPAngle, float fRAngle);
+ static Matrix3 fromEulerAnglesXZY (float fYAngle, float fPAngle, float fRAngle);
+ static Matrix3 fromEulerAnglesYXZ (float fYAngle, float fPAngle, float fRAngle);
+ static Matrix3 fromEulerAnglesYZX (float fYAngle, float fPAngle, float fRAngle);
+ static Matrix3 fromEulerAnglesZXY (float fYAngle, float fPAngle, float fRAngle);
+ static Matrix3 fromEulerAnglesZYX (float fYAngle, float fPAngle, float fRAngle);
+
+ /** eigensolver, matrix must be symmetric */
+ void eigenSolveSymmetric (float afEigenvalue[3],
+ Vector3 akEigenvector[3]) const;
+
+ static void tensorProduct (const Vector3& rkU, const Vector3& rkV,
+ Matrix3& rkProduct);
+ std::string toString() const;
+
+ static const float EPSILON;
+
+ // Special values.
+ // The unguaranteed order of initialization of static variables across
+ // translation units can be a source of annoying bugs, so now the static
+ // special values (like Vector3::ZERO, Color3::WHITE, ...) are wrapped
+ // inside static functions that return references to them.
+ // These functions are intentionally not inlined, because:
+ // "You might be tempted to write [...] them as inline functions
+ // inside their respective header files, but this is something you
+ // must definitely not do. An inline function can be duplicated
+ // in every file in which it appears œóõ½ and this duplication
+ // includes the static object definition. Because inline functions
+ // automatically default to internal linkage, this would result in
+ // having multiple static objects across the various translation
+ // units, which would certainly cause problems. So you must
+ // ensure that there is only one definition of each wrapping
+ // function, and this means not making the wrapping functions inline",
+ // according to Chapter 10 of "Thinking in C++, 2nd ed. Volume 1" by Bruce Eckel,
+ // http://www.mindview.net/
+ static const Matrix3& zero();
+ static const Matrix3& identity();
+
+protected:
+
+ // support for eigensolver
+ void tridiagonal (float afDiag[3], float afSubDiag[3]);
+ bool qLAlgorithm (float afDiag[3], float afSubDiag[3]);
+
+ // support for singular value decomposition
+ static const float ms_fSvdEpsilon;
+ static const int ms_iSvdMaxIterations;
+ static void bidiagonalize (Matrix3& kA, Matrix3& kL,
+ Matrix3& kR);
+ static void golubKahanStep (Matrix3& kA, Matrix3& kL,
+ Matrix3& kR);
+
+ // support for spectral norm
+ static float maxCubicRoot (float afCoeff[3]);
+
+};
+
+
+//----------------------------------------------------------------------------
+/** <code>v * M == M.transpose() * v</code> */
+inline Vector3 operator* (const Vector3& rkPoint, const Matrix3& rkMatrix) {
+ Vector3 kProd;
+
+ for (int r = 0; r < 3; ++r) {
+ kProd[r] =
+ rkPoint[0] * rkMatrix.elt[0][r] +
+ rkPoint[1] * rkMatrix.elt[1][r] +
+ rkPoint[2] * rkMatrix.elt[2][r];
+ }
+
+ return kProd;
+}
+
+
+} // namespace
+
+#endif
+
diff --git a/externals/g3dlite/G3D.lib/include/G3D/Matrix4.h b/externals/g3dlite/G3D.lib/include/G3D/Matrix4.h
new file mode 100644
index 00000000000..5bb6cecfdf9
--- /dev/null
+++ b/externals/g3dlite/G3D.lib/include/G3D/Matrix4.h
@@ -0,0 +1,206 @@
+/**
+ @file Matrix4.h
+
+ 4x4 matrix class
+
+ @maintainer Morgan McGuire, matrix@graphics3d.com
+
+ @created 2003-10-02
+ @edited 2007-04-05
+ */
+
+#ifndef G3D_MATRIX4_H
+#define G3D_MATRIX4_H
+
+#ifdef _MSC_VER
+// Disable conditional expression is constant, which occurs incorrectly on inlined functions
+# pragma warning (push)
+# pragma warning( disable : 4127 )
+#endif
+
+#include "G3D/platform.h"
+#include "G3D/debugAssert.h"
+#include "G3D/Matrix3.h"
+#include "G3D/Vector3.h"
+
+namespace G3D {
+
+/**
+ A 4x4 matrix.
+
+ See also G3D::CoordinateFrame, G3D::Matrix3, G3D::Quat
+ */
+class Matrix4 {
+private:
+
+ float elt[4][4];
+
+ /**
+ Computes the determinant of the 3x3 matrix that lacks excludeRow
+ and excludeCol.
+ */
+ float subDeterminant(int excludeRow, int excludeCol) const;
+
+ // Hidden operators
+ bool operator<(const Matrix4&) const;
+ bool operator>(const Matrix4&) const;
+ bool operator<=(const Matrix4&) const;
+ bool operator>=(const Matrix4&) const;
+
+public:
+ Matrix4(
+ float r1c1, float r1c2, float r1c3, float r1c4,
+ float r2c1, float r2c2, float r2c3, float r2c4,
+ float r3c1, float r3c2, float r3c3, float r3c4,
+ float r4c1, float r4c2, float r4c3, float r4c4);
+
+ /**
+ init should be <B>row major</B>.
+ */
+ Matrix4(const float* init);
+
+ /**
+ a is the upper left 3x3 submatrix and b is the upper right 3x1 submatrix. The last row of the created matrix is (0,0,0,1).
+ */
+ Matrix4(const class Matrix3& upper3x3, const class Vector3& lastCol = Vector3::zero());
+
+ Matrix4(const class CoordinateFrame& c);
+
+ Matrix4(const double* init);
+
+ Matrix4();
+
+ /** Produces an RT transformation that nearly matches this Matrix4.
+ Because a Matrix4 may not be precisely a rotation and translation,
+ this may introduce error. */
+ class CoordinateFrame approxCoordinateFrame() const;
+
+ // Special values.
+ // Intentionally not inlined: see Matrix3::identity() for details.
+ static const Matrix4& identity();
+ static const Matrix4& zero();
+
+ inline float* operator[](int r) {
+ debugAssert(r >= 0);
+ debugAssert(r < 4);
+ return (float*)&elt[r];
+ }
+
+ inline const float* operator[](int r) const {
+ debugAssert(r >= 0);
+ debugAssert(r < 4);
+ return (const float*)&elt[r];
+ }
+
+ inline operator float* () {
+ return (float*)&elt[0][0];
+ }
+
+ inline operator const float* () const {
+ return (const float*)&elt[0][0];
+ }
+
+ Matrix4 operator*(const Matrix4& other) const;
+
+ class Matrix3 upper3x3() const;
+
+ /** Homogeneous multiplication. Let k = M * [v w]^T. result = k.xyz() / k.w */
+ class Vector3 homoMul(const class Vector3& v, float w) const;
+
+ /**
+ Constructs an orthogonal projection matrix from the given parameters.
+ Near and far are the <b>NEGATIVE</b> of the near and far plane Z values
+ (to follow OpenGL conventions).
+ */
+ static Matrix4 orthogonalProjection(
+ float left,
+ float right,
+ float bottom,
+ float top,
+ float nearval,
+ float farval);
+
+ static Matrix4 orthogonalProjection(
+ const class Rect2D& rect,
+ float nearval,
+ float farval);
+
+ static Matrix4 perspectiveProjection(
+ float left,
+ float right,
+ float bottom,
+ float top,
+ float nearval,
+ float farval);
+
+ void setRow(int r, const class Vector4& v);
+ void setColumn(int c, const Vector4& v);
+
+ /** @deprecated */
+ Vector4 getRow(int r) const;
+ /** @deprecated */
+ Vector4 getColumn(int c) const;
+
+ const Vector4& row(int r) const;
+ Vector4 column(int c) const;
+
+ Matrix4 operator*(const float s) const;
+ Vector4 operator*(const Vector4& vector) const;
+
+ Matrix4 transpose() const;
+
+ bool operator!=(const Matrix4& other) const;
+ bool operator==(const Matrix4& other) const;
+
+ float determinant() const;
+ Matrix4 inverse() const;
+
+ /**
+ Transpose of the cofactor matrix (used in computing the inverse).
+ Note: This is in fact only one type of adjoint. More generally,
+ an adjoint of a matrix is any mapping of a matrix which possesses
+ certain properties. This returns the so-called adjugate
+ or classical adjoint.
+ */
+ Matrix4 adjoint() const;
+ Matrix4 cofactor() const;
+
+ /** Serializes row-major */
+ void serialize(class BinaryOutput& b) const;
+ void deserialize(class BinaryInput& b);
+
+ std::string toString() const;
+
+ /** 3D scale matrix */
+ inline static Matrix4 scale(const Vector3& v) {
+ return Matrix4(v.x, 0, 0, 0,
+ 0, v.y, 0, 0,
+ 0, 0, v.z, 0,
+ 0, 0, 0, 1);
+ }
+
+ /** 3D scale matrix */
+ inline static Matrix4 scale(float x, float y, float z) {
+ return scale(Vector3(x, y, z));
+ }
+
+ /** 3D scale matrix */
+ inline static Matrix4 scale(float s) {
+ return scale(s,s,s);
+ }
+
+ /** 3D translation matrix */
+ inline static Matrix4 translation(const Vector3& v) {
+ return Matrix4(Matrix3::identity(), v);
+ }
+};
+
+
+
+} // namespace
+
+#ifdef _MSC_VER
+# pragma warning (pop)
+#endif
+
+#endif
diff --git a/externals/g3dlite/G3D.lib/include/G3D/MeshAlg.h b/externals/g3dlite/G3D.lib/include/G3D/MeshAlg.h
new file mode 100644
index 00000000000..be381eff117
--- /dev/null
+++ b/externals/g3dlite/G3D.lib/include/G3D/MeshAlg.h
@@ -0,0 +1,718 @@
+/**
+ @file MeshAlg.h
+
+ Indexed Mesh algorithms.
+
+ @maintainer Morgan McGuire, matrix@graphics3d.com
+
+ @created 2003-09-14
+ @edited 2008-07-30
+*/
+
+#ifndef G3D_MESHALG_H
+#define G3D_MESHALG_H
+
+#include "G3D/platform.h"
+#include "G3D/Array.h"
+#include "G3D/Vector3.h"
+#include "G3D/CoordinateFrame.h"
+
+namespace G3D {
+
+/**
+ Indexed <B>mesh alg</B>orithms. You have to build your own mesh class.
+ <P>
+ No mesh class is provided with G3D because there isn't an "ideal"
+ mesh format-- one application needs keyframed animation, another
+ skeletal animation, a third texture coordinates, a fourth
+ cannot precompute information, etc. Instead of compromising, this
+ class implements the hard parts of mesh computation and you can write
+ your own ideal mesh class on top of it.
+ */
+class MeshAlg {
+public:
+
+ enum Primitive {LINES, LINE_STRIP, TRIANGLES, TRIANGLE_STRIP,
+ TRIANGLE_FAN, QUADS, QUAD_STRIP, POINTS};
+
+
+ /** Adjacency information for a vertex.
+ Does not contain the vertex position or normal,
+ which are stored in the MeshAlg::Geometry object.
+ <CODE>Vertex</CODE>s must be stored in an array
+ parallel to (indexed in the same way as)
+ MeshAlg::Geometry::vertexArray.
+ */
+ class Vertex {
+ public:
+ Vertex() {}
+
+ /**
+ Array of edges adjacent to this vertex.
+ Let e = edgeIndex[i].
+ edge[(e >= 0) ? e : ~e].vertexIndex[0] == this
+ vertex index.
+
+ Edges may be listed multiple times if they are
+ degenerate.
+ */
+ Array<int> edgeIndex;
+
+ /**
+ Returns true if e or ~e is in the edgeIndex list.
+ */
+ inline bool inEdge(int e) const {
+ return edgeIndex.contains(~e) || edgeIndex.contains(e);
+ }
+
+ /**
+ Array of faces containing this vertex. Faces
+ may be listed multiple times if they are degenerate.
+ */
+ Array<int> faceIndex;
+
+ inline bool inFace(int f) const {
+ debugAssert(f >= 0);
+ return faceIndex.contains(f);
+ }
+ };
+
+
+ /**
+ Oriented, indexed triangle.
+ */
+ class Face {
+ public:
+ Face();
+
+ /**
+ Used by Edge::faceIndex to indicate a missing face.
+ This is a large negative value.
+ */
+ static const int NONE;
+
+
+ /**
+ Vertices in the face in counter-clockwise order.
+ Degenerate faces may include the same vertex multiple times.
+ */
+ int vertexIndex[3];
+
+ inline bool containsVertex(int v) const {
+ return contains(vertexIndex, 3, v);
+ }
+
+ /**
+ Edge indices in counter-clockwise order. Edges are
+ undirected, so it is important to know which way
+ each edge is pointing in a face. This is encoded
+ using negative indices.
+
+ If <CODE>edgeIndex[i] >= 0</CODE> then this face
+ contains the directed edge
+ between vertex indices
+ <CODE>edgeArray[face.edgeIndex[i]].vertexIndex[0]</CODE>
+ and
+ <CODE>edgeArray[face.edgeIndex[i]].vertexIndex[1]</CODE>.
+
+ If <CODE>edgeIndex[i] < 0</CODE> then
+ <CODE>~edgeIndex[i]</CODE> (i.e. the two's
+ complement of) is used and this face contains the directed
+ edge between vertex indices
+ <CODE>edgeArray[~face.edgeIndex[i]].vertexIndex[0]</CODE>
+ and
+ <CODE>edgeArray[~face.edgeIndex[i]].vertexIndex[1]</CODE>.
+
+ Degenerate faces may include the same edge multiple times.
+ */
+ // Temporarily takes on the value Face::NONE during adjacency
+ // computation to indicate an edge that has not yet been assigned.
+ int edgeIndex[3];
+
+ inline bool containsEdge(int e) const {
+ if (e < 0) {
+ e = ~e;
+ }
+ return contains(edgeIndex, 3, e) || contains(edgeIndex, 3, ~e);
+ }
+
+ /** Contains the forward edge e if e >= 0 and the backward edge
+ ~e otherwise. */
+ inline bool containsDirectedEdge(int e) const {
+ return contains(edgeIndex, 3, e);
+ }
+ };
+
+
+ /** Oriented, indexed edge */
+ class Edge {
+ public:
+ Edge();
+
+ /** Degenerate edges may include the same vertex times. */
+ int vertexIndex[2];
+
+ inline bool containsVertex(int v) const {
+ return contains(vertexIndex, 2, v);
+ }
+
+ /**
+ The edge is directed <B>forward</B> in face 0
+ <B>backward</B> in face 1. Face index of MeshAlg::Face::NONE
+ indicates a boundary (a.k.a. crack, broken) edge.
+ */
+ int faceIndex[2];
+
+ /** Returns true if f is contained in the faceIndex array in either slot.
+ To see if it is forward in that face, just check edge.faceIndex[0] == f.*/
+ inline bool inFace(int f) const {
+ return contains(faceIndex, 2, f);
+ }
+
+ /**
+ Returns true if either faceIndex is NONE.
+ */
+ inline bool boundary() const {
+ return (faceIndex[0] == Face::NONE) ||
+ (faceIndex[1] == Face::NONE);
+ }
+
+ /**
+ Returns the reversed edge.
+ */
+ inline Edge reverse() const {
+ Edge e;
+ e.vertexIndex[0] = vertexIndex[1];
+ e.vertexIndex[1] = vertexIndex[0];
+ e.faceIndex[0] = faceIndex[1];
+ e.faceIndex[1] = faceIndex[0];
+ return e;
+ }
+ };
+
+
+ /**
+ Convenient for passing around the per-vertex data that changes under
+ animation. The faces and edges are needed to interpret
+ these values.
+ */
+ class Geometry {
+ public:
+ /** Vertex positions */
+ Array<Vector3> vertexArray;
+
+ /** Vertex normals */
+ Array<Vector3> normalArray;
+
+ /**
+ Assignment is optimized using SSE.
+ */
+ Geometry& operator=(const Geometry& src);
+
+ void clear() {
+ vertexArray.clear();
+ normalArray.clear();
+ }
+ };
+
+ /**
+ Given a set of vertices and a set of indices for traversing them
+ to create triangles, computes other mesh properties.
+
+ <B>Colocated vertices are treated as separate.</B> To have
+ colocated vertices collapsed (necessary for many algorithms,
+ like shadowing), weld the mesh before computing adjacency.
+
+ <I>Recent change: In version 6.00, colocated vertices were automatically
+ welded by this routine and degenerate faces and edges were removed. That
+ is no longer the case.</I>
+
+ Where two faces meet, there are two opposite directed edges. These
+ are collapsed into a single bidirectional edge in the edgeArray.
+ If four faces meet exactly at the same edge, that edge will appear
+ twice in the array, and so on. If an edge is a boundary of the mesh
+ (i.e. if the edge has only one adjacent face) it will appear in the
+ array with one face index set to MeshAlg::Face::NONE.
+
+ @param vertexGeometry %Vertex positions to use when deciding colocation.
+ @param indexArray Order to traverse vertices to make triangles
+ @param faceArray <I>Output</I>
+ @param edgeArray <I>Output</I>. Sorted so that boundary edges are at the end of the array.
+ @param vertexArray <I>Output</I>
+ */
+ static void computeAdjacency(
+ const Array<Vector3>& vertexGeometry,
+ const Array<int>& indexArray,
+ Array<Face>& faceArray,
+ Array<Edge>& edgeArray,
+ Array<Vertex>& vertexArray);
+
+ /**
+ @deprecated Use the other version of computeAdjacency, which takes Array<Vertex>.
+ @param facesAdjacentToVertex <I>Output</I> adjacentFaceArray[v] is an array of
+ indices for faces touching vertex index v
+ */
+ static void computeAdjacency(
+ const Array<Vector3>& vertexArray,
+ const Array<int>& indexArray,
+ Array<Face>& faceArray,
+ Array<Edge>& edgeArray,
+ Array< Array<int> >& facesAdjacentToVertex);
+
+ /**
+ Computes some basic mesh statistics including: min, max mean and median,
+ edge lengths; and min, mean, median, and max face area.
+
+ @param vertexGeometry %Vertex positions to use when deciding colocation.
+ @param indexArray Order to traverse vertices to make triangles
+ @param minEdgeLength Minimum edge length
+ @param meanEdgeLength Mean edge length
+ @param medianEdgeLength Median edge length
+ @param maxEdgeLength Max edge length
+ @param minFaceArea Minimum face area
+ @param meanFaceArea Mean face area
+ @param medianFaceArea Median face area
+ @param maxFaceArea Max face area
+ */
+ static void computeAreaStatistics(
+ const Array<Vector3>& vertexArray,
+ const Array<int>& indexArray,
+ double& minEdgeLength,
+ double& meanEdgeLength,
+ double& medianEdgeLength,
+ double& maxEdgeLength,
+ double& minFaceArea,
+ double& meanFaceArea,
+ double& medianFaceArea,
+ double& maxFaceArea);
+
+private:
+ /**
+ Computes the tangent space basis vectors for
+ a counter-clockwise oriented face.
+
+ @cite Max McGuire
+ */
+ static void computeTangentVectors(
+ const Vector3& normal,
+ const Vector3 position[3],
+ const Vector2 texCoord[3],
+ Vector3& tangent,
+ Vector3& binormal);
+
+ /** Helper for weldAdjacency */
+ static void weldBoundaryEdges(
+ Array<Face>& faceArray,
+ Array<Edge>& edgeArray,
+ Array<Vertex>& vertexArray);
+
+public:
+
+ /**
+ Computes tangent and binormal vectors,
+ which provide a (mostly) consistent
+ parameterization over the surface for
+ effects like bump mapping. In the resulting coordinate frame,
+ T = x (varies with texture s coordinate), B = y (varies with negative texture t coordinate),
+ and N = z for a right-handed coordinate frame. If a billboard is vertical on the screen
+ in view of the camera, the tangent space matches the camera's coordinate frame.
+
+ The vertex, texCoord, tangent, and binormal
+ arrays are parallel arrays.
+
+ The resulting tangent and binormal might not be exactly
+ perpendicular to each other. They are guaranteed to
+ be perpendicular to the normal.
+
+ @cite Max McGuire
+ */
+ static void computeTangentSpaceBasis(
+ const Array<Vector3>& vertexArray,
+ const Array<Vector2>& texCoordArray,
+ const Array<Vector3>& vertexNormalArray,
+ const Array<Face>& faceArray,
+ Array<Vector3>& tangent,
+ Array<Vector3>& binormal);
+
+ /** @deprecated */
+ static void computeNormals(
+ const Array<Vector3>& vertexArray,
+ const Array<Face>& faceArray,
+ const Array< Array<int> >& adjacentFaceArray,
+ Array<Vector3>& vertexNormalArray,
+ Array<Vector3>& faceNormalArray);
+
+ /**
+ Vertex normals are weighted by the area of adjacent faces.
+ Nelson Max showed this is superior to uniform weighting for
+ general meshes in jgt.
+
+ @param vertexNormalArray Output. Unit length
+ @param faceNormalArray Output. Degenerate faces produce zero magnitude normals. Unit length
+ @see weld
+ */
+ static void computeNormals(
+ const Array<Vector3>& vertexGeometry,
+ const Array<Face>& faceArray,
+ const Array<Vertex>& vertexArray,
+ Array<Vector3>& vertexNormalArray,
+ Array<Vector3>& faceNormalArray);
+
+ /** Computes unit length normals in place using the other computeNormals methods.
+ If you already have a face array use another method; it will be faster.
+ @see weld*/
+ static void computeNormals(
+ Geometry& geometry,
+ const Array<int>& indexArray);
+
+ /**
+ Computes face normals only. Significantly faster (especially if
+ normalize is false) than computeNormals.
+ @see weld
+ */
+ static void computeFaceNormals(
+ const Array<Vector3>& vertexArray,
+ const Array<Face>& faceArray,
+ Array<Vector3>& faceNormals,
+ bool normalize = true);
+
+ /**
+ Classifies each face as a backface or a front face relative
+ to the observer point P (which is at infinity when P.w = 0).
+ A face with normal exactly perpendicular to the observer vector
+ may be classified as either a front or a back face arbitrarily.
+ */
+ static void identifyBackfaces(
+ const Array<Vector3>& vertexArray,
+ const Array<Face>& faceArray,
+ const Vector4& P,
+ Array<bool>& backface);
+
+ /** A faster version of identifyBackfaces for the case where
+ face normals have already been computed */
+ static void identifyBackfaces(
+ const Array<Vector3>& vertexArray,
+ const Array<Face>& faceArray,
+ const Vector4& P,
+ Array<bool>& backface,
+ const Array<Vector3>& faceNormals);
+
+ /**
+ Welds nearby and colocated elements of the <I>oldVertexArray</I> together so that
+ <I>newVertexArray</I> contains no vertices within <I>radius</I> of one another.
+ Every vertex in newVertexPositions also appears in oldVertexPositions.
+ This is useful for downsampling meshes and welding cracks created by artist errors
+ or numerical imprecision.
+
+ The two integer arrays map indices back and forth between the arrays according to:
+ <PRE>
+ oldVertexArray[toOld[ni]] == newVertexArray[ni]
+ oldVertexArray[oi] == newVertexArray[toNew[ni]]
+ </PRE>
+
+ Note that newVertexPositions is never longer than oldVertexPositions
+ and is shorter when vertices are welded.
+
+ Welding with a large radius will effectively compute a lower level of detail for
+ the mesh.
+
+ The welding method runs in roughly linear time in the length of oldVertexArray--
+ a uniform spatial grid is used to achieve nearly constant time vertex collapses
+ for uniformly distributed vertices.
+
+ It is sometimes desirable to keep the original vertex ordering but
+ identify the unique vertices. The following code computes
+ array canonical s.t. canonical[v] = first occurance of
+ a vertex near oldVertexPositions[v] in oldVertexPositions.
+
+ <PRE>
+ Array<int> canonical(oldVertexPositions.size()), toNew, toOld;
+ computeWeld(oldVertexPositions, Array<Vector3>(), toNew, toOld, radius);
+ for (int v = 0; v < canonical.size(); ++v) {
+ canonical[v] = toOld[toNew[v]];
+ }
+ </PRE>
+
+ See also G3D::MeshAlg::weldAdjacency.
+
+ @cite The method is that described as the 'Grouper' in Baum, Mann, Smith, and Winget,
+ Making Radiosity Usable: Automatic Preprocessing and Meshing Techniques for
+ the Generation of Accurate Radiosity Solutions, Computer Graphics vol 25, no 4, July 1991.
+
+ @deprecated Use weld.
+ */
+ static void computeWeld(
+ const Array<Vector3>& oldVertexPositions,
+ Array<Vector3>& newVertexPositions,
+ Array<int>& toNew,
+ Array<int>& toOld,
+ double radius = G3D::fuzzyEpsilon);
+
+ /**
+ Modifies the face, edge, and vertex arrays in place so that
+ colocated (within radius) vertices are treated as identical.
+ Note that the vertexArray and corresponding geometry will
+ contain elements that are no longer used. In the vertexArray,
+ these elements are initialized to MeshAlg::Vertex() but not
+ removed (because removal would change the indexing).
+
+ This is a good preprocessing step for algorithms that are only
+ concerned with the shape of a mesh (e.g. cartoon rendering, fur, shadows)
+ and not the indexing of the vertices.
+
+ Use this method when you have already computed adjacency information
+ and want to collapse colocated vertices within that data without
+ disturbing the actual mesh vertices or indexing scheme.
+
+ If you have not computed adjacency already, use MeshAlg::computeWeld
+ instead and compute adjacency information after welding.
+
+ @deprecated Use weld.
+
+ @param faceArray Mutated in place. Size is maintained (degenerate
+ faces are <b>not</B> removed).
+ @param edgeArray Mutated in place. May shrink if boundary edges
+ are welded together.
+ @param vertexArray Mutated in place. Size is maintained (duplicate
+ vertices contain no adjacency info).
+ */
+ static void weldAdjacency(
+ const Array<Vector3>& originalGeometry,
+ Array<Face>& faceArray,
+ Array<Edge>& edgeArray,
+ Array<Vertex>& vertexArray,
+ double radius = G3D::fuzzyEpsilon);
+
+
+ /**
+ Counts the number of edges (in an edge array returned from
+ MeshAlg::computeAdjacency) that have only one adjacent face.
+ */
+ static int countBoundaryEdges(const Array<Edge>& edgeArray);
+
+
+ /**
+ Generates an array of integers from start to start + n - 1 that have run numbers
+ in series then omit the next skip before the next run. Useful for turning
+ a triangle list into an indexed face set.
+
+ Example:
+ <PRE>
+ createIndexArray(10, x);
+ // x = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
+
+ createIndexArray(5, x, 2);
+ // x = [2, 3, 4, 5, 6, 7]
+
+ createIndexArray(6, x, 0, 2, 1);
+ // x = [0, 1, 3, 4, 6, 7]
+ </PRE>
+ */
+ static void createIndexArray(
+ int n,
+ Array<int>& array,
+ int start = 0,
+ int run = 1,
+ int skip = 0);
+
+ /**
+ Computes a conservative, near-optimal axis aligned bounding box and sphere.
+
+ @cite The bounding sphere uses the method from J. Ritter. An effcient bounding sphere. In Andrew S. Glassner, editor, Graphics Gems. Academic Press, Boston, MA, 1990.
+
+ */
+ static void computeBounds(const Array<Vector3>& vertex, class Box& box, class Sphere& sphere);
+
+ /** Computes bounds for a subset of the vertices. It is ok if vertices appear more than once in the index array. */
+ static void computeBounds(const Array<Vector3>& vertex, const Array<int>& index, class Box& box, class Sphere& sphere);
+
+ /**
+ Mutates geometry, texCoord, and indexArray so that the output has collocated vertices collapsed (welded).
+
+ @param vertices Input and output
+ @param textureCoords Input and output
+ @param normals Output only
+ @param indices Input and output. This is an array of trilist indices.
+ @param oldToNewIndex Output argument
+ @param normalSmoothingAngle Varies from 0 (flat shading) to toRadians(180) for extremely smooth shading. Default is toRadians(70)
+ */
+ static void weld(
+ Array<Vector3>& vertices,
+ Array<Vector2>& textureCoords,
+ Array<Vector3>& normals,
+ Array<Array<int>*>& indices,
+ float normalSmoothingAngle = toRadians(70.0f),
+ float vertexWeldRadius = 0.0001f,
+ float textureWeldRadius = 0.0001f,
+ float normalWeldRadius = 0.01f);
+
+ inline static void weld(
+ Array<Vector3>& vertices,
+ Array<Vector2>& textureCoords,
+ Array<Vector3>& normals,
+ Array<int>& indices,
+ float normalSmoothingAngle = toRadians(70.0f),
+ float vertexWeldRadius = 0.0002f,
+ float textureWeldRadius = 0.00001f,
+ float normalWeldRadius = 0.00001f) {
+
+ Array<Array<int>*> meta;
+ meta.append(&indices);
+ weld(vertices, textureCoords, normals, meta, normalSmoothingAngle, vertexWeldRadius, textureWeldRadius, normalWeldRadius);
+ }
+
+ /**
+ In debug mode, asserts that the adjacency references between the
+ face, edge, and vertex array are consistent.
+ */
+ static void debugCheckConsistency(
+ const Array<Face>& faceArray,
+ const Array<Edge>& edgeArray,
+ const Array<Vertex>& vertexArray);
+
+ /**
+ Generates a unit square in the X-Z plane composed of a grid of wCells x hCells
+ squares and then transforms it by xform.
+
+ @param vertex Output vertices
+ @param texCoord Output texture coordinates
+ @param index Output triangle list indices
+ @param textureScale Lower-right texture coordinate
+ @param spaceCentered If true, the coordinates generated are centered at the origin before the transformation.
+ @param twoSided If true, matching top and bottom planes are generated.
+ */
+ static void generateGrid(
+ Array<Vector3>& vertex,
+ Array<Vector2>& texCoord,
+ Array<int>& index,
+ int wCells = 10,
+ int hCells = 10,
+ const Vector2& textureScale = Vector2(1,1),
+ bool spaceCentered = true,
+ bool twoSided = true,
+ const CoordinateFrame& xform = CoordinateFrame());
+
+ /** Converts quadlist (QUADS),
+ triangle fan (TRIANGLE_FAN),
+ tristrip(TRIANGLE_STRIP), and quadstrip (QUAD_STRIP) indices into
+ triangle list (TRIANGLES) indices and appends them to outIndices. */
+ template<class IndexType>
+ static void toIndexedTriList(
+ const Array<IndexType>& inIndices,
+ MeshAlg::Primitive inType,
+ Array<IndexType>& outIndices) {
+
+ debugAssert(
+ inType == MeshAlg::TRIANGLE_STRIP ||
+ inType == MeshAlg::TRIANGLE_FAN ||
+ inType == MeshAlg::QUADS ||
+ inType == MeshAlg::QUAD_STRIP);
+
+ const int inSize = inIndices.size();
+
+ switch(inType) {
+ case MeshAlg::TRIANGLE_FAN:
+ {
+ debugAssert(inSize >= 3);
+
+ int N = outIndices.size();
+ outIndices.resize(N + (inSize - 2) * 3);
+
+ for (IndexType i = 1, outIndex = N; i <= (inSize - 2); ++i, outIndex += 3) {
+ outIndices[outIndex] = inIndices[0];
+ outIndices[outIndex + 1] = inIndices[i];
+ outIndices[outIndex + 2] = inIndices[i + 1];
+ }
+
+ break;
+ }
+
+ case MeshAlg::TRIANGLE_STRIP:
+ {
+ debugAssert(inSize >= 3);
+
+ int N = outIndices.size();
+ outIndices.resize(N + (inSize - 2) * 3);
+
+ bool atEven = false;
+ for (IndexType i = 0, outIndex = N; i <= (inSize - 2); ++i, outIndex += 3) {
+ if (atEven) {
+ outIndices[outIndex] = inIndices[i + 1];
+ outIndices[outIndex + 1] = inIndices[i];
+ outIndices[outIndex + 2] = inIndices[i + 2];
+ atEven = false;
+ } else {
+ outIndices[outIndex] = inIndices[i];
+ outIndices[outIndex + 1] = inIndices[i + 1];
+ outIndices[outIndex + 2] = inIndices[i + 2];
+ atEven = true;
+ }
+ }
+
+ break;
+ }
+
+ case MeshAlg::QUADS:
+ {
+ debugAssert(inIndices.size() >= 4);
+
+ int N = outIndices.size();
+ outIndices.resize(N + (inSize / 4) * 3);
+
+ for (IndexType i = 0, outIndex = N; i <= (inSize - 4); i += 4, outIndex += 6) {
+ outIndices[outIndex] = inIndices[i];
+ outIndices[outIndex + 1] = inIndices[i + 1];
+ outIndices[outIndex + 2] = inIndices[i + 3];
+ outIndices[outIndex + 3] = inIndices[i + 1];
+ outIndices[outIndex + 4] = inIndices[i + 2];
+ outIndices[outIndex + 5] = inIndices[i + 3];
+ }
+
+ break;
+ }
+
+ case MeshAlg::QUAD_STRIP:
+ {
+ debugAssert(inIndices.size() >= 4);
+
+ int N = outIndices.size();
+ outIndices.resize(N + (inSize - 2) * 3);
+
+ for (IndexType i = 0, outIndex = N; i <= (inSize - 2); i += 2, outIndex += 6) {
+ outIndices[outIndex] = inIndices[i];
+ outIndices[outIndex + 1] = inIndices[i + 1];
+ outIndices[outIndex + 2] = inIndices[i + 2];
+ outIndices[outIndex + 3] = inIndices[i + 2];
+ outIndices[outIndex + 4] = inIndices[i + 1];
+ outIndices[outIndex + 5] = inIndices[i + 3];
+ }
+ break;
+ }
+ default:
+ alwaysAssertM(false, "Illegal argument");
+ }
+ }
+
+protected:
+
+ /**
+ Helper for computeAdjacency. If a directed edge with index e already
+ exists from i0 to i1 then e is returned. If a directed edge with index e
+ already exists from i1 to i0, ~e is returned (the complement) and
+ edgeArray[e] is set to f. Otherwise, a new edge is created from i0 to i1
+ with first face index f and its index is returned.
+
+ @param vertexArray Vertex positions to use when deciding colocation.
+
+ @param area Area of face f. When multiple edges of the same direction
+ are found between the same vertices (usually because of degenerate edges)
+ the face with larger area is kept in the edge table.
+ */
+ static int findEdgeIndex(
+ const Array<Vector3>& vertexArray,
+ Array<Edge>& geometricEdgeArray,
+ int i0, int i1, int f, double area);
+};
+}
+#endif
+
diff --git a/externals/g3dlite/G3D.lib/include/G3D/MeshBuilder.h b/externals/g3dlite/G3D.lib/include/G3D/MeshBuilder.h
new file mode 100644
index 00000000000..3256c0de823
--- /dev/null
+++ b/externals/g3dlite/G3D.lib/include/G3D/MeshBuilder.h
@@ -0,0 +1,82 @@
+/**
+ @file MeshBuilder.h
+
+ @maintainer Morgan McGuire, matrix@graphics3d.com
+
+ @created 2002-02-27
+ @edited 2004-10-04
+ */
+#ifndef G3D_MESHBUILDER_H
+#define G3D_MESHBUILDER_H
+
+#include "G3D/platform.h"
+#include "G3D/Array.h"
+#include "G3D/Vector3.h"
+#include "G3D/Triangle.h"
+
+namespace G3D {
+
+/**
+ Allows creation of optimized watertight meshes from unoptimized polygon soups.
+ See also G3D::MeshAlg for algorithms that operate on the output.
+ */
+class MeshBuilder {
+public:
+
+ /**
+ Set setWeldRadius to AUTO_WELD to weld vertices closer than 1/2
+ the smallest edge length in a model.
+ */
+ enum {AUTO_WELD = -100};
+
+private:
+ /** Indices of vertices in <B>or near</B> a grid cell. */
+ typedef Array<int> List;
+
+ std::string name;
+
+ /**
+ All of the triangles, as a long triangle list.
+ */
+ Array<Vector3> triList;
+
+ void centerTriList();
+ void computeBounds(Vector3& min, Vector3& max);
+
+ bool _twoSided;
+
+ /** Collapse radius */
+ double close;
+
+public:
+
+ inline MeshBuilder(bool twoSided = false) : _twoSided(twoSided), close(AUTO_WELD) {}
+
+ /** Writes the model to the arrays, which can then be used with
+ G3D::IFSModel::save and G3D::MeshAlg */
+ void commit(std::string& name, Array<int>& indexArray, Array<Vector3>& vertexArray);
+
+ /**
+ Adds a new triangle to the model. (Counter clockwise)
+ */
+ void addTriangle(const Vector3& a, const Vector3& b, const Vector3& c);
+
+ /**
+ Adds two new triangles to the model. (Counter clockwise)
+ */
+ void addQuad(const Vector3& a, const Vector3& b, const Vector3& c, const Vector3& d);
+
+ void addTriangle(const Triangle& t);
+
+ void setName(const std::string& n);
+
+ /** Vertices within this distance are considered identical.
+ Use AUTO_WELD (the default) to have the distance be a function of the model size.*/
+ void setWeldRadius(double r) {
+ close = r;
+ }
+};
+
+} // namespace
+
+#endif
diff --git a/externals/g3dlite/G3D.lib/include/G3D/NetAddress.h b/externals/g3dlite/G3D.lib/include/G3D/NetAddress.h
new file mode 100644
index 00000000000..8ed20a06690
--- /dev/null
+++ b/externals/g3dlite/G3D.lib/include/G3D/NetAddress.h
@@ -0,0 +1,132 @@
+#ifndef G3D_NETADDRESS_H
+#define G3D_NETADDRESS_H
+
+#include "G3D/platform.h"
+#include "G3D/Table.h"
+
+/** These control the version of Winsock used by G3D.
+ Version 2.0 is standard for G3D 6.09 and later.
+ Version 1.1 is standard for G3D 6.08 and earlier.
+ */
+#define G3D_WINSOCK_MAJOR_VERSION 2
+#define G3D_WINSOCK_MINOR_VERSION 0
+
+#ifdef G3D_WIN32
+# if (G3D_WINSOCK_MAJOR_VERSION == 2)
+# include <winsock2.h>
+# elif (G3D_WINSOCK_MAJOR_VERSION == 1)
+# include <winsock.h>
+# endif
+#else
+# include <sys/types.h>
+# include <sys/socket.h>
+# include <netinet/in.h>
+# ifndef SOCKADDR_IN
+# define SOCKADDR_IN struct sockaddr_in
+# endif
+# ifndef SOCKET
+# define SOCKET int
+# endif
+#endif
+
+#include "G3D/g3dmath.h"
+
+namespace G3D {
+
+class NetAddress {
+private:
+ friend class NetworkDevice;
+ friend class LightweightConduit;
+ friend class ReliableConduit;
+
+ /** Host byte order */
+ void init(uint32 host, uint16 port);
+ void init(const std::string& hostname, uint16 port);
+ NetAddress(const SOCKADDR_IN& a);
+ NetAddress(const struct in_addr& addr, uint16 port = 0);
+
+ SOCKADDR_IN addr;
+
+public:
+ /**
+ In host byte order
+ */
+ NetAddress(uint32 host, uint16 port = 0);
+
+ /**
+ @param port Specified in host byte order (i.e., don't worry about endian issues)
+ */
+ NetAddress(const std::string& hostname, uint16 port);
+
+ /**
+ @param hostnameAndPort in the form "hostname:port" or "ip:port"
+ */
+ NetAddress(const std::string& hostnameAndPort);
+
+ /**
+ @deprecated Use G3D::NetworkDevice::broadcastAddressArray()
+
+ @brief Creates a UDP broadcast address for use with a
+ G3D::LightweightConduit.
+
+ UDP broadcast allows one machine to send a packet to all machines
+ on the same local network. The IP portion of the address is
+ 0xFFFFFFFF, which indicates "broadcast" to the underlying socket
+ API. This feature is not available with the connection-based TCP
+ protocol abstracted by G3D::ReliableConduit; use multisend
+ instead.
+ */
+ static NetAddress broadcastAddress(uint16 port);
+
+ NetAddress();
+
+ void serialize(class BinaryOutput& b) const;
+ void deserialize(class BinaryInput& b);
+
+ /** @brief Returns true if this is not an illegal address. */
+ bool ok() const;
+
+ /** @brief Returns a value in host format (i.e., don't worry about
+ endian issues) */
+ inline uint32 ip() const {
+ return ntohl(addr.sin_addr.s_addr);
+ //return ntohl(addr.sin_addr.S_un.S_addr);
+ }
+
+ inline uint16 port() const {
+ return ntohs(addr.sin_port);
+ }
+
+ std::string ipString() const;
+ std::string toString() const;
+
+};
+
+std::ostream& operator<<(std::ostream& os, const NetAddress&);
+
+} // namespace G3D
+
+template <> struct HashTrait<G3D::NetAddress> {
+ static size_t hashCode(const G3D::NetAddress& key) {
+ return static_cast<size_t>(key.ip() + (static_cast<G3D::uint32>(key.port()) << 16));
+ }
+};
+
+namespace G3D {
+
+/**
+ Two addresses may point to the same computer but be != because
+ they have different IP's.
+ */
+inline bool operator==(const NetAddress& a, const NetAddress& b) {
+ return (a.ip() == b.ip()) && (a.port() == b.port());
+}
+
+
+inline bool operator!=(const NetAddress& a, const NetAddress& b) {
+ return !(a == b);
+}
+
+} // namespace G3D
+
+#endif
diff --git a/externals/g3dlite/G3D.lib/include/G3D/NetworkDevice.h b/externals/g3dlite/G3D.lib/include/G3D/NetworkDevice.h
new file mode 100644
index 00000000000..faffa56d690
--- /dev/null
+++ b/externals/g3dlite/G3D.lib/include/G3D/NetworkDevice.h
@@ -0,0 +1,738 @@
+/**
+ @file NetworkDevice.h
+
+ These classes abstract networking from the socket level to a
+ serialized messaging style that is more appropriate for games. The
+ performance has been tuned for sending many small messages. The
+ message protocol contains a header that prevents them from being used
+ with raw UDP/TCP (e.g. connecting to an HTTP server).
+
+ LightweightConduit and ReliableConduits have different interfaces
+ because they have different semantics. You would never want to
+ interchange them without rewriting the surrounding code.
+
+ NetworkDevice creates conduits because they need access to a global
+ log pointer and because I don't want non-reference counted conduits
+ being created.
+
+ Be careful with threads and reference counting. The reference
+ counters are not threadsafe, and are also not updated correctly if a
+ thread is explicitly killed. Since the conduits will be passed by
+ const XConduitRef& most of the time this doesn't appear as a major
+ problem. With non-blocking conduits, you should need few threads
+ anyway.
+
+ LightweightConduits preceed each message with a 4-byte host order
+ unsigned integer that is the message type. This does not appear in
+ the message serialization/deserialization.
+
+ ReliableConduits preceed each message with two 4-byte host order
+ unsigned integers. The first is the message type and the second
+ indicates the length of the rest of the data. The size does not
+ include the size of the header itself. The minimum message is 9
+ bytes:a 4-byte type, a 4-byte header equal to "1", and one byte of data.
+
+ @maintainer Morgan McGuire, morgan@graphics3d.com
+ @created 2002-11-22
+ @edited 2006-11-25
+ */
+
+#ifndef G3D_NETWORKDEVICE_H
+#define G3D_NETWORKDEVICE_H
+
+#include "G3D/platform.h"
+#include "G3D/NetAddress.h"
+
+#include <string>
+#include <iostream>
+#include "G3D/g3dmath.h"
+
+#include "G3D/ReferenceCount.h"
+#include "G3D/Array.h"
+#include "G3D/BinaryOutput.h"
+
+namespace G3D {
+
+class TextOutput;
+
+class Conduit : public ReferenceCountedObject {
+protected:
+ friend class NetworkDevice;
+ friend class NetListener;
+
+ uint64 mSent;
+ uint64 mReceived;
+ uint64 bSent;
+ uint64 bReceived;
+
+ SOCKET sock;
+
+ /**
+ Used for serialization. One per socket
+ to make this threadsafe.
+ */
+ BinaryOutput binaryOutput;
+
+ Conduit();
+
+public:
+
+ virtual ~Conduit();
+ uint64 bytesSent() const;
+ uint64 messagesSent() const;
+ uint64 bytesReceived() const;
+ uint64 messagesReceived() const;
+
+ /**
+ If true, receive will return true.
+ */
+ virtual bool messageWaiting();
+
+ /**
+ Returns the type of the waiting message (i.e. the type supplied
+ with send). The return value is zero when there is no message
+ waiting.
+
+ One way to use this is to have a Table mapping message types to
+ pre-allocated subclasses so receiving looks like:
+
+ <PRE>
+ // My base class for messages.
+ class Message {
+ virtual void serialize(BinaryOutput&) const;
+ virtual void deserialize(BinaryInput&);
+ virtual void process() = 0;
+ };
+
+ Message* m = table[conduit->waitingMessageType()];
+ conduit->receive(m);
+ m->process();
+ </PRE>
+
+ Another is to simply switch on the message type:
+
+ <pre>
+ switch (conduit->waitingMessageType()) {
+ case 0:
+ // No message
+ break;
+
+ case ENTITY_SPAWN_MSG:
+ {
+ EntitySpawnMsg m;
+ condiut->receive(m);
+ spawnEntity(m.id, m.position, m.modelID);
+ }
+ break;
+ ...
+ }
+ </pre>
+ */
+ virtual uint32 waitingMessageType() = 0;
+
+ /** Returns true if the connection is ok. */
+ bool ok() const;
+};
+
+typedef ReferenceCountedPointer<class ReliableConduit> ReliableConduitRef;
+
+#ifdef __GNUC__
+// Workaround for a known bug in gcc 4.x where htonl produces
+// a spurrious warning.
+// http://gcc.gnu.org/ml/gcc-bugs/2005-10/msg03270.html
+uint32 gcchtonl(uint32);
+#endif
+
+// Messaging and stream APIs must be supported on a single class because
+// sometimes an application will switch modes on a single socket. For
+// example, when transferring 3D level geometry during handshaking with
+// a game server.
+/**
+ A conduit that guarantees messages will arrive, intact and in order.
+ Create on the client using NetworkDevice::createReliableConduit and
+ on the server using NetListener::waitForConnection. Set the reference
+ counted pointer to NULL to disconnect.
+
+ To construct a ReliableConduit:
+ <OL>
+ <LI> Create a G3D::NetworkDevice (if you are using G3D::GApp, it creates
+ one for you) on the client and on the server.
+ <LI> On the server, create a G3D::NetListener using
+ G3D::NetworkDevice::createListener
+ <LI> On the server, invoke G3D::NetListener::waitForConnection.
+ <LI> On the client, call G3D::NetworkDevice::createReliableConduit.
+ You will need the server's G3D::NetAddress. Consider using
+ G3D::DiscoveryClient to find it via broadcasting.
+ </OL>
+
+ */
+class ReliableConduit : public Conduit {
+private:
+ friend class NetworkDevice;
+ friend class NetListener;
+
+ enum State {RECEIVING, HOLDING, NO_MESSAGE} state;
+
+ NetAddress addr;
+
+ /**
+ Type of the incoming message.
+ */
+ uint32 messageType;
+
+ /**
+ Total size of the incoming message (read from the header).
+ */
+ uint32 messageSize;
+
+ /** Shared buffer for receiving messages. */
+ void* receiveBuffer;
+
+ /** Total size of the receiveBuffer. */
+ size_t receiveBufferTotalSize;
+
+ /** Size occupied by the current message... so far. This will be
+ equal to messageSize when the whole message has arrived.
+ */
+ size_t receiveBufferUsedSize;
+
+ ReliableConduit(const NetAddress& addr);
+
+ ReliableConduit(const SOCKET& sock,
+ const NetAddress& addr);
+
+ template<typename T> static void serializeMessage
+ (uint32 t, const T& m, BinaryOutput& b) {
+
+ b.writeUInt32(t);
+
+ // Reserve space for the 4 byte size header
+ b.writeUInt32(0);
+
+ size_t L = b.length();
+ m.serialize(b);
+ if ((size_t)b.length() == L) {
+ // No data was created by serialization.
+ // We need to send at least one byte because receive assumes that
+ // a zero length message is an error.
+ b.writeUInt8(0xFF);
+ }
+
+ uint32 len = b.size() - 8;
+
+ // We send the length first to tell recv how much data to read.
+ // Here we abuse BinaryOutput a bit and write directly into
+ // its buffer, violating the abstraction.
+ // Note that we write to the second set of 4 bytes, which is
+ // the size field.
+ uint32* lenPtr = ((uint32*)b.getCArray()) + 1;
+ #if defined(__GNUC__)
+ *lenPtr = gcchtonl(len);
+ #else
+ *lenPtr = htonl(len);
+ #endif
+ }
+
+
+ void sendBuffer(const BinaryOutput& b);
+
+ /** Accumulates whatever part of the message (not the header) is
+ still waiting on the socket into the receiveBuffer during
+ state = RECEIVING mode. Closes the socket if anything goes
+ wrong. When receiveBufferUsedSize == messageSize, the entire
+ message has arrived. */
+ void receiveIntoBuffer();
+
+ /** Receives the messageType and messageSize from the socket. */
+ void receiveHeader();
+
+public:
+
+ /**
+ Client invokes this to connect to a server. The call blocks until the
+ conduit is opened. The conduit will not be ok() if it fails.
+ */
+ static ReliableConduitRef create(const NetAddress& address);
+
+ /** Closes the socket. */
+ ~ReliableConduit();
+
+
+ // The message is actually copied from the socket to an internal buffer during
+ // this call. Receive only deserializes.
+ virtual bool messageWaiting();
+
+ /**
+ Serializes the message and schedules it to be sent as soon as possible,
+ and then returns immediately. The message can be any <B>class</B> with
+ a serialize and deserialize method. On the receiving side,
+ use G3D::ReliableConduit::waitingMessageType() to detect the incoming
+ message and then invoke G3D::ReliableConduit::receive(msg) where msg
+ is of the same class as the message that was sent.
+
+ The actual data sent across the network is preceeded by the
+ message type and the size of the serialized message as a 32-bit
+ integer. The size is sent because TCP is a stream protocol and
+ doesn't have a concept of discrete messages.
+ */
+ template<typename T> inline void send(uint32 type, const T& message) {
+ binaryOutput.reset();
+ serializeMessage(type, message, binaryOutput);
+ sendBuffer(binaryOutput);
+ }
+
+ /** Sends an empty message with the given type. Useful for sending
+ commands that have no parameters. */
+ void send(uint32 type);
+
+ /** Send the same message to a number of conduits. Useful for sending
+ data from a server to many clients (only serializes once). */
+ template<typename T>
+ inline static void multisend(
+ const Array<ReliableConduitRef>& array,
+ uint32 type,
+ const T& m) {
+
+ if (array.size() > 0) {
+ array[0]->binaryOutput.reset();
+ serializeMessage(type, m, array[0]->binaryOutput);
+
+ for (int i = 0; i < array.size(); ++i) {
+ array[i]->sendBuffer(array[0]->binaryOutput);
+ }
+ }
+ }
+
+ virtual uint32 waitingMessageType();
+
+ /**
+ If a message is waiting, deserializes the waiting message into
+ message and returns true, otherwise returns false. You can
+ determine the type of the message (and therefore, the class
+ of message) using G3D::ReliableConduit::waitingMessageType().
+ */
+ template<typename T> inline bool receive(T& message) {
+ if (! messageWaiting()) {
+ return false;
+ }
+
+ debugAssert(state == HOLDING);
+ // Deserialize
+ BinaryInput b((uint8*)receiveBuffer, receiveBufferUsedSize, G3D_LITTLE_ENDIAN, BinaryInput::NO_COPY);
+ message.deserialize(b);
+
+ // Don't let anyone read this message again. We leave the buffer
+ // allocated for the next caller, however.
+ receiveBufferUsedSize = 0;
+ state = NO_MESSAGE;
+ messageType = 0;
+ messageSize = 0;
+
+ // Potentially read the next message.
+ messageWaiting();
+
+ return true;
+ }
+
+ /** Removes the current message from the queue. */
+ inline void receive() {
+ if (! messageWaiting()) {
+ return;
+ }
+ receiveBufferUsedSize = 0;
+ state = NO_MESSAGE;
+ messageType = 0;
+ messageSize = 0;
+
+ // Potentially read the next message.
+ messageWaiting();
+ }
+
+ NetAddress address() const;
+};
+
+
+typedef ReferenceCountedPointer<class LightweightConduit> LightweightConduitRef;
+
+/**
+ Provides fast but unreliable transfer of messages. On a LAN,
+ LightweightConduit will probably never drop messages but you
+ <I>might</I> get your messages out of order. On an internet
+ connection it might drop messages altogether. Messages are never
+ corrupted, however. LightweightConduit requires a little less setup
+ and overhead than ReliableConduit. ReliableConduit guarantees
+ message delivery and order but requires a persistent connection.
+
+ To set up a LightweightConduit (assuming you have already made
+ subclasses of G3D::NetMessage based on your application's
+ pcommunication protocol):
+
+[Server Side]
+<OL>
+<LI> Call LightweightConduit::create(port, true, false),
+where port is the port on which you will receive messages.
+
+<LI> Poll LightweightConduit::messageWaiting from your main loop. When
+it is true (or, equivalently, when LightweightConduit::waitingMessageType
+is non-zero) there is an incoming message.
+
+<LI> To read the incoming message, call LightweightConduit::receive with
+the appropriate class type, which mist have a deserialize method.
+LightweightConduit::waitingMessageType tells you what class is
+needed (you make up your own message constants for your program; numbers
+under 1000 are reserved for G3D's internal use).
+
+<LI> When done, simply set the G3D::LightweightConduitRef to NULL or let
+it go out of scope and the conduit cleans itself up automatically.
+</OL>
+
+[Client Side]
+<OL>
+<LI> Call G3D::LightweightConduit::create(). If you will
+broadcast to all servers on a LAN, set the third optional argument to
+true (the default is false for no broadcast). You can also set up the
+receive port as if it was a server to send and receive from a single
+LightweightConduit.
+
+<LI> To send, call G3D::LightweightConduit::send with the target address
+and a pointer to an instance of the message you want to send.
+
+<LI> When done, simply set the G3D::LightweightConduitRef to NULL or let
+it go out of scope and the conduit cleans itself up automatically.
+
+</OL>
+ */
+class LightweightConduit : public Conduit {
+private:
+ friend class NetworkDevice;
+
+ /**
+ True when waitingForMessageType has read the message
+ from the network into messageType/messageStream.
+ */
+ bool alreadyReadMessage;
+
+ /**
+ Origin of the received message.
+ */
+ NetAddress messageSender;
+
+ /**
+ The type of the last message received.
+ */
+ uint32 messageType;
+
+ /**
+ The message received (the type has already been read off).
+ */
+ Array<uint8> messageBuffer;
+
+ LightweightConduit(uint16 receivePort, bool enableReceive, bool enableBroadcast);
+
+ void sendBuffer(const NetAddress& a, BinaryOutput& b);
+
+ /** Maximum transmission unit (packet size in bytes) for this socket.
+ May vary between sockets. */
+ int MTU;
+
+
+ template<typename T>
+ void serializeMessage(
+ uint32 type,
+ const T& m,
+ BinaryOutput& b) const {
+
+ debugAssert(type != 0);
+ b.writeUInt32(type);
+ m.serialize(b);
+ b.writeUInt32(1);
+
+ debugAssertM(b.size() < MTU,
+ format("This LightweightConduit is limited to messages of "
+ "%d bytes (Ethernet hardware limit; this is the "
+ "'UDP MTU')", maxMessageSize()));
+
+ if (b.size() >= MTU) {
+ throw LightweightConduit::PacketSizeException(
+ format("This LightweightConduit is limited to messages of "
+ "%d bytes (Ethernet hardware limit; this is the "
+ "'UDP MTU')", maxMessageSize()),
+ b.size() - 4, // Don't count the type header
+ maxMessageSize());
+ }
+ }
+
+public:
+
+ static LightweightConduitRef create(uint16 receivePort, bool enableReceive, bool enableBroadcast);
+
+ class PacketSizeException {
+ public:
+ std::string message;
+ int serializedPacketSize;
+ int maxMessageSize;
+
+ inline PacketSizeException(const std::string& m, int s, int b) :
+ message(m),
+ serializedPacketSize(s),
+ maxMessageSize(b) {}
+ };
+
+ /** Closes the socket. */
+ ~LightweightConduit();
+
+ /** The maximum length of a message that can be sent
+ (G3D places a small header at the front of each UDP packet;
+ this is already taken into account by the value returned).
+ */
+ inline int maxMessageSize() const {
+ return MTU - 4;
+ }
+
+
+ template<typename T> inline void send(const NetAddress& a, uint32 type, const T& msg) {
+ binaryOutput.reset();
+ serializeMessage(type, msg, binaryOutput);
+ sendBuffer(a, binaryOutput);
+ }
+
+ /** Send the same message to multiple addresses (only serializes once).
+ Useful when server needs to send to a known list of addresses
+ (unlike direct UDP broadcast to all addresses on the subnet) */
+ template<typename T> inline void send(const Array<NetAddress>& a, uint32 type, const T& m) {
+ binaryOutput.reset();
+ serializeMessage(type, m, binaryOutput);
+
+ for (int i = 0; i < a.size(); ++i) {
+ sendBuffer(a[i], binaryOutput);
+ }
+ }
+
+ bool receive(NetAddress& sender);
+
+ template<typename T> inline bool receive(NetAddress& sender, T& message) {
+ bool r = receive(sender);
+ if (r) {
+ BinaryInput b((messageBuffer.getCArray() + 4),
+ messageBuffer.size() - 4,
+ G3D_LITTLE_ENDIAN, BinaryInput::NO_COPY);
+ message.deserialize(b);
+ }
+
+ return r;
+ }
+
+ inline bool receive() {
+ static NetAddress ignore;
+ return receive(ignore);
+ }
+
+ virtual uint32 waitingMessageType();
+
+
+ virtual bool messageWaiting();
+};
+
+///////////////////////////////////////////////////////////////////////////////
+
+typedef ReferenceCountedPointer<class NetListener> NetListenerRef;
+
+/**
+ Runs on the server listening for clients trying to make reliable connections.
+ */
+class NetListener : public ReferenceCountedObject {
+private:
+
+ friend class NetworkDevice;
+
+ SOCKET sock;
+
+ /** Port is in host byte order. */
+ NetListener(uint16 port);
+
+public:
+
+ static NetListenerRef create(const uint16 port);
+
+ ~NetListener();
+
+ /** Block until a connection is received. Returns NULL if
+ something went wrong. */
+ ReliableConduitRef waitForConnection();
+
+ /** True if a client is waiting (i.e. waitForConnection will
+ return immediately). */
+ bool clientWaiting() const;
+
+ bool ok() const;
+};
+
+
+///////////////////////////////////////////////////////////////////////////////
+
+/**
+ @brief Abstraction of network (socket) functionality.
+
+ An abstraction over sockets that provides a message-based network
+ infrastructure optimized for sending many small (~500 bytes) messages.
+ All functions always return immediately.
+
+ Create only one NetworkDevice per process (a WinSock restriction).
+
+ NetworkDevice is technically not thread safe. However, as long as
+ you use different conduits on different threads (or lock conduits
+ before sending), you will encounter no problems sharing the single
+ NetworkDevice across multiple threads. That is, do not invoke the same
+ Conduit's send or receive method on two threads at once.
+
+ This assumes that the underlying WinSock/BSD sockets implementation
+ is thread safe. That is not guaranteed, but in practice seems
+ to always be true (see
+ http://tangentsoft.net/wskfaq/intermediate.html#threadsafety)
+
+ <hr>
+
+ IP networks use "network byte order" (big-endian) for
+ communicating integers. "Host byte order" is the endian-ness of
+ the local machine (typically little-endian; see
+ System::endian). The C functions htonl() and ntohl() convert 32-bit
+ values between these formats. G3D only ever exposes host byte order,
+ so programmers rarely need to be aware of the distinction.
+
+ */
+class NetworkDevice {
+public:
+
+ /** @brief Description of an ethernet or wireless ethernet adapter.*/
+ class EthernetAdapter {
+ public:
+ /** Reverse-DNS of the ip address.*/
+ std::string hostname;
+
+ /** Name of the adapter */
+ std::string name;
+
+ /** IP address in host byte order.*/
+ uint32 ip;
+
+ /** Subnet mask in host byte order.*/
+ uint32 subnet;
+
+ /** UDP broadcast address in host byte order.*/
+ uint32 broadcast;
+
+ /** MAC (hardware) address, if known */
+ uint8 mac[6];
+
+ EthernetAdapter();
+
+ /** Produces a text description of this adapter */
+ void describe(TextOutput& t) const;
+ };
+
+private:
+
+ friend class Conduit;
+ friend class LightweightConduit;
+ friend class ReliableConduit;
+ friend class NetListener;
+
+ bool initialized;
+
+ Array<EthernetAdapter> m_adapterArray;
+
+ /** Broadcast addresses available on this machine,
+ extracted from m_adapterArray.*/
+ Array<uint32> m_broadcastAddresses;
+
+ /** Utility method. */
+ void closesocket(SOCKET& sock) const;
+
+ /** Utility method. Returns true on success.*/
+ bool bind(SOCKET sock, const NetAddress& addr) const;
+
+ /** The global instance */
+ static NetworkDevice* s_instance;
+
+ NetworkDevice();
+
+ bool init();
+
+ void _cleanup();
+
+ /** Called from init to update m_adapterArray and
+ m_broadcastAddresses. */
+ void addAdapter(const EthernetAdapter& a);
+
+public:
+
+ /** Prints an IP address to a string.
+ @param ip In host byte order.*/
+ static std::string formatIP(uint32 ip);
+
+ /** Prints a MAC address to a string. */
+ static std::string formatMAC(const uint8 mac[6]);
+
+ ~NetworkDevice();
+
+ /** Returns the available ethernet adapters for the current
+ machine that are online. Does not include the loopback adapter
+ for localhost.*/
+ inline const Array<EthernetAdapter>& adapterArray() const {
+ return m_adapterArray;
+ }
+
+ /** Returns the (unique) IP addresses for UDP broadcasting
+ extracted from adapterArray(). All are in host byte order. */
+ inline const Array<uint32>& broadcastAddressArray() const {
+ return m_broadcastAddresses;
+ }
+
+ /**
+ Returns NULL if there was a problem initializing the network.
+ */
+ static NetworkDevice* instance();
+
+ /**
+ Shuts down the network device (destroying the global instance).
+ */
+ static void cleanup();
+
+ /**
+ Prints a human-readable description of this machine
+ to the text output stream.
+ */
+ void describeSystem(
+ TextOutput& t);
+
+ void describeSystem(
+ std::string& s);
+
+ /** Returns the name (or one of the names) of this computer */
+ std::string localHostName() const;
+
+ /** There is often more than one address for the local host. This
+ returns all of them.
+ @deprecated Use adapterArray()
+ */
+ void localHostAddresses(Array<NetAddress>& array) const;
+};
+
+
+#ifdef __GNUC__
+inline uint32 gcchtonl(uint32 x) {
+ // This pragma fools gcc into surpressing all error messages,
+ // including the bogus one that it creates for htonl
+# pragma GCC system_header
+ return htonl(x);
+}
+#endif
+
+} // G3D namespace
+
+#ifndef _WIN32
+#undef SOCKADDR_IN
+#undef SOCKET
+#endif
+
+#endif
diff --git a/externals/g3dlite/G3D.lib/include/G3D/PhysicsFrame.h b/externals/g3dlite/G3D.lib/include/G3D/PhysicsFrame.h
new file mode 100644
index 00000000000..44eedb6846e
--- /dev/null
+++ b/externals/g3dlite/G3D.lib/include/G3D/PhysicsFrame.h
@@ -0,0 +1,74 @@
+/**
+ @file PhysicsFrame.h
+
+ @maintainer Morgan McGuire, matrix@graphics3d.com
+
+ @created 2002-07-08
+ @edited 2006-01-10
+*/
+
+#ifndef G3D_PHYSICSFRAME_H
+#define G3D_PHYSICSFRAME_H
+
+#include "G3D/platform.h"
+#include "G3D/Vector3.h"
+#include "G3D/Matrix3.h"
+#include "G3D/Quat.h"
+#include "G3D/CoordinateFrame.h"
+#include <math.h>
+#include <string>
+
+
+namespace G3D {
+
+/**
+ An RT transformation using a quaternion; suitable for
+ physics integration.
+
+ This interface is in "Beta" and will change in the next release.
+ */
+class PhysicsFrame {
+public:
+
+ Quat rotation;
+
+ /**
+ Takes object space points to world space.
+ */
+ Vector3 translation;
+
+ /**
+ Initializes to the identity frame.
+ */
+ PhysicsFrame();
+
+ /**
+ Purely translational force
+ */
+ PhysicsFrame(const Vector3& translation) : translation(translation) {}
+
+ PhysicsFrame(const CoordinateFrame& coordinateFrame);
+
+ /** Compose: create the transformation that is <I>other</I> followed by <I>this</I>.*/
+ PhysicsFrame operator*(const PhysicsFrame& other) const;
+
+ virtual ~PhysicsFrame() {}
+
+ CoordinateFrame toCoordinateFrame() const;
+
+ /**
+ Linear interpolation (spherical linear for the rotations).
+ */
+ PhysicsFrame lerp(
+ const PhysicsFrame& other,
+ float alpha) const;
+
+ void deserialize(class BinaryInput& b);
+
+ void serialize(class BinaryOutput& b) const;
+
+};
+
+} // namespace
+
+#endif
diff --git a/externals/g3dlite/G3D.lib/include/G3D/Plane.h b/externals/g3dlite/G3D.lib/include/G3D/Plane.h
new file mode 100644
index 00000000000..1f0b15e0fe4
--- /dev/null
+++ b/externals/g3dlite/G3D.lib/include/G3D/Plane.h
@@ -0,0 +1,161 @@
+/**
+ @file Plane.h
+
+ Plane class
+
+ @maintainer Morgan McGuire, matrix@graphics3d.com
+
+ @created 2001-06-02
+ @edited 2004-07-18
+*/
+
+#ifndef G3D_PLANE_H
+#define G3D_PLANE_H
+
+#include "G3D/platform.h"
+#include "G3D/Vector3.h"
+#include "G3D/Vector4.h"
+#include "G3D/debugAssert.h"
+
+namespace G3D {
+
+/**
+ An infinite 2D plane in 3D space.
+ */
+class Plane {
+private:
+
+ /** normal.Dot(x,y,z) = distance */
+ Vector3 _normal;
+ float _distance;
+
+ /**
+ Assumes the normal has unit length.
+ */
+ Plane(const Vector3& n, float d) : _normal(n), _distance(d) {
+ }
+
+public:
+
+ Plane() : _normal(Vector3::unitY()), _distance(0) {
+ }
+
+ /**
+ Constructs a plane from three points.
+ */
+ Plane(
+ const Vector3& point0,
+ const Vector3& point1,
+ const Vector3& point2);
+
+ /**
+ Constructs a plane from three points, where at most two are
+ at infinity (w = 0, not xyz = inf).
+ */
+ Plane(
+ Vector4 point0,
+ Vector4 point1,
+ Vector4 point2);
+
+ /**
+ The normal will be unitized.
+ */
+ Plane(
+ const Vector3& __normal,
+ const Vector3& point);
+
+ static Plane fromEquation(float a, float b, float c, float d);
+
+ Plane(class BinaryInput& b);
+ void serialize(class BinaryOutput& b) const;
+ void deserialize(class BinaryInput& b);
+
+ virtual ~Plane() {}
+
+ /**
+ Returns true if point is on the side the normal points to or
+ is in the plane.
+ */
+ inline bool halfSpaceContains(Vector3 point) const {
+ // Clamp to a finite range for testing
+ point = point.clamp(Vector3::minFinite(), Vector3::maxFinite());
+
+ // We can get away with putting values *at* the limits of the float32 range into
+ // a dot product, since the dot product is carried out on float64.
+ return _normal.dot(point) >= _distance;
+ }
+
+ /**
+ Returns true if point is on the side the normal points to or
+ is in the plane.
+ */
+ inline bool halfSpaceContains(const Vector4& point) const {
+ if (point.w == 0) {
+ return _normal.dot(point.xyz()) > 0;
+ } else {
+ return halfSpaceContains(point.xyz() / point.w);
+ }
+ }
+
+ /**
+ Returns true if point is on the side the normal points to or
+ is in the plane. Only call on finite points. Faster than halfSpaceContains.
+ */
+ inline bool halfSpaceContainsFinite(const Vector3& point) const {
+ debugAssert(point.isFinite());
+ return _normal.dot(point) >= _distance;
+ }
+
+ /**
+ Returns true if the point is nearly in the plane.
+ */
+ inline bool fuzzyContains(const Vector3 &point) const {
+ return fuzzyEq(point.dot(_normal), _distance);
+ }
+
+ inline const Vector3& normal() const {
+ return _normal;
+ }
+
+ /**
+ Returns distance from point to plane. Distance is negative if point is behind (not in plane in direction opposite normal) the plane.
+ */
+ inline float distance(const Vector3& x) const {
+ return (_normal.dot(x) - _distance);
+ }
+
+ inline Vector3 closestPoint(const Vector3& x) const {
+ return x + (_normal * (-distance(x)));
+ }
+
+ /** Returns normal * distance from origin */
+ Vector3 center() const {
+ return _normal * _distance;
+ }
+
+ /**
+ Inverts the facing direction of the plane so the new normal
+ is the inverse of the old normal.
+ */
+ void flip();
+
+ /**
+ Returns the equation in the form:
+
+ <CODE>normal.Dot(Vector3(<I>x</I>, <I>y</I>, <I>z</I>)) + d = 0</CODE>
+ */
+ void getEquation(Vector3 &normal, double& d) const;
+ void getEquation(Vector3 &normal, float& d) const;
+
+ /**
+ ax + by + cz + d = 0
+ */
+ void getEquation(double& a, double& b, double& c, double& d) const;
+ void getEquation(float& a, float& b, float& c, float& d) const;
+
+ std::string toString() const;
+};
+
+} // namespace
+
+#endif
diff --git a/externals/g3dlite/G3D.lib/include/G3D/PointAABSPTree.h b/externals/g3dlite/G3D.lib/include/G3D/PointAABSPTree.h
new file mode 100644
index 00000000000..c8d527a45c2
--- /dev/null
+++ b/externals/g3dlite/G3D.lib/include/G3D/PointAABSPTree.h
@@ -0,0 +1,1207 @@
+/**
+ @file PointAABSPTree.h
+
+ @maintainer Morgan McGuire, matrix@graphics3d.com
+
+ @created 2004-01-11
+ @edited 2008-11-02
+
+ Copyright 2000-2008, Morgan McGuire.
+ All rights reserved.
+
+ */
+
+#ifndef X_PointKDTree_H
+#define X_PointKDTree_H
+
+#include "G3D/platform.h"
+#include "G3D/Array.h"
+#include "G3D/Table.h"
+#include "G3D/Vector2.h"
+#include "G3D/Vector3.h"
+#include "G3D/Vector4.h"
+#include "G3D/AABox.h"
+#include "G3D/Sphere.h"
+#include "G3D/Box.h"
+#include "G3D/BinaryInput.h"
+#include "G3D/BinaryOutput.h"
+#include "G3D/CollisionDetection.h"
+#include "G3D/GCamera.h"
+#include "G3D/PositionTrait.h"
+#include <algorithm>
+
+
+///////////////////////////////////////////////////////
+
+/** @deprecated */
+inline void getPosition(const G3D::Vector3& v, G3D::Vector3& p) {
+ p = v;
+}
+
+/** @deprecated */
+inline void getPosition(const G3D::Vector4& v, G3D::Vector3& p) {
+ p = v.xyz();
+}
+
+/** @deprecated */
+inline void getPosition(const G3D::Vector2& v, G3D::Vector3& p) {
+ p.x = v.x;
+ p.y = v.y;
+ p.z = 0;
+}
+
+namespace G3D {
+
+/**
+ A set data structure that supports spatial queries using an axis-aligned
+ BSP tree for speed.
+
+ PointKDTree allows you to quickly find points in 3D that lie within
+ a box or sphere. For large sets of objects it is much faster
+ than testing each object for a collision. See also G3D::KDTree; this class
+ is optimized for point sets, e.g.,for use in photon mapping and mesh processing.
+
+ <B>Template Parameters</B>
+
+ <br>
+
+ <br>The template parameter <I>T</I> must be one for which
+ the following functions are overloaded:
+
+ <pre>
+ T::T(); <I>(public constructor of no arguments)</I>
+
+ template<> struct PositionTrait<class T> {
+ static void getPosition(const T& v, G3D::Vector3& p);};
+
+ template <> struct HashTrait<class T> {
+ static size_t hashCode(const T& key);};
+
+ template<> struct EqualsTrait<class T> {
+ static bool equals(const T& a, const T& b); };
+ </pre>
+
+ <p>
+
+ G3D provides these for the Vector2, Vector3, and Vector4 classes.
+ If you use a custom class, or a pointer to a custom class, you will need
+ to define those functions.
+
+ <B>Moving %Set Members</B>
+ <DT>It is important that objects do not move without updating the
+ PointKDTree. If the position of an object is about
+ to change, PointKDTree::remove it before they change and
+ PointKDTree::insert it again afterward. For objects
+ where the hashCode and == operator are invariant with respect
+ to the 3D position,
+ you can use the PointKDTree::update method as a shortcut to
+ insert/remove an object in one step after it has moved.
+
+
+ Note: Do not mutate any value once it has been inserted into PointKDTree. Values
+ are copied interally. All PointKDTree iterators convert to pointers to constant
+ values to reinforce this.
+
+ If you want to mutate the objects you intend to store in a PointKDTree
+ simply insert <I>pointers</I> to your objects instead of the objects
+ themselves, and ensure that the above operations are defined. (And
+ actually, because values are copied, if your values are large you may
+ want to insert pointers anyway, to save space and make the balance
+ operation faster.)
+
+ <B>Dimensions</B>
+ Although designed as a 3D-data structure, you can use the PointKDTree
+ for data distributed along 2 or 1 axes by simply returning bounds
+ that are always zero along one or more dimensions.
+
+*/
+template<class T,
+ class PositionFunc = PositionTrait<T>,
+ class HashFunc = HashTrait<T>,
+ class EqualsFunc = EqualsTrait<T> >
+class PointKDTree {
+protected:
+#define TreeType PointKDTree<T, PositionFunc, HashFunc, EqualsFunc>
+
+ // Unlike the KDTree, the PointKDTree assumes that T elements are
+ // small and keeps the handle and cached position together instead of
+ // placing them in separate bounds arrays. Also note that a copy of T
+ // is kept in the member table and that there is no indirection.
+ class Handle {
+ private:
+ Vector3 m_position;
+
+ public:
+ T value;
+
+ inline Handle() {}
+ inline Handle(const T& v) : value(v) {
+ PositionFunc::getPosition(v, m_position);
+ }
+
+ /** Used by makeNode to create fake handles for partitioning. */
+ void setPosition(const Vector3& v) {
+ m_position = v;
+ }
+
+ inline const Vector3& position() const {
+ return m_position;
+ }
+ };
+
+ /** Returns the bounds of the sub array. Used by makeNode. */
+ static AABox computeBounds(
+ const Array<Handle>& point) {
+
+ if (point.size() == 0) {
+ return AABox(Vector3::inf(), Vector3::inf());
+ }
+
+ AABox bounds(point[0].position());
+
+ for (int p = 0; p < point.size(); ++p) {
+ bounds.merge(point[p].position());
+ }
+
+ return bounds;
+ }
+
+ class Node {
+ public:
+
+ /** Spatial bounds on all values at this node and its children, based purely on
+ the parent's splitting planes. May be infinite */
+ AABox splitBounds;
+
+ Vector3::Axis splitAxis;
+
+ /** Location along the specified axis */
+ float splitLocation;
+
+ /** child[0] contains all values strictly
+ smaller than splitLocation along splitAxis.
+
+ child[1] contains all values strictly
+ larger.
+
+ Both may be NULL if there are not enough
+ values to bother recursing.
+ */
+ Node* child[2];
+
+ /** Values if this is a leaf node). */
+ Array<Handle> valueArray;
+
+ /** Creates node with NULL children */
+ Node() {
+ splitAxis = Vector3::X_AXIS;
+ splitLocation = 0;
+ splitBounds = AABox(-Vector3::inf(), Vector3::inf());
+ for (int i = 0; i < 2; ++i) {
+ child[i] = NULL;
+ }
+ }
+
+ /**
+ Doesn't clone children.
+ */
+ Node(const Node& other) : valueArray(other.valueArray) {
+ splitAxis = other.splitAxis;
+ splitLocation = other.splitLocation;
+ splitBounds = other.splitBounds;
+ for (int i = 0; i < 2; ++i) {
+ child[i] = NULL;
+ }
+ }
+
+ /** Copies the specified subarray of pt into point, NULLs the children.
+ Assumes a second pass will set splitBounds. */
+ Node(const Array<Handle>& pt) {
+ splitAxis = Vector3::X_AXIS;
+ splitLocation = 0;
+ for (int i = 0; i < 2; ++i) {
+ child[i] = NULL;
+ }
+ valueArray = pt;
+ }
+
+
+ /** Deletes the children (but not the values) */
+ ~Node() {
+ for (int i = 0; i < 2; ++i) {
+ delete child[i];
+ }
+ }
+
+
+ /** Returns true if this node is a leaf (no children) */
+ inline bool isLeaf() const {
+ return (child[0] == NULL) && (child[1] == NULL);
+ }
+
+
+ /**
+ Recursively appends all handles and children's handles
+ to the array.
+ */
+ void getHandles(Array<Handle>& handleArray) const {
+ handleArray.append(valueArray);
+ for (int i = 0; i < 2; ++i) {
+ if (child[i] != NULL) {
+ child[i]->getHandles(handleArray);
+ }
+ }
+ }
+
+
+ void verifyNode(const Vector3& lo, const Vector3& hi) {
+ // debugPrintf("Verifying: split %d @ %f [%f, %f, %f], [%f, %f, %f]\n",
+ // splitAxis, splitLocation, lo.x, lo.y, lo.z, hi.x, hi.y, hi.z);
+
+ debugAssert(lo == splitBounds.low());
+ debugAssert(hi == splitBounds.high());
+
+ for (int i = 0; i < valueArray.length(); ++i) {
+ const Vector3& b = valueArray[i].position();
+ debugAssert(splitBounds.contains(b));
+ }
+
+ if (child[0] || child[1]) {
+ debugAssert(lo[splitAxis] < splitLocation);
+ debugAssert(hi[splitAxis] > splitLocation);
+ }
+
+ Vector3 newLo = lo;
+ newLo[splitAxis] = splitLocation;
+ Vector3 newHi = hi;
+ newHi[splitAxis] = splitLocation;
+
+ if (child[0] != NULL) {
+ child[0]->verifyNode(lo, newHi);
+ }
+
+ if (child[1] != NULL) {
+ child[1]->verifyNode(newLo, hi);
+ }
+ }
+
+
+ /**
+ Stores the locations of the splitting planes (the structure but not the content)
+ so that the tree can be quickly rebuilt from a previous configuration without
+ calling balance.
+ */
+ static void serializeStructure(const Node* n, BinaryOutput& bo) {
+ if (n == NULL) {
+ bo.writeUInt8(0);
+ } else {
+ bo.writeUInt8(1);
+ n->splitBounds.serialize(bo);
+ serialize(n->splitAxis, bo);
+ bo.writeFloat32(n->splitLocation);
+ for (int c = 0; c < 2; ++c) {
+ serializeStructure(n->child[c], bo);
+ }
+ }
+ }
+
+ /** Clears the member table */
+ static Node* deserializeStructure(BinaryInput& bi) {
+ if (bi.readUInt8() == 0) {
+ return NULL;
+ } else {
+ Node* n = new Node();
+ n->splitBounds.deserialize(bi);
+ deserialize(n->splitAxis, bi);
+ n->splitLocation = bi.readFloat32();
+ for (int c = 0; c < 2; ++c) {
+ n->child[c] = deserializeStructure(bi);
+ }
+ }
+ }
+
+ /** Returns the deepest node that completely contains bounds. */
+ Node* findDeepestContainingNode(const Vector3& point) {
+
+ // See which side of the splitting plane the bounds are on
+ if (point[splitAxis] < splitLocation) {
+ // Point is on the low side. Recurse into the child
+ // if it exists.
+ if (child[0] != NULL) {
+ return child[0]->findDeepestContainingNode(point);
+ }
+ } else if (point[splitAxis] > splitLocation) {
+ // Point is on the high side, recurse into the child
+ // if it exists.
+ if (child[1] != NULL) {
+ return child[1]->findDeepestContainingNode(point);
+ }
+ }
+
+ // There was no containing child, so this node is the
+ // deepest containing node.
+ return this;
+ }
+
+ /** Appends all members that intersect the box.
+ If useSphere is true, members are tested against the sphere instead. */
+ void getIntersectingMembers(
+ const AABox& sphereBounds,
+ const Sphere& sphere,
+ Array<T>& members) const {
+
+ // Test all values at this node. Extract the
+ // underlying C array for speed
+ const int N = valueArray.size();
+ const Handle* handleArray = valueArray.getCArray();
+
+ const float r2 = square(sphere.radius);
+
+ // Copy the sphere center so that it is on the stack near the radius
+ const Vector3 center = sphere.center;
+ for (int v = 0; v < N; ++v) {
+ if ((center - handleArray[v].position()).squaredLength() <= r2) {
+ members.append(handleArray[v].value);
+ }
+ }
+
+ // If the left child overlaps the box, recurse into it
+ if (child[0] && (sphereBounds.low()[splitAxis] < splitLocation)) {
+ child[0]->getIntersectingMembers(sphereBounds, sphere, members);
+ }
+
+ // If the right child overlaps the box, recurse into it
+ if (child[1] && (sphereBounds.high()[splitAxis] > splitLocation)) {
+ child[1]->getIntersectingMembers(sphereBounds, sphere, members);
+ }
+ }
+
+ /** Appends all members that intersect the box.
+ If useSphere is true, members are tested against the sphere instead.
+
+ Implemented using both box and sphere tests to simplify the implementation
+ of a future beginSphereInteresection iterator using the same underlying
+ BoxIterator class.
+ */
+ void getIntersectingMembers(
+ const AABox& box,
+ const Sphere& sphere,
+ Array<T>& members,
+ bool useSphere) const {
+
+ // Test all values at this node
+ for (int v = 0; v < valueArray.size(); ++v) {
+ if ((useSphere && sphere.contains(valueArray[v].position())) ||
+ (! useSphere && box.contains(valueArray[v].position()))) {
+ members.append(valueArray[v].value);
+ }
+ }
+
+ // If the left child overlaps the box, recurse into it
+ if ((child[0] != NULL) && (box.low()[splitAxis] < splitLocation)) {
+ child[0]->getIntersectingMembers(box, sphere, members, useSphere);
+ }
+
+ // If the right child overlaps the box, recurse into it
+ if ((child[1] != NULL) && (box.high()[splitAxis] > splitLocation)) {
+ child[1]->getIntersectingMembers(box, sphere, members, useSphere);
+ }
+ }
+
+ /**
+ Recurse through the tree, assigning splitBounds fields.
+ */
+ void assignSplitBounds(const AABox& myBounds) {
+ splitBounds = myBounds;
+
+# ifdef G3D_DEBUG
+ if (child[0] || child[1]) {
+ debugAssert(splitBounds.high()[splitAxis] > splitLocation);
+ debugAssert(splitBounds.low()[splitAxis] < splitLocation);
+ }
+# endif
+
+ AABox childBounds[2];
+ myBounds.split(splitAxis, splitLocation, childBounds[0], childBounds[1]);
+
+ for (int c = 0; c < 2; ++c) {
+ if (child[c]) {
+ child[c]->assignSplitBounds(childBounds[c]);
+ }
+ }
+ }
+ };
+
+ class AxisComparator {
+ private:
+ Vector3::Axis sortAxis;
+
+ public:
+
+ AxisComparator(Vector3::Axis s) : sortAxis(s) {}
+
+ inline int operator()(const Handle& A, const Handle& B) const {
+ if (A.position()[sortAxis] > B.position()[sortAxis]) {
+ return -1;
+ } else if (A.position()[sortAxis] < B.position()[sortAxis]) {
+ return 1;
+ } else {
+ return 0;
+ }
+ }
+ };
+
+ /**
+ Recursively subdivides the subarray.
+
+ The source array will be cleared after it is used
+
+ Call assignSplitBounds() on the root node after making a tree.
+ */
+ Node* makeNode(
+ Array<Handle>& source,
+ Array<Handle>& temp,
+ int valuesPerNode,
+ int numMeanSplits) {
+
+ Node* node = NULL;
+
+ if (source.size() <= valuesPerNode) {
+ // Make a new leaf node
+ node = new Node(source);
+
+ // Set the pointers in the memberTable
+ for (int i = 0; i < source.size(); ++i) {
+ memberTable.set(source[i].value, node);
+ }
+
+ } else {
+ // Make a new internal node
+ node = new Node();
+
+ const AABox bounds = computeBounds(source);
+ const Vector3 extent = bounds.high() - bounds.low();
+
+ Vector3::Axis splitAxis = extent.primaryAxis();
+
+ float splitLocation;
+
+ Array<Handle> lt, gt;
+
+ if (numMeanSplits <= 0) {
+ source.medianPartition(lt, node->valueArray, gt, temp, AxisComparator(splitAxis));
+ splitLocation = node->valueArray[0].position()[splitAxis];
+
+ if ((node->valueArray.size() > source.size() / 2) &&
+ (source.size() > 10)) {
+ // Our median split put an awful lot of points on the splitting plane. Try a mean
+ // split instead
+ numMeanSplits = 1;
+ }
+ }
+
+ if (numMeanSplits > 0) {
+ // Compute the mean along the axis
+
+ splitLocation = (bounds.high()[splitAxis] +
+ bounds.low()[splitAxis]) / 2.0;
+
+ Handle splitHandle;
+ Vector3 v;
+ v[splitAxis] = splitLocation;
+ splitHandle.setPosition(v);
+
+ source.partition(splitHandle, lt, node->valueArray, gt, AxisComparator(splitAxis));
+ }
+
+# if defined(G3D_DEBUG) && defined(VERIFY_TREE)
+ for (int i = 0; i < lt.size(); ++i) {
+ const Vector3& v = lt[i].position();
+ debugAssert(v[splitAxis] < splitLocation);
+ }
+ for (int i = 0; i < gt.size(); ++i) {
+ debugAssert(gt[i].position()[splitAxis] > splitLocation);
+ }
+ for (int i = 0; i < node->valueArray.size(); ++i) {
+ debugAssert(node->valueArray[i].position()[splitAxis] == splitLocation);
+ }
+# endif
+
+ node->splitAxis = splitAxis;
+ node->splitLocation = splitLocation;
+
+ // Throw away the source array to save memory
+ source.fastClear();
+
+ if (lt.size() > 0) {
+ node->child[0] = makeNode(lt, temp, valuesPerNode, numMeanSplits - 1);
+ }
+
+ if (gt.size() > 0) {
+ node->child[1] = makeNode(gt, temp, valuesPerNode, numMeanSplits - 1);
+ }
+
+ // Add the values stored at this interior node to the member table
+ for(int i = 0; i < node->valueArray.size(); ++i) {
+ memberTable.set(node->valueArray[i].value, node);
+ }
+
+ }
+
+ return node;
+ }
+
+ /**
+ Recursively clone the passed in node tree, setting
+ pointers for members in the memberTable as appropriate.
+ called by the assignment operator.
+ */
+ Node* cloneTree(Node* src) {
+ Node* dst = new Node(*src);
+
+ // Make back pointers
+ for (int i = 0; i < dst->valueArray.size(); ++i) {
+ memberTable.set(dst->valueArray[i].value, dst);
+ }
+
+ // Clone children
+ for (int i = 0; i < 2; ++i) {
+ if (src->child[i] != NULL) {
+ dst->child[i] = cloneTree(src->child[i]);
+ }
+ }
+
+ return dst;
+ }
+
+ /** Maps members to the node containing them */
+ typedef Table<T, Node*, HashFunc, EqualsFunc> MemberTable;
+ MemberTable memberTable;
+
+ Node* root;
+
+public:
+
+ /** To construct a balanced tree, insert the elements and then call
+ PointKDTree::balance(). */
+ PointKDTree() : root(NULL) {}
+
+
+ PointKDTree(const PointKDTree& src) : root(NULL) {
+ *this = src;
+ }
+
+
+ PointKDTree& operator=(const PointKDTree& src) {
+ delete root;
+ // Clone tree takes care of filling out the memberTable.
+ root = cloneTree(src.root);
+ return *this;
+ }
+
+
+ ~PointKDTree() {
+ clear();
+ }
+
+ /**
+ Throws out all elements of the set and erases the structure of the tree.
+ */
+ void clear() {
+ memberTable.clear();
+ delete root;
+ root = NULL;
+ }
+
+ /** Removes all elements of the set while maintaining the structure of the tree */
+ void clearData() {
+ memberTable.clear();
+ Array<Node*> stack;
+ stack.push(root);
+ while (stack.size() > 0) {
+ Node* node = stack.pop();
+ node->valueArray.fastClear();
+
+ for (int i = 0; i < 2; ++i) {
+ if (node->child[i] != NULL) {
+ stack.push(node->child[i]);
+ }
+ }
+ }
+ }
+
+
+ int size() const {
+ return memberTable.size();
+ }
+
+ /**
+ Inserts an object into the set if it is not
+ already present. O(log n) time. Does not
+ cause the tree to be balanced.
+ */
+ void insert(const T& value) {
+ if (contains(value)) {
+ // Already in the set
+ return;
+ }
+
+ Handle h(value);
+
+ if (root == NULL) {
+ // This is the first node; create a root node
+ root = new Node();
+ }
+
+ Node* node = root->findDeepestContainingNode(h.position());
+
+ // Insert into the node
+ node->valueArray.append(h);
+
+ // Insert into the node table
+ memberTable.set(value, node);
+ }
+
+ /** Inserts each elements in the array in turn. If the tree
+ begins empty (no structure and no elements), this is faster
+ than inserting each element in turn. You still need to balance
+ the tree at the end.*/
+ void insert(const Array<T>& valueArray) {
+ // Pre-size the member table to avoid multiple allocations
+ memberTable.setSizeHint(valueArray.size() + size());
+
+ if (root == NULL) {
+ // Optimized case for an empty tree; don't bother
+ // searching or reallocating the root node's valueArray
+ // as we incrementally insert.
+ root = new Node();
+ root->valueArray.resize(valueArray.size());
+ for (int i = 0; i < valueArray.size(); ++i) {
+ // Insert in opposite order so that we have the exact same
+ // data structure as if we inserted each (i.e., order is reversed
+ // from array).
+ root->valueArray[valueArray.size() - i - 1] = Handle(valueArray[i]);
+ memberTable.set(valueArray[i], root);
+ }
+ } else {
+ // Insert at appropriate tree depth.
+ for (int i = 0; i < valueArray.size(); ++i) {
+ insert(valueArray[i]);
+ }
+ }
+ }
+
+
+ /**
+ Returns true if this object is in the set, otherwise
+ returns false. O(1) time.
+ */
+ bool contains(const T& value) {
+ return memberTable.containsKey(value);
+ }
+
+
+ /**
+ Removes an object from the set in O(1) time.
+ It is an error to remove members that are not already
+ present. May unbalance the tree.
+
+ Removing an element never causes a node (split plane) to be removed...
+ nodes are only changed when the tree is rebalanced. This behavior
+ is desirable because it allows the split planes to be serialized,
+ and then deserialized into an empty tree which can be repopulated.
+ */
+ void remove(const T& value) {
+ debugAssertM(contains(value),
+ "Tried to remove an element from a "
+ "PointKDTree that was not present");
+
+ Array<Handle>& list = memberTable[value]->valueArray;
+
+ // Find the element and remove it
+ for (int i = list.length() - 1; i >= 0; --i) {
+ if (list[i].value == value) {
+ list.fastRemove(i);
+ break;
+ }
+ }
+ memberTable.remove(value);
+ }
+
+
+ /**
+ If the element is in the set, it is removed.
+ The element is then inserted.
+
+ This is useful when the == and hashCode methods
+ on <I>T</I> are independent of the bounds. In
+ that case, you may call update(v) to insert an
+ element for the first time and call update(v)
+ again every time it moves to keep the tree
+ up to date.
+ */
+ void update(const T& value) {
+ if (contains(value)) {
+ remove(value);
+ }
+ insert(value);
+ }
+
+
+ /**
+ Rebalances the tree (slow). Call when objects
+ have moved substantially from their original positions
+ (which unbalances the tree and causes the spatial
+ queries to be slow).
+
+ @param valuesPerNode Maximum number of elements to put at
+ a node.
+
+ @param numMeanSplits numMeanSplits = 0 gives a
+ fully axis aligned BSP-tree, where the balance operation attempts to balance
+ the tree so that every splitting plane has an equal number of left
+ and right children (i.e. it is a <B>median</B> split along that axis).
+ This tends to maximize average performance; all querries will return in the same amount of time.
+
+ You can override this behavior by
+ setting a number of <B>mean</B> (average) splits. numMeanSplits = MAX_INT
+ creates a full oct-tree, which tends to optimize peak performance (some areas of the scene will terminate after few recursive splits) at the expense of
+ peak performance.
+ */
+ void balance(int valuesPerNode = 40, int numMeanSplits = 3) {
+ if (root == NULL) {
+ // Tree is empty
+ return;
+ }
+
+ Array<Handle> handleArray;
+ root->getHandles(handleArray);
+
+ // Delete the old tree
+ clear();
+
+ Array<Handle> temp;
+ root = makeNode(handleArray, temp, valuesPerNode, numMeanSplits);
+ temp.fastClear();
+
+ // Walk the tree, assigning splitBounds. We start with unbounded
+ // space.
+ root->assignSplitBounds(AABox::maxFinite());
+
+# ifdef _DEBUG
+ root->verifyNode(Vector3::minFinite(), Vector3::maxFinite());
+# endif
+ }
+
+private:
+
+ /**
+ Returns the elements
+
+ @param parentMask The mask that this node returned from culledBy.
+ */
+ static void getIntersectingMembers(
+ const Array<Plane>& plane,
+ Array<T>& members,
+ Node* node,
+ uint32 parentMask) {
+
+ int dummy;
+
+ if (parentMask == 0) {
+ // None of these planes can cull anything
+ for (int v = node->valueArray.size() - 1; v >= 0; --v) {
+ members.append(node->valueArray[v].value);
+ }
+
+ // Iterate through child nodes
+ for (int c = 0; c < 2; ++c) {
+ if (node->child[c]) {
+ getIntersectingMembers(plane, members, node->child[c], 0);
+ }
+ }
+ } else {
+
+ if (node->valueArray.size() > 0) {
+ // This is a leaf; check the points
+ debugAssertM(node->child[0] == NULL, "Malformed Point tree");
+ debugAssertM(node->child[1] == NULL, "Malformed Point tree");
+
+ // Test values at this node against remaining planes
+ for (int p = 0; p < plane.size(); ++p) {
+ if ((parentMask >> p) & 1 != 0) {
+ // Test against this plane
+ const Plane& curPlane = plane[p];
+ for (int v = node->valueArray.size() - 1; v >= 0; --v) {
+ if (curPlane.halfSpaceContains(node->valueArray[v].position())) {
+ members.append(node->valueArray[v].value);
+ }
+ }
+ }
+ }
+ } else {
+
+ uint32 childMask = 0xFFFFFF;
+
+ // Iterate through child nodes
+ for (int c = 0; c < 2; ++c) {
+ if (node->child[c] &&
+ ! node->child[c]->splitBounds.culledBy(plane, dummy, parentMask, childMask)) {
+ // This node was not culled
+ getIntersectingMembers(plane, members, node->child[c], childMask);
+ }
+ }
+ }
+ }
+ }
+
+public:
+
+ /**
+ Returns all members inside the set of planes.
+ @param members The results are appended to this array.
+ */
+ void getIntersectingMembers(const Array<Plane>& plane, Array<T>& members) const {
+ if (root == NULL) {
+ return;
+ }
+
+ getIntersectingMembers(plane, members, root, 0xFFFFFF);
+ }
+
+ /**
+ Typically used to find all visible
+ objects inside the view frustum (see also GCamera::getClipPlanes)... i.e. all objects
+ <B>not<B> culled by frustum.
+
+ Example:
+ <PRE>
+ Array<Object*> visible;
+ tree.getIntersectingMembers(camera.frustum(), visible);
+ // ... Draw all objects in the visible array.
+ </PRE>
+ @param members The results are appended to this array.
+ */
+ void getIntersectingMembers(const GCamera::Frustum& frustum, Array<T>& members) const {
+ Array<Plane> plane;
+
+ for (int i = 0; i < frustum.faceArray.size(); ++i) {
+ plane.append(frustum.faceArray[i].plane);
+ }
+
+ getIntersectingMembers(plane, members);
+ }
+
+ /**
+ C++ STL style iterator variable. See beginBoxIntersection().
+ The iterator overloads the -> (dereference) operator, so this
+ acts like a pointer to the current member.
+ */
+ // This iterator turns Node::getIntersectingMembers into a
+ // coroutine. It first translates that method from recursive to
+ // stack based, then captures the system state (analogous to a Scheme
+ // continuation) after each element is appended to the member array,
+ // and allowing the computation to be restarted.
+ class BoxIntersectionIterator {
+ private:
+ friend class TreeType;
+
+ /** True if this is the "end" iterator instance */
+ bool isEnd;
+
+ /** The box that we're testing against. */
+ AABox box;
+
+ /** Node that we're currently looking at. Undefined if isEnd
+ is true. */
+ Node* node;
+
+ /** Nodes waiting to be processed */
+ // We could use backpointers within the tree and careful
+ // state management to avoid ever storing the stack-- but
+ // it is much easier this way and only inefficient if the
+ // caller uses post increment (which they shouldn't!).
+ Array<Node*> stack;
+
+ /** The next index of current->valueArray to return.
+ Undefined when isEnd is true.*/
+ int nextValueArrayIndex;
+
+ BoxIntersectionIterator() : isEnd(true) {}
+
+ BoxIntersectionIterator(const AABox& b, const Node* root) :
+ isEnd(root == NULL), box(b),
+ node(const_cast<Node*>(root)), nextValueArrayIndex(-1) {
+
+ // We intentionally start at the "-1" index of the current
+ // node so we can use the preincrement operator to move
+ // ourselves to element 0 instead of repeating all of the
+ // code from the preincrement method. Note that this might
+ // cause us to become the "end" instance.
+ ++(*this);
+ }
+
+ public:
+
+ inline bool operator!=(const BoxIntersectionIterator& other) const {
+ return ! (*this == other);
+ }
+
+ bool operator==(const BoxIntersectionIterator& other) const {
+ if (isEnd) {
+ return other.isEnd;
+ } else if (other.isEnd) {
+ return false;
+ } else {
+ // Two non-end iterators; see if they match. This is kind of
+ // silly; users shouldn't call == on iterators in general unless
+ // one of them is the end iterator.
+ if ((box != other.box) || (node != other.node) ||
+ (nextValueArrayIndex != other.nextValueArrayIndex) ||
+ (stack.length() != other.stack.length())) {
+ return false;
+ }
+
+ // See if the stacks are the same
+ for (int i = 0; i < stack.length(); ++i) {
+ if (stack[i] != other.stack[i]) {
+ return false;
+ }
+ }
+
+ // We failed to find a difference; they must be the same
+ return true;
+ }
+ }
+
+ /**
+ Pre increment.
+ */
+ BoxIntersectionIterator& operator++() {
+ ++nextValueArrayIndex;
+
+ bool foundIntersection = false;
+ while (! isEnd && ! foundIntersection) {
+
+ // Search for the next node if we've exhausted this one
+ while ((! isEnd) && (nextValueArrayIndex >= node->valueArray.length())) {
+ // If we entered this loop, then the iterator has exhausted the elements at
+ // node (possibly because it just switched to a child node with no members).
+ // This loop continues until it finds a node with members or reaches
+ // the end of the whole intersection search.
+
+ // If the right child overlaps the box, push it onto the stack for
+ // processing.
+ if ((node->child[1] != NULL) &&
+ (box.high()[node->splitAxis] > node->splitLocation)) {
+ stack.push(node->child[1]);
+ }
+
+ // If the left child overlaps the box, push it onto the stack for
+ // processing.
+ if ((node->child[0] != NULL) &&
+ (box.low()[node->splitAxis] < node->splitLocation)) {
+ stack.push(node->child[0]);
+ }
+
+ if (stack.length() > 0) {
+ // Go on to the next node (which may be either one of the ones we
+ // just pushed, or one from farther back the tree).
+ node = stack.pop();
+ nextValueArrayIndex = 0;
+ } else {
+ // That was the last node; we're done iterating
+ isEnd = true;
+ }
+ }
+
+ // Search for the next intersection at this node until we run out of children
+ while (! isEnd && ! foundIntersection && (nextValueArrayIndex < node->valueArray.length())) {
+ if (box.intersects(node->valueArray[nextValueArrayIndex].bounds)) {
+ foundIntersection = true;
+ } else {
+ ++nextValueArrayIndex;
+ // If we exhaust this node, we'll loop around the master loop
+ // to find a new node.
+ }
+ }
+ }
+
+ return *this;
+ }
+
+ /**
+ Post increment (much slower than preincrement!).
+ */
+ BoxIntersectionIterator operator++(int) {
+ BoxIntersectionIterator old = *this;
+ ++this;
+ return old;
+ }
+
+ /** Overloaded dereference operator so the iterator can masquerade as a pointer
+ to a member */
+ const T& operator*() const {
+ alwaysAssertM(! isEnd, "Can't dereference the end element of an iterator");
+ return node->valueArray[nextValueArrayIndex].value;
+ }
+
+ /** Overloaded dereference operator so the iterator can masquerade as a pointer
+ to a member */
+ T const * operator->() const {
+ alwaysAssertM(! isEnd, "Can't dereference the end element of an iterator");
+ return &(stack.last()->valueArray[nextValueArrayIndex].value);
+ }
+
+ /** Overloaded cast operator so the iterator can masquerade as a pointer
+ to a member */
+ operator T*() const {
+ alwaysAssertM(! isEnd, "Can't dereference the end element of an iterator");
+ return &(stack.last()->valueArray[nextValueArrayIndex].value);
+ }
+ };
+
+
+ /**
+ Iterates through the members that intersect the box
+ */
+ BoxIntersectionIterator beginBoxIntersection(const AABox& box) const {
+ return BoxIntersectionIterator(box, root);
+ }
+
+ BoxIntersectionIterator endBoxIntersection() const {
+ // The "end" iterator instance
+ return BoxIntersectionIterator();
+ }
+
+ /**
+ Appends all members whose bounds intersect the box.
+ See also PointKDTree::beginBoxIntersection.
+ */
+ void getIntersectingMembers(const AABox& box, Array<T>& members) const {
+ if (root == NULL) {
+ return;
+ }
+ root->getIntersectingMembers(box, Sphere(Vector3::zero(), 0), members, false);
+ }
+
+
+ /**
+ @param members The results are appended to this array.
+ */
+ void getIntersectingMembers(const Sphere& sphere, Array<T>& members) const {
+ if (root == NULL) {
+ return;
+ }
+
+ AABox box;
+ sphere.getBounds(box);
+ root->getIntersectingMembers(box, sphere, members);
+
+ }
+
+
+ /**
+ Stores the locations of the splitting planes (the structure but not the content)
+ so that the tree can be quickly rebuilt from a previous configuration without
+ calling balance.
+ */
+ void serializeStructure(BinaryOutput& bo) const {
+ Node::serializeStructure(root, bo);
+ }
+
+ /** Clears the member table */
+ void deserializeStructure(BinaryInput& bi) {
+ clear();
+ root = Node::deserializeStructure(bi);
+ }
+
+ /**
+ Returns an array of all members of the set. See also PointKDTree::begin.
+ */
+ void getMembers(Array<T>& members) const {
+ memberTable.getKeys(members);
+ }
+
+
+ /**
+ C++ STL style iterator variable. See begin().
+ Overloads the -> (dereference) operator, so this acts like a pointer
+ to the current member.
+ */
+ class Iterator {
+ private:
+ friend class TreeType;
+
+ // Note: this is a Table iterator, we are currently defining
+ // Set iterator
+ typename MemberTable::Iterator it;
+
+ Iterator(const typename MemberTable::Iterator& it) : it(it) {}
+
+ public:
+ inline bool operator!=(const Iterator& other) const {
+ return !(*this == other);
+ }
+
+ bool operator==(const Iterator& other) const {
+ return it == other.it;
+ }
+
+ /**
+ Pre increment.
+ */
+ Iterator& operator++() {
+ ++it;
+ return *this;
+ }
+
+ /**
+ Post increment (slower than preincrement).
+ */
+ Iterator operator++(int) {
+ Iterator old = *this;
+ ++(*this);
+ return old;
+ }
+
+ const T& operator*() const {
+ return it->key;
+ }
+
+ T* operator->() const {
+ return &(it->key);
+ }
+
+ operator T*() const {
+ return &(it->key);
+ }
+ };
+
+
+ /**
+ C++ STL style iterator method. Returns the first member.
+ Use preincrement (++entry) to get to the next element (iteration
+ order is arbitrary).
+ Do not modify the set while iterating.
+ */
+ Iterator begin() const {
+ return Iterator(memberTable.begin());
+ }
+
+
+ /**
+ C++ STL style iterator method. Returns one after the last iterator
+ element.
+ */
+ Iterator end() const {
+ return Iterator(memberTable.end());
+ }
+#undef TreeType
+};
+
+#define PointAABSPTree PointKDTree
+
+}
+
+#endif
diff --git a/externals/g3dlite/G3D.lib/include/G3D/PointHashGrid.h b/externals/g3dlite/G3D.lib/include/G3D/PointHashGrid.h
new file mode 100644
index 00000000000..8e9726e82da
--- /dev/null
+++ b/externals/g3dlite/G3D.lib/include/G3D/PointHashGrid.h
@@ -0,0 +1,894 @@
+/**
+ @file PointHashGrid.h
+
+ @maintainer Morgan McGuire, morgan@cs.williams.edu
+ @created 2008-07-01
+ @edited 2008-11-02
+
+ Copyright 2000-2008, Morgan McGuire.
+ All rights reserved.
+*/
+#ifndef G3D_POINTHASHGRID_H
+#define G3D_POINTHASHGRID_H
+
+#include "G3D/platform.h"
+#include "G3D/EqualsTrait.h"
+#include "G3D/HashTrait.h"
+#include "G3D/Vector3.h"
+#include "G3D/Vector3int32.h"
+#include "G3D/Array.h"
+#include "G3D/Table.h"
+#include "G3D/AABox.h"
+#include "G3D/Sphere.h"
+
+namespace G3D {
+
+/**
+ Storage of data in a sparse 3D grid of point-based data. The
+ space cost for <I>n</I> elements is O(<I>n</I>). For data with
+ approximately uniform density (with respect to the radius hint),
+ the time cost of searching for neighbors is O(1).
+
+ <i>Value</i> must be supported by a G3D::PositionTrait,
+ G3D::EqualsTrait, and G3D::HashFunc. overrides are provided for
+ common G3D classes like G3D::Vector3.
+*/
+template<class Value,
+ class PosFunc = PositionTrait<Value>,
+ class EqualsFunc = EqualsTrait<Value>,
+ class HashFunc = HashTrait<Vector3int32> >
+class PointHashGrid {
+private:
+
+#define ThisType PointHashGrid<Value, PosFunc, EqualsFunc, HashFunc>
+
+ /** A value annotated with precomputed position and hash code.*/
+ class Entry {
+ public:
+ Vector3 position;
+ Value value;
+ };
+
+ /** One cell of the grid. */
+ typedef Array<Entry> Cell;
+ typedef Table<Vector3int32, Cell, HashFunc> CellTable;
+
+ /** The cube of +/-1 along each dimension. Initialized by initOffsetArray.*/
+ Vector3int32 m_offsetArray[3*3*3];
+
+ /** Incremented every time the data structure is mutated.
+ Used by the iterators to determine if the data structure
+ has changed since iteration began. */
+ int m_epoch;
+
+ /** Extent of a cell along one dimension. */
+ float m_cellWidth;
+
+ /** Conservative bounds; the actual data may be smaller. */
+ AABox m_bounds;
+
+ /** Number of elements. */
+ int m_size;
+
+ /** Non-empty cells indexed by grid position. Actual 3D position is
+ <code>position * m_cellWidth</code>*/
+ CellTable m_data;
+
+
+ /** Intentionally unimplemented: prevent copy construction. */
+ PointHashGrid(const ThisType&);
+
+
+ /** Intentionally unimplemented: prevent assignment. */
+ PointHashGrid& operator=(const ThisType&);
+
+
+ /** Locate the cell and index within that cell containing v. Called by
+ remove() and contains(). */
+ bool find(
+ const Value& v,
+ Vector3int32& foundCellCoord,
+ Cell*& foundCell,
+ int& index) {
+
+ Vector3 pos;
+ PosFunc::getPosition(v, pos);
+
+ Vector3int32 cellCoord;
+ getCellCoord(pos, cellCoord);
+ for (int i = 0; i < 27; ++i) {
+ Vector3int32 c = cellCoord + m_offsetArray[i];
+ Cell* cell = m_data.getPointer(c);
+ if (cell != NULL) {
+ // The cell exists
+ for (int j = 0; j < cell->size(); ++j) {
+ if (EqualsFunc::equals((*cell)[j].value, v)) {
+ foundCell = cell;
+ index = j;
+ foundCellCoord = c;
+ return true;
+ }
+ }
+ }
+ }
+
+ // Not found
+ return false;
+ }
+
+ /** Given a real-space position, returns the cell coord
+ containing it.*/
+ void getCellCoord(const Vector3& pos, Vector3int32& cellCoord) const {
+ for (int a = 0; a < 3; ++a) {
+ cellCoord[a] = iFloor(pos[a] / m_cellWidth);
+ }
+ }
+
+ /** Initializes m_offsetArray. */
+ void initOffsetArray() {
+ int i = 0;
+ Vector3int32 d;
+ for (d.x = -1; d.x <= +1; ++d.x) {
+ for (d.y = -1; d.y <= +1; ++d.y) {
+ for (d.z = -1; d.z <= +1; ++d.z) {
+ m_offsetArray[i] = d;
+ ++i;
+ }
+ }
+ }
+
+ // Put (0, 0, 0) first, so that contains() is most likely to find
+ // the value quickly.
+ i = (1 * 3 + 1) * 3 + 1;
+ debugAssert(m_offsetArray[i] == Vector3int32(0,0,0));
+ Vector3int32 temp = m_offsetArray[0];
+ m_offsetArray[0] = m_offsetArray[i];
+ m_offsetArray[i] = temp;
+ }
+
+public:
+
+ /**
+ @param radiusHint the radius that will typically be used with
+ beginSphereIntersection and beginBoxIntersection. If two <i>Value</i>s are equal,
+ their positions must be within this radius as well.
+ */
+ PointHashGrid(float radiusHint) : m_size(0) {
+ initOffsetArray();
+
+ debugAssertM(radiusHint > 0, "Cell radius must be positive");
+ m_cellWidth = radiusHint;
+ }
+
+ /**
+ If radiusHint is negative, it is automatically chosen to put
+ about 5 values in each grid cell (which means about 27 * 5
+ values for each beginIntersection call).
+ */
+ PointHashGrid(const Array<Value>& init, float radiusHint = -1.0f) : m_size(0) {
+ initOffsetArray();
+
+ Vector3 lo(Vector3::inf());
+ Vector3 hi(-lo);
+
+ // Compute bounds
+ Array<Entry> entry(init.size());
+ for (int i = 0; i < entry.size(); ++i) {
+ const Value& value = init[i];
+ Vector3 pos = m_posFunc(value);
+
+ entry[i].value = value;
+ entry[i].hashCode = m_hashFunc(value);
+ entry[i].position = pos;
+
+ lo = lo.min(pos);
+ hi = hi.max(pos);
+ }
+
+ m_bounds = AABox(lo, hi);
+
+ if (radiusHint <= 0) {
+ // Compute a good cell width based on the bounds.
+ //
+ // N numPerCell
+ // ----- = ---------
+ // volume r^3
+
+ float numPerCell = 5;
+ radiusHint =
+ (float)pow(numPerCell * m_bounds.volume() / init.size(), 1.0 / 3.0);
+
+ if (radiusHint == 0) {
+ // Volume must have been zero because all points were colocated.
+ radiusHint = 0.1f;
+ }
+ }
+
+ insert(init);
+ }
+
+ /** Returns the number of elements. */
+ inline int size() const {
+ return m_size;
+ }
+
+ /** Returns a conservative bounding box around the contents. This is
+ conservative because it is not updated when elements are removed. */
+ const AABox& conservativeBoxBounds() const {
+ return m_bounds;
+ }
+
+ /** Insert @a v at position @a p given by <code>getPosition(v, p)</code>.
+ Multiple elements that are equal may be inserted; all copies will be
+ in the data structure. */
+ void insert(const Value& v) {
+ Vector3 pos;
+ PosFunc::getPosition(v, pos);
+ Vector3int32 cellCoord;
+ getCellCoord(pos, cellCoord);
+
+ // See if the cell already exists
+ Cell* cell = m_data.getPointer(cellCoord);
+
+ if (cell == NULL) {
+ // The cell did not exist; create it
+ m_data.set(cellCoord, Cell());
+ cell = m_data.getPointer(cellCoord);
+ }
+ debugAssert(cell != NULL);
+
+ Entry& entry = cell->next();
+ entry.value = v;
+ entry.position = pos;
+
+ // Update the bounds
+ if (size() == 0) {
+ m_bounds = AABox(pos);
+ } else {
+ m_bounds.merge(pos);
+ }
+
+ ++m_size;
+ ++m_epoch;
+ }
+
+
+ /** Inserts all elements of the array. */
+ void insert(const Array<Value>& v) {
+ for (int i = 0; i < v.size(); ++i) {
+ insert(v[i]);
+ }
+ }
+
+
+ /** If there are multiple copies of an element, you must
+ delete them multiple times.
+
+ @param shrinkAsNeeded If <b>true</b>, deallocate underlying data
+ structures as they are emptied. False increases performace at
+ the cost of memory overhead for dynamic structures.
+
+ @return true if the element was found.
+ */
+ bool remove(const Value& v, bool shrinkIfNecessary = true) {
+ Cell* cell = NULL;
+ int index = 0;
+ Vector3int32 cellCoord;
+
+ if (find(v, cellCoord, cell, index)) {
+ cell->fastRemove(index, shrinkIfNecessary);
+ --m_size;
+ ++m_epoch;
+
+ if ((cell->size() == 0) && shrinkIfNecessary) {
+ // Remove the cell itself
+
+ // Drop our pointer, which is about to dangle
+ cell = NULL;
+ bool success = m_data.remove(cellCoord);
+ debugAssertM(success, "Data structure corrupt: "
+ "tried to remove a cell that doesn't exist.");
+ }
+
+ return true;
+
+ } else {
+ return false;
+ }
+ }
+
+ /** Removes all elements of @v. */
+ void remove(const Array<Value>& v, bool shrink = true) {
+ for (int i = 0; i < v.size(); ++i) {
+ remove(v[i], shrink);
+ }
+ }
+
+ ///////////////////////////////////////////////////////////////////////////
+ ///////////////////////////////////////////////////////////////////////////
+
+ class Iterator {
+ private:
+ friend class ThisType;
+
+ bool m_isEnd;
+
+ const ThisType* m_grid;
+
+ typename CellTable::Iterator m_tableIterator;
+
+ /** Index within m_tableIterator->value of the current value. */
+ int m_arrayIndex;
+
+ const int m_epoch;
+
+ /** End iterator. Note that the m_tableIterator is initialized to the end iterator
+ of a temporary value! This is ok because we'll never look at the value of the
+ m_tableIterator, since we're initializing the "end" Iterator.*/
+ Iterator() : m_isEnd(true), m_grid(NULL), m_tableIterator(CellTable().end()),
+ m_arrayIndex(0), m_epoch(0) {}
+
+ Iterator(const ThisType* grid) :
+ m_isEnd(false),
+ m_grid(grid),
+ m_tableIterator( grid->m_data.begin() ),
+ m_arrayIndex(0),
+ m_epoch(grid->m_epoch) { }
+
+ private:
+
+ const Value& value() const {
+ debugAssert(! m_isEnd);
+ debugAssertM(m_tableIterator->value.size() > m_arrayIndex,
+ "No more elements");
+ return m_tableIterator->value[m_arrayIndex].value;
+ }
+
+ public:
+
+ inline bool operator!=(const Iterator& other) const {
+ if (other.m_isEnd && m_isEnd) {
+ return false;
+ } else {
+ return (m_isEnd != other.m_isEnd) ||
+ (m_tableIterator != other.m_tableIterator) ||
+ (m_arrayIndex != other.m_arrayIndex);
+ }
+ }
+
+ bool operator==(const Iterator& other) const {
+ return !(*this != other);
+ }
+
+ /** Preincrement */
+ Iterator& operator++() {
+ debugAssert(! m_isEnd);
+ debugAssertM(m_epoch == m_grid->m_epoch,
+ "It is illegal to mutate the HashGrid "
+ "while iterating through it.");
+
+ ++m_arrayIndex;
+
+ if (m_arrayIndex >= m_tableIterator->value.size()) {
+ // Move on to the next cell
+ ++m_tableIterator;
+ m_arrayIndex = 0;
+
+ // Check to see if we're at the end
+ m_isEnd = (m_tableIterator == m_grid->m_data.end());
+ }
+
+ return *this;
+ }
+
+ /** Post increment (slower) */
+ Iterator operator++(int) {
+ debugAssert(! m_isEnd);
+ Iterator old = *this;
+ ++(*this);
+ return old;
+ }
+
+ const Value& operator*() const { return value(); }
+ const Value* operator->() const { return &value(); }
+ operator Value*() const { return &value(); }
+ }; // Iterator
+
+
+ /** Iterate through all members. It is an error to mutate the HashGrid
+ while iterating through it. Each member can be accessed by "dereferencing"
+ the iterator:
+
+ <pre>
+ for (Grid::Iterator i = grid.begin(); i != grid.end(), ++i) {
+ const Value& = *i;
+ ...
+ }
+ </pre>
+ */
+ Iterator begin() const {
+ return Iterator(this);
+ }
+
+ const Iterator& end() const {
+ static const Iterator it;
+ return it;
+ }
+
+ ///////////////////////////////////////////////////////////////////////////
+ ///////////////////////////////////////////////////////////////////////////
+
+ // Forward declaration required by older gcc versions for friend declaration in BoxIterator
+ class SphereIterator;
+ class BoxIterator {
+ private:
+ friend class ThisType;
+ friend class SphereIterator;
+
+ bool m_isEnd;
+
+ const ThisType* m_grid;
+
+ /** Lower bound on the boxes covered, inclusive. */
+ Vector3int32 m_lo;
+
+ /** Upper bound on the boxes covered, inclusive.*/
+ Vector3int32 m_hi;
+
+ /** If true, test values against m_box before returning them.*/
+ bool m_exact;
+
+ /** The underlying box in 3D space */
+ AABox m_box;
+
+ /** The iterator winds through the 3D grid between m_lo and (m_lo + m_extent) in
+ Z,Y,X-major order. This is the index keeping track of how
+ far it has come */
+ Vector3int32 m_current;
+
+ /** The current cell. */
+ Cell* m_cell;
+
+ /** Index within m_cell of the current value */
+ int m_arrayIndex;
+
+ const int m_epoch;
+
+
+ /** Called from advance() */
+ void advanceCell() {
+ do {
+ ++m_current.x;
+ if (m_current.x > m_hi.x) {
+ m_current.x = m_lo.x;
+ ++m_current.y;
+ if (m_current.y > m_hi.y) {
+ m_current.y = m_lo.y;
+ ++m_current.z;
+ if (m_current.z > m_hi.z) {
+ m_isEnd = true;
+ return;
+ }
+ }
+ }
+
+ // Pick up the new cell
+ m_cell = m_grid->m_data.getPointer(m_current);
+ // Keep advancing if the cell does not exist
+ } while ((m_cell == NULL) || (m_cell->size() == 0));
+ }
+
+ /** Advance to the next value */
+ void advance() {
+ debugAssert(! m_isEnd);
+
+ do {
+ ++m_arrayIndex;
+ bool inConstructor = (m_cell == NULL);
+ if (inConstructor || m_arrayIndex >= m_cell->size()) {
+ advanceCell();
+ m_arrayIndex = 0;
+
+ if (m_isEnd) {
+ // Ran out of values
+ return;
+ }
+ debugAssert(m_cell != NULL);
+ }
+
+ // Advance until we have a value that can be returned, either
+ // because we don't care about exactness or because it is
+ // guaranteed to be within the box.
+ } while (m_exact && ! m_box.contains(position()));
+ }
+
+
+ /** End iterator */
+ BoxIterator() : m_isEnd(true), m_grid(NULL), m_exact(true), m_current(0,0,0), m_cell(NULL), m_arrayIndex(0), m_epoch(0) {}
+
+ /** Begin iterator */
+ BoxIterator(const ThisType* grid, bool exact, const AABox& box) :
+ m_isEnd(false),
+ m_grid(grid),
+ m_exact(exact),
+ m_box(box),
+ m_current(-1, 0 ,0),
+ m_cell(NULL),
+ m_arrayIndex(0),
+ m_epoch(grid->m_epoch) {
+
+ m_grid->getCellCoord(box.low(), m_lo);
+ m_grid->getCellCoord(box.high(), m_hi);
+
+ // Get to the first value
+ m_current = m_lo;
+ // Back up one so that advancing takes us to the first
+ --m_current.x;
+ advance();
+ }
+
+ const Value& value() const {
+ debugAssert(! m_isEnd);
+ return (*m_cell)[m_arrayIndex].value;
+ }
+
+ /** Used by SphereIterator::advance() */
+ const Vector3& position() const {
+ debugAssert(! m_isEnd);
+ return (*m_cell)[m_arrayIndex].position;
+ }
+
+ public:
+
+ inline bool operator!=(const BoxIterator& other) const {
+ if (other.m_isEnd && m_isEnd) {
+ return false;
+ } else {
+ return (m_isEnd != other.m_isEnd) ||
+ (m_cell != other.m_cell) ||
+ (m_arrayIndex != other.m_arrayIndex);
+ }
+ }
+
+ bool operator==(const BoxIterator& other) const {
+ return !(*this != other);
+ }
+
+ /** Preincrement */
+ BoxIterator& operator++() {
+ debugAssert(! m_isEnd);
+ debugAssertM(m_epoch == m_grid->m_epoch,
+ "It is illegal to mutate the HashGrid "
+ "while iterating through it.");
+
+ advance();
+
+ return *this;
+ }
+
+ /** Post increment (slower) */
+ BoxIterator operator++(int) {
+ Iterator old = *this;
+ ++(*this);
+ return old;
+ }
+
+ const Value& operator*() const { return value(); }
+ const Value* operator->() const { return &value(); }
+ operator Value*() const { return &value(); }
+
+ bool hasMore() const {
+ return ! m_isEnd;
+ }
+ }; // BoxIterator
+
+ /**
+ Finds all values whose positions are within @a box. It is an error to
+ mutate the PointHashGrid while iterating through it.
+
+ @param exact If false, the iterator will execute more quickly but will likely return some
+ values that lie outside the box. Set exact = false if you are going to test the
+ results against the yourself box anyway.
+ */
+ BoxIterator beginBoxIntersection(const AABox& box, bool exact = true) const {
+ return BoxIterator(this, exact, box);
+ }
+
+ const BoxIterator& endBoxIntersection() const {
+ static const BoxIterator it;
+ return it;
+ }
+
+ ///////////////////////////////////////////////////////////////////////////
+ ///////////////////////////////////////////////////////////////////////////
+
+ class SphereIterator {
+ private:
+
+ friend class ThisType;
+
+ bool m_isEnd;
+ Sphere m_sphere;
+ BoxIterator m_boxIterator;
+
+ SphereIterator() : m_isEnd(true) {}
+
+ void advance() {
+ if (! m_boxIterator.hasMore()) {
+ m_isEnd = true;
+ return;
+ }
+
+ while (! m_sphere.contains(m_boxIterator.position())) {
+ ++m_boxIterator;
+
+ if (! m_boxIterator.hasMore()) {
+ m_isEnd = true;
+ return;
+ }
+ }
+ }
+
+ static AABox getBoundingBox(const Sphere& s) {
+ AABox box;
+ s.getBounds(box);
+ return box;
+ }
+
+ SphereIterator(const ThisType* grid, const Sphere& sphere) :
+ m_isEnd(false),
+ m_sphere(sphere),
+ m_boxIterator(grid, false, getBoundingBox(sphere)) {
+
+ // Find the first element that is actually in the sphere,
+ // not just the box.
+ advance();
+ }
+
+ const Value& value() const {
+ return *m_boxIterator;
+ }
+
+ // TODO: if the sphere is very big compared to radius, check each
+ // cell's box to see if the cell itself is actually inside the sphere
+ // before iterating through it, since there may be many boxes outside the sphere.
+
+ public:
+
+ inline bool operator!=(const SphereIterator& other) const {
+ if (other.m_isEnd && m_isEnd) {
+ return false;
+ } else {
+ return
+ (m_isEnd != other.m_isEnd) ||
+ (m_sphere != other.m_sphere) ||
+ (m_boxIterator != other.m_boxIterator);
+ }
+ }
+
+ bool operator==(const SphereIterator& other) const {
+ return !(*this != other);
+ }
+
+
+
+ /** Preincrement */
+ SphereIterator& operator++() {
+ debugAssert(! m_isEnd);
+
+ ++m_boxIterator;
+ advance();
+
+ return *this;
+ }
+
+ /** Post increment (slower) */
+ SphereIterator operator++(int) {
+ Iterator old = *this;
+ ++(*this);
+ return old;
+ }
+
+ const Value& operator*() const { return value(); }
+ const Value* operator->() const { return &value(); }
+ operator Value*() const { return &value(); }
+
+ bool hasMore() const {
+ return ! m_isEnd;
+ }
+ }; // SphereIterator
+
+ /**
+ Finds all values whose positions are within @a sphere. It is an error
+ to mutate the HashGrid while iterating through it.
+ */
+ SphereIterator beginSphereIntersection(const Sphere& sphere) const {
+ return SphereIterator(this, sphere);
+ }
+
+ const SphereIterator& endSphereIntersection() const {
+ static const SphereIterator it;
+ return it;
+ }
+
+ ///////////////////////////////////////////////////////////////////////////
+ ///////////////////////////////////////////////////////////////////////////
+
+ /**
+ Dereference to access the bounds() and size() [element count] of the underlying
+ cell objet.
+
+ Example:
+ <pre>
+ for(PointHashGrid<Vector3>::CellIterator iter = grid.beginCells(); iter != grid.endCells(); ++iter) {
+ entriesFound += iter->size();
+ }
+ */
+ class CellIterator {
+ private:
+ friend class ThisType;
+
+ bool m_isEnd;
+ const ThisType* m_grid;
+ typename CellTable::Iterator m_tableIterator;
+ const int m_epoch;
+
+
+ Cell& cell() {
+ return m_tableIterator->value;
+ }
+
+ public:
+
+ class CellObject {
+ friend class CellIterator;
+ private:
+ const CellIterator* m_parent;
+
+ CellObject() : m_parent(NULL) {}
+
+ public:
+
+ /** Returns the bounds on this cell */
+ AABox bounds() const {
+ const Vector3int32& k = m_parent->m_tableIterator->key;
+ return AABox(Vector3(k) * m_parent->m_cellWidth,
+ Vector3(k + Vector3int32(1, 1, 1)) * m_parent->m_cellWidth);
+ }
+
+ /** Number of elements inside this cell */
+ int size() const {
+ debugAssert(! m_parent->m_isEnd);
+ return m_parent->m_tableIterator->value.size();
+ }
+ };
+
+ private:
+ /** Used to make the indirection work.*/
+ CellObject m_indirection;
+
+ /** End iterator. Note that the m_tableIterator is initialized to the end iterator
+ of a temporary value! This is ok because we'll never look at the value of the
+ m_tableIterator, since we're initializing the "end" Iterator.*/
+ CellIterator() :
+ m_isEnd(true),
+ m_grid(NULL),
+ m_tableIterator( CellTable().end() ),
+ m_epoch(0) {}
+
+ CellIterator(const ThisType* grid) :
+ m_isEnd(false),
+ m_grid(grid),
+ m_tableIterator( grid->m_data.begin()),
+ m_epoch(grid->m_epoch) {
+ m_indirection.m_parent = this;
+ m_isEnd = ! m_tableIterator.hasMore();
+ }
+
+ public:
+
+ const CellObject& operator*() const { return m_indirection; }
+ const CellObject* operator->() const { return &m_indirection; }
+ operator CellObject*() const { return &m_indirection; }
+
+ inline bool operator!=(const CellIterator& other) const {
+ // != is called more often than == during iteration
+ return !(
+ (m_isEnd && other.m_isEnd) ||
+ ((m_isEnd == other.m_isEnd) &&
+ (m_tableIterator != other.m_tableIterator)));
+ }
+
+ bool operator==(const CellIterator& other) const {
+ return !(*this != other);
+ }
+
+ /** Preincrement */
+ CellIterator& operator++() {
+ debugAssertM(m_epoch == m_grid->m_epoch,
+ "It is illegal to mutate the HashGrid while "
+ "iterating through it.");
+ ++m_tableIterator;
+ m_isEnd = ! m_tableIterator.hasMore();
+ return *this;
+ }
+
+ /** Post increment (slower) */
+ CellIterator operator++(int) {
+ Iterator old = *this;
+ ++(*this);
+ return old;
+ }
+
+ bool hasMore() const {
+ return ! m_isEnd;
+ }
+ }; // CellIterator
+
+ /** Iterates through the non-empty cells. This is intended primarily for
+ debugging and visualizing the data structure.*/
+ CellIterator beginCells() const {
+ return CellIterator(this);
+ }
+
+ const CellIterator& endCells() const {
+ static const CellIterator it;
+ return it;
+ }
+
+ ///////////////////////////////////////////////////////////////////////////
+ ///////////////////////////////////////////////////////////////////////////
+
+ /** Returns true if there is a value that is exactly equal to @a. This will
+ check all neighboring cells to avoid roundoff error at cell boundaries.
+ */
+ bool contains(const Value& v) const {
+ Cell* cell = NULL;
+ int index = 0;
+ Vector3int32 cellCoord;
+ return const_cast<ThisType*>(this)->find(v, cellCoord, cell, index);
+ }
+
+ /** Calls delete on all of the values, which are assumed to be pointers.
+ This is a helper to avoid requiring you to iterate through the data
+ structure, removing and deleting each one. Clears the PointHashGrid at the
+ end.
+
+ Using objects (instead of pointers) or reference counted pointers is
+ recommended over using pointers and this deleteAll method.*/
+ void deleteAll() {
+ for (Iterator it = begin(); it.hasMore(); ++it) {
+ delete *it;
+ }
+ clear();
+ }
+
+ /** Removes all data.
+ @param shrink If true, underlying structures are deallocated as
+ they are freed.*/
+ void clear(bool shrink = true) {
+ m_size = 0;
+ m_bounds = AABox();
+ if (! shrink) {
+ // Remove all data
+ for (CellIterator it = beginCells(); it.hasMore(); ++it) {
+ it.cell().clear(true);
+ }
+ } else {
+ m_data.clear();
+ }
+ ++m_epoch;
+ }
+
+ int debugGetDeepestBucketSize() const {
+ return m_data.debugGetDeepestBucketSize();
+ }
+
+ float debugGetAverageBucketSize() const {
+ return m_data.debugGetAverageBucketSize();
+ }
+#undef ThisType
+};
+
+} // G3D
+#endif
diff --git a/externals/g3dlite/G3D.lib/include/G3D/Pointer.h b/externals/g3dlite/G3D.lib/include/G3D/Pointer.h
new file mode 100644
index 00000000000..2b80187bae5
--- /dev/null
+++ b/externals/g3dlite/G3D.lib/include/G3D/Pointer.h
@@ -0,0 +1,275 @@
+/**
+ @file Pointer.h
+
+ @maintainer Morgan McGuire, graphics3d.com
+
+ @created 2007-05-16
+ @edited 2007-06-22
+
+ Copyright 2000-2007, Morgan McGuire.
+ All rights reserved.
+ */
+#ifndef G3D_POINTER_H
+#define G3D_POINTER_H
+
+#include "G3D/debugAssert.h"
+#include "G3D/ReferenceCount.h"
+
+namespace G3D {
+
+/**
+ Acts like a pointer to a value of type ValueType (i.e.,
+ ValueType*), but can operate through accessor methods as well as on
+ a value in memory. This is useful for implementing scripting
+ languages and other applications that need to connect existing APIs
+ by reference.
+
+ Because the accessors require values to be passed by value (instead of by reference)
+ this is primarily useful for objects whose memory size is small.
+
+ <pre>
+ class Foo {
+ public:
+ void setEnabled(bool b);
+ bool getEnabled() const;
+ };
+
+ Foo f;
+ bool b;
+
+ Pointer<bool> p1(&b);
+ Pointer<bool> p2(&f, &Foo::getEnabled, &Foo::setEnabled);
+
+ *p1 = true;
+ *p2 = false;
+ *p2 = *p1; \/\/ Value assignment
+ p2 = p1; \/\/ Pointer aliasing
+
+ \/\/ Or, equivalently:
+ p1.setValue(true);
+ p2.setValue(false);
+
+ p2.setValue(p1.getValue());
+ p2 = p1;
+ </pre>
+
+ <i>Note:</i> Because of the way that dereference is implemented, you cannot pass <code>*p</code> through a function
+ that takes varargs (...), e.g., <code>printf("%d", *p)</code> will produce a compile-time error. Instead use
+ <code>printf("%d",(bool)*p)</code> or <code>printf("%d", p.getValue())</code>.
+
+ */
+template<class ValueType>
+class Pointer {
+private:
+
+ class Interface {
+ public:
+ virtual ~Interface() {};
+ virtual void set(ValueType b) = 0;
+ virtual ValueType get() const = 0;
+ virtual Interface* clone() const = 0;
+ };
+
+ class Memory : public Interface {
+ private:
+
+ ValueType* value;
+
+ public:
+
+ Memory(ValueType* value) : value(value) {
+ debugAssert(value != NULL);
+ }
+
+ virtual void set(ValueType v) {
+ *value = v;
+ }
+
+ virtual ValueType get() const {
+ return *value;
+ }
+
+ virtual Interface* clone() const {
+ return new Memory(value);
+ }
+ };
+
+ template<class T, typename GetMethod, typename SetMethod>
+ class Accessor : public Interface {
+ private:
+
+ T* object;
+ GetMethod getMethod;
+ SetMethod setMethod;
+
+ public:
+
+ Accessor(T* object,
+ GetMethod getMethod,
+ SetMethod setMethod) : object(object), getMethod(getMethod), setMethod(setMethod) {
+ debugAssert(object != NULL);
+ }
+
+ virtual void set(ValueType v) {
+ (object->*setMethod)(v);
+ }
+
+ virtual ValueType get() const {
+ return (object->*getMethod)();
+ }
+
+ virtual Interface* clone() const {
+ return new Accessor(object, getMethod, setMethod);
+ }
+ };
+
+
+ template<class T, typename GetMethod, typename SetMethod>
+ class RefAccessor : public Interface {
+ private:
+
+ ReferenceCountedPointer<T> object;
+ GetMethod getMethod;
+ SetMethod setMethod;
+
+ public:
+
+ RefAccessor(
+ const ReferenceCountedPointer<T>& object,
+ GetMethod getMethod,
+ SetMethod setMethod) : object(object), getMethod(getMethod), setMethod(setMethod) {
+
+ debugAssert(object != NULL);
+ }
+
+ virtual void set(ValueType v) {
+ (object.pointer()->*setMethod)(v);
+ }
+
+ virtual ValueType get() const {
+ return (object.pointer()->*getMethod)();
+ }
+
+ virtual Interface* clone() const {
+ return new RefAccessor(object, getMethod, setMethod);
+ }
+ };
+
+
+ Interface* interface;
+
+public:
+
+ Pointer() : interface(NULL) {};
+
+ /** Allows implicit cast from real pointer */
+ Pointer(ValueType* v) : interface(new Memory(v)) {}
+
+ // Assignment
+ inline Pointer& operator=(const Pointer& r) {
+ delete interface;
+ if (r.interface != NULL) {
+ interface = r.interface->clone();
+ } else {
+ interface = NULL;
+ }
+ return this[0];
+ }
+
+ Pointer(const Pointer& p) : interface(NULL) {
+ this[0] = p;
+ }
+
+ template<class Class>
+ Pointer(const ReferenceCountedPointer<Class>& object,
+ ValueType (Class::*getMethod)() const,
+ void (Class::*setMethod)(ValueType)) :
+ interface(new RefAccessor<Class, ValueType (Class::*)() const, void (Class::*)(ValueType)>(object, getMethod, setMethod)) {}
+
+ template<class Class>
+ Pointer(const ReferenceCountedPointer<Class>& object,
+ const ValueType& (Class::*getMethod)() const,
+ void (Class::*setMethod)(ValueType)) :
+ interface(new RefAccessor<Class, const ValueType& (Class::*)() const, void (Class::*)(ValueType)>(object, getMethod, setMethod)) {}
+
+ template<class Class>
+ Pointer(const ReferenceCountedPointer<Class>& object,
+ ValueType (Class::*getMethod)() const,
+ void (Class::*setMethod)(const ValueType&)) :
+ interface(new RefAccessor<Class, ValueType (Class::*)() const, void (Class::*)(const ValueType&)>(object, getMethod, setMethod)) {}
+
+ template<class Class>
+ Pointer(const ReferenceCountedPointer<Class>& object,
+ const ValueType& (Class::*getMethod)() const,
+ void (Class::*setMethod)(const ValueType&)) :
+ interface(new RefAccessor<Class, const ValueType& (Class::*)() const, void (Class::*)(const ValueType&)>(object, getMethod, setMethod)) {}
+
+ template<class Class>
+ Pointer(Class* object,
+ const ValueType& (Class::*getMethod)() const,
+ void (Class::*setMethod)(const ValueType&)) :
+ interface(new Accessor<Class, const ValueType& (Class::*)() const, void (Class::*)(const ValueType&)>(object, getMethod, setMethod)) {}
+
+ template<class Class>
+ Pointer(Class* object,
+ ValueType (Class::*getMethod)() const,
+ void (Class::*setMethod)(const ValueType&)) :
+ interface(new Accessor<Class, ValueType (Class::*)() const, void (Class::*)(const ValueType&)>(object, getMethod, setMethod)) {}
+
+ template<class Class>
+ Pointer(Class* object,
+ const ValueType& (Class::*getMethod)() const,
+ void (Class::*setMethod)(ValueType)) :
+ interface(new Accessor<Class, const ValueType& (Class::*)() const, void (Class::*)(ValueType)>(object, getMethod, setMethod)) {}
+
+ template<class Class>
+ Pointer(Class* object,
+ ValueType (Class::*getMethod)() const,
+ void (Class::*setMethod)(ValueType)) :
+ interface(new Accessor<Class, ValueType (Class::*)() const, void (Class::*)(ValueType)>(object, getMethod, setMethod)) {}
+
+ ~Pointer() {
+ delete interface;
+ }
+
+ inline const ValueType getValue() const {
+ debugAssert(interface != NULL);
+ return interface->get();
+ }
+
+ inline void setValue(const ValueType& v) {
+ debugAssert(interface != NULL);
+ interface->set(v);
+ }
+
+ class IndirectValue {
+ private:
+
+ friend class Pointer;
+ Pointer* pointer;
+ IndirectValue(Pointer* p) : pointer(p) {}
+
+ public:
+
+ void operator=(const ValueType& v) {
+ pointer->setValue(v);
+ }
+
+ operator ValueType() const {
+ return pointer->getValue();
+ }
+
+ };
+
+ inline IndirectValue operator*() {
+ return IndirectValue(this);
+ }
+
+ inline const ValueType operator*() const {
+ return getValue();
+ }
+};
+
+}
+
+#endif
diff --git a/externals/g3dlite/G3D.lib/include/G3D/PositionTrait.h b/externals/g3dlite/G3D.lib/include/G3D/PositionTrait.h
new file mode 100644
index 00000000000..67a4f64138a
--- /dev/null
+++ b/externals/g3dlite/G3D.lib/include/G3D/PositionTrait.h
@@ -0,0 +1,7 @@
+#ifndef G3D_POSITIONTRAIT_H
+#define G3D_POSITIONTRAIT_H
+
+template<typename Value>
+struct PositionTrait{};
+
+#endif
diff --git a/externals/g3dlite/G3D.lib/include/G3D/Quat.h b/externals/g3dlite/G3D.lib/include/G3D/Quat.h
new file mode 100644
index 00000000000..d4b892623d8
--- /dev/null
+++ b/externals/g3dlite/G3D.lib/include/G3D/Quat.h
@@ -0,0 +1,725 @@
+/**
+ @file Quat.h
+
+ Quaternion
+
+ @maintainer Morgan McGuire, matrix@graphics3d.com
+
+ @created 2002-01-23
+ @edited 2006-05-10
+ */
+
+#ifndef G3D_QUAT_H
+#define G3D_QUAT_H
+
+#include "G3D/platform.h"
+#include "G3D/g3dmath.h"
+#include "G3D/Vector3.h"
+#include "G3D/Matrix3.h"
+#include <string>
+
+namespace G3D {
+
+/**
+ Unit quaternions are used in computer graphics to represent
+ rotation about an axis. Any 3x3 rotation matrix can
+ be stored as a quaternion.
+
+ A quaternion represents the sum of a real scalar and
+ an imaginary vector: ix + jy + kz + w. A unit quaternion
+ representing a rotation by A about axis v has the form
+ [sin(A/2)*v, cos(A/2)]. For a unit quaternion, q.conj() == q.inverse()
+ is a rotation by -A about v. -q is the same rotation as q
+ (negate both the axis and angle).
+
+ A non-unit quaterion q represents the same rotation as
+ q.unitize() (Dam98 pg 28).
+
+ Although quaternion-vector operations (eg. Quat + Vector3) are
+ well defined, they are not supported by this class because
+ they typically are bugs when they appear in code.
+
+ Do not subclass.
+
+ <B>BETA API -- subject to change</B>
+ @cite Erik B. Dam, Martin Koch, Martin Lillholm, Quaternions, Interpolation and Animation. Technical Report DIKU-TR-98/5, Department of Computer Science, University of Copenhagen, Denmark. 1998.
+ */
+class Quat {
+private:
+ // Hidden operators
+ bool operator<(const Quat&) const;
+ bool operator>(const Quat&) const;
+ bool operator<=(const Quat&) const;
+ bool operator>=(const Quat&) const;
+
+public:
+
+ /**
+ q = [sin(angle / 2) * axis, cos(angle / 2)]
+
+ In Watt & Watt's notation, s = w, v = (x, y, z)
+ In the Real-Time Rendering notation, u = (x, y, z), w = w
+ */
+ float x, y, z, w;
+
+ /**
+ Initializes to a zero degree rotation.
+ */
+ inline Quat() : x(0), y(0), z(0), w(1) {}
+
+ Quat(
+ const Matrix3& rot);
+
+ inline Quat(float _x, float _y, float _z, float _w) :
+ x(_x), y(_y), z(_z), w(_w) {}
+
+ /** Defaults to a pure vector quaternion */
+ inline Quat(const Vector3& v, float _w = 0) : x(v.x), y(v.y), z(v.z), w(_w) {
+ }
+
+ /**
+ The real part of the quaternion.
+ */
+ inline const float& real() const {
+ return w;
+ }
+
+ inline float& real() {
+ return w;
+ }
+
+ /** Note: two quats can represent the Quat::sameRotation and not be equal. */
+ bool fuzzyEq(const Quat& q) {
+ return G3D::fuzzyEq(x, q.x) && G3D::fuzzyEq(y, q.y) && G3D::fuzzyEq(z, q.z) && G3D::fuzzyEq(w, q.w);
+ }
+
+ /** True if these quaternions represent the same rotation (note that every rotation is
+ represented by two values; q and -q).
+ */
+ bool sameRotation(const Quat& q) {
+ return fuzzyEq(q) || fuzzyEq(-q);
+ }
+
+ inline Quat operator-() const {
+ return Quat(-x, -y, -z, -w);
+ }
+
+ /**
+ Returns the imaginary part (x, y, z)
+ */
+ inline const Vector3& imag() const {
+ return *(reinterpret_cast<const Vector3*>(this));
+ }
+
+ inline Vector3& imag() {
+ return *(reinterpret_cast<Vector3*>(this));
+ }
+
+ /** q = [sin(angle/2)*axis, cos(angle/2)] */
+ static Quat fromAxisAngleRotation(
+ const Vector3& axis,
+ float angle);
+
+ /** Returns the axis and angle of rotation represented
+ by this quaternion (i.e. q = [sin(angle/2)*axis, cos(angle/2)]) */
+ void toAxisAngleRotation(
+ Vector3& axis,
+ double& angle) const;
+
+ void toAxisAngleRotation(
+ Vector3& axis,
+ float& angle) const {
+ double d;
+ toAxisAngleRotation(axis, d);
+ angle = (float)d;
+ }
+
+ Matrix3 toRotationMatrix() const;
+
+ void toRotationMatrix(
+ Matrix3& rot) const;
+
+ /**
+ Spherical linear interpolation: linear interpolation along the
+ shortest (3D) great-circle route between two quaternions.
+
+ Note: Correct rotations are expected between 0 and PI in the right order.
+
+ @cite Based on Game Physics -- David Eberly pg 538-540
+ @param threshold Critical angle between between rotations at which
+ the algorithm switches to normalized lerp, which is more
+ numerically stable in those situations. 0.0 will always slerp.
+ */
+ Quat slerp(
+ const Quat& other,
+ float alpha,
+ float threshold = 0.05f) const;
+
+ /** Normalized linear interpolation of quaternion components. */
+ Quat nlerp(const Quat& other, float alpha) const;
+
+ /**
+ Negates the imaginary part.
+ */
+ inline Quat conj() const {
+ return Quat(-x, -y, -z, w);
+ }
+
+ inline float sum() const {
+ return x + y + z + w;
+ }
+
+ inline float average() const {
+ return sum() / 4.0f;
+ }
+
+ inline Quat operator*(float s) const {
+ return Quat(x * s, y * s, z * s, w * s);
+ }
+
+ inline Quat& operator*=(float s) {
+ x *= s;
+ y *= s;
+ z *= s;
+ w *= s;
+ return *this;
+ }
+
+ /** @cite Based on Watt & Watt, page 360 */
+ friend Quat operator* (float s, const Quat& q);
+
+ inline Quat operator/(float s) const {
+ return Quat(x / s, y / s, z / s, w / s);
+ }
+
+ inline float dot(const Quat& other) const {
+ return (x * other.x) + (y * other.y) + (z * other.z) + (w * other.w);
+ }
+
+ /** Note that q<SUP>-1</SUP> = q.conj() for a unit quaternion.
+ @cite Dam99 page 13 */
+ inline Quat inverse() const {
+ return conj() / dot(*this);
+ }
+
+ Quat operator-(const Quat& other) const;
+
+ Quat operator+(const Quat& other) const;
+
+ /**
+ Quaternion multiplication (composition of rotations).
+ Note that this does not commute.
+ */
+ Quat operator*(const Quat& other) const;
+
+ /* (*this) * other.inverse() */
+ Quat operator/(const Quat& other) const {
+ return (*this) * other.inverse();
+ }
+
+
+ /** Is the magnitude nearly 1.0? */
+ inline bool isUnit(float tolerance = 1e-5) const {
+ return abs(dot(*this) - 1.0f) < tolerance;
+ }
+
+
+ inline float magnitude() const {
+ return sqrtf(dot(*this));
+ }
+
+ inline Quat log() const {
+ if ((x == 0) && (y == 0) && (z == 0)) {
+ if (w > 0) {
+ return Quat(0, 0, 0, ::logf(w));
+ } else if (w < 0) {
+ // Log of a negative number. Multivalued, any number of the form
+ // (PI * v, ln(-q.w))
+ return Quat((float)pi(), 0, 0, ::logf(-w));
+ } else {
+ // log of zero!
+ return Quat((float)nan(), (float)nan(), (float)nan(), (float)nan());
+ }
+ } else {
+ // Partly imaginary.
+ float imagLen = sqrtf(x * x + y * y + z * z);
+ float len = sqrtf(imagLen * imagLen + w * w);
+ float theta = atan2f(imagLen, (float)w);
+ float t = theta / imagLen;
+ return Quat(t * x, t * y, t * z, ::logf(len));
+ }
+ }
+ /** log q = [Av, 0] where q = [sin(A) * v, cos(A)].
+ Only for unit quaternions
+ debugAssertM(isUnit(), "Log only defined for unit quaternions");
+ // Solve for A in q = [sin(A)*v, cos(A)]
+ Vector3 u(x, y, z);
+ double len = u.magnitude();
+
+ if (len == 0.0) {
+ return
+ }
+ double A = atan2((double)w, len);
+ Vector3 v = u / len;
+
+ return Quat(v * A, 0);
+ }
+ */
+
+ /** exp q = [sin(A) * v, cos(A)] where q = [Av, 0].
+ Only defined for pure-vector quaternions */
+ inline Quat exp() const {
+ debugAssertM(w == 0, "exp only defined for vector quaternions");
+ Vector3 u(x, y, z);
+ float A = u.magnitude();
+ Vector3 v = u / A;
+ return Quat(sinf(A) * v, cosf(A));
+ }
+
+
+ /**
+ Raise this quaternion to a power. For a rotation, this is
+ the effect of rotating x times as much as the original
+ quaterion.
+
+ Note that q.pow(a).pow(b) == q.pow(a + b)
+ @cite Dam98 pg 21
+ */
+ inline Quat pow(float x) const {
+ return (log() * x).exp();
+ }
+
+ inline void unitize() {
+ float mag2 = dot(*this);
+ if (! G3D::fuzzyEq(mag2, 1.0f)) {
+ *this *= rsq(mag2);
+ }
+ }
+
+ /**
+ Returns a unit quaterion obtained by dividing through by
+ the magnitude.
+ */
+ inline Quat toUnit() const {
+ Quat x = *this;
+ x.unitize();
+ return x;
+ }
+
+ /**
+ The linear algebra 2-norm, sqrt(q dot q). This matches
+ the value used in Dam's 1998 tech report but differs from the
+ n(q) value used in Eberly's 1999 paper, which is the square of the
+ norm.
+ */
+ inline float norm() const {
+ return magnitude();
+ }
+
+ // access quaternion as q[0] = q.x, q[1] = q.y, q[2] = q.z, q[3] = q.w
+ //
+ // WARNING. These member functions rely on
+ // (1) Quat not having virtual functions
+ // (2) the data packed in a 4*sizeof(float) memory block
+ const float& operator[] (int i) const;
+ float& operator[] (int i);
+
+ /** Generate uniform random unit quaternion (i.e. random "direction")
+ @cite From "Uniform Random Rotations", Ken Shoemake, Graphics Gems III.
+ */
+ static Quat unitRandom();
+
+ void deserialize(class BinaryInput& b);
+ void serialize(class BinaryOutput& b) const;
+
+ // 2-char swizzles
+
+ Vector2 xx() const;
+ Vector2 yx() const;
+ Vector2 zx() const;
+ Vector2 wx() const;
+ Vector2 xy() const;
+ Vector2 yy() const;
+ Vector2 zy() const;
+ Vector2 wy() const;
+ Vector2 xz() const;
+ Vector2 yz() const;
+ Vector2 zz() const;
+ Vector2 wz() const;
+ Vector2 xw() const;
+ Vector2 yw() const;
+ Vector2 zw() const;
+ Vector2 ww() const;
+
+ // 3-char swizzles
+
+ Vector3 xxx() const;
+ Vector3 yxx() const;
+ Vector3 zxx() const;
+ Vector3 wxx() const;
+ Vector3 xyx() const;
+ Vector3 yyx() const;
+ Vector3 zyx() const;
+ Vector3 wyx() const;
+ Vector3 xzx() const;
+ Vector3 yzx() const;
+ Vector3 zzx() const;
+ Vector3 wzx() const;
+ Vector3 xwx() const;
+ Vector3 ywx() const;
+ Vector3 zwx() const;
+ Vector3 wwx() const;
+ Vector3 xxy() const;
+ Vector3 yxy() const;
+ Vector3 zxy() const;
+ Vector3 wxy() const;
+ Vector3 xyy() const;
+ Vector3 yyy() const;
+ Vector3 zyy() const;
+ Vector3 wyy() const;
+ Vector3 xzy() const;
+ Vector3 yzy() const;
+ Vector3 zzy() const;
+ Vector3 wzy() const;
+ Vector3 xwy() const;
+ Vector3 ywy() const;
+ Vector3 zwy() const;
+ Vector3 wwy() const;
+ Vector3 xxz() const;
+ Vector3 yxz() const;
+ Vector3 zxz() const;
+ Vector3 wxz() const;
+ Vector3 xyz() const;
+ Vector3 yyz() const;
+ Vector3 zyz() const;
+ Vector3 wyz() const;
+ Vector3 xzz() const;
+ Vector3 yzz() const;
+ Vector3 zzz() const;
+ Vector3 wzz() const;
+ Vector3 xwz() const;
+ Vector3 ywz() const;
+ Vector3 zwz() const;
+ Vector3 wwz() const;
+ Vector3 xxw() const;
+ Vector3 yxw() const;
+ Vector3 zxw() const;
+ Vector3 wxw() const;
+ Vector3 xyw() const;
+ Vector3 yyw() const;
+ Vector3 zyw() const;
+ Vector3 wyw() const;
+ Vector3 xzw() const;
+ Vector3 yzw() const;
+ Vector3 zzw() const;
+ Vector3 wzw() const;
+ Vector3 xww() const;
+ Vector3 yww() const;
+ Vector3 zww() const;
+ Vector3 www() const;
+
+ // 4-char swizzles
+
+ Vector4 xxxx() const;
+ Vector4 yxxx() const;
+ Vector4 zxxx() const;
+ Vector4 wxxx() const;
+ Vector4 xyxx() const;
+ Vector4 yyxx() const;
+ Vector4 zyxx() const;
+ Vector4 wyxx() const;
+ Vector4 xzxx() const;
+ Vector4 yzxx() const;
+ Vector4 zzxx() const;
+ Vector4 wzxx() const;
+ Vector4 xwxx() const;
+ Vector4 ywxx() const;
+ Vector4 zwxx() const;
+ Vector4 wwxx() const;
+ Vector4 xxyx() const;
+ Vector4 yxyx() const;
+ Vector4 zxyx() const;
+ Vector4 wxyx() const;
+ Vector4 xyyx() const;
+ Vector4 yyyx() const;
+ Vector4 zyyx() const;
+ Vector4 wyyx() const;
+ Vector4 xzyx() const;
+ Vector4 yzyx() const;
+ Vector4 zzyx() const;
+ Vector4 wzyx() const;
+ Vector4 xwyx() const;
+ Vector4 ywyx() const;
+ Vector4 zwyx() const;
+ Vector4 wwyx() const;
+ Vector4 xxzx() const;
+ Vector4 yxzx() const;
+ Vector4 zxzx() const;
+ Vector4 wxzx() const;
+ Vector4 xyzx() const;
+ Vector4 yyzx() const;
+ Vector4 zyzx() const;
+ Vector4 wyzx() const;
+ Vector4 xzzx() const;
+ Vector4 yzzx() const;
+ Vector4 zzzx() const;
+ Vector4 wzzx() const;
+ Vector4 xwzx() const;
+ Vector4 ywzx() const;
+ Vector4 zwzx() const;
+ Vector4 wwzx() const;
+ Vector4 xxwx() const;
+ Vector4 yxwx() const;
+ Vector4 zxwx() const;
+ Vector4 wxwx() const;
+ Vector4 xywx() const;
+ Vector4 yywx() const;
+ Vector4 zywx() const;
+ Vector4 wywx() const;
+ Vector4 xzwx() const;
+ Vector4 yzwx() const;
+ Vector4 zzwx() const;
+ Vector4 wzwx() const;
+ Vector4 xwwx() const;
+ Vector4 ywwx() const;
+ Vector4 zwwx() const;
+ Vector4 wwwx() const;
+ Vector4 xxxy() const;
+ Vector4 yxxy() const;
+ Vector4 zxxy() const;
+ Vector4 wxxy() const;
+ Vector4 xyxy() const;
+ Vector4 yyxy() const;
+ Vector4 zyxy() const;
+ Vector4 wyxy() const;
+ Vector4 xzxy() const;
+ Vector4 yzxy() const;
+ Vector4 zzxy() const;
+ Vector4 wzxy() const;
+ Vector4 xwxy() const;
+ Vector4 ywxy() const;
+ Vector4 zwxy() const;
+ Vector4 wwxy() const;
+ Vector4 xxyy() const;
+ Vector4 yxyy() const;
+ Vector4 zxyy() const;
+ Vector4 wxyy() const;
+ Vector4 xyyy() const;
+ Vector4 yyyy() const;
+ Vector4 zyyy() const;
+ Vector4 wyyy() const;
+ Vector4 xzyy() const;
+ Vector4 yzyy() const;
+ Vector4 zzyy() const;
+ Vector4 wzyy() const;
+ Vector4 xwyy() const;
+ Vector4 ywyy() const;
+ Vector4 zwyy() const;
+ Vector4 wwyy() const;
+ Vector4 xxzy() const;
+ Vector4 yxzy() const;
+ Vector4 zxzy() const;
+ Vector4 wxzy() const;
+ Vector4 xyzy() const;
+ Vector4 yyzy() const;
+ Vector4 zyzy() const;
+ Vector4 wyzy() const;
+ Vector4 xzzy() const;
+ Vector4 yzzy() const;
+ Vector4 zzzy() const;
+ Vector4 wzzy() const;
+ Vector4 xwzy() const;
+ Vector4 ywzy() const;
+ Vector4 zwzy() const;
+ Vector4 wwzy() const;
+ Vector4 xxwy() const;
+ Vector4 yxwy() const;
+ Vector4 zxwy() const;
+ Vector4 wxwy() const;
+ Vector4 xywy() const;
+ Vector4 yywy() const;
+ Vector4 zywy() const;
+ Vector4 wywy() const;
+ Vector4 xzwy() const;
+ Vector4 yzwy() const;
+ Vector4 zzwy() const;
+ Vector4 wzwy() const;
+ Vector4 xwwy() const;
+ Vector4 ywwy() const;
+ Vector4 zwwy() const;
+ Vector4 wwwy() const;
+ Vector4 xxxz() const;
+ Vector4 yxxz() const;
+ Vector4 zxxz() const;
+ Vector4 wxxz() const;
+ Vector4 xyxz() const;
+ Vector4 yyxz() const;
+ Vector4 zyxz() const;
+ Vector4 wyxz() const;
+ Vector4 xzxz() const;
+ Vector4 yzxz() const;
+ Vector4 zzxz() const;
+ Vector4 wzxz() const;
+ Vector4 xwxz() const;
+ Vector4 ywxz() const;
+ Vector4 zwxz() const;
+ Vector4 wwxz() const;
+ Vector4 xxyz() const;
+ Vector4 yxyz() const;
+ Vector4 zxyz() const;
+ Vector4 wxyz() const;
+ Vector4 xyyz() const;
+ Vector4 yyyz() const;
+ Vector4 zyyz() const;
+ Vector4 wyyz() const;
+ Vector4 xzyz() const;
+ Vector4 yzyz() const;
+ Vector4 zzyz() const;
+ Vector4 wzyz() const;
+ Vector4 xwyz() const;
+ Vector4 ywyz() const;
+ Vector4 zwyz() const;
+ Vector4 wwyz() const;
+ Vector4 xxzz() const;
+ Vector4 yxzz() const;
+ Vector4 zxzz() const;
+ Vector4 wxzz() const;
+ Vector4 xyzz() const;
+ Vector4 yyzz() const;
+ Vector4 zyzz() const;
+ Vector4 wyzz() const;
+ Vector4 xzzz() const;
+ Vector4 yzzz() const;
+ Vector4 zzzz() const;
+ Vector4 wzzz() const;
+ Vector4 xwzz() const;
+ Vector4 ywzz() const;
+ Vector4 zwzz() const;
+ Vector4 wwzz() const;
+ Vector4 xxwz() const;
+ Vector4 yxwz() const;
+ Vector4 zxwz() const;
+ Vector4 wxwz() const;
+ Vector4 xywz() const;
+ Vector4 yywz() const;
+ Vector4 zywz() const;
+ Vector4 wywz() const;
+ Vector4 xzwz() const;
+ Vector4 yzwz() const;
+ Vector4 zzwz() const;
+ Vector4 wzwz() const;
+ Vector4 xwwz() const;
+ Vector4 ywwz() const;
+ Vector4 zwwz() const;
+ Vector4 wwwz() const;
+ Vector4 xxxw() const;
+ Vector4 yxxw() const;
+ Vector4 zxxw() const;
+ Vector4 wxxw() const;
+ Vector4 xyxw() const;
+ Vector4 yyxw() const;
+ Vector4 zyxw() const;
+ Vector4 wyxw() const;
+ Vector4 xzxw() const;
+ Vector4 yzxw() const;
+ Vector4 zzxw() const;
+ Vector4 wzxw() const;
+ Vector4 xwxw() const;
+ Vector4 ywxw() const;
+ Vector4 zwxw() const;
+ Vector4 wwxw() const;
+ Vector4 xxyw() const;
+ Vector4 yxyw() const;
+ Vector4 zxyw() const;
+ Vector4 wxyw() const;
+ Vector4 xyyw() const;
+ Vector4 yyyw() const;
+ Vector4 zyyw() const;
+ Vector4 wyyw() const;
+ Vector4 xzyw() const;
+ Vector4 yzyw() const;
+ Vector4 zzyw() const;
+ Vector4 wzyw() const;
+ Vector4 xwyw() const;
+ Vector4 ywyw() const;
+ Vector4 zwyw() const;
+ Vector4 wwyw() const;
+ Vector4 xxzw() const;
+ Vector4 yxzw() const;
+ Vector4 zxzw() const;
+ Vector4 wxzw() const;
+ Vector4 xyzw() const;
+ Vector4 yyzw() const;
+ Vector4 zyzw() const;
+ Vector4 wyzw() const;
+ Vector4 xzzw() const;
+ Vector4 yzzw() const;
+ Vector4 zzzw() const;
+ Vector4 wzzw() const;
+ Vector4 xwzw() const;
+ Vector4 ywzw() const;
+ Vector4 zwzw() const;
+ Vector4 wwzw() const;
+ Vector4 xxww() const;
+ Vector4 yxww() const;
+ Vector4 zxww() const;
+ Vector4 wxww() const;
+ Vector4 xyww() const;
+ Vector4 yyww() const;
+ Vector4 zyww() const;
+ Vector4 wyww() const;
+ Vector4 xzww() const;
+ Vector4 yzww() const;
+ Vector4 zzww() const;
+ Vector4 wzww() const;
+ Vector4 xwww() const;
+ Vector4 ywww() const;
+ Vector4 zwww() const;
+ Vector4 wwww() const;
+};
+
+inline Quat exp(const Quat& q) {
+ return q.exp();
+}
+
+inline Quat log(const Quat& q) {
+ return q.log();
+}
+
+inline G3D::Quat operator*(double s, const G3D::Quat& q) {
+ return q * (float)s;
+}
+
+inline G3D::Quat operator*(float s, const G3D::Quat& q) {
+ return q * s;
+}
+
+inline float& Quat::operator[] (int i) {
+ debugAssert(i >= 0);
+ debugAssert(i < 4);
+ return ((float*)this)[i];
+}
+
+inline const float& Quat::operator[] (int i) const {
+ debugAssert(i >= 0);
+ debugAssert(i < 4);
+ return ((float*)this)[i];
+}
+
+inline Quat Quat::operator-(const Quat& other) const {
+ return Quat(x - other.x, y - other.y, z - other.z, w - other.w);
+}
+
+inline Quat Quat::operator+(const Quat& other) const {
+ return Quat(x + other.x, y + other.y, z + other.z, w + other.w);
+}
+
+} // Namespace G3D
+
+// Outside the namespace to avoid overloading confusion for C++
+inline G3D::Quat pow(const G3D::Quat& q, double x) {
+ return q.pow((float)x);
+}
+
+
+#endif
diff --git a/externals/g3dlite/G3D.lib/include/G3D/Queue.h b/externals/g3dlite/G3D.lib/include/G3D/Queue.h
new file mode 100644
index 00000000000..b64eced820a
--- /dev/null
+++ b/externals/g3dlite/G3D.lib/include/G3D/Queue.h
@@ -0,0 +1,355 @@
+/**
+ @file Queue.h
+
+ @maintainer Morgan McGuire, graphics3d.com
+
+ @created 2002-07-09
+ @edited 2005-08-20
+ */
+
+#ifndef G3D_QUEUE_H
+#define G3D_QUEUE_H
+
+#include "G3D/platform.h"
+#include "G3D/System.h"
+#include "G3D/debug.h"
+
+namespace G3D {
+
+/**
+ Locate the indices of the break between of the two
+ sections of the circular queue. These are used to
+ construct two for loops that iterate over the whole
+ sequence without using the modulo operator.
+
+ [0 ... secondEnd) [head .... firstEnd)
+ */
+#define FIND_ENDS \
+ int firstEnd = head + num;\
+ int secondEnd = 0;\
+ if (firstEnd > numAllocated) {\
+ secondEnd = firstEnd - numAllocated;\
+ firstEnd = numAllocated;\
+ }
+
+
+/**
+ Dynamic queue that uses a circular buffer for performance.
+
+ Faster than std::deque for objects with constructors.
+ */
+template <class T>
+class Queue {
+private:
+ //
+ // |<---- num ---->|
+ // [ | | | | | | | | | | | | | ]
+ // ^
+ // |
+ // head
+ //
+ //
+
+ /**
+ Only num elements are initialized.
+ */
+ T* data;
+
+ /**
+ Index of the next element to be dequeue-d in data.
+ */
+ int head;
+
+ /**
+ Number of elements (including head) that are visible and initialized.
+ */
+ int num;
+
+ /**
+ Size of data array in elements.
+ */
+ int numAllocated;
+
+ /** If a clear was needed, assumes it already occured */
+ void _copy(const Queue& other) {
+ debugAssert(data == NULL);
+ data = (T*)System::malloc(sizeof(T) * other.numAllocated);
+ debugAssert(data);
+ head = other.head;
+ num = other.num;
+ numAllocated = other.numAllocated;
+
+ FIND_ENDS;
+
+ for (int i = head; i < firstEnd; ++i) {
+ new (data + i)T(other.data[i]);
+ }
+
+ for (int i = 0; i < secondEnd; ++i) {
+ new (data + i)T(other.data[i]);
+ }
+ }
+
+
+ /**
+ Computes a data array index from a queue position. The queue position
+ may be negative.
+ */
+ inline int index(int i) const {
+ return (head + i + numAllocated) % numAllocated;
+ }
+
+ /**
+ Allocates newSize elements and repacks the array.
+ */
+ void repackAndRealloc(int newSize) {
+ // TODO: shrink queue
+ T* old = data;
+ data = (T*)System::malloc(newSize * sizeof(T));
+ debugAssert(data != NULL);
+
+ FIND_ENDS;
+
+ int j = 0;
+ for (int i = head; i < firstEnd; ++i, ++j) {
+ new (data + j)T(old[i]);
+ (old + i)->~T();
+ }
+
+ for (int i = 0; i < secondEnd; ++i, ++j) {
+ new (data + j)T(old[i]);
+ (old + i)->~T();
+ }
+
+ head = 0;
+ System::free(old);
+ numAllocated = newSize;
+ }
+
+ /**
+ Ensure that there is at least one element between
+ the tail and head, wrapping around in the circular
+ buffer.
+ */
+ inline void reserveSpace() {
+ if (num == numAllocated) {
+ repackAndRealloc(numAllocated * 3 + 20);
+ }
+ }
+
+public:
+
+ Queue() :
+ data(NULL),
+ head(0),
+ num(0),
+ numAllocated(0) {
+ }
+
+
+ /**
+ Copy constructor
+ */
+ Queue(const Queue& other) : data(NULL) {
+ _copy(other);
+ }
+
+
+ /**
+ Destructor does not delete() the objects if T is a pointer type
+ (e.g. T = int*) instead, it deletes the pointers themselves and
+ leaves the objects. Call deleteAll if you want to dealocate
+ the objects referenced.
+ */
+ virtual ~Queue() {
+ clear();
+ }
+
+ /**
+ Insert a new element into the front of the queue
+ (a traditional queue only uses pushBack).
+ */
+ inline void pushFront(const T& e) {
+ reserveSpace();
+
+ // Get the index of head-1
+ int i = index(-1);
+
+ // Call the constructor on the newly exposed element.
+ new (data + i)T(e);
+
+ // Reassign the head to point to this index
+ head = i;
+ ++num;
+ }
+
+ /**
+ Insert a new element at the end of the queue.
+ */
+ inline void pushBack(const T& e) {
+ reserveSpace();
+
+ // Get the index of 1+tail
+ int i = index(num);
+
+ // Initialize that element
+ new (data + i)T(e);
+ ++num;
+ }
+
+ /**
+ pushBack
+ */
+ inline void enqueue(const T& e) {
+ pushBack(e);
+ }
+
+
+ /**
+ Remove the last element from the queue. The queue will never
+ shrink in size. (A typical queue only uses popFront).
+ */
+ inline T popBack() {
+ int tail = index(num - 1);
+ T result(data[tail]);
+
+ // Call the destructor
+ (data + tail)->~T();
+ --num;
+
+ return result;
+ }
+
+ /**
+ Remove the next element from the head of the queue. The queue will never
+ shrink in size. */
+ inline T popFront() {
+ T result(data[head]);
+ // Call the destructor
+ (data + head)->~T();
+ head = (head + 1) % numAllocated;
+ --num;
+ return result;
+ }
+
+
+ /**
+ popFront
+ */
+ inline T dequeue() {
+ return popFront();
+ }
+
+ /**
+ Removes all elements (invoking their destructors).
+
+ @param freeStorage If false, the underlying array is not deallocated
+ (allowing fast push in the future), however, the size of the Queue
+ is reported as zero.
+
+ */
+ void clear(bool freeStorage = true) {
+
+ FIND_ENDS;
+
+ // Invoke the destructors on the elements
+ int i;
+ for (i = head; i < firstEnd; ++i) {
+ (data + i)->~T();
+ }
+
+ for (i = 0; i < secondEnd; ++i) {
+ (data + i)->~T();
+ }
+
+ num = 0;
+ head = 0;
+ if (freeStorage) {
+ numAllocated = 0;
+ System::free(data);
+ data = NULL;
+ }
+ }
+
+ /** Clear without freeing the underlying array. */
+ void fastClear() {
+ clear(false);
+ }
+
+ /**
+ Assignment operator.
+ */
+ Queue& operator=(const Queue& other) {
+ clear();
+ _copy(other);
+ return *this;
+ }
+
+ /**
+ Number of elements in the queue.
+ */
+ inline int size() const {
+ return num;
+ }
+
+ /**
+ Number of elements in the queue.
+ */
+ inline int length() const {
+ return size();
+ }
+
+ /**
+ Performs bounds checks in debug mode
+ */
+ inline T& operator[](int n) {
+ debugAssert((n >= 0) && (n < num));
+ return data[index(n)];
+ }
+
+ /**
+ Performs bounds checks in debug mode
+ */
+ inline const T& operator[](int n) const {
+ debugAssert((n >= 0) && (n < num));
+ return data[index(n)];
+ }
+
+
+ /**
+ Returns true if the given element is in the queue.
+ */
+ bool contains(const T& e) const {
+ for (int i = 0; i < size(); ++i) {
+ if ((*this)[i] == e) {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ /**
+ Calls delete on all objects[0...size-1]
+ and sets the queue size to zero.
+ */
+ void deleteAll() {
+ FIND_ENDS;
+
+ int i;
+ for (i = 0; i < secondEnd; ++i) {
+ delete data[i];
+ }
+
+ for (i = head; i < firstEnd; ++i) {
+ delete data[i];
+ }
+ clear();
+ }
+};
+
+#undef FIND_ENDS
+
+}; // namespace
+
+#endif
diff --git a/externals/g3dlite/G3D.lib/include/G3D/Ray.h b/externals/g3dlite/G3D.lib/include/G3D/Ray.h
new file mode 100644
index 00000000000..ab43c82933a
--- /dev/null
+++ b/externals/g3dlite/G3D.lib/include/G3D/Ray.h
@@ -0,0 +1,329 @@
+/**
+ @file Ray.h
+
+ Ray class
+
+ @maintainer Morgan McGuire, matrix@graphics3d.com
+
+ @created 2002-07-12
+ @edited 2006-02-21
+ */
+
+#ifndef G3D_RAY_H
+#define G3D_RAY_H
+
+#include "G3D/platform.h"
+#include "G3D/Vector3.h"
+#include "G3D/Triangle.h"
+
+namespace G3D {
+
+/**
+ A 3D Ray.
+ */
+class Ray {
+private:
+ Ray(const Vector3& origin, const Vector3& direction) {
+ this->origin = origin;
+ this->direction = direction;
+ }
+
+public:
+ Vector3 origin;
+
+ /**
+ Not unit length
+ */
+ Vector3 direction;
+
+ Ray() : origin(Vector3::zero()), direction(Vector3::zero()) {}
+
+ Ray(class BinaryInput& b);
+ void serialize(class BinaryOutput& b) const;
+ void deserialize(class BinaryInput& b);
+
+ /**
+ Creates a Ray from a origin and a (nonzero) direction.
+ */
+ static Ray fromOriginAndDirection(const Vector3& point, const Vector3& direction) {
+ return Ray(point, direction);
+ }
+
+ Ray unit() const {
+ return Ray(origin, direction.unit());
+ }
+
+ /**
+ Returns the closest point on the Ray to point.
+ */
+ Vector3 closestPoint(const Vector3& point) const {
+ float t = direction.dot(point - this->origin);
+ if (t < 0) {
+ return this->origin;
+ } else {
+ return this->origin + direction * t;
+ }
+ }
+
+ /**
+ Returns the closest distance between point and the Ray
+ */
+ float distance(const Vector3& point) const {
+ return (closestPoint(point) - point).magnitude();
+ }
+
+ /**
+ Returns the point where the Ray and plane intersect. If there
+ is no intersection, returns a point at infinity.
+
+ Planes are considered one-sided, so the ray will not intersect
+ a plane where the normal faces in the traveling direction.
+ */
+ Vector3 intersection(const class Plane& plane) const;
+
+ /**
+ Returns the distance until intersection with the (solid) sphere.
+ Will be 0 if inside the sphere, inf if there is no intersection.
+
+ The ray direction is <B>not</B> normalized. If the ray direction
+ has unit length, the distance from the origin to intersection
+ is equal to the time. If the direction does not have unit length,
+ the distance = time * direction.length().
+
+ See also the G3D::CollisionDetection "movingPoint" methods,
+ which give more information about the intersection.
+ */
+ float intersectionTime(const class Sphere& sphere) const;
+
+ float intersectionTime(const class Plane& plane) const;
+
+ float intersectionTime(const class Box& box) const;
+
+ float intersectionTime(const class AABox& box) const;
+
+ /**
+ The three extra arguments are the weights of vertices 0, 1, and 2
+ at the intersection point; they are useful for texture mapping
+ and interpolated normals.
+ */
+ float intersectionTime(
+ const Vector3& v0, const Vector3& v1, const Vector3& v2,
+ const Vector3& edge01, const Vector3& edge02,
+ double& w0, double& w1, double& w2) const;
+
+ /**
+ Ray-triangle intersection for a 1-sided triangle. Fastest version.
+ @cite http://www.acm.org/jgt/papers/MollerTrumbore97/
+ http://www.graphics.cornell.edu/pubs/1997/MT97.html
+ */
+ inline float intersectionTime(
+ const Vector3& vert0,
+ const Vector3& vert1,
+ const Vector3& vert2,
+ const Vector3& edge01,
+ const Vector3& edge02) const;
+
+
+ inline float intersectionTime(
+ const Vector3& vert0,
+ const Vector3& vert1,
+ const Vector3& vert2) const {
+
+ return intersectionTime(vert0, vert1, vert2, vert1 - vert0, vert2 - vert0);
+ }
+
+
+ inline float intersectionTime(
+ const Vector3& vert0,
+ const Vector3& vert1,
+ const Vector3& vert2,
+ double& w0,
+ double& w1,
+ double& w2) const {
+
+ return intersectionTime(vert0, vert1, vert2, vert1 - vert0, vert2 - vert0, w0, w1, w2);
+ }
+
+ /* One-sided triangle
+ */
+ inline float intersectionTime(const Triangle& triangle) const {
+ return intersectionTime(
+ triangle.vertex(0), triangle.vertex(1), triangle.vertex(2),
+ triangle.edge01(), triangle.edge02());
+ }
+
+ inline float intersectionTime(
+ const Triangle& triangle,
+ double& w0,
+ double& w1,
+ double& w2) const {
+ return intersectionTime(triangle.vertex(0), triangle.vertex(1), triangle.vertex(2),
+ triangle.edge01(), triangle.edge02(), w0, w1, w2);
+ }
+
+ /** Refracts about the normal
+ using G3D::Vector3::refractionDirection
+ and bumps the ray slightly from the newOrigin. */
+ Ray refract(
+ const Vector3& newOrigin,
+ const Vector3& normal,
+ float iInside,
+ float iOutside) const;
+
+ /** Reflects about the normal
+ using G3D::Vector3::reflectionDirection
+ and bumps the ray slightly from
+ the newOrigin. */
+ Ray reflect(
+ const Vector3& newOrigin,
+ const Vector3& normal) const;
+};
+
+
+#define EPSILON 0.000001
+#define CROSS(dest,v1,v2) \
+ dest[0]=v1[1]*v2[2]-v1[2]*v2[1]; \
+ dest[1]=v1[2]*v2[0]-v1[0]*v2[2]; \
+ dest[2]=v1[0]*v2[1]-v1[1]*v2[0];
+
+#define DOT(v1,v2) (v1[0]*v2[0]+v1[1]*v2[1]+v1[2]*v2[2])
+
+#define SUB(dest,v1,v2) \
+ dest[0]=v1[0]-v2[0]; \
+ dest[1]=v1[1]-v2[1]; \
+ dest[2]=v1[2]-v2[2];
+
+inline float Ray::intersectionTime(
+ const Vector3& vert0,
+ const Vector3& vert1,
+ const Vector3& vert2,
+ const Vector3& edge1,
+ const Vector3& edge2) const {
+
+ (void)vert1;
+ (void)vert2;
+
+ // Barycenteric coords
+ float u, v;
+
+ float tvec[3], pvec[3], qvec[3];
+
+ // begin calculating determinant - also used to calculate U parameter
+ CROSS(pvec, direction, edge2);
+
+ // if determinant is near zero, ray lies in plane of triangle
+ const float det = DOT(edge1, pvec);
+
+ if (det < EPSILON) {
+ return (float)inf();
+ }
+
+ // calculate distance from vert0 to ray origin
+ SUB(tvec, origin, vert0);
+
+ // calculate U parameter and test bounds
+ u = DOT(tvec, pvec);
+ if ((u < 0.0f) || (u > det)) {
+ // Hit the plane outside the triangle
+ return (float)inf();
+ }
+
+ // prepare to test V parameter
+ CROSS(qvec, tvec, edge1);
+
+ // calculate V parameter and test bounds
+ v = DOT(direction, qvec);
+ if ((v < 0.0f) || (u + v > det)) {
+ // Hit the plane outside the triangle
+ return (float)inf();
+ }
+
+
+ // Case where we don't need correct (u, v):
+ const float t = DOT(edge2, qvec);
+
+ if (t >= 0.0f) {
+ // Note that det must be positive
+ return t / det;
+ } else {
+ // We had to travel backwards in time to intersect
+ return (float)inf();
+ }
+}
+
+
+inline float Ray::intersectionTime(
+ const Vector3& vert0,
+ const Vector3& vert1,
+ const Vector3& vert2,
+ const Vector3& edge1,
+ const Vector3& edge2,
+ double& w0,
+ double& w1,
+ double& w2) const {
+
+ (void)vert1;
+ (void)vert2;
+
+ // Barycenteric coords
+ float u, v;
+
+ float tvec[3], pvec[3], qvec[3];
+
+ // begin calculating determinant - also used to calculate U parameter
+ CROSS(pvec, direction, edge2);
+
+ // if determinant is near zero, ray lies in plane of triangle
+ const float det = DOT(edge1, pvec);
+
+ if (det < EPSILON) {
+ return (float)inf();
+ }
+
+ // calculate distance from vert0 to ray origin
+ SUB(tvec, origin, vert0);
+
+ // calculate U parameter and test bounds
+ u = DOT(tvec, pvec);
+ if ((u < 0.0f) || (u > det)) {
+ // Hit the plane outside the triangle
+ return (float)inf();
+ }
+
+ // prepare to test V parameter
+ CROSS(qvec, tvec, edge1);
+
+ // calculate V parameter and test bounds
+ v = DOT(direction, qvec);
+ if ((v < 0.0f) || (u + v > det)) {
+ // Hit the plane outside the triangle
+ return (float)inf();
+ }
+
+ float t = DOT(edge2, qvec);
+
+ if (t >= 0) {
+ const float inv_det = 1.0f / det;
+ t *= inv_det;
+ u *= inv_det;
+ v *= inv_det;
+
+ w0 = (1.0f - u - v);
+ w1 = u;
+ w2 = v;
+
+ return t;
+ } else {
+ // We had to travel backwards in time to intersect
+ return (float)inf();
+ }
+}
+
+#undef EPSILON
+#undef CROSS
+#undef DOT
+#undef SUB
+
+}// namespace
+
+#endif
diff --git a/externals/g3dlite/G3D.lib/include/G3D/Rect2D.h b/externals/g3dlite/G3D.lib/include/G3D/Rect2D.h
new file mode 100644
index 00000000000..a6c0b7cd0cb
--- /dev/null
+++ b/externals/g3dlite/G3D.lib/include/G3D/Rect2D.h
@@ -0,0 +1,391 @@
+/**
+ @file Rect2D.h
+
+ @maintainer Morgan McGuire, matrix@graphics3d.com
+
+ @created 2003-11-13
+ @created 2008-11-16
+
+ Copyright 2000-2008, Morgan McGuire.
+ All rights reserved.
+ */
+
+#ifndef G3D_RECT2D_H
+#define G3D_RECT2D_H
+
+// Linux defines this as a macro
+#ifdef border
+#undef border
+#endif
+
+#include "G3D/platform.h"
+#include "G3D/Array.h"
+#include "G3D/Vector2.h"
+
+namespace G3D {
+
+/**
+ If you are using this class for pixel rectangles, keep in mind that the last
+ pixel you can draw to is at x0() + width() - 1.
+ */
+class Rect2D {
+private:
+ Vector2 min, max;
+
+ /**
+ Returns true if the whole polygon is clipped.
+ @param p Value of the point
+ @param axis Index [0 or 1] of the axis to clip along?
+ @param clipGreater Are we clipping greater than or less than the line?
+ @param inPoly Polygon being clipped
+ @param outPoly The clipped polygon
+ */
+ template<class T>
+ static bool clipSide2D(
+ const float p, bool clipGreater, int axis,
+ const Array<T>& inPoly, Array<T>& outPoly) {
+
+ outPoly.clear();
+ int i0 = -1;
+
+ Vector2 pt1;
+ bool c1 = true;
+
+ float negate = clipGreater ? -1 : 1;
+
+ // Find a point that is not clipped
+ for (i0 = 0; (i0 < inPoly.length()) && c1; ++i0) {
+ pt1 = inPoly[i0];
+ c1 = (negate * pt1[axis]) < (negate * p);
+ }
+
+ // We incremented i0 one time to many
+ --i0;
+
+ if (c1) {
+ // We could not find an unclipped point
+ return true;
+ }
+
+ outPoly.append(pt1);
+
+ // for each point in inPoly,
+ // if the point is outside the side and the previous one was also outside, continue
+ // if the point is outside the side and the previous one was inside, cut the line
+ // if the point is inside the side and the previous one was also inside, append the points
+ // if the point is inside the side and the previous one was outside, cut the line
+ for (int i = 1; i <= inPoly.length(); ++i) {
+ T pt2 = inPoly[(i + i0) % inPoly.length()];
+ bool c2 = (negate * pt2[axis]) < (negate * p);
+
+ if (c1 ^ c2) {
+
+ if (!c1 && c2 && (i > 1)) {
+ // Unclipped to clipped trasition and not the first iteration
+ outPoly.append(pt1);
+ }
+
+ // only one point is clipped, find where the line crosses the clipping plane
+
+
+ float alpha;
+ if (pt2[axis] == pt1[axis]) {
+ alpha = 0;
+ } else {
+ alpha = (p - pt1[axis]) / (pt2[axis] - pt1[axis]);
+ }
+ outPoly.append(pt1.lerp(pt2, alpha));
+ } else if (! (c1 || c2) && (i != 1)) {
+ // neither point is clipped (don't do this the first time
+ // because we appended the first pt before the loop)
+ outPoly.append(pt1);
+ }
+
+ pt1 = pt2;
+ c1 = c2;
+ }
+
+ return false;
+ }
+
+public:
+
+ inline Rect2D() : min(0, 0), max(0, 0) {}
+
+ /** Creates a rectangle at 0,0 with the given width and height*/
+ inline Rect2D(const Vector2& wh) : min(0, 0), max(wh.x, wh.y) {}
+
+ /** Computes a rectangle that contains both @a a and @a b.
+ Note that even if @a or @b has zero area, its origin will be included.*/
+ inline Rect2D(const Rect2D& a, const Rect2D& b) {
+ min = a.min.min(b.min);
+ max = a.max.max(b.max);
+ }
+
+ /** @brief Uniformly random point on the interior */
+ inline Vector2 randomPoint() const {
+ return Vector2(uniformRandom(0, max.x - min.x) + min.x,
+ uniformRandom(0, max.y - min.y) + min.y);
+ }
+
+ inline float width() const {
+ return max.x - min.x;
+ }
+
+ inline float height() const {
+ return max.y - min.y;
+ }
+
+ inline float x0() const {
+ return min.x;
+ }
+
+ inline float x1() const {
+ return max.x;
+ }
+
+ inline float y0() const {
+ return min.y;
+ }
+
+ inline float y1() const {
+ return max.y;
+ }
+
+ /** Min, min corner */
+ inline Vector2 x0y0() const {
+ return min;
+ }
+
+ inline Vector2 x1y0() const {
+ return Vector2(max.x, min.y);
+ }
+
+ inline Vector2 x0y1() const {
+ return Vector2(min.x, max.y);
+ }
+
+ /** Max,max corner */
+ inline Vector2 x1y1() const {
+ return max;
+ }
+
+ /** Width and height */
+ inline Vector2 wh() const {
+ return max - min;
+ }
+
+ inline Vector2 center() const {
+ return (max + min) * 0.5;
+ }
+
+ inline static Rect2D xyxy(float x0, float y0, float x1, float y1) {
+ Rect2D r;
+
+ r.min.x = G3D::min(x0, x1);
+ r.min.y = G3D::min(y0, y1);
+ r.max.x = G3D::max(x0, x1);
+ r.max.y = G3D::max(y0, y1);
+
+ return r;
+ }
+
+ Rect2D lerp(const Rect2D& other, float alpha) const {
+ Rect2D out;
+
+ out.min = min.lerp(other.min, alpha);
+ out.max = max.lerp(other.max, alpha);
+
+ return out;
+ }
+
+ inline static Rect2D xyxy(const Vector2& v0, const Vector2& v1) {
+ Rect2D r;
+
+ r.min = v0.min(v1);
+ r.max = v0.max(v1);
+
+ return r;
+ }
+
+ inline static Rect2D xywh(float x, float y, float w, float h) {
+ return xyxy(x, y, x + w, y + h);
+ }
+
+ inline static Rect2D xywh(const Vector2& v, const Vector2& w) {
+ return xyxy(v.x, v.y, v.x + w.x, v.y + w.y);
+ }
+
+ inline bool contains(const Vector2& v) const {
+ return (v.x >= min.x) && (v.y >= min.y) && (v.x <= max.x) && (v.y <= max.y);
+ }
+
+ inline bool contains(const Rect2D& r) const {
+ return (min.x <= r.min.x) && (min.y <= r.min.y) &&
+ (max.x >= r.max.x) && (max.y >= r.max.y);
+ }
+
+ /** True if there is non-zero area to the intersection between @a this and @a r.
+ Note that two rectangles that are adjacent do not intersect because there is
+ zero area to the overlap, even though one of them "contains" the corners of the other.*/
+ inline bool intersects(const Rect2D& r) const {
+ return (min.x < r.max.x) && (min.y < r.max.y) &&
+ (max.x > r.min.x) && (max.y > r.min.y);
+ }
+
+ /** Like intersection, but counts the adjacent case as touching. */
+ inline bool intersectsOrTouches(const Rect2D& r) const {
+ return (min.x <= r.max.x) && (min.y <= r.max.y) &&
+ (max.x >= r.min.x) && (max.y >= r.min.y);
+ }
+
+ inline Rect2D operator*(float s) const {
+ return xyxy(min.x * s, min.y * s, max.x * s, max.y * s);
+ }
+
+ inline Rect2D operator/(float s) const {
+ return xyxy(min / s, max / s);
+ }
+
+ inline Rect2D operator/(const Vector2& s) const {
+ return xyxy(min / s, max / s);
+ }
+
+ inline Rect2D operator+(const Vector2& v) const {
+ return xyxy(min + v, max + v);
+ }
+
+ inline Rect2D operator-(const Vector2& v) const {
+ return xyxy(min - v, max - v);
+ }
+
+ inline bool operator==(const Rect2D& other) const {
+ return (min == other.min) && (max == other.max);
+ }
+
+ inline bool operator!=(const Rect2D& other) const {
+ return (min != other.min) || (max != other.max);
+ }
+
+ /** Returns the corners in the order: (min,min), (max,min), (max,max), (min,max). */
+ inline Vector2 corner(int i) const {
+ debugAssert(i >= 0 && i < 4);
+ switch (i & 3) {
+ case 0:
+ return Vector2(min.x, min.y);
+ case 1:
+ return Vector2(max.x, min.y);
+ case 2:
+ return Vector2(max.x, max.y);
+ case 3:
+ return Vector2(min.x, max.y);
+ default:
+ // Should never get here
+ return Vector2(0, 0);
+ }
+ }
+
+
+ /** @deprecated
+ @sa expand() */
+ inline Rect2D border(float delta) const {
+ return Rect2D::xywh(x0() + delta,
+ y0() + delta,
+ width() - 2.0f * delta,
+ height() - 2.0f * delta);
+ }
+
+ /** Returns a new Rect2D that is bigger/smaller by the specified amount
+ (negative is shrink.) */
+ inline Rect2D expand(float delta) const {
+ float newX = x0() - delta;
+ float newY = y0() - delta;
+ float newW = width() + 2.0f * delta;
+ float newH = height() + 2.0f * delta;
+
+ if (newW < 0.0f) {
+ newX = (x0() + width()) / 2.0f;
+ newW = 0.0f;
+ }
+
+ if (newH < 0.0f) {
+ newY = (y0() + height()) / 2.0f;
+ newH = 0.0f;
+ }
+ return Rect2D::xywh(newX, newY, newW, newH);
+ }
+
+ /**
+ Clips so that the rightmost point of the outPoly is at rect.x1 (e.g. a 800x600 window produces
+ rightmost point 799, not 800). The results are suitable for pixel rendering if iRounded.
+ Templated so that it will work for Vector2,3,4 (the z and w components are interpolated linearly).
+ The template parameter must define T.lerp and contain x and y components.
+
+ If the entire polygon is clipped by a single side, the result will be empty.
+ The result might also have zero area but not be empty.
+ */
+ template<class T>
+ void clip(const Array<T>& inPoly, Array<T>& outPoly) const {
+
+ const bool greaterThan = true;
+ const bool lessThan = false;
+ const int X = 0;
+ const int Y = 1;
+
+ Array<T> temp;
+
+ bool entirelyClipped =
+ clipSide2D(x0(), lessThan, X, inPoly, temp) ||
+ clipSide2D(x1(), greaterThan, X, temp, outPoly) ||
+ clipSide2D(y0(), lessThan, Y, outPoly, temp) ||
+ clipSide2D(y1(), greaterThan, Y, temp, outPoly);
+
+ if (entirelyClipped) {
+ outPoly.clear();
+ }
+ }
+
+
+ /** Returns the largest, centered Rect2D that can fit inside this
+ while maintaining the aspect ratio of x:y. Convenient for
+ displaying images in odd-shaped windows.
+ */
+ Rect2D largestCenteredSubRect(float ww, float hh) const {
+ float textureAspect = hh / ww;
+ float viewAspect = height() / width();
+
+ if (viewAspect > textureAspect) {
+ // The view is too tall
+ float h = width() * textureAspect;
+ float y = (height() - h) / 2;
+ return Rect2D::xywh(0, y, width(), h) + corner(0);
+ } else {
+ // The view is too wide
+ float w = height() / textureAspect;
+ float x = (width() - w) / 2;
+ return Rect2D::xywh(x, 0, w, height()) + corner(0);
+ }
+ }
+
+ /**
+ Returns the overlap region between the two rectangles. This may have zero area
+ if they do not intersect. See the two-Rect2D constructor for a way to compute
+ a union-like rectangle.
+ */
+ Rect2D intersect(const Rect2D& other) const {
+ if (intersects(other)) {
+ return Rect2D::xyxy(min.max(other.min), max.min(other.max));
+ }else{
+ return Rect2D::xywh(0, 0, 0, 0);
+ }
+ }
+
+ float area() const {
+ return width() * height();
+ }
+};
+
+typedef Rect2D AABox2D;
+}
+
+#endif
diff --git a/externals/g3dlite/G3D.lib/include/G3D/ReferenceCount.h b/externals/g3dlite/G3D.lib/include/G3D/ReferenceCount.h
new file mode 100644
index 00000000000..f55a22be0e1
--- /dev/null
+++ b/externals/g3dlite/G3D.lib/include/G3D/ReferenceCount.h
@@ -0,0 +1,597 @@
+/**
+ @file ReferenceCount.h
+
+ Reference Counting Garbage Collector for C++
+
+ @maintainer Morgan McGuire, matrix@graphics3d.com
+ @cite Adapted and extended from Justin Miller's "RGC" class that appeared in BYTE magazine.
+ @cite See also http://www.jelovic.com/articles/cpp_without_memory_errors_slides.htm
+
+ @created 2001-10-23
+ @edited 2008-09-25
+*/
+
+#ifndef G3D_RGC_H
+#define G3D_RGC_H
+
+#include "G3D/platform.h"
+#include "G3D/debug.h"
+#include "G3D/AtomicInt32.h"
+
+namespace G3D {
+
+/** Base class for WeakReferenceCountedPointer */
+class _WeakPtr {
+public:
+ inline virtual ~_WeakPtr() {}
+
+protected:
+ friend class ReferenceCountedObject;
+
+ /** Called by ReferenceCountedObject to tell a weak pointer that its underlying object was collected. */
+ virtual void objectCollected() = 0;
+};
+
+/** Used internally by ReferenceCountedObject */
+class _WeakPtrLinkedList {
+public:
+ _WeakPtr* weakPtr;
+ _WeakPtrLinkedList* next;
+
+ inline _WeakPtrLinkedList() : weakPtr(NULL), next(NULL) {}
+
+ /** Inserts this node into the head of the list that previously had n as its head. */
+ inline _WeakPtrLinkedList(_WeakPtr* p, _WeakPtrLinkedList* n) : weakPtr(p), next(n) {}
+};
+
+/**
+ Objects that are reference counted inherit from this. Subclasses
+ <B>must</B> have a public destructor (the default destructor is fine)
+ and <B>publicly</B> inherit ReferenceCountedObject.
+
+ Multiple inheritance from a reference counted object is dangerous-- use
+ at your own risk.
+
+ ReferenceCountedPointer and ReferenceCountedObject are threadsafe.
+ You can create and drop references on multiple threads without
+ violating integrity. WeakReferenceCountedPointer is <i>not</i>
+ threadsafe. Introducing a weak pointer destroys all thread safety,
+ even for strong pointers to the same object (this is inherent in the
+ design of the class; we cannot fix it without slowing down the
+ performance of reference counted objects.)
+
+ <B>Usage Example</B>
+
+ <PRE>
+
+class Foo : public G3D::ReferenceCountedObject {
+public:
+ int x;
+};
+
+class Bar : public Foo {};
+
+typedef G3D::ReferenceCountedPointer<Foo> FooRef;
+typedef G3D::WeakReferenceCountedPointer<Foo> WeakFooRef;
+typedef G3D::ReferenceCountedPointer<Bar> BarRef;
+
+
+int main(int argc, char *argv[]) {
+
+ WeakFooRef x;
+
+ {
+ FooRef a = new Foo();
+
+ // Reference count == 1
+
+ x = a;
+ // Weak references do not increase count
+
+ {
+ FooRef b = a;
+ // Reference count == 2
+ }
+
+ // Reference count == 1
+ }
+ // No more strong references; object automatically deleted.
+ // x is set to NULL automatically.
+
+ // Example of using dynamic cast on reference counted objects
+ BarRef b = new Bar();
+
+ // No cast needed to go down the heirarchy.
+ FooRef f = b;
+
+ // We can't cast the reference object because it is a class.
+ // Instead we must extract the pointer and cast that:
+ b = dynamic_cast<Bar*>(&*f);
+
+ return 0;
+}
+</PRE>
+
+@deprecated To be replaced by boost::shared_ptr in 7.0
+ */
+class ReferenceCountedObject {
+public:
+
+ /**
+ The long name is to keep this from accidentally conflicting with
+ a subclass's variable name. Do not use or explicitly manipulate
+ this value--its type may change in the future and is not part
+ of the supported API.
+ */
+ AtomicInt32 ReferenceCountedObject_refCount;
+
+ /**
+ Linked list of all weak pointers that reference this (some may be
+ on the stack!). Do not use or explicitly manipulate this value.
+ */
+ _WeakPtrLinkedList* ReferenceCountedObject_weakPointer;
+
+protected:
+
+ ReferenceCountedObject() :
+ ReferenceCountedObject_refCount(0),
+ ReferenceCountedObject_weakPointer(0) {
+
+ debugAssertM(isValidHeapPointer(this),
+ "Reference counted objects must be allocated on the heap.");
+ }
+
+public:
+
+ /** Automatically called immediately before the object is deleted.
+ This is not called from the destructor because it needs to be invoked
+ before the subclass destructor.
+ */
+ void ReferenceCountedObject_zeroWeakPointers() {
+ // Tell all of my weak pointers that I'm gone.
+
+ _WeakPtrLinkedList* node = ReferenceCountedObject_weakPointer;
+
+ while (node != 0) {
+
+ // Notify the weak pointer that it is going away
+ node->weakPtr->objectCollected();
+
+ // Free the node and advance
+ _WeakPtrLinkedList* tmp = node;
+ node = node->next;
+ delete tmp;
+ }
+ }
+
+ virtual ~ReferenceCountedObject() {}
+
+
+ /**
+ Note: copies will initially start out with 0
+ references and 0 weak references like any other object.
+ */
+ ReferenceCountedObject(const ReferenceCountedObject& notUsed) :
+ ReferenceCountedObject_refCount(0),
+ ReferenceCountedObject_weakPointer(0) {
+ (void)notUsed;
+ debugAssertM(G3D::isValidHeapPointer(this),
+ "Reference counted objects must be allocated on the heap.");
+ }
+
+ ReferenceCountedObject& operator=(const ReferenceCountedObject& other) {
+ (void)other;
+ // Nothing changes when I am assigned; the reference count on
+ // both objects is the same (although my super-class probably
+ // changes).
+ return *this;
+ }
+};
+
+
+
+/**
+ Use ReferenceCountedPointer<T> in place of T* in your program.
+ T must subclass ReferenceCountedObject.
+@deprecated To be replaced by boost::shared_ptr in 7.0
+ */
+template <class T>
+class ReferenceCountedPointer {
+private:
+
+ T* m_pointer;
+
+public:
+ typedef T element_type;
+
+ inline T* pointer() const {
+ return m_pointer;
+ }
+
+private:
+
+ /** Nulls out the pointer and drops a reference. If the reference
+ count hits zero. */
+ void zeroPointer() {
+ if (m_pointer != NULL) {
+
+ debugAssert(G3D::isValidHeapPointer(m_pointer));
+ debugAssertM(m_pointer->ReferenceCountedObject_refCount.value() > 0,
+ "Dangling reference detected.");
+
+ // Only delete if this instance caused the count to hit
+ // exactly zero. If there is a race condition, the value
+ // may be zero after decrement returns, but only one of
+ // the instances will get a zero return value.
+ if (m_pointer->ReferenceCountedObject_refCount.decrement() == 0) {
+ // We held the last reference, so delete the object.
+ // This test is threadsafe because there is no way for
+ // the reference count to increase after the last
+ // reference was dropped (assuming the application does
+ // not voilate the class abstraction).
+ //debugPrintf(" delete 0x%x\n", m_pointer);
+
+ // We must zero the weak pointers *before* deletion in case there
+ // are cycles of weak references.
+ // Note that since there are no strong references at this point,
+ // it is perfectly fair to zero the weak pointers anyway.
+ m_pointer->ReferenceCountedObject_zeroWeakPointers();
+ delete m_pointer;
+ }
+
+ m_pointer = NULL;
+ }
+ }
+
+ /** Non-atomic (except for the referencec increment). Can only be
+ called in contexts like the copy constructor or initial
+ constructor where it is known that the reference count will
+ not hit zero on some other thread. */
+ void setPointer(T* x) {
+ if (x != m_pointer) {
+ zeroPointer();
+
+ if (x != NULL) {
+ debugAssert(G3D::isValidHeapPointer(x));
+
+ m_pointer = x;
+
+ // Note that the ref count can be zero if this is the
+ // first pointer to it
+ debugAssertM(m_pointer->ReferenceCountedObject_refCount.value() >= 0,
+ "Negative reference count detected.");
+ m_pointer->ReferenceCountedObject_refCount.increment();
+ }
+ }
+ }
+
+public:
+
+ inline ReferenceCountedPointer() : m_pointer(NULL) {}
+
+ /**
+ Allow silent cast <i>to</i> the base class.
+
+ <pre>
+ SubRef s = new Sub();
+ BaseRef b = s;
+ </pre>
+
+ i.e., compile-time subtyping rule
+ RCP&lt;<I>T</I>&gt; &lt;: RCP&lt;<I>S</I>&gt; if <I>T</I> &lt;: <I>S</I>
+ */
+ template <class S>
+ inline ReferenceCountedPointer(const ReferenceCountedPointer<S>& p) :
+ m_pointer(NULL) {
+ setPointer(p.pointer());
+ }
+
+# if (! defined(MSC_VER) || (MSC_VER >= 1300))
+ /**
+ Explicit cast to a subclass. Acts like dynamic cast; the result will be NULL if
+ the cast cannot succeed. Not supported on VC6.
+ <pre>
+ SubRef s = new Sub();
+ BaseRef b = s;
+ s = b.downcast<Sub>(); // Note that the template argument is the object type, not the pointer type.
+ </pre>
+ */
+ template <class S>
+ ReferenceCountedPointer<S> downcast() {
+ return ReferenceCountedPointer<S>(dynamic_cast<S*>(m_pointer));
+ }
+
+ template <class S>
+ const ReferenceCountedPointer<S> downcast() const {
+ return ReferenceCountedPointer<S>(dynamic_cast<const S*>(m_pointer));
+ }
+# endif
+
+ // We need an explicit version of the copy constructor as well or
+ // the default copy constructor will be used.
+ inline ReferenceCountedPointer(const ReferenceCountedPointer<T>& p) : m_pointer(NULL) {
+ setPointer(p.m_pointer);
+ }
+
+ /** Allows construction from a raw pointer. That object will thereafter be
+ reference counted -- do not call delete on it. */
+ inline ReferenceCountedPointer(T* p) : m_pointer(NULL) {
+ setPointer(p);
+ }
+
+ inline ~ReferenceCountedPointer() {
+ zeroPointer();
+ }
+
+ inline size_t hashCode() const {
+ return reinterpret_cast<size_t>(m_pointer);;
+ }
+
+ inline const ReferenceCountedPointer<T>& operator=(const ReferenceCountedPointer<T>& p) {
+ setPointer(p.m_pointer);
+ return *this;
+ }
+
+ inline ReferenceCountedPointer<T>& operator=(T* p) {
+ setPointer(p);
+ return *this;
+ }
+
+ inline bool operator==(const ReferenceCountedPointer<T>& y) const {
+ return (m_pointer == y.m_pointer);
+ }
+
+ inline bool operator!=(const ReferenceCountedPointer<T>& y) const {
+ return (m_pointer != y.m_pointer);
+ }
+
+ bool operator < (const ReferenceCountedPointer<T>& y) const {
+ return (m_pointer < y.m_pointer);
+ }
+
+ bool operator > (const ReferenceCountedPointer<T>& y) const {
+ return (m_pointer > y.m_pointer);
+ }
+
+ bool operator <= (const ReferenceCountedPointer<T>& y) const {
+ return (m_pointer <= y.m_pointer);
+ }
+
+ bool operator >= (const ReferenceCountedPointer<T>& y) const {
+ return (m_pointer >= y.m_pointer);
+ }
+
+ inline T& operator*() const {
+ debugAssertM(m_pointer != NULL, "Dereferenced a NULL ReferenceCountedPointer");
+ return (*m_pointer);
+ }
+
+ inline T* operator->() const {
+ debugAssertM(m_pointer != NULL, "Dereferenced a NULL ReferenceCountedPointer");
+ return m_pointer;
+ }
+
+ inline bool isNull() const {
+ return (m_pointer == NULL);
+ }
+
+ inline bool notNull() const {
+ return (m_pointer != NULL);
+ }
+
+ // TODO: distinguish between last strong and last any pointer
+ /**
+ Returns true if this is the last reference to an object.
+ Useful for flushing memoization caches-- a cache that holds the last
+ reference is unnecessarily keeping an object alive.
+
+ <b>Not threadsafe.</b>
+
+ @deprecated Use WeakReferenceCountedPointer for caches
+ */
+ inline int isLastReference() const {
+ return (m_pointer->ReferenceCountedObject_refCount.value() == 1);
+ }
+};
+
+
+/**
+ A weak pointer allows the object it references to be garbage collected.
+ Weak pointers are commonly used in caches, where it is important to hold
+ a pointer to an object without keeping that object alive solely for the
+ cache's benefit (i.e., the object can be collected as soon as all
+ pointers to it <B>outside</B> the cache are gone). They are also convenient
+ for adding back-pointers in tree and list structures.
+
+ Weak pointers may become NULL at any point (when their target is collected).
+ Therefore the only way to reference the target is to convert to a strong
+ pointer and then check that it is not NULL.
+
+@deprecated To be replaced by boost::weak_ptr in 7.0
+ */
+template <class T>
+class WeakReferenceCountedPointer : public _WeakPtr {
+private:
+
+ /** NULL if the object has been collected. */
+ T* pointer;
+
+public:
+ /**
+ Creates a strong pointer, which prevents the object from being
+ garbage collected. The strong pointer may be NULL, which means
+ that the underlying.
+ */
+ // There is intentionally no way to check if the
+ // WeakReferenceCountedPointer has a null reference without
+ // creating a strong pointer since there is no safe way to use
+ // that information-- the pointer could be collected by a
+ // subsequent statement.
+ ReferenceCountedPointer<T> createStrongPtr() const {
+ // TODO: What if the object's destructor is called while we
+ // are in this method?
+ return ReferenceCountedPointer<T>(pointer);
+ }
+
+
+private:
+
+ /**
+ Thread issues: safe because this is only called when another
+ object is guaranteed to keep p alive for the duration of this
+ call.
+ */
+ void setPointer(T* p) {
+ // TODO: must prevent the object from being collected while in
+ // this method
+
+ zeroPointer();
+ pointer = p;
+
+ if (pointer != 0) {
+ // TODO: threadsafe: must update the list atomically
+
+ // Add myself to the head of my target's list of weak pointers
+ _WeakPtrLinkedList* head =
+ new _WeakPtrLinkedList
+ (this,
+ pointer->ReferenceCountedObject_weakPointer);
+
+ pointer->ReferenceCountedObject_weakPointer = head;
+ } else {
+
+ }
+ }
+
+
+ /**
+ Removes this from its target's list of weak pointers. Called
+ when the weak pointer goes out of scope.
+
+ Thread issues: depends on the thread safety of createStrongPtr.
+ */
+ void zeroPointer() {
+ // Grab a strong reference to prevent the object from being collected while we
+ // are traversing its list.
+ ReferenceCountedPointer<T> strong = createStrongPtr();
+
+ // If the following test fails then the object was collected before we
+ // reached it.
+ if (strong.notNull()) {
+ debugAssertM(pointer->ReferenceCountedObject_weakPointer != NULL,
+ "Weak pointer exists without a backpointer from the object.");
+
+ // Remove myself from my target's list of weak pointers
+ _WeakPtrLinkedList** node = &pointer->ReferenceCountedObject_weakPointer;
+ while ((*node)->weakPtr != this) {
+ node = &((*node)->next);
+ debugAssertM(*node != NULL,
+ "Weak pointer exists without a backpointer from the object (2).");
+ }
+
+ // Node must now point at the node for me. Remove node and
+ // close the linked list behind it.
+ _WeakPtrLinkedList* temp = *node;
+ *node = temp->next;
+
+ // Now delete the node corresponding to me
+ delete temp;
+ }
+
+ pointer = NULL;
+ }
+
+public:
+
+ WeakReferenceCountedPointer() : pointer(0) {}
+
+ /**
+ Allow compile time subtyping rule
+ RCP&lt;<I>T</I>&gt; &lt;: RCP&lt;<I>S</I>&gt; if <I>T</I> &lt;: <I>S</I>
+ */
+ template <class S>
+ inline WeakReferenceCountedPointer(const WeakReferenceCountedPointer<S>& p) : pointer(0) {
+ // Threadsafe: the object cannot be collected while the other pointer exists.
+ setPointer(p.pointer);
+ }
+
+ template <class S>
+ inline WeakReferenceCountedPointer(const ReferenceCountedPointer<S>& p) : pointer(0) {
+ // Threadsafe: the object cannot be collected while the other
+ // pointer exists.
+ setPointer(p.pointer());
+ }
+
+ // Gets called a *lot* when weak pointers are on the stack
+ WeakReferenceCountedPointer(
+ const WeakReferenceCountedPointer<T>& weakPtr) : pointer(0) {
+ setPointer(weakPtr.pointer);
+ }
+
+ WeakReferenceCountedPointer(
+ const ReferenceCountedPointer<T>& strongPtr) : pointer(0) {
+ setPointer(strongPtr.pointer());
+ }
+
+ ~WeakReferenceCountedPointer() {
+ zeroPointer();
+ }
+
+ WeakReferenceCountedPointer<T>& operator=(const WeakReferenceCountedPointer<T>& other) {
+ // Threadsafe: the object cannot be collected while the other pointer exists.
+
+ // I now point at other's target
+ setPointer(other.pointer);
+
+ return *this;
+ }
+
+ WeakReferenceCountedPointer<T>& operator=(const ReferenceCountedPointer<T>& other) {
+
+ // Threadsafe: the object cannot be collected while the other pointer exists.
+
+ // I now point at other's target
+ setPointer(other.pointer());
+
+ return *this;
+ }
+
+ bool operator==(const WeakReferenceCountedPointer<T>& other) const {
+ return pointer == other.pointer;
+ }
+
+ bool operator!=(const WeakReferenceCountedPointer<T>& other) const {
+ return pointer != other.pointer;
+ }
+
+ bool operator < (const WeakReferenceCountedPointer<T>& y) const {
+ return (pointer < y.pointer);
+ }
+
+ bool operator > (const WeakReferenceCountedPointer<T>& y) const {
+ return (pointer > y.pointer);
+ }
+
+ bool operator <= (const WeakReferenceCountedPointer<T>& y) const {
+ return (pointer <= y.pointer);
+ }
+
+ bool operator >= (const ReferenceCountedPointer<T>& y) const {
+ return (pointer >= y.pointer);
+ }
+
+protected:
+
+ /** Invoked by the destructor on ReferenceCountedPointer. */
+ virtual void objectCollected() {
+ debugAssertM(pointer != NULL,
+ "Removed a weak pointer twice.");
+ pointer = NULL;
+ }
+
+};
+
+} // namespace
+
+#endif
+
diff --git a/externals/g3dlite/G3D.lib/include/G3D/RegistryUtil.h b/externals/g3dlite/G3D.lib/include/G3D/RegistryUtil.h
new file mode 100644
index 00000000000..4b47be5f4bd
--- /dev/null
+++ b/externals/g3dlite/G3D.lib/include/G3D/RegistryUtil.h
@@ -0,0 +1,97 @@
+/**
+ @file RegistryUtil.h
+
+ @created 2006-04-06
+ @edited 2006-04-06
+
+ Copyright 2000-2006, Morgan McGuire.
+ All rights reserved.
+*/
+
+#ifndef G3D_REGISTRYUTIL_H
+#define G3D_REGISTRYUTIL_H
+
+#include "G3D/platform.h"
+#include "G3D/g3dmath.h"
+
+// This file is only used on Windows
+#ifdef G3D_WIN32
+
+#include <string>
+
+namespace G3D {
+
+/**
+ Provides generalized Windows registry querying.
+
+ All key names are one string in the format:
+ "[base key]\[sub-keys]"
+
+ A value must now be provided for every query.
+ An empty value string will use the (Default) value.
+
+ [base key] can be any of the following:
+ HKEY_CLASSES_ROOT
+ HKEY_CURRENT_CONFIG
+ HKEY_CURRENT_USER
+ HKEY_LOCAL_MACHINE
+ HKEY_PERFORMANCE_DATA
+ HKEY_PERFORMANCE_NLSTEXT
+ HKEY_PERFORMANCE_TEXT
+ HKEY_USERS
+
+ valueExists() should be used to validate a key+value before reading or writing
+ to ensure that a debug assert or false return is for a different error during
+ reads and writes.
+
+ All read and write calls will assert when a key will not open for reasons other
+ that it does not exist. All read and write calls will assert when the value cannot
+ be read or written for any reason.
+*/
+class RegistryUtil {
+
+public:
+ /** returns true if the key exists and the current user has permission to read */
+ static bool keyExists(const std::string& key);
+
+ /** returns true if the key exists and the current user has permission to read */
+ static bool valueExists(const std::string& key, const std::string& value);
+
+ /** returns false if the key could not be read for any reason. */
+ static bool readInt32(const std::string& key, const std::string& value, int32& data);
+
+ /**
+ Reads an arbitrary amount of data from a binary registry key.
+ returns false if the key could not be read for any reason.
+
+ @beta
+ @param data pointer to the output buffer of sufficient size. Pass NULL as data in order to have available data size returned in dataSize.
+ @param dataSize size of the output buffer. When NULL is passed for data, contains the size of available data on successful return.
+ */
+ static bool readBytes(const std::string& key, const std::string& value, uint8* data, uint32& dataSize);
+
+ /** returns false if the key could not be read for any reason. */
+ static bool readString(const std::string& key, const std::string& value, std::string& data);
+
+ /** returns false if the key could not be written for any reason. */
+ static bool writeInt32(const std::string& key, const std::string& value, int32 data);
+
+ /**
+ Writes an arbitrary amount of data to a binary registry key.
+ returns false if the key could not be written for any reason.
+
+ @param data pointer to the input buffer
+ @param dataSize size of the input buffer that should be written
+ */
+ static bool writeBytes(const std::string& key, const std::string& value, const uint8* data, uint32 dataSize);
+
+ /** returns false if the key could not be written for any reason. */
+ static bool writeString(const std::string& key, const std::string& value, const std::string& data);
+
+};
+
+} // namespace G3D
+
+#endif // G3D_WIN32
+
+#endif // G3D_REGISTRYTUIL_H
diff --git a/externals/g3dlite/G3D.lib/include/G3D/Set.h b/externals/g3dlite/G3D.lib/include/G3D/Set.h
new file mode 100644
index 00000000000..629d802cdd4
--- /dev/null
+++ b/externals/g3dlite/G3D.lib/include/G3D/Set.h
@@ -0,0 +1,160 @@
+/**
+ @file Set.h
+
+ Hash set
+
+ @maintainer Morgan McGuire, matrix@graphics3d.com
+
+ @created 2001-12-09
+ @edited 2006-02-20
+ */
+
+#ifndef G3D_SET_H
+#define G3D_SET_H
+
+#include "G3D/platform.h"
+#include "G3D/Table.h"
+#include <assert.h>
+#include <string>
+
+namespace G3D {
+
+/**
+ An unordered data structure that has at most one of each element.
+ Provides O(1) time insert, remove, and member test (contains).
+
+ Set uses G3D::Table internally, which means that the template type T
+ must define a hashCode and operator== function. See G3D::Table for
+ a discussion of these functions.
+ */
+// There is not copy constructor or assignment operator defined because
+// the default ones are correct for Set.
+template<class T>
+class Set {
+
+ /**
+ If an object is a member, it is contained in
+ this table.
+ */
+ Table<T, bool> memberTable;
+
+public:
+
+ virtual ~Set() {}
+
+ int size() const {
+ return (int)memberTable.size();
+ }
+
+ bool contains(const T& member) const {
+ return memberTable.containsKey(member);
+ }
+
+ /**
+ Inserts into the table if not already present.
+ */
+ void insert(const T& member) {
+ memberTable.set(member, true);
+ }
+
+ /**
+ It is an error to remove members that are not already
+ present.
+ */
+ void remove(const T& member) {
+ memberTable.remove(member);
+ }
+
+ Array<T> getMembers() const {
+ return memberTable.getKeys();
+ }
+
+ void getMembers(Array<T>& keyArray) const {
+ memberTable.getKeys(keyArray);
+ }
+
+ void clear() {
+ memberTable.clear();
+ }
+
+ void deleteAll() {
+ getMembers().deleteAll();
+ clear();
+ }
+
+ /**
+ C++ STL style iterator variable. See begin().
+ */
+ class Iterator {
+ private:
+ friend class Set<T>;
+
+ // Note: this is a Table iterator, we are currently defining
+ // Set iterator
+ typename Table<T, bool>::Iterator it;
+
+ Iterator(const typename Table<T, bool>::Iterator& it) : it(it) {}
+
+ public:
+ inline bool operator!=(const Iterator& other) const {
+ return !(*this == other);
+ }
+
+ bool operator==(const Iterator& other) const {
+ return it == other.it;
+ }
+
+ /**
+ Pre increment.
+ */
+ Iterator& operator++() {
+ ++it;
+ return *this;
+ }
+
+ /**
+ Post increment (slower than preincrement).
+ */
+ Iterator operator++(int) {
+ Iterator old = *this;
+ ++(*this);
+ return old;
+ }
+
+ const T& operator*() const {
+ return it->key;
+ }
+
+ T* operator->() const {
+ return &(it->key);
+ }
+
+ operator T*() const {
+ return &(it->key);
+ }
+ };
+
+
+ /**
+ C++ STL style iterator method. Returns the first member.
+ Use preincrement (++entry) to get to the next element.
+ Do not modify the set while iterating.
+ */
+ Iterator begin() const {
+ return Iterator(memberTable.begin());
+ }
+
+
+ /**
+ C++ STL style iterator method. Returns one after the last iterator
+ element.
+ */
+ const Iterator end() const {
+ return Iterator(memberTable.end());
+ }
+};
+
+}
+
+#endif
+
diff --git a/externals/g3dlite/G3D.lib/include/G3D/Sphere.h b/externals/g3dlite/G3D.lib/include/G3D/Sphere.h
new file mode 100644
index 00000000000..7d4c412185d
--- /dev/null
+++ b/externals/g3dlite/G3D.lib/include/G3D/Sphere.h
@@ -0,0 +1,143 @@
+/**
+ @file Sphere.h
+
+ Sphere class
+
+ @maintainer Morgan McGuire, matrix@graphics3d.com
+
+ @created 2001-06-02
+ @edited 2008-10-07
+ */
+
+#ifndef G3D_SPHERE_H
+#define G3D_SPHERE_H
+
+#include "G3D/platform.h"
+#include "G3D/Vector3.h"
+#include "G3D/Array.h"
+#include "G3D/Sphere.h"
+
+namespace G3D {
+
+/**
+ Sphere.
+ */
+class Sphere {
+private:
+
+ static int32 dummy;
+
+public:
+ Vector3 center;
+ float radius;
+
+ Sphere() {
+ center = Vector3::zero();
+ radius = 0;
+ }
+
+ Sphere(class BinaryInput& b);
+ void serialize(class BinaryOutput& b) const;
+ void deserialize(class BinaryInput& b);
+
+ Sphere(
+ const Vector3& center,
+ float radius) {
+
+ this->center = center;
+ this->radius = radius;
+ }
+
+ virtual ~Sphere() {}
+
+ bool operator==(const Sphere& other) const {
+ return (center == other.center) && (radius == other.radius);
+ }
+
+ bool operator!=(const Sphere& other) const {
+ return !((center == other.center) && (radius == other.radius));
+ }
+
+ /**
+ Returns true if point is less than or equal to radius away from
+ the center.
+ */
+ bool contains(const Vector3& point) const;
+
+ /**
+ @deprecated Use culledBy(Array<Plane>&)
+ */
+ bool culledBy(
+ const class Plane* plane,
+ int numPlanes,
+ int32& cullingPlaneIndex,
+ const uint32 testMask,
+ uint32& childMask) const;
+
+ /**
+ @deprecated Use culledBy(Array<Plane>&)
+ */
+ bool culledBy(
+ const class Plane* plane,
+ int numPlanes,
+ int32& cullingPlaneIndex = dummy,
+ const uint32 testMask = 0xFFFFFFFF) const;
+
+ /**
+ See AABox::culledBy
+ */
+ bool culledBy(
+ const Array<Plane>& plane,
+ int32& cullingPlaneIndex,
+ const uint32 testMask,
+ uint32& childMask) const;
+
+ /**
+ Conservative culling test that does not produce a mask for children.
+ */
+ bool culledBy(
+ const Array<Plane>& plane,
+ int32& cullingPlaneIndex = dummy,
+ const uint32 testMask = 0xFFFFFFFF) const;
+
+ virtual std::string toString() const;
+
+ float volume() const;
+
+ float area() const;
+
+ /**
+ Uniformly distributed on the surface.
+ */
+ Vector3 randomSurfacePoint() const;
+
+ /**
+ Uniformly distributed on the interior (includes surface)
+ */
+ Vector3 randomInteriorPoint() const;
+
+ void getBounds(class AABox& out) const;
+
+ bool intersects(const Sphere& other) const;
+
+ /** Translates the sphere */
+ Sphere operator+(const Vector3& v) const {
+ return Sphere(center + v, radius);
+ }
+
+ /** Translates the sphere */
+ Sphere operator-(const Vector3& v) const {
+ return Sphere(center - v, radius);
+ }
+};
+
+}
+
+template <> struct HashTrait<G3D::Sphere> {
+ static size_t hashCode(const G3D::Sphere& key) {
+ return static_cast<size_t>(key.center.hashCode() + (key.radius * 13));
+ }
+};
+
+
+#endif
diff --git a/externals/g3dlite/G3D.lib/include/G3D/Spline.h b/externals/g3dlite/G3D.lib/include/G3D/Spline.h
new file mode 100644
index 00000000000..af9b08230b8
--- /dev/null
+++ b/externals/g3dlite/G3D.lib/include/G3D/Spline.h
@@ -0,0 +1,367 @@
+/**
+ @file Spline.h
+
+ @author Morgan McGuire, morgan@cs.williams.edu
+ */
+
+#ifndef G3D_SPLINE_H
+#define G3D_SPLINE_H
+
+#include "G3D/platform.h"
+#include "G3D/Array.h"
+#include "G3D/g3dmath.h"
+#include "G3D/Matrix4.h"
+#include "G3D/Vector4.h"
+
+namespace G3D {
+
+/** Common implementation code for all G3D::Spline template parameters */
+class SplineBase {
+public:
+
+ /** Times at which control points occur. Must have the same
+ number of elements as Spline::control. */
+ Array<float> time;
+
+ /** If cyclic, then the control points will be assumed to wrap around.
+ If not cyclic, then the tangents at the ends of the spline
+ point to the final control points.*/
+ bool cyclic;
+
+ /** For a cyclic spline, this is the time elapsed between the last
+ control point and the first. If less than or equal to zero this is
+ assumed to be:
+
+ (time[0] - time[1] + .
+ time[time.size() - 1] - time[time.size() - 2]) / 2.
+ */
+ float finalInterval;
+
+ SplineBase() : cyclic(true), finalInterval(-1) {}
+
+ virtual ~SplineBase() {}
+
+ /** See specification for Spline::finalInterval; this handles the
+ non-positive case. Returns 0 if not cyclic. */
+ float getFinalInterval() const;
+
+ /** Returns the amount of time covered by this spline in one
+ period. For a cyclic spline, this contains the final
+ interval.*/
+ float duration() const;
+
+ /** Computes the derivative spline basis from the control point version. */
+ static Matrix4 computeBasis();
+
+protected:
+
+ /** Assumes that t0 <= s < tn. called by computeIndex. */
+ void computeIndexInBounds(float s, int& i, float& u) const;
+
+public:
+
+ /**
+ Given a time @a s, finds @a i and 0 <= @a u < 1 such that
+ @s = time[@a i] * @a u + time[@a i + 1] * (1 - @a u). Note that
+ @i may be outside the bounds of the time and control arrays;
+ use getControl to handle wraparound and extrapolation issues.
+
+ This function takes expected O(1) time for control points with
+ uniform time sampled control points or for uniformly
+ distributed random time samples, but may take O( log time.size() ) time
+ in the worst case.
+
+ Called from evaluate().
+ */
+ void computeIndex(float s, int& i, float& u) const;
+};
+
+
+/**
+ Smooth parameteric curve implemented using a piecewise 3rd-order
+ Catmull-Rom spline curve. The spline is considered infinite and may
+ either continue linearly from the specified control points or cycle
+ through them. Control points are spaced uniformly in time at unit
+ intervals by default, but irregular spacing may be explicitly
+ specified.
+
+ The dimension of the spline can be set by varying the Control
+ template parameter. For a 1D function, use Spline<float>. For a
+ curve in the plane, Spline<Vector2>. Note that <i>any</i> template
+ parameter that supports operator+(Control) and operator*(float) can
+ be used; you can make splines out of G3D::Vector4, G3D::Matrix3, or
+ your own classes.
+
+ To provide shortest-path interpolation, subclass G3D::Spline and
+ override ensureShortestPath(). To provide normalization of
+ interpolated points (e.g., projecting Quats onto the unit
+ hypersphere) override correct().
+
+ See Real Time Rendering, 2nd edition, ch 12 for a general discussion
+ of splines and their properties.
+
+ @sa G3D::UprightSpline, G3D::QuatSpline
+ */
+template<typename Control>
+class Spline : public SplineBase {
+protected:
+ /** The additive identity control point. */
+ Control zero;
+
+public:
+
+ /** Control points. Must have the same number of elements as
+ Spline::time.*/
+ Array<Control> control;
+
+ Spline() {
+ static Control x;
+ // Hide the fact from C++ that we are using an
+ // uninitialized variable here by pointer arithmetic.
+ // This is ok because any type that is a legal control
+ // point also supports multiplication by float.
+ zero = *(&x) * 0.0f;
+ }
+
+ /** Appends a control point at a specific time that must be
+ greater than that of the previous point. */
+ void append(float t, const Control& c) {
+ debugAssertM((time.size() == 0) || (t > time.last()),
+ "Control points must have monotonically increasing times.");
+ time.append(t);
+ control.append(c);
+ debugAssert(control.size() == time.size());
+ }
+
+
+ /** Appends control point spaced in time based on the previous
+ control point, or spaced at unit intervals if this is the
+ first control point. */
+ void append(const Control& c) {
+ switch (time.size()) {
+ case 0:
+ append(0, c);
+ break;
+
+ case 1:
+ if (time[0] == 0) {
+ append(1, c);
+ } else {
+ append(time[0], c);
+ }
+ break;
+
+ default:
+ append(2 * time[time.size() - 1] - time[time.size() - 2], c);
+ }
+ debugAssert(control.size() == time.size());
+ }
+
+ /** Erases all control points and times, but retains the state of
+ cyclic and finalInterval.
+ */
+ void clear() {
+ control.clear();
+ time.clear();
+ }
+
+
+ /** Number of control points */
+ int size() const {
+ debugAssert(time.size() == control.size());
+ return control.size();
+ }
+
+
+ /** Returns the requested control point and time sample based on
+ array index. If the array index is out of bounds, wraps (for
+ a cyclic spline) or linearly extrapolates (for a non-cyclic
+ spline), assuming time intervals follow the first or last
+ sample recorded.
+
+ Calls correct() on the control point if it was extrapolated.
+
+ Returns 0 if there are no control points.
+
+ @sa Spline::control and Spline::time for the underlying
+ control point array; Spline::computeIndex to find the index
+ given a time.
+ */
+ void getControl(int i, float& t, Control& c) const {
+ int N = control.size();
+ if (N == 0) {
+ c = zero;
+ t = 0;
+ } else if (cyclic) {
+ c = control[iWrap(i, N)];
+
+ if (i < 0) {
+ // Wrapped around bottom
+
+ // Number of times we wrapped around the cyclic array
+ int wraps = (N + 1 - i) / N;
+ int j = (i + wraps * N) % N;
+ t = time[j] - wraps * duration();
+
+ } else if (i < N) {
+
+ t = time[i];
+
+ } else {
+ // Wrapped around top
+
+ // Number of times we wrapped around the cyclic array
+ int wraps = i / N;
+ int j = i % N;
+ t = time[j] + wraps * duration();
+ }
+
+ } else if (i < 0) {
+ // Are there enough points to extrapolate?
+ if (N >= 2) {
+ // Step away from control point 0
+ float dt = time[1] - time[0];
+
+ // Extrapolate (note; i is negative)
+ c = control[1] * float(i) + control[0] * float(1 - i);
+ correct(c);
+ t = dt * i + time[0];
+
+ } else {
+ // Just clamp
+ c = control[0];
+
+ // Only 1 time; assume 1s intervals
+ t = time[0] + i;
+ }
+
+ } else if (i >= N) {
+ if (N >= 2) {
+ float dt = time[N - 1] - time[N - 2];
+
+ // Extrapolate
+ c = control[N - 1] * float(i - N + 2) + control[N - 2] * -float(i - N + 1);
+ correct(c);
+ t = time[N - 1] + dt * (i - N + 1);
+
+ } else {
+ // Return the last, clamping
+ c = control.last();
+ // Only 1 time; assume 1s intervals
+ t = time[0] + i;
+ }
+ } else {
+ // In bounds
+ c = control[i];
+ t = time[i];
+ }
+ }
+
+protected:
+
+ /** Returns a series of N control points and times, fixing
+ boundary issues. The indices may be assumed to be treated
+ cyclically. */
+ void getControls(int i, float* T, Control* A, int N) const {
+ for (int j = 0; j < N; ++j) {
+ getControl(i + j, T[j], A[j]);
+ }
+ ensureShortestPath(A, N);
+ }
+
+ /**
+ Mutates the array of N control points. It is useful to override this
+ method by one that wraps the values if they are angles or quaternions
+ for which "shortest path" interpolation is significant.
+ */
+ virtual void ensureShortestPath(Control* A, int N) const {}
+
+ /** Normalize or otherwise adjust this interpolated Control. */
+ virtual void correct(Control& A) const {}
+
+public:
+
+
+ /**
+ Return the position at time s. The spline is defined outside
+ of the time samples by extrapolation or cycling.
+ */
+ Control evaluate(float s) const {
+ debugAssertM(control.size() == time.size(), "Corrupt spline: wrong number of control points.");
+
+ /*
+ @cite http://www.gamedev.net/reference/articles/article1497.asp
+ Derivation of basis matrix follows.
+
+ Given control points with positions p[i] at times t[i], 0 <= i <= 3, find the position
+ at time t[1] <= s <= t[2].
+
+ Let u = s - t[0]
+ Let U = [u^0 u^1 u^2 u^3] = [1 u u^2 u^3]
+ Let dt0 = t[0] - t[-1]
+ Let dt1 = t[1] - t[0]
+ Let dt2 = t[2] - t[1]
+ */
+
+ // Index of the first control point (i.e., the u = 0 point)
+ int i = 0;
+ // Fractional part of the time
+ float u = 0;
+
+ computeIndex(s, i, u);
+
+ Control p[4];
+ float t[4];
+ getControls(i - 1, t, p, 4);
+ float dt0 = t[1] - t[0];
+ float dt1 = t[2] - t[1];
+ float dt2 = t[3] - t[2];
+
+ static const Matrix4 basis = computeBasis();
+
+ // Powers of u
+ Vector4 uvec((float)(u*u*u), (float)(u*u), (float)u, 1.0f);
+
+ // Compute the weights on each of the control points.
+ const Vector4& weights = uvec * basis;
+
+ // Compute the weighted sum of the neighboring control points.
+ Control sum;
+
+ const Control& p0 = p[0];
+ const Control& p1 = p[1];
+ const Control& p2 = p[2];
+ const Control& p3 = p[3];
+
+ const Control& dp0 = p1 + (p0*-1.0f);
+ const Control& dp1 = p2 + (p1*-1.0f);
+ const Control& dp2 = p3 + (p2*-1.0f);
+
+ // The factor of 1/2 from averaging two time intervals is
+ // already factored into the basis
+
+ // tan1 = (dp0 / dt0 + dp1 / dt1) * ((dt0 + dt1) * 0.5);
+ // The last term normalizes for unequal time intervals
+ float x = (dt0 + dt1) * 0.5f;
+ float n0 = x / dt0;
+ float n1 = x / dt1;
+ float n2 = x / dt2;
+ const Control& dp1n1 = dp1 * n1;
+ const Control& tan1 = dp0 * n0 + dp1n1;
+ const Control& tan2 = dp1n1 + dp2 * n2;
+
+ sum =
+ tan1 * weights[0]+
+ p1 * weights[1] +
+ p2 * weights[2] +
+ tan2 * weights[3];
+
+
+ correct(sum);
+ return sum;
+ }
+};
+
+}
+
+#endif
diff --git a/externals/g3dlite/G3D.lib/include/G3D/Stopwatch.h b/externals/g3dlite/G3D.lib/include/G3D/Stopwatch.h
new file mode 100644
index 00000000000..aee3b0f69c9
--- /dev/null
+++ b/externals/g3dlite/G3D.lib/include/G3D/Stopwatch.h
@@ -0,0 +1,108 @@
+/**
+ @file Stopwatch.h
+
+ @maintainer Morgan McGuire, matrix@graphics3d.com
+
+ @created 2005-10-05
+ @edited 2005-10-05
+
+ Copyright 2000-2003, Morgan McGuire.
+ All rights reserved.
+ */
+
+#ifndef G3D_STOPWATCH_H
+#define G3D_STOPWATCH_H
+
+#include "G3D/platform.h"
+#include "G3D/Queue.h"
+#include "G3D/G3DGameUnits.h"
+#include "G3D/g3dmath.h"
+
+namespace G3D {
+
+/**
+ Utility class for profiling duration and frame rates.
+ */
+class Stopwatch {
+private:
+ /** True between tick and tock */
+ bool inBetween;
+
+ /** The initial cycle count. */
+ uint64 cycleStart;
+
+ /** The time at which tick was called. */
+ RealTime timeStart;
+
+ /** The time at which the previous tock was called, -1 if never. */
+ RealTime lastTockTime;
+
+ RealTime lastDuration;
+ int64 lastCycleCount;
+
+ /** Frames per second. */
+ double m_fps;
+
+ /** Weighted fps */
+ double emwaFPS;
+ double m_smoothFPS;
+
+ /** Weighted duration */
+ RealTime emwaDuration;
+
+ /** The overhead for calling into the class. */
+ int64 cycleOverhead;
+
+ /** Called from the constructor. */
+ void computeOverhead();
+
+public:
+
+ Stopwatch();
+
+ /** Returns the number of times that tick was called per wall-clock second;
+ e.g. frames-per-second. */
+ double FPS() const {
+ return m_fps;
+ }
+
+ /** Amount of time between the most recent tick and tock calls. 0 if tick has
+ never been called. */
+ RealTime elapsedTime() const {
+ return lastDuration;
+ }
+
+ /** Time-smoothed value that is stable to the nearest 1%.
+ This is useful if you are displaying elapsed time in real-time
+ and want a stable number.*/
+ RealTime smoothElapsedTime() const {
+ return emwaDuration;
+ }
+
+ /** Time-smoothed value of fps that is stable to the nearest integer for fps > 10 and
+ to the first decimal place for fps <= 10.
+ This is useful if you
+ are displaying the frame rate in real-time and want a stable (readable) number.*/
+ double smoothFPS() const {
+ return m_smoothFPS;
+ }
+
+ /** The elapsed cycle time between tick and tock. An attempt is made to factor out all
+ tick/tock overhead, so that back-to-back calls should return zero.
+ Unreliable on non-x86 platforms.*/
+ uint64 elapsedCycles() const {
+ return lastCycleCount;
+ }
+
+ /** Call at the beginning of the period that you want timed. */
+ void tick();
+
+ /** Call at the end of the period that you want timed. */
+ void tock();
+};
+
+
+}
+
+#endif
+
diff --git a/externals/g3dlite/G3D.lib/include/G3D/System.h b/externals/g3dlite/G3D.lib/include/G3D/System.h
new file mode 100644
index 00000000000..3755dc5e36f
--- /dev/null
+++ b/externals/g3dlite/G3D.lib/include/G3D/System.h
@@ -0,0 +1,390 @@
+/**
+ @file System.h
+
+ @maintainer Morgan McGuire, matrix@graphics3d.com
+
+ @cite Rob Wyatt http://www.gamasutra.com/features/wyatts_world/19990709/processor_detection_01.htm
+ @cite Benjamin Jurke http://www.flipcode.com/cgi-bin/msg.cgi?showThread=COTD-ProcessorDetectionClass&forum=cotd&id=-1
+ @cite Michael Herf http://www.stereopsis.com/memcpy.html
+
+ @created 2003-01-25
+ @edited 2008-10-14
+ */
+
+#ifndef G3D_SYSTEM_H
+#define G3D_SYSTEM_H
+
+#include "G3D/platform.h"
+#include "G3D/g3dmath.h"
+#include "G3D/G3DGameUnits.h"
+#include "G3D/BinaryFormat.h"
+#include <string>
+
+#ifdef G3D_OSX
+# include <CoreServices/CoreServices.h>
+#endif
+
+namespace G3D {
+
+/**
+ Routine used by the demos to find the data. Searches in
+ ../data, ../../data, etc. up to 5 levels back. Checks
+ common locations like c:\libraries\g3d-<version>\data
+ and some hard-coded paths on the Brown University file
+ system.
+ */
+std::string demoFindData(bool errorIfNotFound = true);
+
+/** G3D, SDL, and IJG libraries require license documentation
+ to be distributed with your program. This generates the
+ string that must appear in your documentation.
+ <B>Your program can be commercial, closed-source</B> under
+ any license you want.*/
+std::string license();
+
+/**
+The order in which the bytes of an integer are stored on a machine. Intel/AMD chips tend to be G3D_LITTLE_ENDIAN, Mac PPC's and Suns are G3D_BIG_ENDIAN. However, this is primarily used to specify the byte order of file formats, which are fixed.
+*/
+enum G3DEndian {
+ G3D_BIG_ENDIAN, G3D_LITTLE_ENDIAN
+};
+
+/**
+ OS and processor abstraction. The first time any method is called the processor
+ will be analyzed. Future calls are then fast.
+
+ Timing function overview:
+ System::getCycleCount
+ - actual cycle count
+
+ System::getTick
+ - High-resolution time in seconds since program started
+
+ System::getLocalTime
+ - High-resolution time in seconds since Jan 1, 1970
+ (because it is stored in a double, this may be less
+ accurate than getTick)
+
+ */
+class System {
+public:
+
+ /** Called automatically by the other System routines.*/
+ static void init();
+
+ /** */
+ static bool hasMMX();
+
+ /** */
+ static bool hasCPUID();
+
+ /** */
+ static bool hasSSE();
+
+ /** */
+ static bool hasSSE2();
+
+ /** */
+ static bool hasSSE3();
+
+ /** */
+ static bool has3DNow();
+
+
+ /** */
+ static bool hasRDTSC();
+
+ static const std::string& cpuVendor();
+
+ /** e.g. "Windows", "GNU/Linux" */
+ static const std::string& operatingSystem();
+
+ /** */
+ static const std::string& cpuArchitecture();
+
+ /**
+ Returns the endianness of this machine.
+ */
+ static G3DEndian machineEndian();
+
+ /**
+ Returns the current date as a string in the form YYYY-MM-DD
+ */
+ static std::string currentDateString();
+
+ /**
+ Guarantees that the start of the array is aligned to the
+ specified number of bytes.
+ */
+ static void* alignedMalloc(size_t bytes, size_t alignment);
+
+ /**
+ Uses pooled storage to optimize small allocations (1 byte to 5 kilobytes).
+ Can be 10x to 100x faster than calling ::malloc or new.
+
+ The result must be freed with free.
+
+ Threadsafe on Win32.
+
+ @sa calloc realloc OutOfMemoryCallback free
+ */
+ static void* malloc(size_t bytes);
+
+ static void* calloc(size_t n, size_t x);
+
+ /**
+ @param size Size of memory that the system was trying to allocate
+ @param recoverable If true, the system will attempt to allocate again
+ if the callback returns true. If false, malloc is going to return
+ NULL and this invocation is just to notify the application.
+ @return Return true to force malloc to attempt allocation again if the
+ error was recoverable.
+ */
+ typedef bool (*OutOfMemoryCallback)(size_t size, bool recoverable);
+
+ /**
+ When System::malloc fails to allocate memory because the system is
+ out of memory, it invokes this handler (if it is not NULL).
+ The argument to the callback is the amount of memory that malloc
+ was trying to allocate when it ran out. If the callback returns
+ true, System::malloc will attempt to allocate the memory again.
+ If the callback returns false, then System::malloc will return NULL.
+
+ You can use outOfMemoryCallback to free data structures or to
+ register the failure.
+ */
+ static OutOfMemoryCallback outOfMemoryCallback;
+
+ /**
+ Version of realloc that works with System::malloc.
+ */
+ static void* realloc(void* block, size_t bytes);
+
+ /** Returns a string describing how well System::malloc is using its internal pooled storage.
+ "heap" memory was slow to allocate; the other data sizes are comparatively fast.*/
+ static std::string mallocPerformance();
+ static void resetMallocPerformanceCounters();
+
+ /**
+ Returns a string describing the current usage of the buffer pools used for
+ optimizing System::malloc.
+ */
+ static std::string mallocStatus();
+
+ /**
+ Free data allocated with System::malloc.
+
+ Threadsafe on Win32.
+ */
+ static void free(void* p);
+
+ /**
+ Frees memory allocated with alignedMalloc.
+ */
+ static void alignedFree(void* ptr);
+
+ /** An implementation of memcpy that may be up to 2x as fast as the C library
+ one on some processors. Guaranteed to have the same behavior as memcpy
+ in all cases. */
+ static void memcpy(void* dst, const void* src, size_t numBytes);
+
+ /** An implementation of memset that may be up to 2x as fast as the C library
+ one on some processors. Guaranteed to have the same behavior as memset
+ in all cases. */
+ static void memset(void* dst, uint8 value, size_t numBytes);
+
+ /**
+ Returns the fully qualified filename for the currently running executable.
+
+ This is more reliable than arg[0], which may be intentionally set
+ to an incorrect value by a calling program, relative to a now
+ non-current directory, or obfuscated by sym-links.
+
+ @cite Linux version written by Nicolai Haehnle <prefect_@gmx.net>, http://www.flipcode.com/cgi-bin/msg.cgi?showThread=COTD-getexename&forum=cotd&id=-1
+ */
+ static std::string currentProgramFilename();
+
+ /** Name of this program. Note that you can mutate this string to set your app name explicitly.*/
+ static std::string& appName();
+
+ /** G3D Version string */
+ static const std::string& version();
+
+ /**
+ Either Debug or Release, depending on whether _DEBUG was defined at compile-time for the library.
+ */
+ static const std::string& build();
+
+ /**
+ Causes the current thread to yield for the specified duration
+ and consume almost no CPU.
+ The sleep will be extremely precise; it uses System::time()
+ to calibrate the exact yeild time.
+ */
+ static void sleep(RealTime t);
+
+ /**
+ Clears the console.
+ Console programs only.
+ */
+ static void consoleClearScreen();
+
+ /**
+ Returns true if a key is waiting.
+ Console programs only.
+ */
+ static bool consoleKeyPressed();
+
+ /**
+ Blocks until a key is read (use consoleKeyPressed to determine if
+ a key is waiting to be read) then returns the character code for
+ that key.
+ */
+ static int consoleReadKey();
+
+ /**
+ The actual time (measured in seconds since
+ Jan 1 1970 midnight).
+
+ Adjusted for local timezone and daylight savings
+ time. This is as accurate and fast as getCycleCount().
+ */
+ static RealTime time();
+
+ /**
+ To count the number of cycles a given operation takes:
+
+ <PRE>
+ unsigned long count;
+ System::beginCycleCount(count);
+ ...
+ System::endCycleCount(count);
+ // count now contains the cycle count for the intervening operation.
+
+ */
+ static void beginCycleCount(uint64& cycleCount);
+ static void endCycleCount(uint64& cycleCount);
+
+ static uint64 getCycleCount();
+
+ /** Set an environment variable for the current process */
+ static void setEnv(const std::string& name, const std::string& value);
+
+ /** Get an environment variable for the current process. Returns NULL if the variable doesn't exist. */
+ static const char* getEnv(const std::string& name);
+
+ /**
+ Prints a human-readable description of this machine
+ to the text output stream. Either argument may be NULL.
+ */
+ static void describeSystem(
+ class TextOutput& t);
+
+ static void describeSystem(
+ std::string& s);
+
+ /** Returns the speed of processor 0 in MHz.
+ Always returns 0 on linux.*/
+ static int cpuSpeedMHz();
+
+
+ /** On Win32, returns the clipboard text contents. Does nothing on other
+ platforms (yet) */
+ static std::string getClipboardText();
+
+ /** Copies the text to the clipboard on Win32. */
+ static void setClipboardText(const std::string& s);
+
+ /**
+ Tries to locate the resource by looking in related directories.
+ If found, returns the full path to the resource, otherwise
+ returns the empty string.
+ */
+ static std::string findDataFile(const std::string& full, bool errorIfNotFound = true);
+
+ /**
+ Sets the path that the application is using as its data directory.
+ Used by findDataDir as an initial search location. GApp sets this
+ upon constrution.
+ */
+ static void setAppDataDir(const std::string& path);
+
+private:
+ /**
+ (CKO) Note: Not sure why these are specifically needed
+ for OS X. I made them private though.
+ */
+# ifdef G3D_OSX
+ static long m_OSXCPUSpeed; //In Cycles/Second
+ static double m_secondsPerNS;
+# endif
+};
+
+
+#ifdef _MSC_VER
+ inline uint64 System::getCycleCount() {
+ uint32 timehi, timelo;
+
+ // Use the assembly instruction rdtsc, which gets the current
+ // cycle count (since the process started) and puts it in edx:eax.
+ __asm
+ {
+ rdtsc;
+ mov timehi, edx;
+ mov timelo, eax;
+ }
+
+ return ((uint64)timehi << 32) + (uint64)timelo;
+ }
+
+#elif defined(G3D_LINUX)
+
+ inline uint64 System::getCycleCount() {
+ uint32 timehi, timelo;
+
+ __asm__ __volatile__ (
+ "rdtsc "
+ : "=a" (timelo),
+ "=d" (timehi)
+ : );
+
+ return ((uint64)timehi << 32) + (uint64)timelo;
+ }
+
+#elif defined(G3D_OSX)
+
+ inline uint64 System::getCycleCount() {
+ //Note: To put off extra processing until the end, this does not
+ //return the actual clock cycle count. It is a bus cycle count.
+ //When endCycleCount() is called, it converts the two into a difference
+ //of clock cycles
+
+ return (uint64) UnsignedWideToUInt64(UpTime());
+ //return (uint64) mach_absolute_time();
+ }
+
+#endif
+
+inline void System::beginCycleCount(uint64& cycleCount) {
+ cycleCount = getCycleCount();
+}
+
+
+inline void System::endCycleCount(uint64& cycleCount) {
+ #ifndef G3D_OSX
+ cycleCount = getCycleCount() - cycleCount;
+ #else
+ AbsoluteTime end = UpTime();
+ init();
+ Nanoseconds diffNS =
+ AbsoluteDeltaToNanoseconds(end, UInt64ToUnsignedWide(cycleCount));
+ cycleCount =
+ (uint64) ((double) (System::m_OSXCPUSpeed) *
+ (double) UnsignedWideToUInt64(diffNS) * m_secondsPerNS);
+ #endif
+}
+
+
+} // namespace
+
+#endif
diff --git a/externals/g3dlite/G3D.lib/include/G3D/Table.h b/externals/g3dlite/G3D.lib/include/G3D/Table.h
new file mode 100644
index 00000000000..9ccd4f5c101
--- /dev/null
+++ b/externals/g3dlite/G3D.lib/include/G3D/Table.h
@@ -0,0 +1,770 @@
+/**
+ @file Table.h
+
+ Templated hash table class.
+
+ @maintainer Morgan McGuire, morgan@cs.williams.edu
+ @created 2001-04-22
+ @edited 2008-07-01
+ Copyright 2000-2008, Morgan McGuire.
+ All rights reserved.
+ */
+
+#ifndef G3D_TABLE_H
+#define G3D_TABLE_H
+
+#include <cstddef>
+#include <string>
+
+#include "G3D/platform.h"
+#include "G3D/Array.h"
+#include "G3D/debug.h"
+#include "G3D/System.h"
+#include "G3D/g3dmath.h"
+#include "G3D/EqualsTrait.h"
+#include "G3D/HashTrait.h"
+
+#ifdef _MSC_VER
+# pragma warning (push)
+ // Debug name too long warning
+# pragma warning (disable : 4786)
+#endif
+
+namespace G3D {
+
+/**
+ An unordered data structure mapping keys to values.
+
+ Key must be a pointer, an int, a std::string or provide overloads for:
+
+ <PRE>
+ template<> struct HashTrait<class Key> {
+ static size_t hashCode(const Key& key) { return reinterpret_cast<size_t>( ... ); }
+ };
+ </PRE>
+
+ and one of
+
+ <PRE>
+ template<> struct EqualsTrait<class Key>{
+ static bool equals(const Key& a, const Key& b) { return ... ; }
+ };
+
+
+ bool operator==(const Key&, const Key&);
+ </PRE>
+
+ G3D pre-defines HashTrait specializations for common types (like <CODE>int</CODE> and <CODE>std::string</CODE>).
+ If you use a Table with a different type you must write those functions yourself. For example,
+ an enum would use:
+
+ <PRE>
+ template<> struct HashTrait<MyEnum> {
+ static size_t equals(const MyEnum& key) const { return reinterpret_cast<size_t>( key ); }
+ };
+ </PRE>
+
+ And rely on the default enum operator==.
+
+
+ Periodically check that debugGetLoad() is low (> 0.1). When it gets near
+ 1.0 your hash function is badly designed and maps too many inputs to
+ the same output.
+ */
+template<class Key, class Value, class HashFunc = HashTrait<Key>, class EqualsFunc = EqualsTrait<Key> >
+class Table {
+public:
+
+ /**
+ The pairs returned by iterator.
+ */
+ class Entry {
+ public:
+ Key key;
+ Value value;
+ Entry() {}
+ Entry(const Key& k, const Value& v) : key(k), value(v) {}
+ };
+
+private:
+
+ typedef Table<Key, Value, HashFunc, EqualsFunc> ThisType;
+
+ /**
+ Linked list nodes used internally by HashTable.
+ */
+ class Node {
+ public:
+ Entry entry;
+ size_t hashCode;
+ Node* next;
+
+ /** Provide pooled allocation for speed. */
+ inline void* operator new (size_t size) {
+ return System::malloc(size);
+ }
+
+ inline void operator delete (void* p) {
+ System::free(p);
+ }
+
+ Node(const Key& k, const Value& v, size_t h, Node* n)
+ : entry(k, v), hashCode(h), next(n) {
+ }
+
+ /**
+ Clones a whole chain;
+ */
+ Node* clone() {
+ return new Node(this->entry.key, this->entry.value, hashCode, (next == NULL) ? NULL : next->clone());
+ }
+ };
+
+ void checkIntegrity() const {
+# ifdef G3D_DEBUG
+ debugAssert(bucket == NULL || isValidHeapPointer(bucket));
+ for (size_t b = 0; b < numBuckets; ++b) {
+ Node* node = bucket[b];
+ debugAssert(node == NULL || isValidHeapPointer(node));
+ while (node != NULL) {
+ debugAssert(node == NULL || isValidHeapPointer(node));
+ node = node->next;
+ }
+ }
+# endif
+ }
+
+ /**
+ Number of elements in the table.
+ */
+ size_t _size;
+
+ /**
+ Array of Node*.
+ We don't use Array<Node*> because Table is lower level.
+ Some elements may be NULL.
+ */
+ Node** bucket;
+
+ /**
+ Length of the bucket array.
+ */
+ size_t numBuckets;
+
+ /**
+ Re-hashes for a larger bucket size.
+ */
+ void resize(size_t newSize) {
+
+ // Hang onto the old bucket array
+ Node** oldBucket = bucket;
+
+ // Allocate a new bucket array with the new size
+ bucket = (Node**)System::calloc(sizeof(Node*), newSize);
+ debugAssertM(bucket != NULL, "System::calloc returned NULL. Out of memory.");
+ // Move each node to its new hash location
+ for (size_t b = 0; b < numBuckets; ++b) {
+ Node* node = oldBucket[b];
+
+ // There is a linked list of nodes at this bucket
+ while (node != NULL) {
+ // Hang onto the old next pointer
+ Node* nextNode = node->next;
+
+ // Insert at the head of the list for bucket[i]
+ size_t i = node->hashCode % newSize;
+ node->next = bucket[i];
+ bucket[i] = node;
+
+ // Move on to the next node
+ node = nextNode;
+ }
+
+ // Drop the old pointer for cleanliness when debugging
+ oldBucket[b] = NULL;
+ }
+
+ // Delete the old storage
+ System::free(oldBucket);
+ this->numBuckets = newSize;
+
+ checkIntegrity();
+ }
+
+
+ void copyFrom(const ThisType& h) {
+ if (&h == this) {
+ return;
+ }
+
+ debugAssert(bucket == NULL);
+ _size = h._size;
+ numBuckets = h.numBuckets;
+ bucket = (Node**)System::calloc(sizeof(Node*), numBuckets);
+
+ for (size_t b = 0; b < numBuckets; b++) {
+ if (h.bucket[b] != NULL) {
+ bucket[b] = h.bucket[b]->clone();
+ }
+ }
+
+ checkIntegrity();
+ }
+
+ /**
+ Frees the heap structures for the nodes.
+ */
+ void freeMemory() {
+ checkIntegrity();
+
+ for (size_t b = 0; b < numBuckets; b++) {
+ Node* node = bucket[b];
+ while (node != NULL) {
+ Node* next = node->next;
+ delete node;
+ node = next;
+ }
+ bucket[b] = NULL;
+ }
+ System::free(bucket);
+ bucket = NULL;
+ numBuckets = 0;
+ _size = 0;
+ }
+
+
+public:
+
+ /**
+ Creates an empty hash table. This causes some heap allocation to occur.
+ */
+ Table() : bucket(NULL) {
+ numBuckets = 10;
+ _size = 0;
+ bucket = (Node**)System::calloc(sizeof(Node*), numBuckets);
+ checkIntegrity();
+ }
+
+ /**
+ Recommends that the table resize to anticipate at least this number of elements.
+ */
+ void setSizeHint(size_t n) {
+ size_t s = n * 3;
+ if (s > numBuckets) {
+ resize(s);
+ }
+ }
+
+ /**
+ Destroys all of the memory allocated by the table, but does <B>not</B>
+ call delete on keys or values if they are pointers. If you want to
+ deallocate things that the table points at, use getKeys() and Array::deleteAll()
+ to delete them.
+ */
+ virtual ~Table() {
+ freeMemory();
+ }
+
+ Table(const ThisType& h) {
+ numBuckets = 0;
+ _size = 0;
+ bucket = NULL;
+ this->copyFrom(h);
+ checkIntegrity();
+ }
+
+
+ Table& operator=(const ThisType& h) {
+ // No need to copy if the argument is this
+ if (this != &h) {
+ // Free the existing nodes
+ freeMemory();
+ this->copyFrom(h);
+ checkIntegrity();
+ }
+ return *this;
+ }
+
+ /**
+ Returns the length of the deepest bucket.
+ */
+ size_t debugGetDeepestBucketSize() const {
+ size_t deepest = 0;
+
+ for (size_t b = 0; b < numBuckets; b++) {
+ size_t count = 0;
+ Node* node = bucket[b];
+ while (node != NULL) {
+ node = node->next;
+ ++count;
+ }
+
+ if (count > deepest) {
+ deepest = count;
+ }
+ }
+
+ return deepest;
+ }
+
+ /**
+ Returns the average size of non-empty buckets.
+ */
+ float debugGetAverageBucketSize() const {
+ size_t num = 0;
+ size_t count = 0;
+
+ for (size_t b = 0; b < numBuckets; b++) {
+ Node* node = bucket[b];
+ if (node != NULL) {
+ ++num;
+ while (node != NULL) {
+ node = node->next;
+ ++count;
+ }
+ }
+ }
+
+ return (float)((double)count / num);
+ }
+
+ /**
+ A small load (close to zero) means the hash table is acting very
+ efficiently most of the time. A large load (close to 1) means
+ the hash table is acting poorly-- all operations will be very slow.
+ A large load will result from a bad hash function that maps too
+ many keys to the same code.
+ */
+ double debugGetLoad() const {
+ return debugGetDeepestBucketSize() / (double)size();
+ }
+
+ /**
+ Returns the number of buckets.
+ */
+ size_t debugGetNumBuckets() const {
+ return numBuckets;
+ }
+
+ /**
+ C++ STL style iterator variable. See begin().
+ */
+ class Iterator {
+ private:
+ friend class Table<Key, Value, HashFunc, EqualsFunc>;
+
+ /**
+ Bucket index.
+ */
+ size_t index;
+
+ /**
+ Linked list node.
+ */
+ Node* node;
+ ThisType* table;
+ size_t numBuckets;
+ Node** bucket;
+ bool isDone;
+
+ /**
+ Creates the end iterator.
+ */
+ Iterator(const ThisType* table) : table(const_cast<ThisType*>(table)) {
+ isDone = true;
+ }
+
+ Iterator(const ThisType* table, size_t numBuckets, Node** bucket) :
+ table(const_cast<ThisType*>(table)),
+ numBuckets(numBuckets),
+ bucket(bucket) {
+
+ if (numBuckets == 0) {
+ // Empty table
+ isDone = true;
+ return;
+ }
+
+ index = 0;
+ node = bucket[index];
+ isDone = false;
+ findNext();
+ }
+
+ /**
+ Finds the next element, setting isDone if one can't be found.
+ Looks at the current element first.
+ */
+ void findNext() {
+ while (node == NULL) {
+ index++;
+ if (index >= numBuckets) {
+ isDone = true;
+ break;
+ } else {
+ node = bucket[index];
+ }
+ }
+ }
+
+ public:
+ inline bool operator!=(const Iterator& other) const {
+ return !(*this == other);
+ }
+
+ bool operator==(const Iterator& other) const {
+ if (other.isDone || isDone) {
+ // Common case; check against isDone.
+ return (isDone == other.isDone) && (other.table == table);
+ } else {
+ return
+ (table == other.table) &&
+ (node == other.node) &&
+ (index == other.index);
+ }
+ }
+
+ /**
+ Pre increment.
+ */
+ Iterator& operator++() {
+ node = node->next;
+ findNext();
+ return *this;
+ }
+
+ /**
+ Post increment (slower than preincrement).
+ */
+ Iterator operator++(int) {
+ Iterator old = *this;
+ ++(*this);
+ return old;
+ }
+
+ const Entry& operator*() const {
+ return node->entry;
+ }
+
+ Entry* operator->() const {
+ return &(node->entry);
+ }
+
+ operator Entry*() const {
+ return &(node->entry);
+ }
+
+ bool hasMore() const {
+ return ! isDone;
+ }
+ };
+
+
+ /**
+ C++ STL style iterator method. Returns the first Entry, which
+ contains a key and value. Use preincrement (++entry) to get to
+ the next element. Do not modify the table while iterating.
+ */
+ Iterator begin() const {
+ return Iterator(this, numBuckets, bucket);
+ }
+
+ /**
+ C++ STL style iterator method. Returns one after the last iterator
+ element.
+ */
+ const Iterator end() const {
+ return Iterator(this);
+ }
+
+ /**
+ Removes all elements
+ */
+ void clear() {
+ freeMemory();
+ numBuckets = 10;
+ _size = 0;
+ bucket = (Node**)System::calloc(sizeof(Node*), numBuckets);
+ }
+
+
+ /**
+ Returns the number of keys.
+ */
+ size_t size() const {
+ return _size;
+ }
+
+
+ /**
+ If you insert a pointer into the key or value of a table, you are
+ responsible for deallocating the object eventually. Inserting
+ key into a table is O(1), but may cause a potentially slow rehashing.
+ */
+ void set(const Key& key, const Value& value) {
+ size_t code = HashFunc::hashCode(key);
+ size_t b = code % numBuckets;
+
+ // Go to the bucket
+ Node* n = bucket[b];
+
+ // No bucket, so this must be the first
+ if (n == NULL) {
+ bucket[b] = new Node(key, value, code, NULL);
+ ++_size;
+ return;
+ }
+
+ size_t bucketLength = 1;
+
+ // Sometimes a bad hash code will cause all elements
+ // to collide. Detect this case and don't rehash when
+ // it occurs; nothing good will come from the rehashing.
+ bool allSameCode = true;
+
+ // Try to find the node
+ do {
+ allSameCode = allSameCode && (code == n->hashCode);
+
+ if ((code == n->hashCode) && EqualsFunc::equals(n->entry.key, key)) {
+ // Replace the existing node.
+ n->entry.value = value;
+ return;
+ }
+
+ n = n->next;
+ ++bucketLength;
+ } while (n != NULL);
+
+ const size_t maxBucketLength = 3;
+ // (Don't bother changing the size of the table if all entries
+ // have the same hashcode--they'll still collide)
+ if ((bucketLength > maxBucketLength) &&
+ ! allSameCode &&
+ (numBuckets < _size * 15)) {
+
+ // This bucket was really large; rehash if all elements
+ // don't have the same hashcode the number of buckets is
+ // reasonable.
+
+ // Back off the scale factor as the number of buckets gets
+ // large
+ float f = 3.0f;
+ if (numBuckets > 1000000) {
+ f = 1.5f;
+ } else if (numBuckets > 100000) {
+ f = 2.0f;
+ }
+ int newSize = iMax((int)(numBuckets * f) + 1, (int)(_size * f));
+ resize(newSize);
+ }
+
+ // Not found; insert at the head.
+ b = code % numBuckets;
+ bucket[b] = new Node(key, value, code, bucket[b]);
+ ++_size;
+ }
+
+ /**
+ Removes an element from the table if it is present.
+ @return true if the element was found and removed, otherwise false
+ */
+ bool remove(const Key& key) {
+
+ size_t code = HashFunc::hashCode(key);
+ size_t b = code % numBuckets;
+
+ // Go to the bucket
+ Node* n = bucket[b];
+
+ if (n == NULL) {
+ return false;
+ }
+
+ Node* previous = NULL;
+
+ // Try to find the node
+ do {
+ if ((code == n->hashCode) && EqualsFunc::equals(n->entry.key, key)) {
+ // This is the node; remove it
+
+ // Replace the previous's next pointer
+ if (previous == NULL) {
+ bucket[b] = n->next;
+ } else {
+ previous->next = n->next;
+ }
+
+ // Delete the node
+ delete n;
+ --_size;
+ return true;
+ }
+
+ previous = n;
+ n = n->next;
+ } while (n != NULL);
+
+
+ return false;
+ //alwaysAssertM(false, "Tried to remove a key that was not in the table.");
+ }
+
+ /**
+ Returns the value associated with key.
+ @deprecated Use get(key, val) or getPointer(key)
+ */
+ Value& get(const Key& key) const {
+
+ size_t code = HashFunc::hashCode(key);
+ size_t b = code % numBuckets;
+
+ Node* node = bucket[b];
+
+ while (node != NULL) {
+ if ((node->hashCode == code) && EqualsFunc::equals(node->entry.key, key)) {
+ return node->entry.value;
+ }
+ node = node->next;
+ }
+
+ debugAssertM(false, "Key not found");
+ // The next line is here just to make
+ // a compiler warning go away.
+ return node->entry.value;
+ }
+
+
+ /** Returns a pointer to the element if it exists, or NULL if it does not.
+ Note that if your value type <i>is</i> a pointer, the return value is
+ a pointer to a pointer. Do not remove the element while holding this
+ pointer.
+
+ It is easy to accidentally mis-use this method. Consider making
+ a Table<Value*> and using get(key, val) instead, which makes you manage
+ the memory for the values yourself and is less likely to result in
+ pointer errors.
+ */
+ Value* getPointer(const Key& key) const {
+ size_t code = HashFunc::hashCode(key);
+ size_t b = code % numBuckets;
+
+ Node* node = bucket[b];
+
+ while (node != NULL) {
+ if ((node->hashCode == code) && EqualsFunc::equals(node->entry.key, key)) {
+ // found key
+ return &(node->entry.value);
+ }
+ node = node->next;
+ }
+
+ // Failed to find key
+ return NULL;
+ }
+
+ /**
+ If the key is present in the table, val is set to the associated value and returns true.
+ If the key is not present, returns false.
+ */
+ bool get(const Key& key, Value& val) const {
+ Value* v = getPointer(key);
+ if (v != NULL) {
+ val = *v;
+ return true;
+ } else {
+ return false;
+ }
+ }
+
+ /**
+ Returns true if key is in the table.
+ */
+ bool containsKey(const Key& key) const {
+ size_t code = HashFunc::hashCode(key);
+ size_t b = code % numBuckets;
+
+ Node* node = bucket[b];
+
+ while (node != NULL) {
+ if ((node->hashCode == code) && EqualsFunc::equals(node->entry.key, key)) {
+ return true;
+ }
+ node = node->next;
+ } while (node != NULL);
+
+ return false;
+ }
+
+
+ /**
+ Short syntax for get.
+ */
+ inline Value& operator[](const Key &key) const {
+ return get(key);
+ }
+
+
+ /**
+ Returns an array of all of the keys in the table.
+ You can iterate over the keys to get the values.
+ @deprecated
+ */
+ Array<Key> getKeys() const {
+ Array<Key> keyArray;
+ getKeys(keyArray);
+ return keyArray;
+ }
+
+ void getKeys(Array<Key>& keyArray) const {
+ keyArray.resize(0, DONT_SHRINK_UNDERLYING_ARRAY);
+ for (size_t i = 0; i < numBuckets; i++) {
+ Node* node = bucket[i];
+ while (node != NULL) {
+ keyArray.append(node->entry.key);
+ node = node->next;
+ }
+ }
+ }
+
+ /**
+ Calls delete on all of the keys and then clears the table.
+ */
+ void deleteKeys() {
+ for (size_t i = 0; i < numBuckets; i++) {
+ Node* node = bucket[i];
+ while (node != NULL) {
+ delete node->entry.key;
+ node = node->next;
+ }
+ }
+ clear();
+ }
+
+ /**
+ Calls delete on all of the values. This is unsafe--
+ do not call unless you know that each value appears
+ at most once.
+
+ Does not clear the table, so you are left with a table
+ of NULL pointers.
+ */
+ void deleteValues() {
+ for (size_t i = 0; i < numBuckets; ++i) {
+ Node* node = bucket[i];
+ while (node != NULL) {
+ delete node->entry.value;
+ node->entry.value = NULL;
+ node = node->next;
+ }
+ }
+ }
+};
+
+} // namespace
+
+#ifdef _MSC_VER
+# pragma warning (pop)
+#endif
+
+#endif
diff --git a/externals/g3dlite/G3D.lib/include/G3D/TextInput.h b/externals/g3dlite/G3D.lib/include/G3D/TextInput.h
new file mode 100644
index 00000000000..b6dcad39b8b
--- /dev/null
+++ b/externals/g3dlite/G3D.lib/include/G3D/TextInput.h
@@ -0,0 +1,693 @@
+/**
+ @file TextInput.h
+
+ Simple text lexer/tokenizer.
+
+ @maintainer Morgan McGuire, morgan@graphics3d.com
+
+ @cite Based on a lexer written by Aaron Orenstein.
+
+ @created 2002-11-27
+ @edited 2006-10-24
+
+ Copyright 2000-2007, Morgan McGuire.
+ All rights reserved.
+ */
+
+#ifndef G3D_TEXTINPUT_H
+#define G3D_TEXTINPUT_H
+
+#include "G3D/platform.h"
+#include "G3D/Array.h"
+#include "G3D/Set.h"
+#include <string>
+#include <queue>
+#include <ctype.h>
+#include <stdio.h>
+
+namespace G3D {
+
+/**
+ For use with TextInput.
+ */
+class Token {
+public:
+
+ /**
+ More detailed type information than Type.
+ */
+ enum ExtendedType {
+ DOUBLE_QUOTED_TYPE,
+ SINGLE_QUOTED_TYPE,
+ SYMBOL_TYPE,
+ FLOATING_POINT_TYPE,
+ INTEGER_TYPE,
+ BOOLEAN_TYPE,
+ END_TYPE
+ };
+
+ /**
+ Strings are enclosed in quotes, symbols are not.
+ */
+ enum Type {
+ STRING = DOUBLE_QUOTED_TYPE,
+ SYMBOL = SYMBOL_TYPE,
+ NUMBER = FLOATING_POINT_TYPE,
+ BOOLEAN = BOOLEAN_TYPE,
+ END = END_TYPE
+ };
+
+private:
+
+ friend class TextInput;
+
+ /**
+ Holds the actual value, which might be any type. If a number, it will be
+ parsed at runtime.
+ */
+ std::string _string;
+
+ bool _bool;
+ int _line;
+ int _character;
+ Type _type;
+ ExtendedType _extendedType;
+
+public:
+
+ Token() :
+ _string(""),
+ _bool(false),
+ _line(0),
+ _character(0),
+ _type(END),
+ _extendedType(END_TYPE) {}
+
+ Token(Type t, ExtendedType e, const std::string& s, int L, int c)
+ : _string(s), _bool(false), _line(L), _character(c), _type(t), _extendedType(e) {}
+
+ Token(Type t, ExtendedType e, const std::string& s, bool b, int L, int c)
+ : _string(s), _bool(b), _line(L), _character(c), _type(t), _extendedType(e) {}
+
+ Type type() const {
+ return _type;
+ }
+
+ ExtendedType extendedType() const {
+ return _extendedType;
+ }
+
+ /**
+ The value of a single or double quote string (not including the quotes),
+ the name of a symbol, or the exact textual representation of a number as
+ parsed from the input.
+ */
+ const std::string& string() const {
+ return _string;
+ }
+
+ bool boolean() const {
+ return _bool;
+ }
+
+ /**
+ Starting line of the input from which this token was parsed. Starts
+ at 1.
+ */
+ int line() const {
+ return _line;
+ }
+
+ /**
+ Starting character position in the input line from which this token was
+ parsed. Starts at 1.
+ */
+ int character() const {
+ return _character;
+ }
+
+ /** Return the numeric value for a number type, or zero if this is
+ not a number type.
+ */
+ double number() const;
+};
+
+
+/**
+ A simple style tokenizer for reading text files. TextInput handles a
+ superset of C++,Java, Matlab, and Bash code text including single
+ line comments, block comments, quoted strings with escape sequences,
+ and operators. TextInput recognizes several categories of tokens,
+ which are separated by white space, quotation marks, or the end of a
+ recognized operator:
+
+ <ul>
+ <li><CODE>Token::SINGLE_QUOTED_TYPE</CODE> string of characters surrounded by single quotes, e.g., 'x', '\0', 'foo'.
+ <li><CODE>Token::DOUBLE_QUOTED_TYPE</CODE> string of characters surrounded by double quotes, e.g., "x", "abc\txyz", "b o b".
+ <li><CODE>Token::SYMBOL_TYPE</CODE> legal C++ operators, keywords, and identifiers. e.g., >=, Foo, _X, class, {
+ <li><CODE>Token::INTEGER_TYPE</CODE> numbers without decimal places or exponential notation. e.g., 10, 0x17F, 32, 0, -155
+ <li><CODE>Token::FLOATING_POINT_TYPE</CODE> numbers with decimal places or exponential notation. e.g., 1e3, -1.2, .4, 0.5
+ <li><CODE>Token::BOOLEAN_TYPE</CODE> special symbols like "true" and "false"; the exact details can be configured in TextInput::Settings
+ </ul>
+
+ <P>The special ".." and "..." tokens are always recognized in
+ addition to normal C++ operators. Additional tokens can be made
+ available by changing the Settings.
+
+ Negative numbers are handled specially because of the ambiguity between unary minus and negative numbers--
+ see the note on TextInput::read.
+
+ TextInput does not have helper functions for types with non-obvious
+ formatting, or helpers that would be redundant. Use the serialize
+ methods instead for parsing specific types like int, Vector3, and
+ Color3.
+
+ Inside quoted strings escape sequences are converted. Thus the
+ string token for ["a\nb"] is 'a', followed by a newline, followed by
+ 'b'. Outside of quoted strings, escape sequences are not converted,
+ so the token sequence for [a\nb] is symbol 'a', symbol '\', symbol
+ 'nb' (this matches what a C++ parser would do). The exception is
+ that a specified TextInput::Settings::otherCommentCharacter preceeded
+ by a backslash is assumed to be an escaped comment character and is
+ returned as a symbol token instead of being parsed as a comment
+ (this is what a LaTex or VRML parser would do).
+
+ <B>Examples</B>
+
+ <PRE>
+ TextInput ti(TextInput::FROM_STRING, "name = \"Max\", height = 6");
+
+ Token t;
+
+ t = ti.read();
+ debugAssert(t.type == Token::SYMBOL);
+ debugAssert(t.sval == "name");
+
+ ti.read();
+ debugAssert(t.type == Token::SYMBOL);
+ debugAssert(t.sval == "=");
+
+ std::string name = ti.read().sval;
+ ti.read();
+ </PRE>
+
+ <PRE>
+ TextInput ti(TextInput::FROM_STRING, "name = \"Max\", height = 6");
+ ti.readSymbols("name", "=");
+ std::string name = ti.readString();
+ ti.readSymbols(",", "height", "=");
+ double height = ti. readNumber();
+ </PRE>
+
+ Assumes that the file is not modified once opened.
+ */
+class TextInput {
+public:
+
+ /** Tokenizer configuration options. */
+ class Settings {
+ public:
+ /** If true, slash-star marks a multi-line comment. Default
+ is true. */
+ bool cComments;
+
+ /** If true, // begins a single line comment. Default is true. */
+ bool cppComments;
+
+ /** If true, \r, \n, \t, \0, \\ and other escape sequences inside
+ strings are converted to the equivalent C++ escaped character.
+ If false, backslashes are treated literally. It is convenient to
+ set to false if reading Windows paths, for example, like
+ c:\foo\bar.
+
+ Default is true.
+ */
+ bool escapeSequencesInStrings;
+
+ /** If non-NUL, specifies a character that begins single line
+ comments ('#' and '%' are popular choices). This is independent
+ of the cppComments flag. If the character appears in text with a
+ backslash in front of it, it is considered escaped and is not
+ treated as a comment character.
+
+ Default is '\0'.
+ */
+ char otherCommentCharacter;
+
+ /** Another (optional) 1-comment character. Useful for files that
+ support multiple comment syntaxes. Default is '\0'.
+ */
+ char otherCommentCharacter2;
+
+ /** If true, "-1" parses as the number -1 instead of the
+ symbol "-" followed by the number 1. Default is true.*/
+ bool signedNumbers;
+
+ /** If true, strings can be marked with single quotes (e.g.,
+ 'aaa'). If false, the quote character is parsed as a
+ symbol. Default is true. Backquote (`) is always parsed
+ as a symbol. */
+ bool singleQuotedStrings;
+
+ /** If set to a non-empty string, that string will be used in
+ place of the real file name (or in place of a pseudonym
+ constructed from the buffer if given FROM_STRING) in
+ tokens and exceptions.
+
+ Default is empty.
+ */
+ std::string sourceFileName;
+
+
+ /** Added to the line number reported by peekLineNumber and in
+ exceptions. Useful for concatenating files that are
+ parsed separately. Default is zero. */
+ int startingLineNumberOffset;
+
+ /**
+ Parse -1.#IND00 as the floating point number returned by
+ nan(), -1.#INF00 as -inf(), and 1.#INF00 as inf(). Note
+ that the C99 standard specifies that a variety of formats
+ like "NaN" and "nan" are to be used; these are easier to
+ parse yourself and not currently supported by readNumber.
+
+ An alternative to specifying msvcSpecials is to read numbers as:
+ <pre>
+ Token x = t.read();
+ Token y = t.peek();
+ if ((x.string() == "-1.") &&
+ (y.string() == "#INF00") &&
+ (y.character() == x.character() + 3) &&
+ (y.line() == x.line()) {
+ t.read();
+ return nan();
+ }
+ // ... similar cases for inf
+ </pre>
+
+ If the single-comment character was #, the floating point
+ special format overrides the comment and will be parsed
+ instead.
+
+ If signedNumbers is false msvcSpecials will not be parsed.
+
+ Default is true. */
+ bool msvcSpecials;
+
+ /**
+ Parse the following set of useful proof symbols:
+
+ =>
+ ::>
+ <::
+ :>
+ <:
+ |-
+ ::=
+ :=
+ <-
+
+ Default is false.
+ */
+ bool proofSymbols;
+
+ /**
+ When parsing booleans and msvcSpecials, is case significant?
+ Default is {true}
+ */
+ bool caseSensitive;
+
+ /** All symbols that will become the 'true' boolean token. See also caseSensitive.
+ Clear this value to disable parsing of true booleans.
+
+ Default is {true}.
+ */
+ Set<std::string> trueSymbols;
+
+ /** See trueSymbols. Default is {false}*/
+ Set<std::string> falseSymbols;
+
+ Settings ();
+ };
+
+private:
+
+ std::deque<Token> stack;
+
+ /**
+ Characters to be tokenized.
+ */
+ Array<char> buffer;
+
+ /**
+ Offset of current character (the next character to consumed) in
+ input buffer.
+ */
+ unsigned int currentCharOffset;
+
+ /**
+ Line number of next character to be consumed from the input buffer. (1
+ indicates first line of input.)
+
+ Note that this is the line number of the @e next character to be
+ consumed from the input, not the line number of the @e last character
+ consumed!
+ */
+ unsigned int lineNumber;
+
+ /**
+ Character number (within the line) of the next character to be consumed
+ from the input buffer. (1 indicates first character of the line).
+
+ Note that this is the character number of the @e next character to be
+ consumed from the input, not the character number of the @e last
+ character consumed!
+ */
+ unsigned int charNumber;
+
+ /** Configuration options. This includes the file name that will be
+ reported in tokens and exceptions. */
+ Settings options;
+
+ void init();
+
+ /**
+ Consumes the next character from the input buffer, and returns that
+ character. Updates lineNumber and charNumber to reflect the location of
+ the next character in the input buffer.
+
+ Note: you shouldn't be using the return value of this function in most
+ cases. In general, you should peekInputChar() to get the next
+ character, determine what to do with it, then consume it with this
+ function (or with eatAndPeekInputChar()). Given that usage, in most
+ instances you already know what this function would return!
+ */
+ int eatInputChar();
+
+ /**
+ Returns the next character from the input buffer, without consuming any
+ characters. Can also be used to look deeper into the input buffer.
+ Does not modify lineNumber or charNumber.
+
+ @param distance Index of the character in the input buffer to peek at,
+ relative to the next character. Default is 0, for the next character in
+ the input buffer.
+ */
+ int peekInputChar(unsigned int distance = 0);
+
+ /**
+ Helper function to consume the next character in the input buffer and
+ peek at the one following (without consuming it).
+ */
+ inline int eatAndPeekInputChar() {
+ eatInputChar();
+ return peekInputChar(0);
+ }
+
+ /**
+ Read the next token, returning an END token if no more input is
+ available.
+ */
+ Token nextToken();
+
+ /**
+ Helper for nextToken. Appends characters to t._string until the end
+ delimiter is reached.
+
+ When called, the next character in the input buffer should be first the
+ first character after the opening delimiter character.
+ */
+ void parseQuotedString(unsigned char delimiter, Token& t);
+
+public:
+
+ class TokenException {
+ public:
+ /** Name of file being parsed when exception occurred. */
+ std::string sourceFile;
+
+ /** Line number of start of token which caused the exception. 1 is
+ the first line of the file. Note that you can use
+ TextInput::Settings::startingLineNumberOffset to shift the effective line
+ number that is reported.
+ */
+ int line;
+
+ /** Character number in the line of start of token which caused the
+ exception. 1 is the character in the line.
+ */
+ int character;
+
+ /** Pre-formatted error message */
+ std::string message;
+
+ virtual ~TokenException() {}
+
+ protected:
+
+ TokenException(
+ const std::string& src,
+ int ln,
+ int ch);
+
+ };
+
+ /** While parsing a number of the form 1.#IN?00, ? was
+ not 'D' or 'F'. */
+ class BadMSVCSpecial : public TokenException {
+ public:
+
+ BadMSVCSpecial(
+ const std::string& src,
+ int ln,
+ int ch);
+ };
+
+ /** Thrown by the read methods. */
+ class WrongTokenType : public TokenException {
+ public:
+ Token::Type expected;
+ Token::Type actual;
+
+ WrongTokenType(
+ const std::string& src,
+ int ln,
+ int ch,
+ Token::Type e,
+ Token::Type a);
+ };
+
+ class WrongSymbol : public TokenException {
+ public:
+ std::string expected;
+ std::string actual;
+
+ WrongSymbol(
+ const std::string& src,
+ int ln,
+ int ch,
+ const std::string& e,
+ const std::string& a);
+ };
+
+
+ /** String read from input did not match expected string. */
+ class WrongString : public TokenException {
+ public:
+ std::string expected;
+ std::string actual;
+
+ WrongString(
+ const std::string& src,
+ int ln,
+ int ch,
+ const std::string& e,
+ const std::string& a);
+ };
+
+ TextInput(const std::string& filename, const Settings& settings = Settings());
+
+ enum FS {FROM_STRING};
+ /** Creates input directly from a string. The first argument must be
+ TextInput::FROM_STRING.
+ */
+ TextInput(FS fs, const std::string& str, const Settings& settings = Settings());
+
+ /** Returns true while there are tokens remaining. */
+ bool hasMore();
+
+ /** Read the next token (which will be the END token if ! hasMore()).
+
+ Signed numbers can be handled in one of two modes. If the option
+ TextInput::Settings::signedNumbers is true,
+ A '+' or '-' immediately before a number is prepended onto that number and
+ if there is intervening whitespace, it is read as a separate symbol.
+
+ If TextInput::Settings::signedNumbers is false,
+ read() does not distinguish between a plus or minus symbol next
+ to a number and a positive/negative number itself. For example, "x - 1" and "x -1"
+ will be parsed the same way by read().
+
+ In both cases, readNumber() will contract a leading "-" or "+" onto
+ a number.
+ */
+ Token read();
+
+
+ /** Read one token (or possibly two) as a number or throws
+ WrongTokenType, and returns the number.
+
+ If the first token in the input is a number, it is returned directly.
+
+ If TextInput::Settings::signedNumbers is false and the input stream
+ contains a '+' or '-' symbol token immediately followed by a number
+ token, both tokens will be consumed and a single token will be
+ returned by this method.
+
+ WrongTokenType will be thrown if one of the input conditions
+ described above is not satisfied. When an exception is thrown, no
+ tokens are consumed.
+ */
+ double readNumber();
+
+ bool readBoolean();
+
+ /** Reads a string token or throws WrongTokenType, and returns the token.
+
+ Use this method (rather than readString) if you want the token's
+ location as well as its value.
+
+ WrongTokenType will be thrown if the next token in the input stream
+ is not a string. When an exception is thrown, no tokens are
+ consumed.
+ */
+ Token readStringToken();
+
+ /** Like readStringToken, but returns the token's string.
+
+ Use this method (rather than readStringToken) if you want the token's
+ value but don't really care about its location in the input. Use of
+ readStringToken is encouraged for better error reporting.
+ */
+ std::string readString();
+
+ /** Reads a specific string token or throws either WrongTokenType or
+ WrongString. If the next token in the input is a string matching @p
+ s, it will be consumed.
+
+ Use this method if you want to match a specific string from the
+ input. In that case, typically error reporting related to the token
+ is only going to occur because of a mismatch, so no location
+ information is needed by the caller.
+
+ WrongTokenType will be thrown if the next token in the input stream
+ is not a string. WrongString will be thrown if the next token in the
+ input stream is a string but does not match the @p s parameter. When
+ an exception is thrown, no tokens are consumed.
+ */
+ void readString(const std::string& s);
+
+
+ /** Reads a symbol token or throws WrongTokenType, and returns the token.
+
+ Use this method (rather than readSymbol) if you want the token's
+ location as well as its value.
+
+ WrongTokenType will be thrown if the next token in the input stream
+ is not a symbol. When an exception is thrown, no tokens are
+ consumed.
+ */
+ Token readSymbolToken();
+
+ /** Like readSymbolToken, but returns the token's string.
+
+ Use this method (rather than readSymbolToken) if you want the token's
+ value but don't really care about its location in the input. Use of
+ readSymbolToken is encouraged for better error reporting.
+ */
+ std::string readSymbol();
+
+ /** Reads a specific symbol token or throws either WrongTokenType or
+ WrongSymbol. If the next token in the input is a symbol matching @p
+ symbol, it will be consumed.
+
+ Use this method if you want to match a specific symbol from the
+ input. In that case, typically error reporting related to the token
+ is only going to occur because of a mismatch, so no location
+ information is needed by the caller.
+
+ WrongTokenType will be thrown if the next token in the input stream
+ is not a symbol. WrongSymbol will be thrown if the next token in the
+ input stream is a symbol but does not match the @p symbol parameter.
+ When an exception is thrown, no tokens are consumed.
+ */
+ void readSymbol(const std::string& symbol);
+
+
+ /** Read a series of two specific symbols. See readSymbol. */
+ inline void readSymbols(const std::string& s1, const std::string& s2) {
+ readSymbol(s1);
+ readSymbol(s2);
+ }
+
+ /** Read a series of three specific symbols. See readSymbol. */
+ inline void readSymbols(
+ const std::string& s1,
+ const std::string& s2,
+ const std::string& s3) {
+ readSymbol(s1);
+ readSymbol(s2);
+ readSymbol(s3);
+ }
+
+ /** Read a series of four specific symbols. See readSymbol. */
+ inline void readSymbols(
+ const std::string& s1,
+ const std::string& s2,
+ const std::string& s3,
+ const std::string& s4) {
+ readSymbol(s1);
+ readSymbol(s2);
+ readSymbol(s3);
+ readSymbol(s4);
+ }
+
+ /** Return a copy of the next token in the input stream, but don't remove
+ it from the input stream.
+ */
+ Token peek();
+
+ /** Returns the line number for the @e next token. See also peek. */
+ int peekLineNumber();
+
+ /** Returns the character number (relative to the line) for the @e next
+ token in the input stream. See also peek.
+ */
+ int peekCharacterNumber();
+
+ /** Take a previously read token and push it back at the front of the
+ input stream.
+
+ Can be used in the case where more than one token of read-ahead is
+ needed (i.e., when peek doesn't suffice).
+ */
+ void push(const Token& t);
+
+ /** Returns the filename from which this input is drawn, or the first few
+ characters of the string if created from a string.
+ If options::filename is non-empty that will replace the
+ true filename.*/
+ const std::string& filename() const;
+};
+
+void deserialize(bool& b, TextInput& ti);
+void deserialize(int& b, TextInput& ti);
+void deserialize(uint8& b, TextInput& ti);
+void deserialize(double& b, TextInput& ti);
+void deserialize(float& b, TextInput& ti);
+void deserialize(std::string& b, TextInput& ti);
+
+} // namespace
+
+#endif
+
diff --git a/externals/g3dlite/G3D.lib/include/G3D/TextOutput.h b/externals/g3dlite/G3D.lib/include/G3D/TextOutput.h
new file mode 100644
index 00000000000..6ae7d14fe00
--- /dev/null
+++ b/externals/g3dlite/G3D.lib/include/G3D/TextOutput.h
@@ -0,0 +1,249 @@
+/**
+ @file TextOutput.h
+
+ @maintainer Morgan McGuire, morgan@graphics3d.com
+ @created 2004-06-21
+ @edited 2006-10-24
+
+ Copyright 2000-2007, Morgan McGuire.
+ All rights reserved.
+ */
+
+#ifndef G3D_TEXTOUTPUT_H
+#define G3D_TEXTOUTPUT_H
+
+#include "G3D/platform.h"
+#include "G3D/Array.h"
+#include <string>
+
+namespace G3D {
+
+/**
+ Convenient formatting of ASCII text written to a file.
+ <P>
+
+ The core writeString, writeNumber, and writeSymbol methods map to TextInput's
+ methods. Number and Symbol each print an additional space that is used to
+ separate adjacent tokens.
+
+ TextOutput::printf allows arbitrary text to be conveniently dumped
+ en-masse. Use [de]serialize(bool, TextOutput) and other overloads to read/write
+ primitive types in a standardized manner and
+
+ <P>
+ When a word-wrap line break occurs, all whitespace between words is replaced
+ with a single newline (the newline may be two characters-- see
+ G3D::TextOutput::Options::NewlineStyle). Word wrapping occurs against
+ the number of columns specified by Options::numColumns, <I>minus</I> the current
+ indent level.
+
+ Indenting adds the specified number of spaces immediately after a newline.
+ If a newline was followed by spaces in the original string, these are added
+ to the indent spaces. Indenting <B>will</B> indent blank lines and will leave
+ indents after the last newline of a file (if the indent level is non-zero at the end).
+
+ <P><B>Serialization/Marshalling</B>
+ <DT>Text serialization is accomplished using TextOutput by defining the pair of
+ methods:
+
+ <PRE>
+ void serialize(TextOutput& to) const;
+ void deserialize(TextInput& ti);
+ </PRE>
+
+ See also G3D::TextInput.
+
+ <P>
+ <B>BETA API</B>
+ <DT>This API is subject to change in future versions.
+ */
+class TextOutput {
+public:
+
+ class Settings {
+ public:
+ /**
+ WRAP_NONE Word wrapping is disabled
+ WRAP_WITHOUT_BREAKING Word-wrap, but don't break continuous lines that
+ are longer than numColumns (default)
+ WRAP_ALWAYS Wrap even if it means breaking a continuous line or
+ a quoted string.
+
+ Word wrapping is only allowed at whitespaces ('\n', '\r', '\t', ' '); it
+ will not occur after commas, punctuation, minus signs, or any other characters
+ */
+ enum WordWrapMode {WRAP_NONE, WRAP_WITHOUT_BREAKING, WRAP_ALWAYS};
+
+ /** Defaults to WRAP_WITHOUT_BREAKING */
+ WordWrapMode wordWrap;
+
+ /** Is word-wrapping allowed to insert newlines inside double quotes?
+ Default: false */
+ bool allowWordWrapInsideDoubleQuotes;
+
+ /** Number of columns for word wrapping. Default: 8 */
+ int numColumns;
+
+ /** Number of spaces in each indent. Default: 4 */
+ int spacesPerIndent;
+
+ /** Style of newline used by word wrapping and by (optional) conversion.
+ default: Windows: NEWLINE_WINDOWS, Linux, OS X: NEWLINE_UNIX.
+ */
+ enum NewlineStyle {NEWLINE_WINDOWS, NEWLINE_UNIX};
+
+ NewlineStyle newlineStyle;
+
+ /** If true, all newlines are converted to NewlineStyle regardless of
+ how they start out. Default: true. */
+ bool convertNewlines;
+
+ /** Used by writeBoolean */
+ std::string trueSymbol;
+
+ /** Used by writeBoolean */
+ std::string falseSymbol;
+
+ Settings() :
+ wordWrap(WRAP_WITHOUT_BREAKING),
+ allowWordWrapInsideDoubleQuotes(false),
+ numColumns(80),
+ spacesPerIndent(4),
+ convertNewlines(true),
+ trueSymbol("true"),
+ falseSymbol("false") {
+ #ifdef G3D_WIN32
+ newlineStyle = NEWLINE_WINDOWS;
+ #else
+ newlineStyle = NEWLINE_UNIX;
+ #endif
+ }
+ };
+
+private:
+
+ /** Used by indentAndAppend to tell when we are writing the
+ first character of a new line.
+
+ So that push/popIndent work correctly, we cannot indent
+ immediately after writing a newline. Instead we must
+ indent on writing the first character <B>after</B> that
+ newline.
+ */
+ bool startingNewLine;
+
+ /** Number of characters at the end of the buffer since the last newline */
+ int currentColumn;
+
+ /** True if we have seen an open " and no close ".*/
+ bool inDQuote;
+
+ /** Empty if there is none */
+ std::string filename;
+
+ Array<char> data;
+
+ Settings option;
+
+ /** Number of indents to prepend before each line. Always set using setIndentLevel.*/
+ int indentLevel;
+
+ void setIndentLevel(int i);
+
+ /** Actual number of spaces to indent. */
+ int indentSpaces;
+
+ /** the newline character(s) */
+ std::string newline;
+
+ void setOptions(const Settings& _opt);
+
+ /** Converts to the desired newlines. Called from vprintf */
+ void convertNewlines(const std::string& in, std::string& out);
+
+ /** Called from vprintf */
+ void wordWrapIndentAppend(const std::string& str);
+
+ /** Appends the character to data, indenting whenever a newline is encountered.
+ Called from wordWrapIndentAppend */
+ void indentAppend(char c);
+
+public:
+
+ explicit TextOutput(const std::string& filename, const Settings& options = Settings());
+
+ /** Constructs a text output that can later be commited to a string instead of a file.*/
+ explicit TextOutput(const Settings& options = Settings());
+
+ /** Commit to the filename specified on the constructor.
+ <B>Not</B> called from the destructor; you must call
+ it yourself.
+ @param flush If true (default) the file is ready for reading when the method returns, otherwise
+ the method returns immediately and writes the file in the background.*/
+ void commit(bool flush = true);
+
+ /** Commits to this string */
+ void commitString(std::string& string);
+
+ /** Increase indent level by 1 */
+ void pushIndent();
+
+ void popIndent();
+
+ /** Produces a new string that contains the output */
+ std::string commitString();
+
+ /** Writes a quoted string. Special characters in the string (e.g., \, \t, \n) are escaped so that
+ TextInput will produce the identical string on reading.*/
+ void writeString(const std::string& string);
+
+ void writeBoolean(bool b);
+
+ void writeNumber(double n);
+
+ void writeNumber(int n);
+
+ void writeNewline();
+ void writeNewlines(int numLines);
+
+ /** The symbol is written without quotes. Symbols are required to begin with a
+ letter or underscore and contain only letters, underscores, and numbers
+ or be a C++ symbol (e.g. "{", "(", "++", etc.)
+ so that they may be properly parsed by TextInput::readSymbol. Symbols are
+ printed with a trailing space.*/
+ void writeSymbol(const std::string& string);
+
+ /** Convenient idiom for writing multiple symbols in a row, e.g.
+ writeSymbols("name", "="); The empty symbols are not written.
+ */
+ void writeSymbols(
+ const std::string& a,
+ const std::string& b = "",
+ const std::string& c = "",
+ const std::string& d = "",
+ const std::string& e = "",
+ const std::string& f = "");
+
+ /** Normal printf conventions. Note that the output will be reformatted
+ for word-wrapping and newlines */
+ void __cdecl printf(const char* fmt, ...)
+ G3D_CHECK_PRINTF_METHOD_ARGS;
+
+ // Can't pass by reference because that confuses va_start
+ void __cdecl printf(const std::string fmt, ...);
+ void __cdecl vprintf(const char* fmt, va_list argPtr)
+ G3D_CHECK_VPRINTF_METHOD_ARGS;
+};
+
+// Primitive serializers
+void serialize(const bool& b, TextOutput& to);
+void serialize(const int& b, TextOutput& to);
+void serialize(const uint8& b, TextOutput& to);
+void serialize(const double& b, TextOutput& to);
+void serialize(const float& b, TextOutput& to);
+void serialize(const std::string& b, TextOutput& to);
+void serialize(const char* b, TextOutput& to);
+
+}
+
+#endif
diff --git a/externals/g3dlite/G3D.lib/include/G3D/ThreadSet.h b/externals/g3dlite/G3D.lib/include/G3D/ThreadSet.h
new file mode 100644
index 00000000000..59c57b062ae
--- /dev/null
+++ b/externals/g3dlite/G3D.lib/include/G3D/ThreadSet.h
@@ -0,0 +1,79 @@
+#ifndef G3D_THREADSET_H
+#define G3D_THREADSET_H
+
+#include "G3D/platform.h"
+#include "G3D/Array.h"
+#include "G3D/ReferenceCount.h"
+#include "G3D/GThread.h"
+
+namespace G3D {
+
+/** Manages a set of threads. All methods are threadsafe except for
+ the iterator begin/end.
+
+ @beta*/
+class ThreadSet : public ReferenceCountedObject {
+public:
+ /** Intended to allow future use with a template parameter.*/
+ typedef GThread Thread;
+
+ typedef ReferenceCountedPointer<Thread> ThreadRef;
+ typedef ReferenceCountedPointer<ThreadSet> Ref;
+ typedef Array<ThreadRef>::Iterator Iterator;
+ typedef Array<ThreadRef>::ConstIterator ConstIterator;
+
+private:
+
+ /** Protects m_thread */
+ GMutex m_lock;
+
+ /** Threads in the set */
+ Array<ThreadRef> m_thread;
+
+public:
+
+ /** Total number of threads (some of which may be completed). */
+ int size() const;
+
+ /** Number of threads that have been started */
+ int numStarted() const;
+
+ /** Start all threads that are not currently started */
+ void start() const;
+
+ /** Terminate all threads that are currently started */
+ void terminate() const;
+
+ /** Waits until all started threads have completed. */
+ void waitForCompletion() const;
+
+ /** Remove all (not stopping them) */
+ void clear();
+
+ /** Removes completed threads and returns the new size.*/
+ int removeCompleted();
+
+ /** Inserts a new thread, if it is not already present, and
+ returns the new number of threads.*/
+ int insert(const ThreadRef& t);
+
+ /** Removes a thread. Returns true if the thread was present and
+ removed. */
+ bool remove(const ThreadRef& t);
+
+ bool contains(const ThreadRef& t) const;
+
+ /** It is an error to mutate the ThreadSet while iterating through it. */
+ Iterator begin();
+
+ Iterator end();
+
+ ConstIterator begin() const;
+
+ ConstIterator end() const;
+};
+
+
+} // namespace G3D
+
+#endif
diff --git a/externals/g3dlite/G3D.lib/include/G3D/Triangle.h b/externals/g3dlite/G3D.lib/include/G3D/Triangle.h
new file mode 100644
index 00000000000..8b67acf4624
--- /dev/null
+++ b/externals/g3dlite/G3D.lib/include/G3D/Triangle.h
@@ -0,0 +1,143 @@
+/**
+ @file Triangle.h
+
+ @maintainer Morgan McGuire, matrix@graphics3d.com
+
+ @created 2003-04-05
+ @edited 2008-10-06
+
+ @cite Random point method by Greg Turk, Generating random points in triangles. In A. S. Glassner, ed., Graphics Gems, pp. 24-28. Academic Press, 1990
+
+ Copyright 2000-2008, Morgan McGuire.
+ All rights reserved.
+ */
+
+#ifndef G3D_TRIANGLE_H
+#define G3D_TRIANGLE_H
+
+#include "G3D/platform.h"
+#include "G3D/g3dmath.h"
+#include "G3D/Vector3.h"
+#include "G3D/Plane.h"
+#include "G3D/BoundsTrait.h"
+#include "G3D/debugAssert.h"
+#include <string>
+
+namespace G3D {
+
+/**
+ A generic triangle representation. This should not be used
+ as the underlying triangle for creating models; it is intended
+ for providing fast property queries but requires a lot of
+ storage and is mostly immutable.
+ */
+class Triangle {
+private:
+ friend class CollisionDetection;
+ friend class Ray;
+
+ Vector3 _vertex[3];
+
+ /** edgeDirection[i] is the normalized vector v[i+1] - v[i] */
+ Vector3 edgeDirection[3];
+ float edgeMagnitude[3];
+ Plane _plane;
+ Vector3::Axis _primaryAxis;
+
+ /** vertex[1] - vertex[0] */
+ Vector3 _edge01;
+
+ /** vertex[2] - vertex[0] */
+ Vector3 _edge02;
+
+ float _area;
+
+ void init(const Vector3& v0, const Vector3& v1, const Vector3& v2);
+
+public:
+
+ Triangle(class BinaryInput& b);
+ void serialize(class BinaryOutput& b);
+ void deserialize(class BinaryInput& b);
+
+ Triangle();
+
+ Triangle(const Vector3& v0, const Vector3& v1, const Vector3& v2);
+
+ ~Triangle();
+
+ /** 0, 1, or 2 */
+ inline const Vector3& vertex(int n) const {
+ debugAssert((n >= 0) && (n < 3));
+ return _vertex[n];
+ }
+
+ /** vertex[1] - vertex[0] */
+ inline const Vector3& edge01() const {
+ return _edge01;
+ }
+
+ /** vertex[2] - vertex[0] */
+ inline const Vector3& edge02() const {
+ return _edge02;
+ }
+
+ float area() const;
+
+ Vector3::Axis primaryAxis() const {
+ return _primaryAxis;
+ }
+
+ const Vector3& normal() const;
+
+ /** Barycenter */
+ Vector3 center() const;
+
+ const Plane& plane() const;
+
+ /** Returns a random point in the triangle. */
+ Vector3 randomPoint() const;
+
+ inline void getRandomSurfacePoint(Vector3& P,
+ Vector3& N = Vector3::dummy) const {
+ P = randomPoint();
+ N = normal();
+ }
+ /**
+ For two triangles to be equal they must have
+ the same vertices <I>in the same order</I>.
+ That is, vertex[0] == vertex[0], etc.
+ */
+ inline bool operator==(const Triangle& other) const {
+ for (int i = 0; i < 3; ++i) {
+ if (_vertex[i] != other._vertex[i]) {
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ inline size_t hashCode() const {
+ return
+ _vertex[0].hashCode() +
+ (_vertex[1].hashCode() >> 2) +
+ (_vertex[2].hashCode() >> 3);
+ }
+
+ void getBounds(class AABox&) const;
+
+};
+
+} // namespace G3D
+
+template <> struct HashTrait<G3D::Triangle> {
+ static size_t hashCode(const G3D::Triangle& key) { return key.hashCode(); }
+};
+
+
+template<> struct BoundsTrait<class G3D::Triangle> {
+ static void getBounds(const G3D::Triangle& t, G3D::AABox& out) { t.getBounds(out); }
+};
+
+#endif
diff --git a/externals/g3dlite/G3D.lib/include/G3D/UprightFrame.h b/externals/g3dlite/G3D.lib/include/G3D/UprightFrame.h
new file mode 100644
index 00000000000..52db3080b80
--- /dev/null
+++ b/externals/g3dlite/G3D.lib/include/G3D/UprightFrame.h
@@ -0,0 +1,83 @@
+/**
+ @file UprightFrame.h
+
+ @author Morgan McGuire, morgan@cs.williams.edu
+ */
+
+#ifndef G3D_UPRIGHTFRAME_H
+#define G3D_UPRIGHTFRAME_H
+
+#include "G3D/platform.h"
+#include "G3D/Spline.h"
+#include "G3D/Vector3.h"
+#include "G3D/CoordinateFrame.h"
+
+namespace G3D {
+
+/**
+ Coordinate frame expressed in Euler angles.
+ Unlike a G3D::Quat, UprightFrame always keeps the reference frame from rolling about its own z axis.
+ Particularly useful for cameras.
+
+ @sa G3D::CoordinateFrame, G3D::Matrix4, G3D::PhysicsFrame, G3D::UprightSpline, G3D::UprightSplineManipulator
+ */
+class UprightFrame {
+public:
+
+ Vector3 translation;
+
+ /** -pi/2 < pitch < pi/2 in radians about the X-axis */
+ float pitch;
+
+ /** In radians about the Y-axis */
+ float yaw;
+
+ inline UprightFrame(const Vector3& t = Vector3::zero(), float p = 0, float y = 0)
+ : translation(t), pitch(p), yaw(y) {}
+
+ UprightFrame(const CoordinateFrame& cframe);
+
+ CoordinateFrame toCoordinateFrame() const;
+
+ /** Supports implicit cast to CoordinateFrame */
+ inline operator CoordinateFrame() const {
+ return toCoordinateFrame();
+ }
+
+ /** Required for use with spline */
+ UprightFrame operator+(const UprightFrame& other) const;
+
+ /** Required for use with spline */
+ UprightFrame operator*(const float k) const;
+
+ /**
+ Unwraps the yaw values in the elements of the array such that
+ they still represent the same angles but strictly increase/decrease
+ without wrapping about zero. For use with Spline<UprightFrame>
+ */
+ static void unwrapYaw(UprightFrame* a, int N);
+
+ void serialize(class BinaryOutput& b) const;
+ void deserialize(class BinaryInput& b);
+};
+
+/** Shortest-path linear velocity spline for camera positions. Always keeps the camera from rolling.
+@sa G3D::UprightSplineManipulator, G3D::UprightFrame
+*/
+class UprightSpline : public Spline<UprightFrame> {
+protected:
+
+ virtual void ensureShortestPath(UprightFrame* A, int N) const {
+ UprightFrame::unwrapYaw(A, N);
+ }
+
+public:
+
+ void serialize(class BinaryOutput& b) const;
+ void deserialize(class BinaryInput& b);
+
+};
+
+}
+
+#endif
diff --git a/externals/g3dlite/G3D.lib/include/G3D/Vector2.h b/externals/g3dlite/G3D.lib/include/G3D/Vector2.h
new file mode 100644
index 00000000000..b610a1a3500
--- /dev/null
+++ b/externals/g3dlite/G3D.lib/include/G3D/Vector2.h
@@ -0,0 +1,457 @@
+/**
+ @file Vector2.h
+
+ 2D vector class
+
+ @maintainer Morgan McGuire, morgan@cs.williams.edu
+
+ @created 2001-06-02
+ @edited 2008-11-30
+
+ Copyright 2000-2008, Morgan McGuire.
+ All rights reserved.
+*/
+
+#ifndef G3D_VECTOR2_H
+#define G3D_VECTOR2_H
+
+#include <string>
+
+#include "G3D/platform.h"
+#include "G3D/g3dmath.h"
+#include "G3D/Table.h"
+#include "G3D/HashTrait.h"
+#include "G3D/Vector2int16.h"
+
+namespace G3D {
+
+class Vector2;
+class Vector3;
+class Vector4;
+
+/**
+ Do not subclass-- this implementation makes assumptions about the
+ memory layout.
+ */
+class Vector2 {
+private:
+ // Hidden operators
+ bool operator<(const Vector2&) const;
+ bool operator>(const Vector2&) const;
+ bool operator<=(const Vector2&) const;
+ bool operator>=(const Vector2&) const;
+
+public:
+ float x;
+ float y;
+
+ /** Creates the zero vector */
+ Vector2();
+ Vector2(class TextInput& t);
+ Vector2(class BinaryInput& b);
+ Vector2(float x, float y);
+ Vector2(float coordinate[2]);
+ Vector2(double coordinate[2]);
+ Vector2(const Vector2& other);
+ Vector2(const class Vector2int16& other);
+
+ void serialize(class BinaryOutput& b) const;
+ void deserialize(class BinaryInput& b);
+
+ void serialize(class TextOutput& t) const;
+ void deserialize(class TextInput& t);
+
+ float& operator[](int i);
+ const float& operator[](int i) const;
+ operator float*();
+ operator const float*() const;
+
+ // assignment and comparison
+ Vector2& operator=(const Vector2& other);
+ bool operator==(const Vector2& other) const;
+ bool operator!=(const Vector2& other) const;
+ size_t hashCode() const;
+ bool fuzzyEq(const Vector2& other) const;
+ bool fuzzyNe(const Vector2& other) const;
+
+ /** Returns true if this vector has finite length */
+ bool isFinite() const;
+
+ /** Returns true if this vector has length == 0 */
+ bool isZero() const;
+
+ /** Returns true if this vector has length == 1 */
+ bool isUnit() const;
+
+ // arithmetic operations
+ Vector2 operator+(const Vector2& v) const;
+ Vector2 operator-(const Vector2& v) const;
+ Vector2 operator*(float s) const;
+
+ /** Array (pointwise) multiplication */
+ Vector2 operator*(const Vector2& v) const;
+
+ /** Array division */
+ Vector2 operator/(const Vector2& v) const;
+ Vector2 operator/(float s) const;
+
+ /** Unary minus */
+ Vector2 operator-() const;
+
+ /** x + y */
+ inline float sum() const {
+ return x + y;
+ }
+
+ /**
+ Linear interpolation
+ */
+ inline Vector2 lerp(const Vector2& v, float alpha) const {
+ return (*this) + (v - *this) * alpha;
+ }
+
+ inline Vector2 clamp(const Vector2& low, const Vector2& high) const {
+ return Vector2(
+ G3D::clamp(x, low.x, high.x),
+ G3D::clamp(y, low.y, high.y));
+ }
+
+ inline Vector2 clamp(float low, float high) const {
+ return Vector2(
+ (float)G3D::clamp(x, low, high),
+ (float)G3D::clamp(y, low, high));
+ }
+
+ // arithmetic updates
+ Vector2& operator+=(const Vector2&);
+ Vector2& operator-=(const Vector2&);
+ Vector2& operator*=(float);
+ Vector2& operator/=(float);
+ Vector2& operator*=(const Vector2&);
+ Vector2& operator/=(const Vector2&);
+
+ // vector operations
+
+ /** */
+ float length() const;
+
+ /** Returns a unit-length vector */
+ Vector2 direction() const;
+
+ /**
+ Potentially less accurate but faster than direction().
+ Only works if System::hasSSE is true.
+ */
+ Vector2 fastDirection() const {
+ return direction();
+ }
+
+ float squaredLength() const;
+ float dot(const Vector2& s) const;
+
+ /**
+ Make this vector have unit length and return the old length.
+ If the vector length was less than tolerance, do not normalize.
+ */
+ float unitize(float fTolerance = 1e-06);
+
+ Vector2 min(const Vector2& v) const;
+ Vector2 max(const Vector2& v) const;
+
+ /** Uniformly distributed random vector on the unit sphere */
+ static Vector2 random();
+
+ // Special values.
+ // Intentionally not inlined: see Matrix3::identity() for details.
+ static const Vector2& zero();
+ inline static const Vector2& one() { static const Vector2 v(1, 1); return v; }
+ static const Vector2& unitX();
+ static const Vector2& unitY();
+ static const Vector2& inf();
+ static const Vector2& nan();
+ /** smallest (most negative) representable vector */
+ static const Vector2& minFinite();
+ /** Largest representable vector */
+ static const Vector2& maxFinite();
+
+ std::string toString() const;
+
+ // 2-char swizzles
+
+ Vector2 xx() const;
+ Vector2 yx() const;
+ Vector2 xy() const;
+ Vector2 yy() const;
+
+ // 3-char swizzles
+
+ Vector3 xxx() const;
+ Vector3 yxx() const;
+ Vector3 xyx() const;
+ Vector3 yyx() const;
+ Vector3 xxy() const;
+ Vector3 yxy() const;
+ Vector3 xyy() const;
+ Vector3 yyy() const;
+
+ // 4-char swizzles
+
+ Vector4 xxxx() const;
+ Vector4 yxxx() const;
+ Vector4 xyxx() const;
+ Vector4 yyxx() const;
+ Vector4 xxyx() const;
+ Vector4 yxyx() const;
+ Vector4 xyyx() const;
+ Vector4 yyyx() const;
+ Vector4 xxxy() const;
+ Vector4 yxxy() const;
+ Vector4 xyxy() const;
+ Vector4 yyxy() const;
+ Vector4 xxyy() const;
+ Vector4 yxyy() const;
+ Vector4 xyyy() const;
+ Vector4 yyyy() const;
+
+};
+
+inline Vector2 operator*(double s, const Vector2& v) {
+ return v * (float)s;
+}
+
+inline Vector2 operator*(float s, const Vector2& v) {
+ return v * s;
+}
+
+inline Vector2 operator*(int s, const Vector2& v) {
+ return v * (float)s;
+}
+
+
+inline Vector2::Vector2 () : x(0.0f), y(0.0f) {
+}
+
+
+inline Vector2::Vector2(float _x, float _y) : x(_x), y(_y) {
+}
+
+
+inline Vector2::Vector2 (float afCoordinate[2]) {
+ x = afCoordinate[0];
+ y = afCoordinate[1];
+}
+
+
+
+inline Vector2::Vector2 (double afCoordinate[2]) {
+ x = (float)afCoordinate[0];
+ y = (float)afCoordinate[1];
+}
+
+
+inline Vector2::Vector2 (const Vector2& rkVector) {
+ x = rkVector.x;
+ y = rkVector.y;
+}
+
+
+inline Vector2::Vector2 (const Vector2int16& v) : x(v.x), y(v.y) {
+}
+
+
+inline float& Vector2::operator[] (int i) {
+ return ((float*)this)[i];
+}
+
+
+inline const float& Vector2::operator[] (int i) const {
+ return ((float*)this)[i];
+}
+
+
+inline Vector2::operator float* () {
+ return (float*)this;
+}
+
+inline Vector2::operator const float* () const {
+ return (float*)this;
+}
+
+
+inline Vector2& Vector2::operator= (const Vector2& rkVector) {
+ x = rkVector.x;
+ y = rkVector.y;
+ return *this;
+}
+
+
+inline bool Vector2::operator== (const Vector2& rkVector) const {
+ return ( x == rkVector.x && y == rkVector.y);
+}
+
+
+inline bool Vector2::operator!= (const Vector2& rkVector) const {
+ return ( x != rkVector.x || y != rkVector.y);
+}
+
+
+inline Vector2 Vector2::operator+ (const Vector2& rkVector) const {
+ return Vector2(x + rkVector.x, y + rkVector.y);
+}
+
+
+inline Vector2 Vector2::operator- (const Vector2& rkVector) const {
+ return Vector2(x - rkVector.x, y - rkVector.y);
+}
+
+
+inline Vector2 Vector2::operator* (float fScalar) const {
+ return Vector2(fScalar*x, fScalar*y);
+}
+
+
+
+inline Vector2 Vector2::operator- () const {
+ return Vector2( -x, -y);
+}
+
+
+
+inline Vector2& Vector2::operator+= (const Vector2& rkVector) {
+ x += rkVector.x;
+ y += rkVector.y;
+ return *this;
+}
+
+
+
+inline Vector2& Vector2::operator-= (const Vector2& rkVector) {
+ x -= rkVector.x;
+ y -= rkVector.y;
+ return *this;
+}
+
+
+
+inline Vector2& Vector2::operator*= (float fScalar) {
+ x *= fScalar;
+ y *= fScalar;
+ return *this;
+}
+
+
+
+
+inline Vector2& Vector2::operator*= (const Vector2& rkVector) {
+ x *= rkVector.x;
+ y *= rkVector.y;
+ return *this;
+}
+
+
+
+inline Vector2& Vector2::operator/= (const Vector2& rkVector) {
+ x /= rkVector.x;
+ y /= rkVector.y;
+ return *this;
+}
+
+
+inline Vector2 Vector2::operator* (const Vector2& rkVector) const {
+ return Vector2(x * rkVector.x, y * rkVector.y);
+}
+
+
+
+inline Vector2 Vector2::operator/ (const Vector2& rkVector) const {
+ return Vector2(x / rkVector.x, y / rkVector.y);
+}
+
+
+inline float Vector2::squaredLength () const {
+ return x*x + y*y;
+}
+
+
+inline float Vector2::length () const {
+ return sqrtf(x*x + y*y);
+}
+
+
+inline Vector2 Vector2::direction () const {
+ float lenSquared = x * x + y * y;
+
+ if (lenSquared != 1.0f) {
+ return *this / sqrtf(lenSquared);
+ } else {
+ return *this;
+ }
+}
+
+
+
+inline float Vector2::dot (const Vector2& rkVector) const {
+ return x*rkVector.x + y*rkVector.y;
+}
+
+
+
+inline Vector2 Vector2::min(const Vector2 &v) const {
+ return Vector2(G3D::min(v.x, x), G3D::min(v.y, y));
+}
+
+
+
+inline Vector2 Vector2::max(const Vector2 &v) const {
+ return Vector2(G3D::max(v.x, x), G3D::max(v.y, y));
+}
+
+
+
+inline bool Vector2::fuzzyEq(const Vector2& other) const {
+ return G3D::fuzzyEq((*this - other).squaredLength(), 0);
+}
+
+
+
+inline bool Vector2::fuzzyNe(const Vector2& other) const {
+ return G3D::fuzzyNe((*this - other).squaredLength(), 0);
+}
+
+
+
+inline bool Vector2::isFinite() const {
+ return G3D::isFinite(x) && G3D::isFinite(y);
+}
+
+
+
+inline bool Vector2::isZero() const {
+ return (x == 0.0f) && (y == 0.0f);
+}
+
+
+
+inline bool Vector2::isUnit() const {
+ return squaredLength() == 1.0f;
+}
+
+} // namespace G3D
+
+template <>
+struct HashTrait<G3D::Vector2> {
+ static size_t hashCode(const G3D::Vector2& key) {
+ return key.hashCode();
+ }
+};
+
+
+// Intentionally outside namespace to avoid operator overloading confusion
+inline G3D::Vector2 operator*(double s, const G3D::Vector2& v) {
+ return v * (float)s;
+}
+inline G3D::Vector2 operator*(int s, const G3D::Vector2& v) {
+ return v * (float)s;
+}
+
+#endif
diff --git a/externals/g3dlite/G3D.lib/include/G3D/Vector2int16.h b/externals/g3dlite/G3D.lib/include/G3D/Vector2int16.h
new file mode 100644
index 00000000000..b7149ad6c90
--- /dev/null
+++ b/externals/g3dlite/G3D.lib/include/G3D/Vector2int16.h
@@ -0,0 +1,137 @@
+/**
+ @file Vector2int16.h
+
+ @maintainer Morgan McGuire, matrix@brown.edu
+
+ @created 2003-08-09
+ @edited 2004-01-03
+
+ Copyright 2000-2006, Morgan McGuire.
+ All rights reserved.
+ */
+
+#ifndef VECTOR2INT16_H
+#define VECTOR2INT16_H
+
+#include "G3D/platform.h"
+#include "G3D/g3dmath.h"
+#include "G3D/HashTrait.h"
+
+namespace G3D {
+
+/**
+ A Vector2 that packs its fields into uint16s.
+ */
+#ifdef G3D_WIN32
+ // Switch to tight alignment
+ #pragma pack(push, 2)
+#endif
+
+class Vector2int16 {
+private:
+ // Hidden operators
+ bool operator<(const Vector2int16&) const;
+ bool operator>(const Vector2int16&) const;
+ bool operator<=(const Vector2int16&) const;
+ bool operator>=(const Vector2int16&) const;
+
+public:
+ G3D::int16 x;
+ G3D::int16 y;
+
+ Vector2int16() : x(0), y(0) {}
+ Vector2int16(G3D::int16 _x, G3D::int16 _y) : x(_x), y(_y){}
+ Vector2int16(const class Vector2& v);
+ Vector2int16(class BinaryInput& bi);
+
+ inline G3D::int16& operator[] (int i) {
+ debugAssert(((unsigned int)i) <= 1);
+ return ((G3D::int16*)this)[i];
+ }
+
+ inline const G3D::int16& operator[] (int i) const {
+ debugAssert(((unsigned int)i) <= 1);
+ return ((G3D::int16*)this)[i];
+ }
+
+ inline Vector2int16 operator+(const Vector2int16& other) const {
+ return Vector2int16(x + other.x, y + other.y);
+ }
+
+ inline Vector2int16 operator-(const Vector2int16& other) const {
+ return Vector2int16(x - other.x, y - other.y);
+ }
+
+ inline Vector2int16 operator*(const Vector2int16& other) const {
+ return Vector2int16(x * other.x, y * other.y);
+ }
+
+ inline Vector2int16 operator*(const int s) const {
+ return Vector2int16(x * s, y * s);
+ }
+
+ inline Vector2int16& operator+=(const Vector2int16& other) {
+ x += other.x;
+ y += other.y;
+ return *this;
+ }
+
+ /** Shifts both x and y */
+ inline Vector2int16 operator>>(const int s) const {
+ return Vector2int16(x >> s, y >> s);
+ }
+
+ /** Shifts both x and y */
+ inline Vector2int16 operator<<(const int s) const {
+ return Vector2int16(x << s, y << s);
+ }
+
+ inline Vector2int16& operator-=(const Vector2int16& other) {
+ x -= other.x;
+ y -= other.y;
+ return *this;
+ }
+
+ inline Vector2int16& operator*=(const Vector2int16& other) {
+ x *= other.x;
+ y *= other.y;
+ return *this;
+ }
+
+ Vector2int16 clamp(const Vector2int16& lo, const Vector2int16& hi);
+
+ inline bool operator== (const Vector2int16& rkVector) const {
+ return ((int32*)this)[0] == ((int32*)&rkVector)[0];
+ }
+
+ inline bool operator!= (const Vector2int16& rkVector) const {
+ return ((int32*)this)[0] != ((int32*)&rkVector)[0];
+ }
+
+ Vector2int16 max(const Vector2int16& v) const {
+ return Vector2int16(iMax(x, v.x), iMax(y, v.y));
+ }
+
+ Vector2int16 min(const Vector2int16& v) const {
+ return Vector2int16(iMin(x, v.x), iMin(y, v.y));
+ }
+
+ void serialize(class BinaryOutput& bo) const;
+ void deserialize(class BinaryInput& bi);
+}
+#if defined(G3D_LINUX) || defined(G3D_OSX)
+ __attribute((aligned(1)))
+#endif
+;
+
+#ifdef G3D_WIN32
+ #pragma pack(pop)
+#endif
+
+}
+
+template<> struct HashTrait<G3D::Vector2int16> {
+ static size_t hashCode(const G3D::Vector2int16& key) { return static_cast<size_t>(key.x + ((int)key.y << 16)); }
+};
+
+#endif
diff --git a/externals/g3dlite/G3D.lib/include/G3D/Vector3.h b/externals/g3dlite/G3D.lib/include/G3D/Vector3.h
new file mode 100644
index 00000000000..d37638a229d
--- /dev/null
+++ b/externals/g3dlite/G3D.lib/include/G3D/Vector3.h
@@ -0,0 +1,761 @@
+/**
+ @file Vector3.h
+
+ 3D vector class
+
+ @maintainer Morgan McGuire, morgan@cs.williams.edu
+
+ @created 2001-06-02
+ @edited 2008-11-01
+ Copyright 2000-2008, Morgan McGuire.
+ All rights reserved.
+ */
+
+#ifndef G3D_VECTOR3_H
+#define G3D_VECTOR3_H
+
+#include "G3D/platform.h"
+#include "G3D/g3dmath.h"
+#include "G3D/Vector2.h"
+#include "G3D/Table.h"
+#include "G3D/HashTrait.h"
+#include "G3D/PositionTrait.h"
+#include "G3D/Vector2.h"
+#include <iostream>
+#include <string>
+
+//----------------------------------------------------------------------------
+#ifdef SSE
+ // If you receive an error on this line, it is because you do not have the file
+ // xmmintrin.h needed for MMX & SSE extensions. Download and install
+ //
+ // http://download.microsoft.com/download/vstudio60ent/SP5/Wideband-Full/WIN98Me/EN-US/vs6sp5.exe
+ // and
+ // http://download.microsoft.com/download/vb60ent/Update/6/W9X2KXP/EN-US/vcpp5.exe
+ //
+ // to get this file.
+# include <xmmintrin.h>
+#endif
+
+namespace G3D {
+
+class Vector2;
+class Vector3;
+class Vector4;
+class Vector4int8;
+
+/**
+ <B>Swizzles</B>
+ Vector classes have swizzle operators, e.g. <CODE>v.xy()</CODE>, that
+ allow selection of arbitrary sub-fields. These cannot be used as write
+ masks. Examples
+
+ <PRE>
+Vector3 v(1, 2, 3);
+Vector3 j;
+Vector2 b;
+
+b = v.xz();
+j = b.xx();
+</PRE>
+
+
+ <B>Warning</B>
+
+ Do not subclass-- this implementation makes assumptions about the
+ memory layout.
+ */
+class Vector3 {
+private:
+ /**
+ Reflect this vector about the (not necessarily unit) normal.
+ Note that if used for a collision or ray reflection you
+ must negate the resulting vector to get a direction pointing
+ <I>away</I> from the collision.
+
+ <PRE>
+ V' N V
+
+ r ^ -,
+ \ | /
+ \|/
+ </PRE>
+
+ See also Vector3::reflectionDirection
+ */
+ Vector3 reflectAbout(const Vector3& normal) const;
+
+ // Hidden operators
+ bool operator<(const Vector3&) const;
+ bool operator>(const Vector3&) const;
+ bool operator<=(const Vector3&) const;
+ bool operator>=(const Vector3&) const;
+
+public:
+ // construction
+ Vector3();
+
+ /** Divides by 127 */
+ Vector3(const Vector4int8&);
+ explicit Vector3(class BinaryInput& b);
+ Vector3(float _x, float _y, float _z);
+ explicit Vector3(const class Vector2& v, float _z);
+ explicit Vector3(float coordinate[3]);
+ explicit Vector3(double coordinate[3]);
+ Vector3(const Vector3& rkVector);
+ Vector3(const class Vector3int16& v);
+ explicit Vector3(class TextInput& t);
+
+ /** Format is three float32's */
+ void serialize(class BinaryOutput& b) const;
+ void deserialize(class BinaryInput& b);
+
+ /** Format is "(%f, %f, %f)" */
+ void serialize(class TextOutput& t) const;
+ void deserialize(class TextInput& t);
+
+ // coordinates
+ float x, y, z;
+
+ // access vector V as V[0] = V.x, V[1] = V.y, V[2] = V.z
+ //
+ // WARNING. These member functions rely on
+ // (1) Vector3 not having virtual functions
+ // (2) the data packed in a 3*sizeof(float) memory block
+ const float& operator[] (int i) const;
+ float& operator[] (int i);
+
+ inline operator float* () {
+ return (float*)this;
+ }
+
+ operator const float* () const {
+ return (float*)this;
+ }
+
+ enum Axis {X_AXIS=0, Y_AXIS=1, Z_AXIS=2, DETECT_AXIS=-1};
+
+ /**
+ Returns the largest dimension. Particularly convenient for determining
+ which plane to project a triangle onto for point-in-polygon tests.
+ */
+ Axis primaryAxis() const;
+
+ // assignment and comparison
+ Vector3& operator= (const Vector3& rkVector);
+ bool operator== (const Vector3& rkVector) const;
+ bool operator!= (const Vector3& rkVector) const;
+ size_t hashCode() const;
+ bool fuzzyEq(const Vector3& other) const;
+ bool fuzzyNe(const Vector3& other) const;
+
+ /** Returns true if this vector has finite length. */
+ bool isFinite() const;
+
+ /** Returns true if this vector has length ~= 0 */
+ bool isZero() const;
+
+ /** Returns true if this vector has length ~= 1 */
+ bool isUnit() const;
+
+ // arithmetic operations
+ Vector3 operator+ (const Vector3& v) const;
+ Vector3 operator- (const Vector3& v) const;
+ Vector3 operator* (float s) const;
+ Vector3 operator/ (float s) const;
+ Vector3 operator* (const Vector3& v) const;
+ Vector3 operator/ (const Vector3& v) const;
+ Vector3 operator- () const;
+
+ // arithmetic updates
+ Vector3& operator+= (const Vector3& v);
+ Vector3& operator-= (const Vector3& v);
+ Vector3& operator*= (float s);
+ Vector3& operator/= (float s);
+ Vector3& operator*= (const Vector3& v);
+ Vector3& operator/= (const Vector3& v);
+
+ /** Same as magnitude */
+ float length() const;
+
+ float magnitude() const;
+
+ /**
+ The result is a nan vector if the length is almost zero.
+ */
+ Vector3 direction() const;
+
+ /**
+ Potentially less accurate but faster than direction().
+ Only works if System::hasSSE is true.
+ */
+ Vector3 fastDirection() const;
+
+
+ /**
+ See also G3D::Ray::reflect.
+ The length is 1.
+ <PRE>
+ V' N V
+
+ r ^ /
+ \ | /
+ \|'-
+ </PRE>
+ */
+ Vector3 reflectionDirection(const Vector3& normal) const;
+
+
+ /**
+ Returns Vector3::zero() if the length is nearly zero, otherwise
+ returns a unit vector.
+ */
+ inline Vector3 directionOrZero() const {
+ float mag = magnitude();
+ if (G3D::fuzzyEq(mag, 0.0f)) {
+ return Vector3::zero();
+ } else if (G3D::fuzzyEq(mag, 1.0f)) {
+ return *this;
+ } else {
+ return *this * (1.0f / mag);
+ }
+ }
+
+ /**
+ Returns the direction of a refracted ray,
+ where iExit is the index of refraction for the
+ previous material and iEnter is the index of refraction
+ for the new material. Like Vector3::reflectionDirection,
+ the result has length 1 and is
+ pointed <I>away</I> from the intersection.
+
+ Returns Vector3::zero() in the case of total internal refraction.
+
+ @param iOutside The index of refraction (eta) outside
+ (on the <I>positive</I> normal side) of the surface.
+
+ @param iInside The index of refraction (eta) inside
+ (on the <I>negative</I> normal side) of the surface.
+
+ See also G3D::Ray::refract.
+ <PRE>
+ N V
+
+ ^ /
+ | /
+ |'-
+ __--
+ V'<--
+ </PRE>
+ */
+ Vector3 refractionDirection(
+ const Vector3& normal,
+ float iInside,
+ float iOutside) const;
+
+ /** Synonym for direction */
+ inline Vector3 unit() const {
+ return direction();
+ }
+
+ /** Returns a normalized vector. May be computed with lower
+ precision than unit */
+ inline Vector3 fastUnit() const {
+ return fastDirection();
+ }
+
+ /** Same as squaredMagnitude */
+ float squaredLength() const;
+
+ float squaredMagnitude () const;
+
+ float dot(const Vector3& rkVector) const;
+
+ float unitize(float tolerance = 1e-06);
+
+ /** Cross product. Note that two cross products in a row
+ can be computed more cheaply: v1 x (v2 x v3) = (v1 dot v3) v2 - (v1 dot v2) v3.
+ */
+ Vector3 cross(const Vector3& rkVector) const;
+ Vector3 unitCross (const Vector3& rkVector) const;
+
+ /**
+ Returns a matrix such that v.cross() * w = v.cross(w).
+ <PRE>
+ [ 0 -v.z v.y ]
+ [ v.z 0 -v.x ]
+ [ -v.y v.x 0 ]
+ </PRE>
+ */
+ class Matrix3 cross() const;
+
+ Vector3 min(const Vector3 &v) const;
+ Vector3 max(const Vector3 &v) const;
+
+ /** Smallest element */
+ inline float min() const {
+ return G3D::min(G3D::min(x, y), z);
+ }
+
+ /** Largest element */
+ inline float max() const {
+ return G3D::max(G3D::max(x, y), z);
+ }
+
+ std::string toString() const;
+
+ inline Vector3 clamp(const Vector3& low, const Vector3& high) const {
+ return Vector3(
+ G3D::clamp(x, low.x, high.x),
+ G3D::clamp(y, low.y, high.y),
+ G3D::clamp(z, low.z, high.z));
+ }
+
+ inline Vector3 clamp(float low, float high) const {
+ return Vector3(
+ G3D::clamp(x, low, high),
+ G3D::clamp(y, low, high),
+ G3D::clamp(z, low, high));
+ }
+
+ /**
+ Linear interpolation
+ */
+ inline Vector3 lerp(const Vector3& v, float alpha) const {
+ return (*this) + (v - *this) * alpha;
+ }
+
+ /** Gram-Schmidt orthonormalization. */
+ static void orthonormalize (Vector3 akVector[3]);
+
+ /** Random unit vector, uniformly distributed */
+ static Vector3 random();
+
+ /** Random unit vector, distributed
+ so that the probability of V is proportional
+ to max(V dot Normal, 0).
+
+ @cite Henrik Wann Jensen, Realistic Image Synthesis using Photon Mapping eqn 2.24
+ */
+ static Vector3 cosRandom(const Vector3& normal);
+
+
+ /**
+ Random vector distributed over the hemisphere about normal.
+ */
+ static Vector3 hemiRandom(const Vector3& normal);
+
+ // Input W must be initialize to a nonzero vector, output is {U,V,W}
+ // an orthonormal basis. A hint is provided about whether or not W
+ // is already unit length.
+ static void generateOrthonormalBasis (Vector3& rkU, Vector3& rkV,
+ Vector3& rkW, bool bUnitLengthW = true);
+
+ inline float sum() const {
+ return x + y + z;
+ }
+
+ inline float average() const {
+ return sum() / 3.0f;
+ }
+
+ // Special values.
+ inline static const Vector3& zero() { static Vector3 v(0, 0, 0); return v; }
+ inline static const Vector3& one() { static Vector3 v(1, 1, 1); return v; }
+ inline static const Vector3& unitX() { static Vector3 v(1, 0, 0); return v; }
+ inline static const Vector3& unitY() { static Vector3 v(0, 1, 0); return v; }
+ inline static const Vector3& unitZ() { static Vector3 v(0, 0, 1); return v; }
+ inline static const Vector3& inf() { static Vector3 v((float)G3D::inf(), (float)G3D::inf(), (float)G3D::inf()); return v; }
+ inline static const Vector3& nan() { static Vector3 v((float)G3D::nan(), (float)G3D::nan(), (float)G3D::nan()); return v; }
+ /** Smallest (most negative) representable vector */
+ inline static const Vector3& minFinite(){ static Vector3 v(-FLT_MAX, -FLT_MAX, -FLT_MAX); return v; }
+ /** Largest representable vector */
+ inline static const Vector3& maxFinite(){ static Vector3 v(FLT_MAX, FLT_MAX, FLT_MAX); return v; }
+
+ // 2-char swizzles
+
+ Vector2 xx() const;
+ Vector2 yx() const;
+ Vector2 zx() const;
+ Vector2 xy() const;
+ Vector2 yy() const;
+ Vector2 zy() const;
+ Vector2 xz() const;
+ Vector2 yz() const;
+ Vector2 zz() const;
+
+ // 3-char swizzles
+
+ Vector3 xxx() const;
+ Vector3 yxx() const;
+ Vector3 zxx() const;
+ Vector3 xyx() const;
+ Vector3 yyx() const;
+ Vector3 zyx() const;
+ Vector3 xzx() const;
+ Vector3 yzx() const;
+ Vector3 zzx() const;
+ Vector3 xxy() const;
+ Vector3 yxy() const;
+ Vector3 zxy() const;
+ Vector3 xyy() const;
+ Vector3 yyy() const;
+ Vector3 zyy() const;
+ Vector3 xzy() const;
+ Vector3 yzy() const;
+ Vector3 zzy() const;
+ Vector3 xxz() const;
+ Vector3 yxz() const;
+ Vector3 zxz() const;
+ Vector3 xyz() const;
+ Vector3 yyz() const;
+ Vector3 zyz() const;
+ Vector3 xzz() const;
+ Vector3 yzz() const;
+ Vector3 zzz() const;
+
+ // 4-char swizzles
+
+ Vector4 xxxx() const;
+ Vector4 yxxx() const;
+ Vector4 zxxx() const;
+ Vector4 xyxx() const;
+ Vector4 yyxx() const;
+ Vector4 zyxx() const;
+ Vector4 xzxx() const;
+ Vector4 yzxx() const;
+ Vector4 zzxx() const;
+ Vector4 xxyx() const;
+ Vector4 yxyx() const;
+ Vector4 zxyx() const;
+ Vector4 xyyx() const;
+ Vector4 yyyx() const;
+ Vector4 zyyx() const;
+ Vector4 xzyx() const;
+ Vector4 yzyx() const;
+ Vector4 zzyx() const;
+ Vector4 xxzx() const;
+ Vector4 yxzx() const;
+ Vector4 zxzx() const;
+ Vector4 xyzx() const;
+ Vector4 yyzx() const;
+ Vector4 zyzx() const;
+ Vector4 xzzx() const;
+ Vector4 yzzx() const;
+ Vector4 zzzx() const;
+ Vector4 xxxy() const;
+ Vector4 yxxy() const;
+ Vector4 zxxy() const;
+ Vector4 xyxy() const;
+ Vector4 yyxy() const;
+ Vector4 zyxy() const;
+ Vector4 xzxy() const;
+ Vector4 yzxy() const;
+ Vector4 zzxy() const;
+ Vector4 xxyy() const;
+ Vector4 yxyy() const;
+ Vector4 zxyy() const;
+ Vector4 xyyy() const;
+ Vector4 yyyy() const;
+ Vector4 zyyy() const;
+ Vector4 xzyy() const;
+ Vector4 yzyy() const;
+ Vector4 zzyy() const;
+ Vector4 xxzy() const;
+ Vector4 yxzy() const;
+ Vector4 zxzy() const;
+ Vector4 xyzy() const;
+ Vector4 yyzy() const;
+ Vector4 zyzy() const;
+ Vector4 xzzy() const;
+ Vector4 yzzy() const;
+ Vector4 zzzy() const;
+ Vector4 xxxz() const;
+ Vector4 yxxz() const;
+ Vector4 zxxz() const;
+ Vector4 xyxz() const;
+ Vector4 yyxz() const;
+ Vector4 zyxz() const;
+ Vector4 xzxz() const;
+ Vector4 yzxz() const;
+ Vector4 zzxz() const;
+ Vector4 xxyz() const;
+ Vector4 yxyz() const;
+ Vector4 zxyz() const;
+ Vector4 xyyz() const;
+ Vector4 yyyz() const;
+ Vector4 zyyz() const;
+ Vector4 xzyz() const;
+ Vector4 yzyz() const;
+ Vector4 zzyz() const;
+ Vector4 xxzz() const;
+ Vector4 yxzz() const;
+ Vector4 zxzz() const;
+ Vector4 xyzz() const;
+ Vector4 yyzz() const;
+ Vector4 zyzz() const;
+ Vector4 xzzz() const;
+ Vector4 yzzz() const;
+ Vector4 zzzz() const;
+
+ /** A value that can be passed to ignore a parameter. Never look at the result of dummy. */
+ static Vector3 dummy;
+};
+
+inline G3D::Vector3 operator*(float s, const G3D::Vector3& v) {
+ return v * s;
+}
+
+inline G3D::Vector3 operator*(double s, const G3D::Vector3& v) {
+ return v * (float)s;
+}
+
+inline G3D::Vector3 operator*(int s, const G3D::Vector3& v) {
+ return v * (float)s;
+}
+
+std::ostream& operator<<(std::ostream& os, const Vector3&);
+
+
+void serialize(const Vector3::Axis& a, class BinaryOutput& bo);
+void deserialize(Vector3::Axis& a, class BinaryInput& bo);
+
+
+//----------------------------------------------------------------------------
+inline Vector3::Vector3() : x(0.0f), y(0.0f), z(0.0f) {
+}
+
+//----------------------------------------------------------------------------
+
+inline Vector3::Vector3 (float fX, float fY, float fZ) : x(fX), y(fY), z(fZ) {
+}
+
+//----------------------------------------------------------------------------
+inline Vector3::Vector3 (float V[3]) : x(V[0]), y(V[1]), z(V[2]){
+}
+//----------------------------------------------------------------------------
+inline Vector3::Vector3 (double V[3]) : x((float)V[0]), y((float)V[1]), z((float)V[2]){
+}
+
+//----------------------------------------------------------------------------
+inline Vector3::Vector3 (const Vector3& V) : x(V.x), y(V.y), z(V.z) {
+}
+
+//----------------------------------------------------------------------------
+
+//inline Vector3::Vector3 (const __m128& m) {
+ // Cast from SSE packed floats
+// *this = *(Vector3*)&m;
+//}
+
+//----------------------------------------------------------------------------
+inline const float& Vector3::operator[] (int i) const {
+ return ((float*)this)[i];
+}
+
+inline float& Vector3::operator[] (int i) {
+ return ((float*)this)[i];
+}
+
+
+//----------------------------------------------------------------------------
+inline Vector3& Vector3::operator= (const Vector3& rkVector) {
+ x = rkVector.x;
+ y = rkVector.y;
+ z = rkVector.z;
+ return *this;
+}
+
+//----------------------------------------------------------------------------
+
+inline bool Vector3::fuzzyEq(const Vector3& other) const {
+ return G3D::fuzzyEq((*this - other).squaredMagnitude(), 0);
+}
+
+//----------------------------------------------------------------------------
+
+inline bool Vector3::fuzzyNe(const Vector3& other) const {
+ return G3D::fuzzyNe((*this - other).squaredMagnitude(), 0);
+}
+
+//----------------------------------------------------------------------------
+
+inline bool Vector3::isFinite() const {
+ return G3D::isFinite(x) && G3D::isFinite(y) && G3D::isFinite(z);
+}
+
+//----------------------------------------------------------------------------
+inline bool Vector3::operator== (const Vector3& rkVector) const {
+ return ( x == rkVector.x && y == rkVector.y && z == rkVector.z );
+}
+
+//----------------------------------------------------------------------------
+inline bool Vector3::operator!= (const Vector3& rkVector) const {
+ return ( x != rkVector.x || y != rkVector.y || z != rkVector.z );
+}
+
+//----------------------------------------------------------------------------
+inline Vector3 Vector3::operator+ (const Vector3& rkVector) const {
+ return Vector3(x + rkVector.x, y + rkVector.y, z + rkVector.z);
+}
+
+//----------------------------------------------------------------------------
+inline Vector3 Vector3::operator- (const Vector3& rkVector) const {
+ return Vector3(x - rkVector.x, y - rkVector.y, z - rkVector.z);
+}
+
+//----------------------------------------------------------------------------
+inline Vector3 Vector3::operator* (const Vector3& rkVector) const {
+ return Vector3(x * rkVector.x, y * rkVector.y, z * rkVector.z);
+}
+
+inline Vector3 Vector3::operator*(float f) const {
+ return Vector3(x * f, y * f, z * f);
+}
+
+//----------------------------------------------------------------------------
+inline Vector3 Vector3::operator/ (const Vector3& rkVector) const {
+ return Vector3(x / rkVector.x, y / rkVector.y, z / rkVector.z);
+}
+
+//----------------------------------------------------------------------------
+inline Vector3 Vector3::operator- () const {
+ return Vector3(-x, -y, -z);
+}
+
+//----------------------------------------------------------------------------
+inline Vector3& Vector3::operator+= (const Vector3& rkVector) {
+ x += rkVector.x;
+ y += rkVector.y;
+ z += rkVector.z;
+ return *this;
+}
+
+//----------------------------------------------------------------------------
+inline Vector3& Vector3::operator-= (const Vector3& rkVector) {
+ x -= rkVector.x;
+ y -= rkVector.y;
+ z -= rkVector.z;
+ return *this;
+}
+
+//----------------------------------------------------------------------------
+inline Vector3& Vector3::operator*= (float fScalar) {
+ x *= fScalar;
+ y *= fScalar;
+ z *= fScalar;
+ return *this;
+}
+
+//----------------------------------------------------------------------------
+inline Vector3& Vector3::operator*= (const Vector3& rkVector) {
+ x *= rkVector.x;
+ y *= rkVector.y;
+ z *= rkVector.z;
+ return *this;
+}
+
+//----------------------------------------------------------------------------
+inline Vector3& Vector3::operator/= (const Vector3& rkVector) {
+ x /= rkVector.x;
+ y /= rkVector.y;
+ z /= rkVector.z;
+ return *this;
+}
+
+//----------------------------------------------------------------------------
+inline float Vector3::squaredMagnitude () const {
+ return x*x + y*y + z*z;
+}
+
+//----------------------------------------------------------------------------
+inline float Vector3::squaredLength () const {
+ return squaredMagnitude();
+}
+
+//----------------------------------------------------------------------------
+inline float Vector3::magnitude() const {
+ return sqrtf(x*x + y*y + z*z);
+}
+
+//----------------------------------------------------------------------------
+inline float Vector3::length() const {
+ return magnitude();
+}
+
+//----------------------------------------------------------------------------
+inline Vector3 Vector3::direction () const {
+ float lenSquared = squaredMagnitude();
+ float invSqrt = 1.0f / sqrtf(lenSquared);
+ return Vector3(x * invSqrt, y * invSqrt, z * invSqrt);
+}
+
+//----------------------------------------------------------------------------
+
+inline Vector3 Vector3::fastDirection () const {
+ float lenSquared = x * x + y * y + z * z;
+ float invSqrt = rsq(lenSquared);
+ return Vector3(x * invSqrt, y * invSqrt, z * invSqrt);
+}
+
+//----------------------------------------------------------------------------
+inline float Vector3::dot (const Vector3& rkVector) const {
+ return x*rkVector.x + y*rkVector.y + z*rkVector.z;
+}
+
+//----------------------------------------------------------------------------
+inline Vector3 Vector3::cross (const Vector3& rkVector) const {
+ return Vector3(y*rkVector.z - z*rkVector.y, z*rkVector.x - x*rkVector.z,
+ x*rkVector.y - y*rkVector.x);
+}
+
+//----------------------------------------------------------------------------
+inline Vector3 Vector3::unitCross (const Vector3& rkVector) const {
+ Vector3 kCross(y*rkVector.z - z*rkVector.y, z*rkVector.x - x*rkVector.z,
+ x*rkVector.y - y*rkVector.x);
+ kCross.unitize();
+ return kCross;
+}
+
+//----------------------------------------------------------------------------
+inline Vector3 Vector3::min(const Vector3 &v) const {
+ return Vector3(G3D::min(v.x, x), G3D::min(v.y, y), G3D::min(v.z, z));
+}
+
+//----------------------------------------------------------------------------
+inline Vector3 Vector3::max(const Vector3 &v) const {
+ return Vector3(G3D::max(v.x, x), G3D::max(v.y, y), G3D::max(v.z, z));
+}
+
+//----------------------------------------------------------------------------
+inline bool Vector3::isZero() const {
+ return G3D::fuzzyEq(squaredMagnitude(), 0.0f);
+}
+
+//----------------------------------------------------------------------------
+
+inline bool Vector3::isUnit() const {
+ return G3D::fuzzyEq(squaredMagnitude(), 1.0f);
+}
+
+} // namespace G3D
+
+
+template <>
+struct HashTrait<G3D::Vector3> {
+ static size_t hashCode(const G3D::Vector3& key) {
+ return key.hashCode();
+ }
+};
+
+
+template<> struct PositionTrait<class G3D::Vector2> {
+ static void getPosition(const G3D::Vector2& v, G3D::Vector3& p) { p = G3D::Vector3(v, 0); }
+};
+
+template<> struct PositionTrait<class G3D::Vector3> {
+ static void getPosition(const G3D::Vector3& v, G3D::Vector3& p) { p = v; }
+};
+
+
+#endif
diff --git a/externals/g3dlite/G3D.lib/include/G3D/Vector3int16.h b/externals/g3dlite/G3D.lib/include/G3D/Vector3int16.h
new file mode 100644
index 00000000000..f3f30a5bab4
--- /dev/null
+++ b/externals/g3dlite/G3D.lib/include/G3D/Vector3int16.h
@@ -0,0 +1,130 @@
+/**
+ @file Vector3int16.h
+
+ @maintainer Morgan McGuire, matrix@brown.edu
+
+ @created 2003-04-07
+ @edited 2003-06-24
+ Copyright 2000-2004, Morgan McGuire.
+ All rights reserved.
+ */
+
+#ifndef VECTOR3INT16_H
+#define VECTOR3INT16_H
+
+#include "G3D/platform.h"
+#include "G3D/g3dmath.h"
+#include "G3D/HashTrait.h"
+
+namespace G3D {
+
+/**
+ A Vector3 that packs its fields into uint16s.
+ */
+#ifdef G3D_WIN32
+ // Switch to tight alignment
+ #pragma pack(push, 2)
+#endif
+
+class Vector3int16 {
+private:
+ // Hidden operators
+ bool operator<(const Vector3int16&) const;
+ bool operator>(const Vector3int16&) const;
+ bool operator<=(const Vector3int16&) const;
+ bool operator>=(const Vector3int16&) const;
+
+public:
+ G3D::int16 x;
+ G3D::int16 y;
+ G3D::int16 z;
+
+ Vector3int16() : x(0), y(0), z(0) {}
+ Vector3int16(G3D::int16 _x, G3D::int16 _y, G3D::int16 _z) : x(_x), y(_y), z(_z) {}
+ Vector3int16(const class Vector3& v);
+ Vector3int16(class BinaryInput& bi);
+
+ void serialize(class BinaryOutput& bo) const;
+ void deserialize(class BinaryInput& bi);
+
+ inline G3D::int16& operator[] (int i) {
+ debugAssert(i <= 2);
+ return ((G3D::int16*)this)[i];
+ }
+
+ inline const G3D::int16& operator[] (int i) const {
+ debugAssert(i <= 2);
+ return ((G3D::int16*)this)[i];
+ }
+
+ inline Vector3int16 operator+(const Vector3int16& other) const {
+ return Vector3int16(x + other.x, y + other.y, z + other.z);
+ }
+
+ inline Vector3int16 operator-(const Vector3int16& other) const {
+ return Vector3int16(x - other.x, y - other.y, z - other.z);
+ }
+
+ inline Vector3int16 operator*(const Vector3int16& other) const {
+ return Vector3int16(x * other.x, y * other.y, z * other.z);
+ }
+
+ inline Vector3int16 operator*(const int s) const {
+ return Vector3int16(x * s, y * s, z * s);
+ }
+
+ inline Vector3int16& operator+=(const Vector3int16& other) {
+ x += other.x;
+ y += other.y;
+ z += other.y;
+ return *this;
+ }
+
+ inline Vector3int16& operator-=(const Vector3int16& other) {
+ x -= other.x;
+ y -= other.y;
+ z -= other.z;
+ return *this;
+ }
+
+ inline Vector3int16& operator*=(const Vector3int16& other) {
+ x *= other.x;
+ y *= other.y;
+ z *= other.z;
+ return *this;
+ }
+
+ inline bool operator== (const Vector3int16& rkVector) const {
+ return ( x == rkVector.x && y == rkVector.y && z == rkVector.z );
+ }
+
+ inline bool operator!= (const Vector3int16& rkVector) const {
+ return ( x != rkVector.x || y != rkVector.y || z != rkVector.z );
+ }
+
+ Vector3int16 max(const Vector3int16& v) const {
+ return Vector3int16(iMax(x, v.x), iMax(y, v.y), iMax(z, v.z));
+ }
+
+ Vector3int16 min(const Vector3int16& v) const {
+ return Vector3int16(iMin(x, v.x), iMin(y, v.y), iMin(z, v.z));
+ }
+
+ std::string toString() const;
+}
+#if defined(G3D_LINUX) || defined(G3D_OSX)
+ __attribute((aligned(1)))
+#endif
+;
+
+#ifdef G3D_WIN32
+ #pragma pack(pop)
+#endif
+
+}
+
+template <> struct HashTrait<G3D::Vector3int16> {
+ static size_t hashCode(const G3D::Vector3int16& key) { return static_cast<size_t>(key.x + ((int)key.y << 5) + ((int)key.z << 10)); }
+};
+
+#endif
diff --git a/externals/g3dlite/G3D.lib/include/G3D/Vector3int32.h b/externals/g3dlite/G3D.lib/include/G3D/Vector3int32.h
new file mode 100644
index 00000000000..01c8581b47a
--- /dev/null
+++ b/externals/g3dlite/G3D.lib/include/G3D/Vector3int32.h
@@ -0,0 +1,138 @@
+/**
+ @file Vector3int32.h
+
+ @maintainer Morgan McGuire, matrix@brown.edu
+
+ @created 2008-07-01
+ @edited 2008-07-01
+ Copyright 2000-2008, Morgan McGuire.
+ All rights reserved.
+ */
+
+#ifndef VECTOR3INT32_H
+#define VECTOR3INT32_H
+
+#include "G3D/platform.h"
+#include "G3D/g3dmath.h"
+#include "G3D/HashTrait.h"
+
+namespace G3D {
+
+/**
+ A Vector3 that packs its fields into uint32s.
+ */
+#ifdef G3D_WIN32
+ // Switch to tight alignment
+ #pragma pack(push, 4)
+#endif
+
+class Vector3int32 {
+private:
+ // Hidden operators
+ bool operator<(const Vector3int32&) const;
+ bool operator>(const Vector3int32&) const;
+ bool operator<=(const Vector3int32&) const;
+ bool operator>=(const Vector3int32&) const;
+
+public:
+ G3D::int32 x;
+ G3D::int32 y;
+ G3D::int32 z;
+
+ Vector3int32() : x(0), y(0), z(0) {}
+ Vector3int32(int _x, int _y, int _z) : x(_x), y(_y), z(_z) {}
+ Vector3int32(const class Vector3int16& v);
+ Vector3int32(const class Vector3& v);
+ Vector3int32(class BinaryInput& bi);
+
+ void serialize(class BinaryOutput& bo) const;
+ void deserialize(class BinaryInput& bi);
+
+ inline G3D::int32& operator[] (int i) {
+ debugAssert(i <= 2);
+ return ((G3D::int32*)this)[i];
+ }
+
+ inline const G3D::int32& operator[] (int i) const {
+ debugAssert(i <= 2);
+ return ((G3D::int32*)this)[i];
+ }
+
+ inline Vector3int32 operator+(const Vector3int32& other) const {
+ return Vector3int32(x + other.x, y + other.y, z + other.z);
+ }
+
+ inline Vector3int32 operator-(const Vector3int32& other) const {
+ return Vector3int32(x - other.x, y - other.y, z - other.z);
+ }
+
+ inline Vector3int32 operator*(const Vector3int32& other) const {
+ return Vector3int32(x * other.x, y * other.y, z * other.z);
+ }
+
+ inline Vector3int32 operator*(const int s) const {
+ return Vector3int32(x * s, y * s, z * s);
+ }
+
+ inline Vector3int32& operator+=(const Vector3int32& other) {
+ x += other.x;
+ y += other.y;
+ z += other.y;
+ return *this;
+ }
+
+ inline Vector3int32& operator-=(const Vector3int32& other) {
+ x -= other.x;
+ y -= other.y;
+ z -= other.z;
+ return *this;
+ }
+
+ inline Vector3int32& operator*=(const Vector3int32& other) {
+ x *= other.x;
+ y *= other.y;
+ z *= other.z;
+ return *this;
+ }
+
+ inline bool operator== (const Vector3int32& rkVector) const {
+ return ( x == rkVector.x && y == rkVector.y && z == rkVector.z );
+ }
+
+ inline bool operator!= (const Vector3int32& rkVector) const {
+ return ( x != rkVector.x || y != rkVector.y || z != rkVector.z );
+ }
+
+ Vector3int32 max(const Vector3int32& v) const {
+ return Vector3int32(iMax(x, v.x), iMax(y, v.y), iMax(z, v.z));
+ }
+
+ Vector3int32 min(const Vector3int32& v) const {
+ return Vector3int32(iMin(x, v.x), iMin(y, v.y), iMin(z, v.z));
+ }
+
+ std::string toString() const;
+}
+#if defined(G3D_LINUX) || defined(G3D_OSX)
+ __attribute((aligned(1)))
+#endif
+;
+
+#ifdef G3D_WIN32
+ #pragma pack(pop)
+#endif
+
+}
+
+template <> struct HashTrait<G3D::Vector3int32> {
+ static size_t hashCode(const G3D::Vector3int32& key) {
+ // Mask for the top bit of a uint32
+ const G3D::uint32 top = (1 << 31);
+ // Mask for the bottom 10 bits of a uint32
+ const G3D::uint32 bot = 0x000003FF;
+ return static_cast<size_t>(((key.x & top) | ((key.y & top) >> 1) | ((key.z & top) >> 2)) |
+ (((key.x & bot) << 19) ^ ((key.y & bot) << 10) ^ (key.z & bot)));
+ }
+};
+
+#endif
diff --git a/externals/g3dlite/G3D.lib/include/G3D/Vector4.h b/externals/g3dlite/G3D.lib/include/G3D/Vector4.h
new file mode 100644
index 00000000000..d60ff76ea8b
--- /dev/null
+++ b/externals/g3dlite/G3D.lib/include/G3D/Vector4.h
@@ -0,0 +1,717 @@
+/**
+ @file Vector4.h
+
+ Homogeneous vector class.
+
+ @maintainer Morgan McGuire, morgan@cs.williams.edu
+
+ @created 2002-07-09
+ @edited 2008-11-01
+
+ Copyright 2000-2008, Morgan McGuire.
+ All rights reserved.
+ */
+
+#ifndef G3D_VECTOR4_H
+#define G3D_VECTOR4_H
+
+#include "G3D/platform.h"
+#include "G3D/g3dmath.h"
+#include "G3D/Vector3.h"
+#include "G3D/Vector2.h"
+#include "G3D/Table.h"
+#include "G3D/HashTrait.h"
+#include "G3D/PositionTrait.h"
+#include <string>
+
+namespace G3D {
+
+class Vector2;
+class Vector3;
+class Vector4;
+class Vector4int8;
+
+/**
+ Do not subclass-- this implementation makes assumptions about the
+ memory layout.
+ */
+class Vector4 {
+private:
+ // Hidden operators
+ bool operator<(const Vector4&) const;
+ bool operator>(const Vector4&) const;
+ bool operator<=(const Vector4&) const;
+ bool operator>=(const Vector4&) const;
+
+public:
+ // construction
+ Vector4();
+ Vector4(float fX, float fY, float fZ, float fW);
+ Vector4(float afCoordinate[4]);
+ Vector4(const Vector4& rkVector);
+ Vector4(const class Color4& c);
+ Vector4(const Vector3& rkVector, float fW);
+ Vector4(const Vector2& v1, const Vector2& v2);
+ Vector4(const Vector2& v1, float fz, float fw);
+
+ /** Divides by 127 when converting */
+ Vector4(const Vector4int8&);
+
+ Vector4(class BinaryInput& b);
+ void serialize(class BinaryOutput& b) const;
+ void deserialize(class BinaryInput& b);
+
+ // coordinates
+ float x, y, z, w;
+
+ // access vector V as V[0] = V.x, V[1] = V.y, V[2] = V.z, etc.
+ //
+ // WARNING. These member functions rely on
+ // (1) Vector4 not having virtual functions
+ // (2) the data packed in a 4*sizeof(float) memory block
+ float& operator[] (int i);
+ const float& operator[] (int i) const;
+ operator float* ();
+ operator const float* () const;
+
+ // assignment and comparison
+ Vector4& operator= (const Vector4& rkVector);
+ bool operator== (const Vector4& rkVector) const;
+ bool operator!= (const Vector4& rkVector) const;
+
+ inline void set(float _x, float _y, float _z, float _w) {
+ x = _x;
+ y = _y;
+ z = _z;
+ w = _w;
+ }
+
+ inline void set(const Vector3& v, float _w) {
+ x = v.x;
+ y = v.y;
+ z = v.z;
+ w = _w;
+ }
+
+ inline void set(const Vector2& v, float _z, float _w) {
+ x = v.x;
+ y = v.y;
+ z = _z;
+ w = _w;
+ }
+
+ size_t hashCode() const;
+ bool fuzzyEq(const Vector4& other) const;
+ bool fuzzyNe(const Vector4& other) const;
+
+ inline static const Vector4& inf() { static Vector4 v((float)G3D::inf(), (float)G3D::inf(), (float)G3D::inf(), (float)G3D::inf()); return v; }
+ inline static const Vector4& nan() { static Vector4 v((float)G3D::nan(), (float)G3D::nan(), (float)G3D::nan(), (float)G3D::nan()); return v; }
+
+ /** sqrt(this->dot(*this)) */
+ float length() const;
+ float squaredLength() const;
+
+ inline float sum() const {
+ return x + y + z + w;
+ }
+
+ /** Returns true if this vector has finite length */
+ bool isFinite() const;
+
+ /** Returns true if this vector has length == 0 */
+ bool isZero() const;
+
+ /** Returns true if this vector has length == 1 */
+ bool isUnit() const;
+
+ // arithmetic operations
+ Vector4 operator+ (const Vector4& rkVector) const;
+ Vector4 operator- (const Vector4& rkVector) const;
+
+ inline Vector4 operator*(const Vector4& rkVector) const {
+ return Vector4(x * rkVector.x, y * rkVector.y, z * rkVector.z, w * rkVector.w);
+ }
+
+ inline Vector4 operator/(const Vector4& rkVector) const {
+ return Vector4(x / rkVector.x, y / rkVector.y, z / rkVector.z, w / rkVector.w);
+ }
+
+ Vector4 operator*(const class Matrix4& M) const;
+
+ Vector4 operator* (float fScalar) const;
+ Vector4 operator/ (float fScalar) const;
+ Vector4 operator- () const;
+ friend Vector4 operator* (float, const Vector4& rkVector);
+
+ // arithmetic updates
+ Vector4& operator+= (const Vector4& rkVector);
+ Vector4& operator-= (const Vector4& rkVector);
+ Vector4& operator*= (float fScalar);
+ Vector4& operator/= (float fScalar);
+
+ inline Vector4 clamp(const Vector4& low, const Vector4& high) const {
+ return Vector4(
+ G3D::clamp(x, low.x, high.x),
+ G3D::clamp(y, low.y, high.y),
+ G3D::clamp(z, low.z, high.z),
+ G3D::clamp(w, low.w, high.w));
+ }
+
+ inline Vector4 clamp(float low, float high) const {
+ return Vector4(
+ G3D::clamp(x, low, high),
+ G3D::clamp(y, low, high),
+ G3D::clamp(z, low, high),
+ G3D::clamp(w, low, high));
+ }
+
+ float dot (const Vector4& rkVector) const;
+
+ Vector4 min(const Vector4& v) const;
+ Vector4 max(const Vector4& v) const;
+
+ std::string toString() const;
+
+ /**
+ Linear interpolation
+ */
+ Vector4 lerp(const Vector4& v, float alpha) const;
+
+ // 2-char swizzles
+
+ Vector2 xx() const;
+ Vector2 yx() const;
+ Vector2 zx() const;
+ Vector2 wx() const;
+ Vector2 xy() const;
+ Vector2 yy() const;
+ Vector2 zy() const;
+ Vector2 wy() const;
+ Vector2 xz() const;
+ Vector2 yz() const;
+ Vector2 zz() const;
+ Vector2 wz() const;
+ Vector2 xw() const;
+ Vector2 yw() const;
+ Vector2 zw() const;
+ Vector2 ww() const;
+
+ // 3-char swizzles
+
+ Vector3 xxx() const;
+ Vector3 yxx() const;
+ Vector3 zxx() const;
+ Vector3 wxx() const;
+ Vector3 xyx() const;
+ Vector3 yyx() const;
+ Vector3 zyx() const;
+ Vector3 wyx() const;
+ Vector3 xzx() const;
+ Vector3 yzx() const;
+ Vector3 zzx() const;
+ Vector3 wzx() const;
+ Vector3 xwx() const;
+ Vector3 ywx() const;
+ Vector3 zwx() const;
+ Vector3 wwx() const;
+ Vector3 xxy() const;
+ Vector3 yxy() const;
+ Vector3 zxy() const;
+ Vector3 wxy() const;
+ Vector3 xyy() const;
+ Vector3 yyy() const;
+ Vector3 zyy() const;
+ Vector3 wyy() const;
+ Vector3 xzy() const;
+ Vector3 yzy() const;
+ Vector3 zzy() const;
+ Vector3 wzy() const;
+ Vector3 xwy() const;
+ Vector3 ywy() const;
+ Vector3 zwy() const;
+ Vector3 wwy() const;
+ Vector3 xxz() const;
+ Vector3 yxz() const;
+ Vector3 zxz() const;
+ Vector3 wxz() const;
+ Vector3 xyz() const;
+ Vector3 yyz() const;
+ Vector3 zyz() const;
+ Vector3 wyz() const;
+ Vector3 xzz() const;
+ Vector3 yzz() const;
+ Vector3 zzz() const;
+ Vector3 wzz() const;
+ Vector3 xwz() const;
+ Vector3 ywz() const;
+ Vector3 zwz() const;
+ Vector3 wwz() const;
+ Vector3 xxw() const;
+ Vector3 yxw() const;
+ Vector3 zxw() const;
+ Vector3 wxw() const;
+ Vector3 xyw() const;
+ Vector3 yyw() const;
+ Vector3 zyw() const;
+ Vector3 wyw() const;
+ Vector3 xzw() const;
+ Vector3 yzw() const;
+ Vector3 zzw() const;
+ Vector3 wzw() const;
+ Vector3 xww() const;
+ Vector3 yww() const;
+ Vector3 zww() const;
+ Vector3 www() const;
+
+ // 4-char swizzles
+
+ Vector4 xxxx() const;
+ Vector4 yxxx() const;
+ Vector4 zxxx() const;
+ Vector4 wxxx() const;
+ Vector4 xyxx() const;
+ Vector4 yyxx() const;
+ Vector4 zyxx() const;
+ Vector4 wyxx() const;
+ Vector4 xzxx() const;
+ Vector4 yzxx() const;
+ Vector4 zzxx() const;
+ Vector4 wzxx() const;
+ Vector4 xwxx() const;
+ Vector4 ywxx() const;
+ Vector4 zwxx() const;
+ Vector4 wwxx() const;
+ Vector4 xxyx() const;
+ Vector4 yxyx() const;
+ Vector4 zxyx() const;
+ Vector4 wxyx() const;
+ Vector4 xyyx() const;
+ Vector4 yyyx() const;
+ Vector4 zyyx() const;
+ Vector4 wyyx() const;
+ Vector4 xzyx() const;
+ Vector4 yzyx() const;
+ Vector4 zzyx() const;
+ Vector4 wzyx() const;
+ Vector4 xwyx() const;
+ Vector4 ywyx() const;
+ Vector4 zwyx() const;
+ Vector4 wwyx() const;
+ Vector4 xxzx() const;
+ Vector4 yxzx() const;
+ Vector4 zxzx() const;
+ Vector4 wxzx() const;
+ Vector4 xyzx() const;
+ Vector4 yyzx() const;
+ Vector4 zyzx() const;
+ Vector4 wyzx() const;
+ Vector4 xzzx() const;
+ Vector4 yzzx() const;
+ Vector4 zzzx() const;
+ Vector4 wzzx() const;
+ Vector4 xwzx() const;
+ Vector4 ywzx() const;
+ Vector4 zwzx() const;
+ Vector4 wwzx() const;
+ Vector4 xxwx() const;
+ Vector4 yxwx() const;
+ Vector4 zxwx() const;
+ Vector4 wxwx() const;
+ Vector4 xywx() const;
+ Vector4 yywx() const;
+ Vector4 zywx() const;
+ Vector4 wywx() const;
+ Vector4 xzwx() const;
+ Vector4 yzwx() const;
+ Vector4 zzwx() const;
+ Vector4 wzwx() const;
+ Vector4 xwwx() const;
+ Vector4 ywwx() const;
+ Vector4 zwwx() const;
+ Vector4 wwwx() const;
+ Vector4 xxxy() const;
+ Vector4 yxxy() const;
+ Vector4 zxxy() const;
+ Vector4 wxxy() const;
+ Vector4 xyxy() const;
+ Vector4 yyxy() const;
+ Vector4 zyxy() const;
+ Vector4 wyxy() const;
+ Vector4 xzxy() const;
+ Vector4 yzxy() const;
+ Vector4 zzxy() const;
+ Vector4 wzxy() const;
+ Vector4 xwxy() const;
+ Vector4 ywxy() const;
+ Vector4 zwxy() const;
+ Vector4 wwxy() const;
+ Vector4 xxyy() const;
+ Vector4 yxyy() const;
+ Vector4 zxyy() const;
+ Vector4 wxyy() const;
+ Vector4 xyyy() const;
+ Vector4 yyyy() const;
+ Vector4 zyyy() const;
+ Vector4 wyyy() const;
+ Vector4 xzyy() const;
+ Vector4 yzyy() const;
+ Vector4 zzyy() const;
+ Vector4 wzyy() const;
+ Vector4 xwyy() const;
+ Vector4 ywyy() const;
+ Vector4 zwyy() const;
+ Vector4 wwyy() const;
+ Vector4 xxzy() const;
+ Vector4 yxzy() const;
+ Vector4 zxzy() const;
+ Vector4 wxzy() const;
+ Vector4 xyzy() const;
+ Vector4 yyzy() const;
+ Vector4 zyzy() const;
+ Vector4 wyzy() const;
+ Vector4 xzzy() const;
+ Vector4 yzzy() const;
+ Vector4 zzzy() const;
+ Vector4 wzzy() const;
+ Vector4 xwzy() const;
+ Vector4 ywzy() const;
+ Vector4 zwzy() const;
+ Vector4 wwzy() const;
+ Vector4 xxwy() const;
+ Vector4 yxwy() const;
+ Vector4 zxwy() const;
+ Vector4 wxwy() const;
+ Vector4 xywy() const;
+ Vector4 yywy() const;
+ Vector4 zywy() const;
+ Vector4 wywy() const;
+ Vector4 xzwy() const;
+ Vector4 yzwy() const;
+ Vector4 zzwy() const;
+ Vector4 wzwy() const;
+ Vector4 xwwy() const;
+ Vector4 ywwy() const;
+ Vector4 zwwy() const;
+ Vector4 wwwy() const;
+ Vector4 xxxz() const;
+ Vector4 yxxz() const;
+ Vector4 zxxz() const;
+ Vector4 wxxz() const;
+ Vector4 xyxz() const;
+ Vector4 yyxz() const;
+ Vector4 zyxz() const;
+ Vector4 wyxz() const;
+ Vector4 xzxz() const;
+ Vector4 yzxz() const;
+ Vector4 zzxz() const;
+ Vector4 wzxz() const;
+ Vector4 xwxz() const;
+ Vector4 ywxz() const;
+ Vector4 zwxz() const;
+ Vector4 wwxz() const;
+ Vector4 xxyz() const;
+ Vector4 yxyz() const;
+ Vector4 zxyz() const;
+ Vector4 wxyz() const;
+ Vector4 xyyz() const;
+ Vector4 yyyz() const;
+ Vector4 zyyz() const;
+ Vector4 wyyz() const;
+ Vector4 xzyz() const;
+ Vector4 yzyz() const;
+ Vector4 zzyz() const;
+ Vector4 wzyz() const;
+ Vector4 xwyz() const;
+ Vector4 ywyz() const;
+ Vector4 zwyz() const;
+ Vector4 wwyz() const;
+ Vector4 xxzz() const;
+ Vector4 yxzz() const;
+ Vector4 zxzz() const;
+ Vector4 wxzz() const;
+ Vector4 xyzz() const;
+ Vector4 yyzz() const;
+ Vector4 zyzz() const;
+ Vector4 wyzz() const;
+ Vector4 xzzz() const;
+ Vector4 yzzz() const;
+ Vector4 zzzz() const;
+ Vector4 wzzz() const;
+ Vector4 xwzz() const;
+ Vector4 ywzz() const;
+ Vector4 zwzz() const;
+ Vector4 wwzz() const;
+ Vector4 xxwz() const;
+ Vector4 yxwz() const;
+ Vector4 zxwz() const;
+ Vector4 wxwz() const;
+ Vector4 xywz() const;
+ Vector4 yywz() const;
+ Vector4 zywz() const;
+ Vector4 wywz() const;
+ Vector4 xzwz() const;
+ Vector4 yzwz() const;
+ Vector4 zzwz() const;
+ Vector4 wzwz() const;
+ Vector4 xwwz() const;
+ Vector4 ywwz() const;
+ Vector4 zwwz() const;
+ Vector4 wwwz() const;
+ Vector4 xxxw() const;
+ Vector4 yxxw() const;
+ Vector4 zxxw() const;
+ Vector4 wxxw() const;
+ Vector4 xyxw() const;
+ Vector4 yyxw() const;
+ Vector4 zyxw() const;
+ Vector4 wyxw() const;
+ Vector4 xzxw() const;
+ Vector4 yzxw() const;
+ Vector4 zzxw() const;
+ Vector4 wzxw() const;
+ Vector4 xwxw() const;
+ Vector4 ywxw() const;
+ Vector4 zwxw() const;
+ Vector4 wwxw() const;
+ Vector4 xxyw() const;
+ Vector4 yxyw() const;
+ Vector4 zxyw() const;
+ Vector4 wxyw() const;
+ Vector4 xyyw() const;
+ Vector4 yyyw() const;
+ Vector4 zyyw() const;
+ Vector4 wyyw() const;
+ Vector4 xzyw() const;
+ Vector4 yzyw() const;
+ Vector4 zzyw() const;
+ Vector4 wzyw() const;
+ Vector4 xwyw() const;
+ Vector4 ywyw() const;
+ Vector4 zwyw() const;
+ Vector4 wwyw() const;
+ Vector4 xxzw() const;
+ Vector4 yxzw() const;
+ Vector4 zxzw() const;
+ Vector4 wxzw() const;
+ Vector4 xyzw() const;
+ Vector4 yyzw() const;
+ Vector4 zyzw() const;
+ Vector4 wyzw() const;
+ Vector4 xzzw() const;
+ Vector4 yzzw() const;
+ Vector4 zzzw() const;
+ Vector4 wzzw() const;
+ Vector4 xwzw() const;
+ Vector4 ywzw() const;
+ Vector4 zwzw() const;
+ Vector4 wwzw() const;
+ Vector4 xxww() const;
+ Vector4 yxww() const;
+ Vector4 zxww() const;
+ Vector4 wxww() const;
+ Vector4 xyww() const;
+ Vector4 yyww() const;
+ Vector4 zyww() const;
+ Vector4 wyww() const;
+ Vector4 xzww() const;
+ Vector4 yzww() const;
+ Vector4 zzww() const;
+ Vector4 wzww() const;
+ Vector4 xwww() const;
+ Vector4 ywww() const;
+ Vector4 zwww() const;
+ Vector4 wwww() const;
+
+};
+
+
+//----------------------------------------------------------------------------
+inline Vector4::Vector4() {
+ x = y = z = w = 0;
+}
+
+//----------------------------------------------------------------------------
+
+inline Vector4::Vector4 (float fX, float fY, float fZ, float fW) {
+ x = fX;
+ y = fY;
+ z = fZ;
+ w = fW;
+}
+
+//----------------------------------------------------------------------------
+inline Vector4::Vector4 (float afCoordinate[4]) {
+ x = afCoordinate[0];
+ y = afCoordinate[1];
+ z = afCoordinate[2];
+ w = afCoordinate[3];
+}
+
+//----------------------------------------------------------------------------
+inline Vector4::Vector4(const Vector4& rkVector) {
+ x = rkVector.x;
+ y = rkVector.y;
+ z = rkVector.z;
+ w = rkVector.w;
+}
+//----------------------------------------------------------------------------
+inline Vector4::Vector4(const Vector3& rkVector, float fW) {
+ x = rkVector.x;
+ y = rkVector.y;
+ z = rkVector.z;
+ w = fW;
+}
+
+//----------------------------------------------------------------------------
+inline float& Vector4::operator[] (int i) {
+ return ((float*)this)[i];
+}
+
+//----------------------------------------------------------------------------
+inline const float& Vector4::operator[] (int i) const {
+ return ((float*)this)[i];
+}
+
+//----------------------------------------------------------------------------
+inline Vector4::operator float* () {
+ return (float*)this;
+}
+
+inline Vector4::operator const float* () const {
+ return (float*)this;
+}
+
+//----------------------------------------------------------------------------
+inline Vector4& Vector4::operator= (const Vector4& rkVector) {
+ x = rkVector.x;
+ y = rkVector.y;
+ z = rkVector.z;
+ w = rkVector.w;
+ return *this;
+}
+
+//----------------------------------------------------------------------------
+inline bool Vector4::operator== (const Vector4& rkVector) const {
+ return ( (x == rkVector.x) && (y == rkVector.y) && (z == rkVector.z) && (w == rkVector.w));
+}
+
+//----------------------------------------------------------------------------
+inline bool Vector4::operator!= (const Vector4& rkVector) const {
+ return ( x != rkVector.x || y != rkVector.y || z != rkVector.z || w != rkVector.w);
+}
+
+//----------------------------------------------------------------------------
+inline Vector4 Vector4::operator+ (const Vector4& rkVector) const {
+ return Vector4(x + rkVector.x, y + rkVector.y, z + rkVector.z, w + rkVector.w);
+}
+
+//----------------------------------------------------------------------------
+inline Vector4 Vector4::operator- (const Vector4& rkVector) const {
+ return Vector4(x - rkVector.x, y - rkVector.y, z - rkVector.z, w - rkVector.w);
+}
+
+//----------------------------------------------------------------------------
+inline Vector4 Vector4::operator* (float fScalar) const {
+ return Vector4(fScalar*x, fScalar*y, fScalar*z, fScalar*w);
+}
+
+//----------------------------------------------------------------------------
+inline Vector4 Vector4::operator- () const {
+ return Vector4( -x, -y, -z, -w);
+}
+
+//----------------------------------------------------------------------------
+inline Vector4& Vector4::operator+= (const Vector4& rkVector) {
+ x += rkVector.x;
+ y += rkVector.y;
+ z += rkVector.z;
+ w += rkVector.w;
+ return *this;
+}
+
+//----------------------------------------------------------------------------
+inline Vector4& Vector4::operator-= (const Vector4& rkVector) {
+ x -= rkVector.x;
+ y -= rkVector.y;
+ z -= rkVector.z;
+ w -= rkVector.w;
+ return *this;
+}
+
+//----------------------------------------------------------------------------
+
+inline Vector4 Vector4::lerp(const Vector4& v, float alpha) const {
+ return (*this) + (v - *this) * alpha;
+}
+
+
+//----------------------------------------------------------------------------
+inline Vector4& Vector4::operator*= (float fScalar) {
+ x *= fScalar;
+ y *= fScalar;
+ z *= fScalar;
+ w *= fScalar;
+ return *this;
+}
+
+
+//----------------------------------------------------------------------------
+inline float Vector4::dot(const Vector4& rkVector) const {
+ return x*rkVector.x + y*rkVector.y + z*rkVector.z + w*rkVector.w;
+}
+
+//----------------------------------------------------------------------------
+inline Vector4 Vector4::min(const Vector4 &v) const {
+ return Vector4(G3D::min(v.x, x), G3D::min(v.y, y), G3D::min(v.z, z), G3D::min(v.w, w));
+}
+
+//----------------------------------------------------------------------------
+inline Vector4 Vector4::max(const Vector4 &v) const {
+ return Vector4(G3D::max(v.x, x), G3D::max(v.y, y), G3D::max(v.z, z), G3D::max(v.w, w));
+}
+
+//----------------------------------------------------------------------------
+inline bool Vector4::isZero() const {
+ return (x == 0.0f) && (y == 0.0f) && (z == 0.0f) && (w == 0.0f);
+}
+
+//----------------------------------------------------------------------------
+
+inline bool Vector4::isFinite() const {
+ return G3D::isFinite(x) && G3D::isFinite(y) && G3D::isFinite(z) && G3D::isFinite(w);
+}
+
+//----------------------------------------------------------------------------
+
+inline bool Vector4::isUnit() const {
+ return squaredLength() == 1.0;
+}
+
+//----------------------------------------------------------------------------
+
+inline float Vector4::length() const {
+ return sqrtf(squaredLength());
+}
+
+//----------------------------------------------------------------------------
+
+inline float Vector4::squaredLength() const {
+ return x * x + y * y + z * z + w * w;
+}
+
+}
+
+template <> struct HashTrait<G3D::Vector4> {
+ static size_t hashCode(const G3D::Vector4& key) { return key.hashCode(); }
+};
+
+
+template<> struct PositionTrait<class G3D::Vector4> {
+ static void getPosition(const G3D::Vector4& v, G3D::Vector3& p) { p = v.xyz(); }
+};
+
+inline G3D::Vector4 operator* (float s, const G3D::Vector4& v) {
+ return v * s;
+}
+
+#endif
diff --git a/externals/g3dlite/G3D.lib/include/G3D/Vector4int8.h b/externals/g3dlite/G3D.lib/include/G3D/Vector4int8.h
new file mode 100644
index 00000000000..8476a2d008b
--- /dev/null
+++ b/externals/g3dlite/G3D.lib/include/G3D/Vector4int8.h
@@ -0,0 +1,113 @@
+/**
+ @file Vector4int8.h
+
+ Homogeneous vector class.
+
+ @maintainer Morgan McGuire, matrix@graphics3d.com
+
+ @created 2007-02-09
+ @edited 2007-02-09
+
+ Copyright 2000-2007, Morgan McGuire.
+ All rights reserved.
+ */
+
+#ifndef G3D_VECTOR4INT8_H
+#define G3D_VECTOR4INT8_H
+
+#include "G3D/platform.h"
+#include "G3D/g3dmath.h"
+
+namespace G3D {
+
+class Vector3;
+class Vector4;
+
+/**
+ Homogeneous vector stored efficiently in four signed int8s.
+
+ */
+class Vector4int8 {
+private:
+ // Hidden operators
+ bool operator<(const Vector4int8&) const;
+ bool operator>(const Vector4int8&) const;
+ bool operator<=(const Vector4int8&) const;
+ bool operator>=(const Vector4int8&) const;
+
+
+ /** For fast operations, treat this packed data structure as
+ an int32 */
+ inline uint32& asInt32() {
+ return *reinterpret_cast<uint32*>(this);
+ }
+
+ inline const uint32& asInt32() const {
+ return *reinterpret_cast<const uint32*>(this);
+ }
+
+public:
+ // construction
+ inline Vector4int8() : x(0), y(0), z(0), w(0) {}
+
+ /** Multiplies the source by 127 and clamps to (-128, 127) when converting */
+ Vector4int8(const Vector4& source);
+
+ /** Multiplies the source by 127 and clamps to (-128, 127) when converting */
+ Vector4int8(const Vector3& source, int8 w);
+
+ inline Vector4int8(int8 x, int8 y, int8 z, int8 w) : x(x), y(y), z(z), w(w) {}
+
+ Vector4int8(class BinaryInput& b);
+ void serialize(class BinaryOutput& b) const;
+ void deserialize(class BinaryInput& b);
+
+ // coordinates
+ int8 x, y, z, w;
+
+ inline operator int8* () {
+ return reinterpret_cast<int8*>(this);
+ }
+
+ inline operator const int8* () const {
+ return reinterpret_cast<const int8*>(this);
+ }
+
+ // access vector V as V[0] = V.x, V[1] = V.y, V[2] = V.z, etc.
+ //
+ // WARNING. These member functions rely on
+ // (1) Vector4int8 not having virtual functions
+ // (2) the data packed in a 4*sizeof(int8) memory block
+ inline int8& operator[] (int i) {
+ debugAssert(i >= 0 && i <= 4);
+ return ((int8*)this)[i];
+ }
+
+ const int8& operator[] (int i) const {
+ debugAssert(i >= 0 && i <= 4);
+ return ((const int8*)this)[i];
+ }
+
+ // assignment and comparison
+ Vector4int8& operator= (const Vector4int8& other) {
+ asInt32() = other.asInt32();
+ return *this;
+ }
+
+ inline bool operator== (const Vector4int8& other) const {
+ return asInt32() == other.asInt32();
+ }
+
+ inline bool operator!= (const Vector4int8& other) const {
+ return ! (*this == other);
+ }
+
+ inline unsigned int hashCode() const {
+ return asInt32();
+ }
+};
+
+} // namespace G3D
+
+
+#endif
diff --git a/externals/g3dlite/G3D.lib/include/G3D/WeakCache.h b/externals/g3dlite/G3D.lib/include/G3D/WeakCache.h
new file mode 100644
index 00000000000..87988aaf5cb
--- /dev/null
+++ b/externals/g3dlite/G3D.lib/include/G3D/WeakCache.h
@@ -0,0 +1,90 @@
+/**
+ @file WeakCache.h
+
+ @maintainer Morgan McGuire, graphics3d.com
+
+ @created 2007-05-16
+ @edited 2007-05-16
+
+ Copyright 2000-2007, Morgan McGuire.
+ All rights reserved.
+ */
+#ifndef G3D_WEAKCACHE_H
+#define G3D_WEAKCACHE_H
+
+#include "G3D/ReferenceCount.h"
+#include "G3D/Table.h"
+
+namespace G3D {
+
+/**
+ A cache that does not prevent its members from being garbage collected.
+ Useful to avoid loading or computing an expression twice. Useful
+ for memoization and dynamic programming.
+
+ Maintains a table of weak pointers. Weak pointers do not prevent
+ an object from being garbage collected. If the object is garbage
+ collected, the cache removes its reference.
+
+ There are no "contains" or "iterate" methods because elements can be
+ flushed from the cache at any time if they are garbage collected.
+
+ Example:
+ <pre>
+ WeakCache<std::string, TextureRef> textureCache;
+
+ TextureRef loadTexture(std::string s) {
+ TextureRef t = textureCache[s];
+
+ if (t.isNull()) {
+ t = Texture::fromFile(s);
+ textureCache.set(s, t);
+ }
+
+ return t;
+ }
+
+
+ </pre>
+ */
+template<class Key, class ValueRef>
+class WeakCache {
+ typedef WeakReferenceCountedPointer<typename ValueRef::element_type> ValueWeakRef;
+
+private:
+
+ Table<Key, ValueWeakRef> table;
+
+public:
+ /**
+ Returns NULL if the object is not in the cache
+ */
+ ValueRef operator[](const Key& k) {
+ if (table.containsKey(k)) {
+ ValueWeakRef w = table[k];
+ ValueRef s = w.createStrongPtr();
+ if (s.isNull()) {
+ // This object has been collected; clean out its key
+ table.remove(k);
+ }
+ return s;
+ } else {
+ return NULL;
+ }
+ }
+
+ void set(const Key& k, ValueRef v) {
+ table.set(k, v);
+ }
+
+ /** Removes k from the cache or does nothing if it is not currently in the cache.*/
+ void remove(const Key& k) {
+ if (table.containsKey(k)) {
+ table.remove(k);
+ }
+ }
+};
+
+}
+#endif
+
diff --git a/externals/g3dlite/G3D.lib/include/G3D/WrapMode.h b/externals/g3dlite/G3D.lib/include/G3D/WrapMode.h
new file mode 100644
index 00000000000..f97e68d6910
--- /dev/null
+++ b/externals/g3dlite/G3D.lib/include/G3D/WrapMode.h
@@ -0,0 +1,79 @@
+/**
+ @file WrapMode.h
+
+ @maintainer Morgan McGuire, graphics3d.com
+
+ @created 2007-04-17
+ @edited 2007-04-17
+
+ Copyright 2000-2007, Morgan McGuire.
+ All rights reserved.
+ */
+
+#ifndef G3D_WRAPMODE_H
+#define G3D_WRAPMODE_H
+
+#include "G3D/platform.h"
+#include "G3D/enumclass.h"
+
+#ifdef IGNORE
+# undef IGNORE
+#endif
+#ifdef ZERO
+# undef ZERO
+#endif
+#ifdef ERROR
+# undef ERROR
+#endif
+
+namespace G3D {
+
+/**
+ Describes the behavior of G3D::Texture, G3D::Map2D, G3D::Image3, etc. when accessing an out-of-bounds
+ pixel. Not all classes support all modes.
+
+ Refer to these as scoped enums, e.g., <code>WrapMode m = WrapMode::CLAMP;</code>.
+
+ WrapMode::IGNORE silently discards attempts to write to out
+ of bounds locations and returns an undefined value for reading
+ from out of bounds locations.
+
+ WrapMode::ERROR generates an error when the
+ pixel indices are out of bounds
+
+ WrapMode::CLAMP makes out of bounds pixels equal to the last in-range pixel along that dimension.
+
+ WrapMode::TILE computes out of bounds pixels modulo the dimension
+
+ WrapMode::ZERO treats out of bounds values as the zero value, which varies in definition
+ according to the class used. For example, with a G3D::Texture, ZERO = Color4(0,0,0,0).
+
+ Uses the "Intelligent Enum" design pattern
+ http://www.codeguru.com/cpp/cpp/cpp_mfc/article.php/c4001/
+ */
+class WrapMode {
+public:
+ /** Don't use this enum; use WrapMode instances instead. */
+ enum Value {
+ CLAMP,
+ TILE,
+ ZERO,
+ IGNORE,
+ ERROR
+ };
+
+private:
+
+ Value value;
+
+public:
+
+ G3D_DECLARE_ENUM_CLASS_METHODS(WrapMode);
+
+};
+
+} // namespace G3D
+
+G3D_DECLARE_ENUM_CLASS_HASHCODE(G3D::WrapMode);
+
+#endif
diff --git a/externals/g3dlite/G3D.lib/include/G3D/debug.h b/externals/g3dlite/G3D.lib/include/G3D/debug.h
new file mode 100644
index 00000000000..a96babc6fa0
--- /dev/null
+++ b/externals/g3dlite/G3D.lib/include/G3D/debug.h
@@ -0,0 +1,66 @@
+/**
+ @file debug.h
+
+ @maintainer Morgan McGuire, matrix@graphics3d.com
+
+ @created 2001-08-26
+ @edited 2006-02-16
+
+ Copyright 2000-2006, Morgan McGuire.
+ All rights reserved.
+*/
+
+#ifndef G3D_DEBUG_H
+#define G3D_DEBUG_H
+
+#include "G3D/platform.h"
+#ifdef _MSC_VER
+ #include <crtdbg.h>
+#endif
+
+#include "G3D/debugPrintf.h"
+#include "G3D/debugAssert.h"
+
+namespace G3D {
+
+#ifdef _MSC_VER
+ // Turn off 64-bit warnings
+# pragma warning(push)
+# pragma warning( disable : 4312)
+# pragma warning( disable : 4267)
+# pragma warning( disable : 4311)
+#endif
+
+
+/**
+ Useful for debugging purposes.
+ */
+inline bool isValidHeapPointer(const void* x) {
+ #ifdef _MSC_VER
+ return
+ (x != (void*)0xcccccccc) && (x != (void*)0xdeadbeef) && (x != (void*)0xfeeefeee);
+ #else
+ return x != NULL;
+ #endif
+}
+
+/**
+ Returns true if the pointer is likely to be
+ a valid pointer (instead of an arbitrary number).
+ Useful for debugging purposes.
+ */
+inline bool isValidPointer(const void* x) {
+ #ifdef _MSC_VER
+ return x != ((void*)0xcccccccc) && (x != (void*)0xdeadbeef) && (x != (void*)0xfeeefeee);
+ #else
+ return x != NULL;
+ #endif
+}
+
+#ifdef _MSC_VER
+# pragma warning(pop)
+#endif
+
+}
+
+#endif
diff --git a/externals/g3dlite/G3D.lib/include/G3D/debugAssert.h b/externals/g3dlite/G3D.lib/include/G3D/debugAssert.h
new file mode 100644
index 00000000000..f9b5bea4e9f
--- /dev/null
+++ b/externals/g3dlite/G3D.lib/include/G3D/debugAssert.h
@@ -0,0 +1,236 @@
+/**
+ @file debugAssert.h
+
+ debugAssert(expression);
+ debugAssertM(expression, message);
+
+ @cite
+ John Robbins, Microsoft Systems Journal Bugslayer Column, Feb 1999.
+ <A HREF="http://msdn.microsoft.com/library/periodic/period99/feb99_BUGSLAYE_BUGSLAYE.htm">
+ http://msdn.microsoft.com/library/periodic/period99/feb99_BUGSLAYE_BUGSLAYE.htm</A>
+
+ @cite
+ Douglas Cox, An assert() Replacement, Code of The Day, flipcode, Sept 19, 2000
+ <A HREF="http://www.flipcode.com/cgi-bin/msg.cgi?showThread=COTD-AssertReplace&forum=cotd&id=-1">
+ http://www.flipcode.com/cgi-bin/msg.cgi?showThread=COTD-AssertReplace&forum=cotd&id=-1</A>
+
+ @maintainer Morgan McGuire, matrix@graphics3d.com
+
+ @created 2001-08-26
+ @edited 2006-01-12
+
+ Copyright 2000-2006, Morgan McGuire.
+ All rights reserved.
+ */
+
+#ifndef G3D_DEBUGASSERT_H
+#define G3D_DEBUGASSERT_H
+
+#include <string>
+#include "G3D/platform.h"
+
+#include <cstdlib>
+
+#ifdef _MSC_VER
+// conditional expression is constant
+# pragma warning (disable : 4127)
+#endif
+
+#ifdef G3D_LINUX
+ // Needed so we can define a global display
+ // pointer for debugAssert.
+ #include <X11/Xlib.h>
+ #include <X11/Xutil.h>
+ #include <X11/Xatom.h>
+#endif
+
+#ifdef G3D_OSX
+ // Need this for DebugStr()
+ #import <CoreServices/CoreServices.h>
+#endif
+
+
+/**
+ @def debugBreak()
+
+ Break at the current location (i.e. don't push a procedure stack frame
+ before breaking).
+ */
+
+/**
+ @def debugAssert(exp)
+ Breaks if the expression is false. If G3D_DEBUG_NOGUI is defined, prompts at
+ the console, otherwise pops up a dialog. The user may then break (debug),
+ ignore, ignore always, or halt the program.
+
+ The assertion is also posted to the clipboard under Win32.
+ */
+
+/**
+ @def debugAssertM(exp, msg)
+ Breaks if the expression is false and displays a message. If G3D_DEBUG_NOGUI
+ is defined, prompts at the console, otherwise pops up a dialog. The user may
+ then break (debug), ignore, ignore always, or halt the program.
+
+ The assertion is also posted to the clipboard under Win32.
+ */
+
+namespace G3D {
+typedef bool (*AssertionHook)(
+ const char* _expression,
+ const std::string& message,
+ const char* filename,
+ int lineNumber,
+ bool& ignoreAlways,
+ bool useGuiPrompt);
+
+/**
+ Allows customization of the global function invoked when a debugAssert fails.
+ The initial value is G3D::_internal::_handleDebugAssert_. G3D will invoke
+ rawBreak if the hook returns true. If NULL, assertions are not handled.
+*/
+void setAssertionHook(AssertionHook hook);
+
+AssertionHook assertionHook();
+
+/**
+ Called by alwaysAssertM in case of failure in release mode. If returns
+ true then the program exits with -1 (you can replace this with your own
+ version that throws an exception or has other failure modes).
+ */
+void setFailureHook(AssertionHook hook);
+AssertionHook failureHook();
+
+namespace _internal {
+ extern AssertionHook _debugHook;
+ extern AssertionHook _failureHook;
+} // internal
+} // G3D
+
+/**
+ @def __debugPromptShowDialog__
+ @internal
+ */
+
+#ifdef G3D_DEBUG
+
+# if defined(_MSC_VER)
+# define rawBreak() ::DebugBreak();
+# elif defined(__i386__)
+ // gcc on intel
+# define rawBreak() __asm__ __volatile__ ( "int $3" );
+# else
+ // some other gcc
+# define rawBreak() ::abort()
+# endif
+// old mac code:
+//# define rawBreak() DebugStr((const unsigned char*)("\nG3D: Invoking breakpoint in debugger.")); /* XCode must be set to break on Debugger()/DebugStr() */
+
+
+# define debugBreak() G3D::_internal::_releaseInputGrab_(); rawBreak(); G3D::_internal::_restoreInputGrab_();
+# define debugAssert(exp) debugAssertM(exp, "Debug assertion failure")
+
+ #ifdef G3D_DEBUG_NOGUI
+ #define __debugPromptShowDialog__ false
+ #else
+ #define __debugPromptShowDialog__ true
+ #endif
+
+ #define debugAssertM(exp, message) do { \
+ static bool __debugAssertIgnoreAlways__ = false; \
+ if (!__debugAssertIgnoreAlways__ && !(exp)) { \
+ G3D::_internal::_releaseInputGrab_(); \
+ if ((G3D::_internal::_debugHook != NULL) && \
+ G3D::_internal::_debugHook((const char*)(#exp), message, __FILE__, __LINE__, __debugAssertIgnoreAlways__, __debugPromptShowDialog__)) { \
+ rawBreak(); \
+ } \
+ G3D::_internal::_restoreInputGrab_(); \
+ } \
+ } while (0)
+
+ #define alwaysAssertM debugAssertM
+
+#else // Release
+ #ifdef G3D_DEBUG_NOGUI
+ #define __debugPromptShowDialog__ false
+ #else
+ #define __debugPromptShowDialog__ true
+ #endif
+
+ // In the release build, just define away assertions.
+ #define rawBreak() do {} while (0)
+ #define debugAssert(exp) do {} while (0)
+ #define debugAssertM(exp, message) do {} while (0)
+ #define debugBreak() do {} while (0)
+
+ // But keep the 'always' assertions
+ #define alwaysAssertM(exp, message) { \
+ static bool __alwaysAssertIgnoreAlways__ = false; \
+ if (!__alwaysAssertIgnoreAlways__ && !(exp)) { \
+ G3D::_internal::_releaseInputGrab_(); \
+ if ((G3D::_internal::_failureHook != NULL) && \
+ G3D::_internal::_failureHook(#exp, message, __FILE__, __LINE__, __alwaysAssertIgnoreAlways__, __debugPromptShowDialog__)) { \
+ ::exit(-1); \
+ } \
+ G3D::_internal::_restoreInputGrab_(); \
+ } \
+ }
+
+#endif // if debug
+
+
+
+namespace G3D { namespace _internal {
+
+#ifdef G3D_LINUX
+ /**
+ A pointer to the X11 display. Initially NULL. If set to a
+ non-null value (e.g. by SDLWindow), debugAssert attempts to use
+ this display to release the mouse/input grab when an assertion
+ fails.
+ */
+ extern Display* x11Display;
+
+ /**
+ A pointer to the X11 window. Initially NULL. If set to a
+ non-null value (e.g. by SDLWindow), debugAssert attempts to use
+ this window to release the mouse/input grab when an assertion
+ fails.
+ */
+ extern Window x11Window;
+#endif
+
+/**
+ Pops up an assertion dialog or prints an assertion
+
+ ignoreAlways - return result of pressing the ignore button.
+ useGuiPrompt - if true, shows a dialog
+ */
+bool _handleDebugAssert_(
+ const char* expression,
+ const std::string& message,
+ const char* filename,
+ int lineNumber,
+ bool& ignoreAlways,
+ bool useGuiPrompt);
+
+bool _handleErrorCheck_(
+ const char* expression,
+ const std::string& message,
+ const char* filename,
+ int lineNumber,
+ bool& ignoreAlways,
+ bool useGuiPrompt);
+
+/** Attempts to give the user back their mouse and keyboard if they
+ were locked to the current window.
+ @internal*/
+void _releaseInputGrab_();
+
+/** Attempts to restore the state before _releaseInputGrab_.
+ @internal*/
+void _restoreInputGrab_();
+
+}; }; // namespace
+
+#endif
diff --git a/externals/g3dlite/G3D.lib/include/G3D/debugPrintf.h b/externals/g3dlite/G3D.lib/include/G3D/debugPrintf.h
new file mode 100644
index 00000000000..bc3315c8a6d
--- /dev/null
+++ b/externals/g3dlite/G3D.lib/include/G3D/debugPrintf.h
@@ -0,0 +1,62 @@
+/**
+ @file debugPrintf.h
+
+ @maintainer Morgan McGuire, matrix@graphics3d.com
+
+ @created 2001-08-26
+ @edited 2007-07-20
+
+ Copyright 2000-2007, Morgan McGuire.
+ All rights reserved.
+ */
+
+#ifndef G3D_DEBUGPRINTF_H
+#define G3D_DEBUGPRINTF_H
+
+#include "G3D/platform.h"
+#include <stdio.h>
+#include <cstdarg>
+#include "G3D/format.h"
+#include <string>
+
+namespace G3D {
+
+typedef void (*ConsolePrintHook)(const std::string&);
+
+namespace _internal {
+ extern ConsolePrintHook _consolePrintHook;
+}
+
+/** Called by consolePrintf after the log and terminal have been written to.
+ Used by GConsole to intercept printing routines.*/
+void setConsolePrintHook(ConsolePrintHook h);
+
+ConsolePrintHook consolePrintHook();
+
+/**
+ Sends output to the log and to the last GConsole instantiated.
+
+ Guarantees that the output has been flushed by the time the routine
+ returns.
+ @sa G3D::logPrintf, G3D::screenPrintf
+ @return The string that was printed
+ */
+std::string __cdecl consolePrintf(const char* fmt ...) G3D_CHECK_PRINTF_ARGS;
+std::string consolePrint(const std::string&);
+
+/**
+ Under visual studio, appears in the VS debug pane.
+ On unix-based operating systems the output is sent to stderr.
+
+ Also sends output to the console (G3D::consolePrintf) if there is a consolePrintHook,
+ and log (G3D::logPrintf), and flushes before returning.
+
+ @return The string that was printed
+*/
+std::string __cdecl debugPrintf(const char* fmt ...) G3D_CHECK_PRINTF_ARGS;
+std::string debugPrint(const std::string&);
+
+} // namespace G3D
+
+#endif
+
diff --git a/externals/g3dlite/G3D.lib/include/G3D/enumclass.h b/externals/g3dlite/G3D.lib/include/G3D/enumclass.h
new file mode 100644
index 00000000000..7e7bc3f6ac3
--- /dev/null
+++ b/externals/g3dlite/G3D.lib/include/G3D/enumclass.h
@@ -0,0 +1,141 @@
+/**
+ @file G3D/enumclass.h
+
+ @maintainer Morgan McGuire, morgan@graphics3d.com
+ @created 2007-01-27
+ @edited 2007-07-20
+*/
+#ifndef G3D_ENUMCLASS_H
+#define G3D_ENUMCLASS_H
+
+#include "G3D/Table.h"
+#include "G3D/BinaryInput.h"
+#include "G3D/BinaryOutput.h"
+
+/**
+ Creates a series of methods that turn a class into a scoped enumeration.
+ Uses the "Intelligent Enum" design pattern
+ http://www.codeguru.com/cpp/cpp/cpp_mfc/article.php/c4001/
+
+ Enum classes are initialized to their zero value by default.
+
+ See GLG3D/GKey.h for an example.
+ */
+#define G3D_DECLARE_ENUM_CLASS_METHODS(Classname)\
+ inline Classname(char v) : value((Value)v) {}\
+\
+ inline Classname() : value((Value)0) {}\
+\
+ inline Classname(const Value v) : value(v) {}\
+\
+ explicit inline Classname(int v) : value((Value)v) {}\
+\
+ /** Support cast back to the Value type, which is needed to allow implicit assignment inside unions. */\
+ /*inline operator Value() const {
+ return value;
+ }*/\
+\
+ inline operator int() const {\
+ return (int)value;\
+ }\
+\
+ inline bool operator== (const Classname other) const {\
+ return value == other.value;\
+ }\
+\
+ inline bool operator== (const Classname::Value other) const {\
+ return value == other;\
+ }\
+\
+ inline bool operator!= (const Classname other) const {\
+ return value != other.value;\
+ }\
+\
+ inline bool operator!= (const Classname::Value other) const {\
+ return value != other;\
+ }\
+\
+ inline bool operator< (const Classname other) const {\
+ return value < other.value;\
+ }\
+\
+ inline bool operator> (const Classname other) const {\
+ return value > other.value;\
+ }\
+\
+ inline bool operator>= (const Classname other) const {\
+ return value >= other.value;\
+ }\
+\
+ inline bool operator<= (const Classname other) const {\
+ return value <= other.value;\
+ }\
+\
+ inline bool operator< (const Value other) const {\
+ return value < other;\
+ }\
+\
+ inline bool operator> (const Value other) const {\
+ return value > other;\
+ }\
+\
+ inline bool operator<= (const Value other) const {\
+ return value <= other;\
+ }\
+\
+ inline bool operator>= (const Value other) const {\
+ return value >= other;\
+ }\
+\
+ inline Classname& operator-- () {\
+ value = (Value)((int)value - 1);\
+ return *this;\
+ }\
+\
+ inline Classname& operator++ () {\
+ value = (Value)((int)value + 1);\
+ return *this;\
+ }\
+\
+ inline Classname& operator+= (const int x) {\
+ value = (Value)((int)value + x);\
+ return *this;\
+ }\
+\
+ inline Classname& operator-= (const int x) {\
+ value = (Value)((int)value - x);\
+ return *this;\
+ }\
+\
+ inline Classname operator+ (const int x) const {\
+ return Classname((int)value + x);\
+ }\
+\
+ inline Classname operator- (const int x) const {\
+ return Classname((int)value - x);\
+ }\
+\
+ inline unsigned int hashCode() const {\
+ return (unsigned int)value;\
+ }\
+\
+ void serialize(BinaryOutput& b) const {\
+ b.writeInt32(value);\
+ }\
+\
+ void deserialize(BinaryInput& b) {\
+ value = (Value)b.readInt32();\
+ }
+
+#define G3D_DECLARE_ENUM_CLASS_HASHCODE(Classname)\
+template <> struct HashTrait<Classname::Value> \
+{ \
+ static size_t hashCode(Classname::Value key) { return static_cast<size_t>(key); } \
+}; \
+ \
+template <> struct HashTrait<Classname> \
+{ \
+ static size_t hashCode(Classname key) { return static_cast<size_t>(key.hashCode()); } \
+};
+
+#endif
diff --git a/externals/g3dlite/G3D.lib/include/G3D/fileutils.h b/externals/g3dlite/G3D.lib/include/G3D/fileutils.h
new file mode 100644
index 00000000000..ead20720870
--- /dev/null
+++ b/externals/g3dlite/G3D.lib/include/G3D/fileutils.h
@@ -0,0 +1,246 @@
+/**
+ @file fileutils.h
+
+ @maintainer Morgan McGuire, matrix@graphics3d.com
+
+ @author 2002-06-06
+ @edited 2007-01-18
+
+ Copyright 2000-2007, Morgan McGuire.
+ All rights reserved.
+ */
+
+#ifndef G3D_FILEUTILS_H
+#define G3D_FILEUTILS_H
+
+#include "G3D/platform.h"
+#include <string>
+#include <stdio.h>
+#include "G3D/Array.h"
+#include "G3D/Set.h"
+#include "G3D/g3dmath.h"
+
+#ifdef G3D_WIN32
+// For chdir, mkdir, etc.
+# include <direct.h>
+#endif
+
+namespace G3D {
+
+ namespace _internal {
+ extern Set<std::string> currentFilesUsed;
+ }
+
+/** Returns all the files used by G3D and GLG3D during the current execution. */
+Array<std::string> filesUsed();
+
+std::string readWholeFile(
+ const std::string& filename);
+
+
+/** Reads from a zip file and decompresses the desired contents
+ into memory. Does not support recursive zip calls (i.e. a .zip
+ stored within another .zip)
+
+ @param file the path, of the format C:\...\something.zip\...\desiredfile.ext
+ @param data a pointer to the memory where the file will be stored
+ @param length the size of the file decompressed to memory */
+void zipRead(const std::string& file,
+ void*& data,
+ size_t& length);
+
+
+/** Closes the contents of a zip file that had been decompressed to
+ memory. Must be called in tandem with zipRead() to avoid memory
+ leaks.
+
+ @param data the pointer to the decompressed file in memory */
+void zipClose(void* data);
+
+
+/**
+ @param flush If true (default), the file is ready for reading as soon
+ as the function returns. If false, the function returns immediately and
+ writes the file in the background.
+ */
+void writeWholeFile(
+ const std::string& filename,
+ const std::string& str,
+ bool flush = true);
+
+/**
+ Creates the directory (which may optionally end in a /)
+ and any parents needed to reach it.
+ */
+void createDirectory(
+ const std::string& dir);
+
+/**
+ Fully qualifies a filename. The filename may contain wildcards,
+ in which case the wildcards will be preserved in the returned value.
+ */
+std::string resolveFilename(const std::string& filename);
+
+/**
+ Appends all files matching filespec to the files array. The names
+ will not contain paths unless includePath == true. These may be
+ relative to the current directory unless the filespec is fully qualified
+ (can be done with resolveFilename).
+ Wildcards can only appear to the right of the last slash in filespec.
+ Works with .zip files used as paths, if filespec is passed in the form
+ C:\...\something.zip\* Does not work recursively with zipfiles (a
+ .zip within a .zip will not work)
+ */
+void getFiles(
+ const std::string& filespec,
+ Array<std::string>& files,
+ bool includePath = false);
+
+/**
+ Appends all directories matching filespec to the files array. The names
+ will not contain paths unless includePath == true. These may be
+ relative to the current directory unless the filespec is fully qualified
+ (can be done with resolveFilename).
+ Does not append special directories "." or "..".
+ Works with .zip files used as paths, if filespec is passed in the form
+ C:\...\something.zip\* Does not work recursively with zipfiles (a
+ .zip within a .zip will not work)
+ */
+void getDirs(
+ const std::string& filespec,
+ Array<std::string>& files,
+ bool includePath = false);
+
+
+/** Returns true if the specified path exists and is a directory */
+bool isDirectory(const std::string& filespec);
+
+
+/** Returns true if the specified filename exists and is a zipfile */
+bool isZipfile(const std::string& filename);
+
+
+/** Returns the length of the file. If
+ filename specifies a path that contains a zipfile, but the
+ contents within are specified correctly, returns the
+ uncompressed size of the requested file. Returns -1 if
+ the file does not exist.
+
+ @param filename the path to test, may contain .zip
+*/
+int64 fileLength(const std::string& filename);
+
+/**
+ Copies the file
+ */
+void copyFile(
+ const std::string& source,
+ const std::string& dest);
+
+/** Returns a temporary file that is open for read/write access. This
+ tries harder than the ANSI tmpfile, so it may succeed when that fails. */
+FILE* createTempFile();
+
+/**
+ Returns true if the given file (or directory) exists.
+
+ @param filename the path to test. must not end in a trailing slash.
+ @param lookInZipfiles if the path does not exist, calls zipfileExists()
+ */
+bool fileExists(
+ const std::string& filename,
+ const bool lookInZipfiles = true);
+
+/**
+ Returns true if the given file (or directory) exists
+ within a zipfile. Called if fileExists initially
+ returns false and the lookInZipfiles flag has been set.
+ Must not end in a trailing slash. Does not work for recursive
+ zipfiles (.zips within another .zip)
+
+ @param filename the path to test
+ @param outZipfile the path to the .zip file
+ @param outInternalFile the path (within the .zip) where the desired file is located, if valid
+
+ */
+bool zipfileExists(
+ const std::string& filename,
+ std::string& outZipfile,
+ std::string& outInternalFile
+ );
+
+bool zipfileExists(const std::string& filename);
+
+/**
+ Parses a filename into four useful pieces.
+
+ Examples:
+
+ c:\a\b\d.e
+ root = "c:\"
+ path = "a" "b"
+ base = "d"
+ ext = "e"
+
+ /a/b/d.e
+ root = "/"
+ path = "a" "b"
+ base = "d"
+ ext = "e"
+
+ /a/b
+ root = "/"
+ path = "a"
+ base = "b"
+ ext = "e"
+
+ */
+void parseFilename(
+ const std::string& filename,
+ std::string& drive,
+ Array<std::string>& path,
+ std::string& base,
+ std::string& ext);
+
+
+/**
+ Returns the part of the filename that includes the base and ext from
+ parseFilename (i.e. everything to the right of the path).
+ */
+std::string filenameBaseExt(const std::string& filename);
+
+/**
+ Returns the extension on a filename.
+ */
+std::string filenameExt(const std::string& filename);
+
+
+/** Returns the portion of a filename to the left of the last period
+ and to the right of the last slash or colon.
+ */
+std::string filenameBase(const std::string& filename);
+
+/** Creates a unique filename base in the current directory using the
+ specified prefix.*/
+std::string generateFilenameBase(const std::string& prefix = "");
+
+/**
+ Returns the drive (if Win32) and path from a filename, including
+ a slash if there was one.
+ <CODE>filenamePath(f) + filenameBaseExt(f) == f</CODE>
+ */
+std::string filenamePath(const std::string& filename);
+
+/** Returns true if '*' or '?' appears in the string */
+bool filenameContainsWildcards(const std::string& filename);
+
+/** Returns true if dst does not exist or src is newer than dst. Works on both files and directories. */
+bool fileIsNewer(const std::string& src, const std::string& dst);
+
+/** Appends file onto dirname, ensuring a / if needed. */
+std::string pathConcat(const std::string& dirname, const std::string& file);
+
+} // namespace
+
+#endif
+
diff --git a/externals/g3dlite/G3D.lib/include/G3D/filter.h b/externals/g3dlite/G3D.lib/include/G3D/filter.h
new file mode 100644
index 00000000000..74a32ad01ea
--- /dev/null
+++ b/externals/g3dlite/G3D.lib/include/G3D/filter.h
@@ -0,0 +1,29 @@
+/**
+ @file G3D/filter.h
+
+ @author Morgan McGuire, matrix@graphics3d.com
+ @created 2007-03-01
+ @edited 2007-03-01
+
+ Copyright 2000-2007, Morgan McGuire.
+ All rights reserved.
+ */
+#ifndef G3D_FILTER_H
+#define G3D_FILTER_H
+
+#include "G3D/platform.h"
+#include "G3D/Array.h"
+#include "G3D/g3dmath.h"
+
+namespace G3D {
+/**
+ Generates a set of 1D gaussian filter coefficients of size N. The coefficients
+ are centered on element (N-1)/2 and have standard deviation given by std. The coefficients
+ are normalized such that the sum across coeff is 1.0.
+
+ Matches the results returned by Matlab <code>fspecial('gaussian', [1, N], std)</code>
+ */
+void gaussian1D(Array<float>& coeff, int N = 5, float std = 0.5f);
+}
+
+#endif
diff --git a/externals/g3dlite/G3D.lib/include/G3D/format.h b/externals/g3dlite/G3D.lib/include/G3D/format.h
new file mode 100644
index 00000000000..f993a74038f
--- /dev/null
+++ b/externals/g3dlite/G3D.lib/include/G3D/format.h
@@ -0,0 +1,44 @@
+/**
+ @file format.h
+
+ @maintainer Morgan McGuire, matrix@graphics3d.com
+
+ @author 2000-09-09
+ @edited 2005-11-03
+
+ Copyright 2000-2005, Morgan McGuire.
+ All rights reserved.
+ */
+
+#ifndef G3D_FORMAT_H
+#define G3D_FORMAT_H
+
+#include "G3D/platform.h"
+#include <string>
+#include <stdio.h>
+#include <cstdarg>
+
+namespace G3D {
+
+/**
+ Produces a string from arguments of the style of printf. This avoids
+ problems with buffer overflows when using sprintf and makes it easy
+ to use the result functionally. This function is fast when the resulting
+ string is under 160 characters (not including terminator) and slower
+ when the string is longer.
+ */
+std::string __cdecl format(
+ const char* fmt
+ ...) G3D_CHECK_PRINTF_ARGS;
+
+/**
+ Like format, but can be called with the argument list from a ... function.
+ */
+std::string vformat(
+ const char* fmt,
+ va_list argPtr) G3D_CHECK_VPRINTF_ARGS;
+
+
+} // namespace
+
+#endif
diff --git a/externals/g3dlite/G3D.lib/include/G3D/g3dmath.h b/externals/g3dlite/G3D.lib/include/G3D/g3dmath.h
new file mode 100644
index 00000000000..0a4f01f11c6
--- /dev/null
+++ b/externals/g3dlite/G3D.lib/include/G3D/g3dmath.h
@@ -0,0 +1,810 @@
+/**
+ @file g3dmath.h
+
+ Math util class.
+
+ @maintainer Morgan McGuire, matrix@graphics3d.com
+ @cite highestBit by Jukka Liimatta
+
+ @created 2001-06-02
+ @edited 2006-01-16
+
+ Copyright 2000-2006, Morgan McGuire.
+ All rights reserved.
+ */
+
+#ifndef G3DMATH_H
+#define G3DMATH_H
+
+#ifdef _MSC_VER
+// Disable conditional expression is constant, which occurs incorrectly on inlined functions
+# pragma warning (push)
+# pragma warning (disable : 4127)
+// disable: "C++ exception handler used"
+# pragma warning (disable : 4530)
+#endif
+
+#include "G3D/platform.h"
+#include <ctype.h>
+#include <float.h>
+#include <limits>
+#include <stdlib.h>
+
+#ifdef _MSC_VER
+ // Visual Studio is missing inttypes.h
+# ifndef PRId64
+# define PRId64 "I64d"
+# endif
+#else
+#include <inttypes.h>
+#endif
+
+/*These defines enable functionality introduced with the 1999 ISO C
+**standard. They must be defined before the inclusion of math.h to
+**engage them. If optimisation is enabled, these functions will be
+**inlined. With optimisation switched off, you have to link in the
+**maths library using -lm.
+*/
+
+#define _ISOC9X_SOURCE1
+#define _ISOC99_SOURCE1
+#define __USE_ISOC9X1
+#define __USE_ISOC991
+
+#include <math.h>
+
+#include "G3D/debug.h"
+
+#undef min
+#undef max
+
+namespace G3D {
+
+#ifdef _MSC_VER
+
+/**
+ Win32 implementation of the C99 fast rounding routines.
+
+ @cite routines are
+ Copyright (C) 2001 Erik de Castro Lopo <erikd AT mega-nerd DOT com>
+
+ Permission to use, copy, modify, distribute, and sell this file for any
+ purpose is hereby granted without fee, provided that the above copyright
+ and this permission notice appear in all copies. No representations are
+ made about the suitability of this software for any purpose. It is
+ provided "as is" without express or implied warranty.
+*/
+
+__inline long int lrint (double flt) {
+ int intgr;
+
+ _asm {
+ fld flt
+ fistp intgr
+ };
+
+ return intgr;
+}
+
+__inline long int lrintf(float flt) {
+ int intgr;
+
+ _asm {
+ fld flt
+ fistp intgr
+ };
+
+ return intgr;
+}
+#endif
+
+
+
+const double fuzzyEpsilon = 0.00001;
+
+/** Returns a reference to a static double.
+ This value should not be tested against directly, instead
+ G3D::isNan() and G3D::isFinite() will return reliable results. */
+inline const double& inf() {
+
+ // double is a standard type and should have infinity
+ static const double i = std::numeric_limits<double>::infinity();
+ return i;
+}
+
+/** Returns a reference to a static double.
+ This value should not be tested against directly, instead
+ G3D::isNan() and G3D::isFinite() will return reliable results. */
+inline const double& nan() {
+
+ // double is a standard type and should have quiet NaN
+ static const double n = std::numeric_limits<double>::quiet_NaN();
+ return n;
+}
+
+/** Returns a reference to a static double. Use instead of G3D_PI. */
+inline const double& pi() {
+ static const double p = 3.1415926535898;
+ return p;
+}
+
+/** Returns a reference to a static double. */
+inline const double& halfPi() {
+ static const double p = pi() / 2.0;
+ return p;
+}
+
+/** Returns a reference to a static double. */
+inline const double& twoPi() {
+ static const double p = pi() * 2.0;;
+ return p;
+}
+
+typedef signed char int8;
+typedef unsigned char uint8;
+typedef short int16;
+typedef unsigned short uint16;
+typedef int int32;
+typedef unsigned int uint32;
+
+#ifdef _MSC_EXTENSIONS
+ typedef __int64 int64;
+ typedef unsigned __int64 uint64;
+#elif ! defined(_MSC_VER)
+ typedef int64_t int64;
+ typedef uint64_t uint64;
+#else
+ typedef long long int64;
+ typedef unsigned long long uint64;
+#endif
+
+typedef float float32;
+typedef double float64;
+
+int iAbs(int iValue);
+int iCeil(double fValue);
+
+/**
+ Clamps the value to the range [low, hi] (inclusive)
+ */
+int iClamp(int val, int low, int hi);
+int16 iClamp(int16 val, int16 low, int16 hi);
+double clamp(double val, double low, double hi);
+float clamp(float val, float low, float hi);
+
+/**
+ Returns a + (b - a) * f;
+ */
+inline double lerp(double a, double b, double f) {
+ return a + (b - a) * f;
+}
+
+inline float lerp(float a, float b, float f) {
+ return a + (b - a) * f;
+}
+
+/**
+ Wraps the value to the range [0, hi) (exclusive
+ on the high end). This is like the clock arithmetic
+ produced by % (modulo) except the result is guaranteed
+ to be positive.
+ */
+int iWrap(int val, int hi);
+
+int iFloor(double fValue);
+
+int iSign(int iValue);
+int iSign(double fValue);
+
+inline int iSign(float f) {
+ return iSign((double)f);
+}
+
+
+/**
+ Fast round to integer using the lrint routine.
+ Typically 6x faster than casting to integer.
+ */
+inline int iRound(double fValue) {
+ return lrint(fValue);
+}
+
+/**
+ Fast round to integer using the lrint routine.
+ Typically 6x faster than casting to integer.
+ */
+inline int iRound(float f) {
+ return lrintf(f);
+}
+
+/**
+ Returns a random number uniformly at random between low and hi
+ (inclusive).
+ */
+int iRandom(int low, int hi);
+
+double abs (double fValue);
+double aCos (double fValue);
+double aSin (double fValue);
+double aTan (double fValue);
+double aTan2 (double fY, double fX);
+double sign (double fValue);
+double square (double fValue);
+
+/**
+ Returns true if the argument is a finite real number.
+ */
+bool isFinite(double x);
+
+/**
+ Returns true if the argument is NaN (not a number).
+ You can't use x == nan to test this because all
+ comparisons against nan return false.
+ */
+bool isNaN(double x);
+
+/**
+ Computes x % 3.
+ */
+int iMod3(int x);
+
+/**
+ Uniform random number between low and hi, inclusive. [low, hi]
+ */
+float uniformRandom(float low = 0.0f, float hi = 1.0f);
+
+/**
+ Normally distributed random number.
+ */
+float gaussRandom(float mean = 0.0f, float stdev = 1.0f);
+
+template <class T>
+inline T min(const T& x, const T& y) {
+ return std::min<T>(x, y);
+}
+
+template <class T>
+inline T min(const T& x, const T& y, const T& z) {
+ return std::min<T>(std::min<T>(x, y), z);
+}
+
+template <class T>
+inline T min(const T& x, const T& y, const T& z, const T& w) {
+ return std::min<T>(std::min<T>(x, y), std::min<T>(z, w));
+}
+
+template <class T>
+inline T max(const T& x, const T& y) {
+ return std::max<T>(x, y);
+}
+
+template <class T>
+inline T max(const T& x, const T& y, const T& z) {
+ return std::max<T>(std::max<T>(x, y), z);
+}
+
+template <class T>
+inline T max(const T& x, const T& y, const T& z, const T& w) {
+ return std::max<T>(std::max<T>(x, y), std::max<T>(z, w));
+}
+
+int iMin(int x, int y);
+int iMax(int x, int y);
+
+double square(double x);
+double sumSquares(double x, double y);
+double sumSquares(double x, double y, double z);
+double distance(double x, double y);
+double distance(double x, double y, double z);
+
+/**
+ Returnes the 0-based index of the highest 1 bit from
+ the left. -1 means the number was 0.
+
+ @cite Based on code by jukka@liimatta.org
+ */
+int highestBit(uint32 x);
+
+/**
+ Note that fuzzyEq(a, b) && fuzzyEq(b, c) does not imply
+ fuzzyEq(a, c), although that will be the case on some
+ occasions.
+ */
+bool fuzzyEq(double a, double b);
+
+/** True if a is definitely not equal to b.
+ Guaranteed false if a == b.
+ Possibly false when a != b.*/
+bool fuzzyNe(double a, double b);
+
+/** Is a strictly greater than b? (Guaranteed false if a <= b).
+ (Possibly false if a > b) */
+bool fuzzyGt(double a, double b);
+
+/** Is a near or greater than b? */
+bool fuzzyGe(double a, double b);
+
+/** Is a strictly less than b? (Guaranteed false if a >= b)*/
+bool fuzzyLt(double a, double b);
+
+/** Is a near or less than b? */
+bool fuzzyLe(double a, double b);
+
+/**
+ Computes 1 / sqrt(x).
+ */
+inline float rsq(float x) {
+ return 1.0f / sqrtf(x);
+}
+
+/**
+ Uses SSE to implement rsq.
+ @cite Nick nicolas@capens.net
+ */
+inline float SSErsq(float x) {
+
+ #if defined(SSE) && defined(G3D_WIN32)
+ __asm {
+ movss xmm0, x
+ rsqrtss xmm0, xmm0
+ movss x, xmm0
+ }
+ return x;
+ #else
+ return 1.0f / sqrt(x);
+ #endif
+}
+
+/**
+ Return the next power of 2 higher than the input
+ If the input is already a power of 2, the output will be the same
+ as the input.
+ */
+int ceilPow2(unsigned int in);
+
+/** Returns 2^x */
+inline int pow2(unsigned int x) {
+ return 1 << x;
+}
+
+
+/** Log base 2 */
+inline float log2(float x) {
+ return ::logf(x) / ::logf(2.0f);
+}
+
+/** Log base 2 */
+inline double log2(double x) {
+ return ::log(x) / ::log(2.0);
+}
+
+/** Log base 2 */
+inline double log2(int x) {
+ return log2((double)x);
+}
+
+
+/**
+ * True if num is a power of two.
+ */
+bool isPow2(int num);
+
+bool isOdd(int num);
+bool isEven(int num);
+
+double toRadians(double deg);
+double toDegrees(double rad);
+
+/**
+ Returns true if x is not exactly equal to 0.0f.
+ */
+inline bool any(float x) {
+ return x != 0;
+}
+
+/**
+ Returns true if x is not exactly equal to 0.0f.
+ */
+inline bool all(float x) {
+ return x != 0;
+}
+
+/**
+ v / v (for DirectX/Cg support)
+ */
+inline float normalize(float v) {
+ return v / v;
+}
+
+/**
+ a * b (for DirectX/Cg support)
+ */
+inline float dot(float a, float b) {
+ return a * b;
+}
+
+
+/**
+ a * b (for DirectX/Cg support)
+ */
+inline float mul(float a, float b) {
+ return a * b;
+}
+
+/**
+ 2^x
+ */
+inline double exp2(double x) {
+ return pow(2.0, x);
+}
+
+inline double rsqrt(double x) {
+ return 1.0 / sqrt(x);
+}
+
+
+/**
+ sin(x)/x
+ */
+inline double sinc(double x) {
+ double r = sin(x) / x;
+
+ if (isNaN(r)) {
+ return 1.0;
+ } else {
+ return r;
+ }
+}
+
+/**
+ Computes a floating point modulo; the result is t wrapped to the range [lo, hi).
+ */
+inline float wrap(float t, float lo, float hi) {
+ if ((t >= lo) && (t < hi)) {
+ return t;
+ }
+
+ debugAssert(hi > lo);
+
+ float interval = hi - lo;
+
+ return t - interval * iFloor((t - lo) / interval);
+}
+
+
+inline double wrap(double t, double lo, double hi) {
+ if ((t >= lo) && (t < hi)) {
+ return t;
+ }
+
+ debugAssert(hi > lo);
+
+ double interval = hi - lo;
+
+ return t - interval * iFloor((t - lo) / interval);
+}
+
+inline double wrap(double t, double hi) {
+ return wrap(t, 0.0, hi);
+}
+
+
+inline bool isNaN(double x) {
+ bool b1 = (x < 0.0);
+ bool b2 = (x >= 0.0);
+ bool b3 = !(b1 || b2);
+ return b3;
+}
+
+inline bool isFinite(double x) {
+ return ! isNaN(x) && (x < G3D::inf()) && (x > -G3D::inf());
+}
+
+//----------------------------------------------------------------------------
+inline int iAbs (int iValue) {
+ return ( iValue >= 0 ? iValue : -iValue );
+}
+
+//----------------------------------------------------------------------------
+inline int iCeil (double fValue) {
+ return int(::ceil(fValue));
+}
+
+//----------------------------------------------------------------------------
+
+inline int iClamp(int val, int low, int hi) {
+ debugAssert(low <= hi);
+ if (val <= low) {
+ return low;
+ } else if (val >= hi) {
+ return hi;
+ } else {
+ return val;
+ }
+}
+
+//----------------------------------------------------------------------------
+
+inline int16 iClamp(int16 val, int16 low, int16 hi) {
+ debugAssert(low <= hi);
+ if (val <= low) {
+ return low;
+ } else if (val >= hi) {
+ return hi;
+ } else {
+ return val;
+ }
+}
+
+//----------------------------------------------------------------------------
+
+inline double clamp(double val, double low, double hi) {
+ debugAssert(low <= hi);
+ if (val <= low) {
+ return low;
+ } else if (val >= hi) {
+ return hi;
+ } else {
+ return val;
+ }
+}
+
+inline float clamp(float val, float low, float hi) {
+ debugAssert(low <= hi);
+ if (val <= low) {
+ return low;
+ } else if (val >= hi) {
+ return hi;
+ } else {
+ return val;
+ }
+}
+//----------------------------------------------------------------------------
+
+inline int iWrap(int val, int hi) {
+ if (val < 0) {
+ return ((val % hi) + hi) % hi;
+ } else {
+ return val % hi;
+ }
+}
+
+//----------------------------------------------------------------------------
+inline int iFloor (double fValue) {
+ return int(::floor(fValue));
+}
+
+//----------------------------------------------------------------------------
+inline int iSign (int iValue) {
+ return ( iValue > 0 ? + 1 : ( iValue < 0 ? -1 : 0 ) );
+}
+
+inline int iSign (double fValue) {
+ return ( fValue > 0.0 ? + 1 : ( fValue < 0.0 ? -1 : 0 ) );
+}
+
+//----------------------------------------------------------------------------
+inline double abs (double fValue) {
+ return double(::fabs(fValue));
+}
+
+//----------------------------------------------------------------------------
+inline double aCos (double fValue) {
+ if ( -1.0 < fValue ) {
+ if ( fValue < 1.0 )
+ return double(::acos(fValue));
+ else
+ return 0.0;
+ } else {
+ return pi();
+ }
+}
+
+//----------------------------------------------------------------------------
+inline double aSin (double fValue) {
+ if ( -1.0 < fValue ) {
+ if ( fValue < 1.0 ) {
+ return double(::asin(fValue));
+ } else {
+ return -halfPi();
+ }
+ } else {
+ return halfPi();
+ }
+}
+
+//----------------------------------------------------------------------------
+inline double aTan (double fValue) {
+ return double(::atan(fValue));
+}
+
+//----------------------------------------------------------------------------
+inline double aTan2 (double fY, double fX) {
+ return double(::atan2(fY, fX));
+}
+
+//----------------------------------------------------------------------------
+inline double sign (double fValue) {
+ if (fValue > 0.0) {
+ return 1.0;
+ }
+
+ if (fValue < 0.0) {
+ return -1.0;
+ }
+
+ return 0.0;
+}
+
+inline float sign (float fValue) {
+ if (fValue > 0.0f) {
+ return 1.0f;
+ }
+
+ if (fValue < 0.0f) {
+ return -1.0f;
+ }
+
+ return 0.0f;
+}
+
+
+inline float uniformRandom(float low, float hi) {
+ return (hi - low) * float(::rand()) / float(RAND_MAX) + low;
+}
+
+//----------------------------------------------------------------------------
+inline double square(double x) {
+ return x * x;
+}
+
+//----------------------------------------------------------------------------
+inline double sumSquares(double x, double y) {
+ return x*x + y*y;
+}
+
+//----------------------------------------------------------------------------
+inline double sumSquares(double x, double y, double z) {
+ return x*x + y*y + z*z;
+}
+
+//----------------------------------------------------------------------------
+inline double distance(double x, double y) {
+ return sqrt(sumSquares(x, y));
+}
+
+//----------------------------------------------------------------------------
+inline double distance(double x, double y, double z) {
+ return sqrt(sumSquares(x, y, z));
+}
+
+//----------------------------------------------------------------------------
+
+/** @deprecated use G3D::min */
+inline int iMin(int x, int y) {
+ return (x >= y) ? y : x;
+}
+
+//----------------------------------------------------------------------------
+/** @deprecated use G3D::min */
+inline int iMax(int x, int y) {
+ return (x >= y) ? x : y;
+}
+
+//----------------------------------------------------------------------------
+inline int ceilPow2(unsigned int in) {
+ in -= 1;
+
+ in |= in >> 16;
+ in |= in >> 8;
+ in |= in >> 4;
+ in |= in >> 2;
+ in |= in >> 1;
+
+ return in + 1;
+}
+
+inline bool isPow2(int num) {
+ return ((num & -num) == num);
+}
+
+inline bool isOdd(int num) {
+ return (num & 1) == 1;
+}
+
+inline bool isEven(int num) {
+ return (num & 1) == 0;
+}
+
+inline double toRadians(double deg) {
+ return deg * pi() / 180.0;
+}
+
+inline double toDegrees(double rad) {
+ return rad * 180.0 / pi();
+}
+
+inline float toRadians(float deg) {
+ return deg * (float)pi() / 180.0f;
+}
+
+inline float toDegrees(float rad) {
+ return rad * 180.0f / (float)pi();
+}
+
+inline float toRadians(int deg) {
+ return deg * (float)pi() / 180.0f;
+}
+
+inline float toDegrees(int rad) {
+ return rad * 180.0f / (float)pi();
+}
+/**
+ Computes an appropriate epsilon for comparing a and b.
+ */
+inline double eps(double a, double b) {
+ // For a and b to be nearly equal, they must have nearly
+ // the same magnitude. This means that we can ignore b
+ // since it either has the same magnitude or the comparison
+ // will fail anyway.
+ (void)b;
+ const double aa = abs(a) + 1;
+ if (aa == inf()) {
+ return fuzzyEpsilon;
+ } else {
+ return fuzzyEpsilon * aa;
+ }
+}
+
+inline bool fuzzyEq(double a, double b) {
+ return (a == b) || (abs(a - b) <= eps(a, b));
+}
+
+inline bool fuzzyNe(double a, double b) {
+ return ! fuzzyEq(a, b);
+}
+
+inline bool fuzzyGt(double a, double b) {
+ return a > b + eps(a, b);
+}
+
+inline bool fuzzyGe(double a, double b) {
+ return a > b - eps(a, b);
+}
+
+inline bool fuzzyLt(double a, double b) {
+ return a < b - eps(a, b);
+}
+
+inline bool fuzzyLe(double a, double b) {
+ return a < b + eps(a, b);
+}
+
+inline int iMod3(int x) {
+ return x % 3;
+}
+
+/**
+ Given a 32-bit integer, returns the integer with the bytes in the opposite order.
+ */
+inline uint32 flipEndian32(const uint32 x) {
+ return (x << 24) | ((x & 0xFF00) << 8) |
+ ((x & 0xFF0000) >> 8) | ((x & 0xFF000000) >> 24);
+}
+
+/**
+ Given a 16-bit integer, returns the integer with the bytes in the opposite order.
+ */
+inline uint16 flipEndian16(const uint16 x) {
+ return (x << 8) | ((x & 0xFF00) >> 8);
+}
+
+
+} // namespace
+
+#ifdef _MSC_VER
+# pragma warning (pop)
+#endif
+
+#endif
+
diff --git a/externals/g3dlite/G3D.lib/include/G3D/platform.h b/externals/g3dlite/G3D.lib/include/G3D/platform.h
new file mode 100644
index 00000000000..be9ec4d5123
--- /dev/null
+++ b/externals/g3dlite/G3D.lib/include/G3D/platform.h
@@ -0,0 +1,256 @@
+/**
+ @file platform.h
+
+ #defines for platform specific issues.
+
+ @maintainer Morgan McGuire, matrix@graphics3d.com
+
+ @created 2003-06-09
+ @edited 2007-07-30
+ */
+
+#ifndef G3D_PLATFORM_H
+#define G3D_PLATFORM_H
+
+/**
+ The version number of G3D in the form: MmmBB ->
+ version M.mm [beta BB]
+ */
+#define G3D_VER 70100
+
+#if defined(G3D_RELEASEDEBUG)
+# define G3D_DEBUGRELEASE
+#endif
+
+#if defined(G3D_DEBUGRELEASE) && defined(_DEBUG)
+# undef _DEBUG
+#endif
+
+#if !defined(G3D_DEBUG) && (defined(_DEBUG) || defined(G3D_DEBUGRELEASE))
+# define G3D_DEBUG
+#endif
+
+#ifdef _MSC_VER
+ #define G3D_WIN32
+#elif defined(__FreeBSD__) || defined(__OpenBSD__)
+ #define G3D_FREEBSD
+ #define G3D_LINUX
+#elif defined(__linux__)
+ #define G3D_LINUX
+#elif defined(__APPLE__)
+ #define G3D_OSX
+
+ // Prevent OS X fp.h header from being included; it defines
+ // pi as a constant, which creates a conflict with G3D
+#define __FP__
+#else
+ #error Unknown platform
+#endif
+
+
+// Default to compiling with SSE, but if you want to compile
+// without installing SP5.0 and the Processor Pack on Windows, compile with NO_SSE
+// defined (can be passed to the compiler command line with /D "NO_SSE")
+#if !defined(NO_SSE)
+ #define SSE
+#endif
+
+// On g++, recognize cases where the -msse2 flag was not specified
+#if defined(SSE) && defined(__GNUC__) && ! defined (__SSE__)
+# undef SSE
+#endif
+
+
+// Verify that the supported compilers are being used and that this is a known
+// processor.
+
+#ifdef G3D_LINUX
+# ifndef __GNUC__
+# error G3D only supports the gcc compiler on Linux.
+# endif
+#endif
+
+#ifdef G3D_OSX
+# ifndef __GNUC__
+# error G3D only supports the gcc compiler on OS X.
+# endif
+
+# if defined(__i386__)
+# define G3D_OSX_INTEL
+# elif defined(__PPC__)
+# define G3D_OSX_PPC
+# else
+# define G3D_OSX_UNKNOWN
+# endif
+
+#endif
+
+
+#ifdef _MSC_VER
+// Microsoft Visual C++ 8.0 ("Express") = 1400
+// Microsoft Visual C++ 7.1 ("2003") _MSC_VER = 1310
+// Microsoft Visual C++ 7.0 ("2002") _MSC_VER = 1300
+// Microsoft Visual C++ 6.0 _MSC_VER = 1200
+// Microsoft Visual C++ 5.0 _MSC_VER = 1100
+
+// Turn off warnings about deprecated C routines (TODO: revisit)
+# pragma warning (disable : 4996)
+
+// Turn off "conditional expression is constant" warning; MSVC generates this
+// for debug assertions in inlined methods.
+# pragma warning (disable : 4127)
+
+# define G3D_DEPRECATED __declspec(deprecated)
+
+// Prevent Winsock conflicts by hiding the winsock API
+# ifndef _WINSOCKAPI_
+# define _G3D_INTERNAL_HIDE_WINSOCK_
+# define _WINSOCKAPI_
+# endif
+
+// Disable 'name too long for browse information' warning
+# pragma warning (disable : 4786)
+// TODO: remove
+# pragma warning (disable : 4244)
+
+# define ZLIB_WINAPI
+
+# define restrict
+
+# define G3D_CHECK_PRINTF_ARGS
+# define G3D_CHECK_VPRINTF_ARGS
+# define G3D_CHECK_PRINTF_METHOD_ARGS
+# define G3D_CHECK_VPRINTF_METHOD_ARGS
+
+ // On MSVC, we need to link against the multithreaded DLL version of
+ // the C++ runtime because that is what SDL and ZLIB are compiled
+ // against. This is not the default for MSVC, so we set the following
+ // defines to force correct linking.
+ //
+ // For documentation on compiler options, see:
+ // http://msdn.microsoft.com/library/default.asp?url=/library/en-us/vccore/html/_core_.2f.md.2c_2f.ml.2c_2f.mt.2c_2f.ld.asp
+ // http://msdn.microsoft.com/library/default.asp?url=/library/en-us/vccore98/HTML/_core_Compiler_Reference.asp
+ //
+
+ // DLL runtime
+ #ifndef _DLL
+ #define _DLL
+ #endif
+
+ // Multithreaded runtime
+ #ifndef _MT
+ #define _MT 1
+ #endif
+
+ // Ensure that we aren't forced into the static lib
+ #ifdef _STATIC_CPPLIB
+ #undef _STATIC_CPPLIB
+ #endif
+
+ #ifdef _DEBUG
+ #pragma comment (linker, "/NODEFAULTLIB:LIBCMTD.LIB")
+ #pragma comment (linker, "/NODEFAULTLIB:LIBCPMTD.LIB")
+ #pragma comment (linker, "/NODEFAULTLIB:LIBCPD.LIB")
+ #pragma comment (linker, "/DEFAULTLIB:MSVCPRTD.LIB")
+ #pragma comment(linker, "/NODEFAULTLIB:LIBCD.LIB")
+ #pragma comment(linker, "/DEFAULTLIB:MSVCRTD.LIB")
+ #else
+ #pragma comment(linker, "/NODEFAULTLIB:LIBC.LIB")
+ #pragma comment(linker, "/DEFAULTLIB:MSVCRT.LIB")
+ #pragma comment (linker, "/NODEFAULTLIB:LIBCMT.LIB")
+ #pragma comment (linker, "/NODEFAULTLIB:LIBCPMT.LIB")
+ #pragma comment(linker, "/NODEFAULTLIB:LIBCP.LIB")
+ #pragma comment (linker, "/DEFAULTLIB:MSVCPRT.LIB")
+ #endif
+
+ // Now set up external linking
+
+# ifdef _DEBUG
+ // zlib was linked against the release MSVCRT; force
+ // the debug version.
+# pragma comment(linker, "/NODEFAULTLIB:MSVCRT.LIB")
+# endif
+
+
+# ifndef WIN32_LEAN_AND_MEAN
+# define WIN32_LEAN_AND_MEAN 1
+# endif
+
+
+# define NOMINMAX 1
+# ifndef _WIN32_WINNT
+# define _WIN32_WINNT 0x0500
+# endif
+# include <windows.h>
+# undef WIN32_LEAN_AND_MEAN
+# undef NOMINMAX
+
+# ifdef _G3D_INTERNAL_HIDE_WINSOCK_
+# undef _G3D_INTERNAL_HIDE_WINSOCK_
+# undef _WINSOCKAPI_
+# endif
+
+
+# define G3D_START_AT_MAIN()\
+int WINAPI G3D_WinMain(HINSTANCE hInst, HINSTANCE hPrev, LPSTR szCmdLine, int sw);\
+int WINAPI WinMain(HINSTANCE hInst, HINSTANCE hPrev, LPSTR szCmdLine, int sw) {\
+ return G3D_WinMain(hInst, hPrev, szCmdLine, sw);\
+}
+
+#else
+
+# define G3D_START_AT_MAIN()
+
+#endif // win32
+
+#ifdef __GNUC__
+
+# include <stdint.h>
+
+# if __STDC_VERSION__ < 199901
+# define restrict __restrict__
+# endif
+
+# define G3D_DEPRECATED __attribute__((__deprecated__))
+
+// setup function calling conventions
+# if defined(__i386__) && ! defined(__x86_64__)
+
+# ifndef __cdecl
+# define __cdecl __attribute__((cdecl))
+# endif
+
+# ifndef __stdcall
+# define __stdcall __attribute__((stdcall))
+# endif
+
+# elif defined(__x86_64__) || defined(__powerpc__)
+
+# ifndef __cdecl
+# define __cdecl
+# endif
+
+# ifndef __stdcall
+# define __stdcall
+# endif
+# endif // calling conventions
+
+# define G3D_CHECK_PRINTF_METHOD_ARGS __attribute__((__format__(__printf__, 2, 3)))
+# define G3D_CHECK_VPRINTF_METHOD_ARGS __attribute__((__format__(__printf__, 2, 0)))
+# define G3D_CHECK_PRINTF_ARGS __attribute__((__format__(__printf__, 1, 2)))
+# define G3D_CHECK_VPRINTF_ARGS __attribute__((__format__(__printf__, 1, 0)))
+#endif
+
+
+/**
+ @def STR(expression)
+
+ Creates a string from the expression. Frequently used with G3D::Shader
+ to express shading programs inline.
+
+ <CODE>STR(this becomes a string)<PRE> evaluates the same as <CODE>"this becomes a string"</CODE>
+ */
+#define STR(x) #x
+
+// Header guard
+#endif
diff --git a/externals/g3dlite/G3D.lib/include/G3D/prompt.h b/externals/g3dlite/G3D.lib/include/G3D/prompt.h
new file mode 100644
index 00000000000..e25b955ab28
--- /dev/null
+++ b/externals/g3dlite/G3D.lib/include/G3D/prompt.h
@@ -0,0 +1,67 @@
+/**
+ @file prompt.h
+
+ @maintainer Morgan McGuire, matrix@graphics3d.com
+ @cite Windows GUI code by Max McGuire
+
+ @created 2001-08-26
+ @edited 2006-08-13
+ */
+
+#ifndef G3D_PROMPT_H
+#define G3D_PROMPT_H
+
+#include "platform.h"
+#include <string>
+
+namespace G3D {
+
+/**
+ Prints a prompt to stdout and waits for user input. The return value is
+ the number of the user's choice (the first is 0, if there are no
+ choices, returns 0).
+
+ @param useGui Under Win32, use a GUI, not stdout prompt.
+ @param windowTitle The title for the prompt window
+ @param promptx The text string to prompt the user with
+ @param choice An array of strings that are the choices the user may make
+ @param numChoices The length of choice.
+
+ @cite Windows dialog interface by Max McGuire, mmcguire@ironlore.com
+ @cite Font setting code by Kurt Miller, kurt@flipcode.com
+ */
+int prompt(
+ const char* windowTitle,
+ const char* promptx,
+ const char** choice,
+ int numChoices,
+ bool useGui);
+
+/**
+ Prints a prompt and waits for user input. The return value is
+ the number of the user's choice (the first is 0, if there are no
+ choices, returns 0).
+ <P>Uses GUI under Win32, stdout prompt otherwise.
+ */
+inline int prompt(
+ const char* windowTitle,
+ const char* promptx,
+ const char** choice,
+ int numChoices) {
+
+ return prompt(windowTitle, promptx, choice, numChoices, true);
+}
+
+
+/**
+ Displays a GUI prompt with "Ok" as the only choice.
+ */
+void msgBox(
+ const std::string& message,
+ const std::string& title = "Message");
+
+
+}; // namespace
+
+#endif
+
diff --git a/externals/g3dlite/G3D.lib/include/G3D/serialize.h b/externals/g3dlite/G3D.lib/include/G3D/serialize.h
new file mode 100644
index 00000000000..2382c0ee0fd
--- /dev/null
+++ b/externals/g3dlite/G3D.lib/include/G3D/serialize.h
@@ -0,0 +1,30 @@
+#ifndef G3D_SERIALIZE_H
+#define G3D_SERIALIZE_H
+
+#include "G3D/BinaryInput.h"
+#include "G3D/BinaryOutput.h"
+#include "G3D/Array.h"
+
+namespace G3D {
+
+
+template<typename T>
+void serialize(const Array<T>& array, BinaryOutput& b) {
+ b.writeInt32(array.size());
+ for (int i = 0; i < array.size(); ++i) {
+ serialize(array[i], b);
+ }
+}
+
+template<typename T>
+void deserialize(Array<T>& array, BinaryInput& b) {
+ int N = b.readInt32();
+ array.resize(N);
+ for (int i = 0; i < array.size(); ++i) {
+ deserialize(array[i], b);
+ }
+}
+
+}
+
+#endif
diff --git a/externals/g3dlite/G3D.lib/include/G3D/splinefunc.h b/externals/g3dlite/G3D.lib/include/G3D/splinefunc.h
new file mode 100644
index 00000000000..a9daad9b578
--- /dev/null
+++ b/externals/g3dlite/G3D.lib/include/G3D/splinefunc.h
@@ -0,0 +1,118 @@
+/**
+ @file spline.h
+
+ @maintainer Morgan McGuire, matrix@graphics3d.com
+
+ @created 2004-07-25
+ @edited 2007-05-05
+ */
+
+#ifndef G3D_SPLINEFUNC_H
+#define G3D_SPLINEFUNC_H
+
+namespace G3D {
+
+#include "G3D/platform.h"
+#include "G3D/debug.h"
+#include "G3D/Array.h"
+#include "G3D/g3dmath.h"
+
+/**
+ Interpolates a property according to a piecewise linear spline. This provides
+ C0 continuity but the derivatives are not smooth.
+ <P>
+ Example:
+ <CODE>
+ const double times[] = {MIDNIGHT, SUNRISE - HOUR, SUNRISE, SUNRISE + sunRiseAndSetTime / 4, SUNRISE + sunRiseAndSetTime, SUNSET - sunRiseAndSetTime, SUNSET - sunRiseAndSetTime / 2, SUNSET, SUNSET + HOUR/2, DAY};
+ const Color3 color[] = {Color3(0, .0, .1), Color3(0, .0, .1), Color3::black(), Color3::black(), Color3::white() * .25, Color3::white() * .25, Color3(.5, .2, .2), Color3(.05, .05, .1), Color3(0, .0, .1), Color3(0, .0, .1)};
+ ambient = linearSpline(time, times, color, 10);
+ </CODE>
+
+ See also G3D::Spline
+
+ @param x The spline is a function of x; this is the sample to choose.
+ @param controlX controlX[i], controlY[i] is a control points. It is assumed
+ that controlX are strictly increasing. XType must support
+ the "<" operator and a subtraction operator that returns
+ a number.
+ @param controlY YType must support multiplication and addition.
+ @param numControl The number of control points.
+ */
+template<class XType, class YType>
+YType linearSpline(double x, const XType* controlX, const YType* controlY, int numControl) {
+ debugAssert(numControl >= 1);
+
+ // Off the beginning
+ if ((numControl == 1) || (x < controlX[0])) {
+ return controlY[0];
+ }
+
+ for (int i = 1; i < numControl; ++i) {
+ if (x < controlX[i]) {
+ const double alpha = (double)(controlX[i] - x) / (controlX[i] - controlX[i - 1]);
+ return controlY[i] * (1 - alpha) + controlY[i - 1] * alpha;
+ }
+ }
+
+ // Off the end
+ return controlY[numControl - 1];
+}
+
+
+ /** See also G3D::Spline*/
+template<class YType> YType cyclicCatmullRomSpline(
+ double t,
+ const YType* controlY,
+ int numPoints) {
+
+ debugAssert(numPoints >= 3);
+
+ t = wrap(t, numPoints);
+
+ // Find the indices of adjacent control points
+ int i = iFloor(t);
+
+ // Compute the distance from the control point
+ t = t - i;
+
+ // Shift back one point for correct indexing
+ i += numPoints - 1;
+
+ // Pick up four control points
+ const YType& P0 = controlY[(i + 0) % numPoints];
+ const YType& P1 = controlY[(i + 1) % numPoints];
+ const YType& P2 = controlY[(i + 2) % numPoints];
+ const YType& P3 = controlY[(i + 3) % numPoints];
+
+ return 0.5 * ((2 * P1) +
+ (-P0 + P2) * t +
+ (2*P0 - 5*P1 + 4*P2 - P3) * t*t +
+ (-P0 + 3*P1- 3*P2 + P3) * t*t*t);
+}
+
+/**
+ A cubic spline with regularly spaced
+ control points. The spline interpolates
+ the control points. The spline
+ will wrap from the last point back to the first.
+
+ The t parameter is on the range [0, controlY.size()],
+ where integers correspond to control points exactly.
+
+ See also G3D::Spline
+
+ @cite http://www.mvps.org/directx/articles/catmull/
+*/
+template<class YType> YType cyclicCatmullRomSpline(
+ double t,
+ const Array<YType>& controlY) {
+
+ int numPoints = controlY.size();
+ return cyclicCatmullRomSpline(t, controlY.getCArray(), numPoints);
+}
+
+}
+
+#endif
+
+
diff --git a/externals/g3dlite/G3D.lib/include/G3D/stringutils.h b/externals/g3dlite/G3D.lib/include/G3D/stringutils.h
new file mode 100644
index 00000000000..20a2ff6e795
--- /dev/null
+++ b/externals/g3dlite/G3D.lib/include/G3D/stringutils.h
@@ -0,0 +1,130 @@
+/**
+ @file stringutils.h
+
+ @maintainer Morgan McGuire, matrix@graphics3d.com
+
+ @author 2000-09-09
+ @edited 2008-08-05
+ */
+
+#ifndef G3D_STRINGUTILS_H
+#define G3D_STRINGUTILS_H
+
+#include "G3D/platform.h"
+#include "G3D/Array.h"
+#include <cstring>
+
+namespace G3D {
+
+extern const char* NEWLINE;
+
+/**
+ Returns true if the test string begins with the pattern string.
+ */
+bool beginsWith(
+ const std::string& test,
+ const std::string& pattern);
+
+/**
+ Returns true if the test string ends with the pattern string.
+ */
+bool endsWith(
+ const std::string& test,
+ const std::string& pattern);
+
+/**
+ Produces a new string that is the input string
+ wrapped at a certain number of columns (where
+ the line is broken at the latest space before the
+ column limit.) Platform specific NEWLINEs
+ are inserted to wrap.
+ */
+std::string wordWrap(
+ const std::string& input,
+ int numCols);
+
+/**
+ A comparison function for passing to Array::sort.
+ */
+int stringCompare(
+ const std::string& s1,
+ const std::string& s2);
+
+int stringPtrCompare(
+ const std::string* s1,
+ const std::string* s2);
+
+/**
+ Returns a new string that is an uppercase version of x.
+ */
+std::string toUpper(
+ const std::string& x);
+
+std::string toLower(
+ const std::string& x);
+
+/**
+ Splits x at each occurance of splitChar.
+ */
+G3D::Array<std::string> stringSplit(
+ const std::string& x,
+ char splitChar);
+
+/**
+ joinChar is not inserted at the beginning or end, just in between
+ elements.
+ */
+std::string stringJoin(
+ const G3D::Array<std::string>& a,
+ char joinChar);
+
+std::string stringJoin(
+ const G3D::Array<std::string>& a,
+ const std::string& joinStr);
+
+/**
+ Strips whitespace from both ends of the string.
+ */
+std::string trimWhitespace(
+ const std::string& s);
+
+/** These standard C functions are renamed for clarity/naming
+ conventions and to return bool, not int.
+ */
+inline bool isWhiteSpace(const unsigned char c) {
+ return isspace(c) != 0;
+}
+
+/** These standard C functions are renamed for clarity/naming
+ conventions and to return bool, not int.
+ */
+inline bool isNewline(const unsigned char c) {
+ return (c == '\n') || (c == '\r');
+}
+
+/** These standard C functions are renamed for clarity/naming
+ conventions and to return bool, not int.
+ */
+inline bool isDigit(const unsigned char c) {
+ return isdigit(c) != 0;
+}
+
+/** These standard C functions are renamed for clarity/naming
+ conventions and to return bool, not int.
+ */
+inline bool isLetter(const unsigned char c) {
+ return isalpha(c) != 0;
+}
+
+inline bool isSlash(const unsigned char c) {
+ return (c == '\\') || (c == '/');
+}
+
+inline bool isQuote(const unsigned char c) {
+ return (c == '\'') || (c == '\"');
+}
+
+}; // namespace
+
+#endif
+
diff --git a/externals/g3dlite/G3D.lib/include/G3D/uint128.h b/externals/g3dlite/G3D.lib/include/G3D/uint128.h
new file mode 100644
index 00000000000..3e970cb2a7e
--- /dev/null
+++ b/externals/g3dlite/G3D.lib/include/G3D/uint128.h
@@ -0,0 +1,51 @@
+/**
+ @file uint128.h
+
+ @maintainer Morgan McGuire, matrix@graphics3d.com
+ @author Kyle Whitson
+
+ @created 2008-07-17
+ @edited 2008-07-17
+ */
+
+#ifndef G3D_UINT128_H
+#define G3D_UINT128_H
+
+#include "G3D/g3dmath.h"
+
+namespace G3D {
+
+/** Limited functionality 128-bit unsigned integer. This is primarily to support FNV hashing and other
+ cryptography applications. See the GMP library for high-precision C++ math support. */
+class uint128 {
+public:
+
+ G3D::uint64 hi;
+ G3D::uint64 lo;
+
+ uint128(const uint64& lo);
+
+ uint128(const uint64& hi, const uint64& lo);
+
+ uint128& operator+=(const uint128& x);
+
+ uint128& operator*=(const uint128& x);
+
+ uint128& operator^=(const uint128& x);
+
+ uint128& operator&=(const uint128& x);
+
+ uint128& operator|=(const uint128& x);
+
+ bool operator==(const uint128& x);
+
+ uint128& operator>>=(const int x);
+
+ uint128& operator<<=(const int x);
+
+ uint128 operator&(const uint128& x);
+
+};
+}
+
+#endif
diff --git a/externals/g3dlite/G3D.lib/include/G3D/vectorMath.h b/externals/g3dlite/G3D.lib/include/G3D/vectorMath.h
new file mode 100644
index 00000000000..aedc731e7f8
--- /dev/null
+++ b/externals/g3dlite/G3D.lib/include/G3D/vectorMath.h
@@ -0,0 +1,235 @@
+/**
+ @file vectorMath.h
+
+ Function aliases for popular vector methods.
+
+ @maintainer Morgan McGuire, matrix@graphics3d.com
+
+ @created: 2001-06-02
+ @edited: 2004-02-02
+ Copyright 2000-2004, Morgan McGuire.
+ All rights reserved.
+ */
+
+#ifndef G3D_VECTORMATH_H
+#define G3D_VECTORMATH_H
+
+#include "G3D/platform.h"
+#include "G3D/g3dmath.h"
+#include "G3D/Vector2.h"
+#include "G3D/Vector3.h"
+#include "G3D/Vector4.h"
+#include "G3D/Matrix3.h"
+#include "G3D/Matrix4.h"
+#include "G3D/Color1.h"
+#include "G3D/Color3.h"
+#include "G3D/Color4.h"
+
+
+namespace G3D {
+
+
+inline Matrix4 mul(const Matrix4& a, const Matrix4& b) {
+ return a * b;
+}
+
+inline Vector4 mul(const Matrix4& m, const Vector4& v) {
+ return m * v;
+}
+
+inline Vector3 mul(const Matrix3& m, const Vector3& v) {
+ return m * v;
+}
+
+inline Matrix3 mul(const Matrix3& a, const Matrix3& b) {
+ return a * b;
+}
+
+inline float dot(const Vector2& a, const Vector2& b) {
+ return a.dot(b);
+}
+
+inline float dot(const Vector3& a, const Vector3& b) {
+ return a.dot(b);
+}
+
+inline float dot(const Vector4& a, const Vector4& b) {
+ return a.dot(b);
+}
+
+inline Vector2 normalize(const Vector2& v) {
+ return v / v.length();
+}
+
+inline Vector3 normalize(const Vector3& v) {
+ return v / v.magnitude();
+}
+
+inline Vector4 normalize(const Vector4& v) {
+ return v / v.length();
+}
+
+inline Vector2 abs(const Vector2& v) {
+ return Vector2(::fabsf(v.x), ::fabsf(v.y));
+}
+
+inline Vector3 abs(const Vector3& v) {
+ return Vector3(::fabsf(v.x), ::fabsf(v.y), ::fabsf(v.z));
+}
+
+inline Vector4 abs(const Vector4& v) {
+ return Vector4(::fabsf(v.x), ::fabsf(v.y), ::fabsf(v.z), ::fabsf(v.w));
+}
+
+inline bool all(const Vector2& v) {
+ return (v.x != 0) && (v.y != 0);
+}
+
+inline bool all(const Vector3& v) {
+ return (v.x != 0) && (v.y != 0) && (v.z != 0);
+}
+
+inline bool all(const Vector4& v) {
+ return (v.x != 0) && (v.y != 0) && (v.z != 0) && (v.w != 0);
+}
+
+inline bool any(const Vector2& v) {
+ return (v.x != 0) || (v.y != 0);
+}
+
+inline bool any(const Vector3& v) {
+ return (v.x != 0) || (v.y != 0) || (v.z != 0);
+}
+
+inline bool any(const Vector4& v) {
+ return (v.x != 0) || (v.y != 0) || (v.z != 0) || (v.w != 0);
+}
+
+inline Vector2 clamp(const Vector2& v, const Vector2& a, const Vector2& b) {
+ return v.clamp(a, b);
+}
+
+inline Vector3 clamp(const Vector3& v, const Vector3& a, const Vector3& b) {
+ return v.clamp(a, b);
+}
+
+inline Vector4 clamp(const Vector4& v, const Vector4& a, const Vector4& b) {
+ return v.clamp(a, b);
+}
+
+inline Vector2 lerp(const Vector2& v1, const Vector2& v2, float f) {
+ return v1.lerp(v2, f);
+}
+
+inline Vector3 lerp(const Vector3& v1, const Vector3& v2, float f) {
+ return v1.lerp(v2, f);
+}
+
+inline Vector4 lerp(const Vector4& v1, const Vector4& v2, float f) {
+ return v1.lerp(v2, f);
+}
+
+inline Color1 lerp(const Color1& v1, const Color1& v2, float f) {
+ return v1.lerp(v2, f);
+}
+
+inline Color3 lerp(const Color3& v1, const Color3& v2, float f) {
+ return v1.lerp(v2, f);
+}
+
+inline Color4 lerp(const Color4& v1, const Color4& v2, float f) {
+ return v1.lerp(v2, f);
+}
+
+inline Vector3 cross(const Vector3& v1, const Vector3& v2) {
+ return v1.cross(v2);
+}
+
+inline double determinant(const Matrix3& m) {
+ return m.determinant();
+}
+
+inline double determinant(const Matrix4& m) {
+ return m.determinant();
+}
+
+inline Vector2 min(const Vector2& v1, const Vector2& v2) {
+ return v1.min(v2);
+}
+
+inline Vector3 min(const Vector3& v1, const Vector3& v2) {
+ return v1.min(v2);
+}
+
+inline Vector4 min(const Vector4& v1, const Vector4& v2) {
+ return v1.min(v2);
+}
+
+inline Color3 min(const Color3& v1, const Color3& v2) {
+ return v1.min(v2);
+}
+
+inline Color4 min(const Color4& v1, const Color4& v2) {
+ return v1.min(v2);
+}
+
+inline Vector2 max(const Vector2& v1, const Vector2& v2) {
+ return v1.max(v2);
+}
+
+inline Vector3 max(const Vector3& v1, const Vector3& v2) {
+ return v1.max(v2);
+}
+
+inline Vector4 max(const Vector4& v1, const Vector4& v2) {
+ return v1.max(v2);
+}
+
+inline Color3 max(const Color3& v1, const Color3& v2) {
+ return v1.max(v2);
+}
+
+inline Color4 max(const Color4& v1, const Color4& v2) {
+ return v1.max(v2);
+}
+
+inline Vector2 sign(const Vector2& v) {
+ return Vector2((float)sign(v.x), (float)sign(v.y));
+}
+
+inline Vector3 sign(const Vector3& v) {
+ return Vector3((float)sign(v.x), (float)sign(v.y), (float)sign(v.z));
+}
+
+inline Vector4 sign(const Vector4& v) {
+ return Vector4((float)sign(v.x), (float)sign(v.y), (float)sign(v.z), (float)sign(v.w));
+}
+
+inline float length(float v) {
+ return ::fabsf(v);
+}
+
+inline float length(const Vector2& v) {
+ return v.length();
+}
+
+inline float length(const Vector3& v) {
+ return v.magnitude();
+}
+
+inline float length(const Vector4& v) {
+ return v.length();
+}
+
+/**
+ Computes the log of each component. Useful for
+ inverting the monitor gamma function or simulating
+ perceptual response.
+ */
+inline Color3 log(const Color3& c) {
+ return Color3(::logf(c.r), ::logf(c.g), ::logf(c.b));
+}
+
+}
+
+#endif
diff --git a/externals/g3dlite/G3D.lib/source/AABox.cpp b/externals/g3dlite/G3D.lib/source/AABox.cpp
new file mode 100644
index 00000000000..9e871aa8af7
--- /dev/null
+++ b/externals/g3dlite/G3D.lib/source/AABox.cpp
@@ -0,0 +1,342 @@
+/**
+ @file AABox.cpp
+
+ @maintainer Morgan McGuire, matrix@graphics3d.com
+
+ @created 2004-01-10
+ @edited 2006-01-11
+*/
+
+#include "G3D/platform.h"
+#include "G3D/AABox.h"
+#include "G3D/Box.h"
+#include "G3D/Plane.h"
+#include "G3D/Sphere.h"
+#include "G3D/BinaryInput.h"
+#include "G3D/BinaryOutput.h"
+
+
+namespace G3D {
+
+
+void AABox::serialize(class BinaryOutput& b) const {
+ b.writeVector3(lo);
+ b.writeVector3(hi);
+}
+
+
+void AABox::deserialize(class BinaryInput& b) {
+ lo = b.readVector3();
+ hi = b.readVector3();
+}
+
+
+void AABox::split(const Vector3::Axis& axis, float location, AABox& low, AABox& high) const {
+ // Low, medium, and high along the chosen axis
+ float L = G3D::min(location, lo[axis]);
+ float M = G3D::min(G3D::max(location, lo[axis]), hi[axis]);
+ float H = G3D::max(location, hi[axis]);
+
+ // Copy over this box.
+ high = low = *this;
+
+ // Now move the split points along the special axis
+ low.lo[axis] = L;
+ low.hi[axis] = M;
+ high.lo[axis] = M;
+ high.hi[axis] = H;
+}
+
+
+Vector3 AABox::randomSurfacePoint() const {
+ Vector3 extent = hi - lo;
+ float aXY = extent.x * extent.y;
+ float aYZ = extent.y * extent.z;
+ float aZX = extent.z * extent.x;
+
+ float r = (float)uniformRandom(0.0f, aXY + aYZ + aZX);
+
+ // Choose evenly between positive and negative face planes
+ float d = ((float)uniformRandom(0, 1) < 0.5f) ? 0.0f : 1.0f;
+
+ // The probability of choosing a given face is proportional to
+ // its area.
+ if (r < aXY) {
+ return
+ lo +
+ Vector3(
+ (float)uniformRandom(0.0f, extent.x),
+ (float)uniformRandom(0.0f, extent.y),
+ d * extent.z);
+ } else if (r < aYZ) {
+ return
+ lo +
+ Vector3(
+ d * extent.x,
+ (float)uniformRandom(0, extent.y),
+ (float)uniformRandom(0, extent.z));
+ } else {
+ return
+ lo +
+ Vector3(
+ (float)uniformRandom(0, extent.x),
+ d * extent.y,
+ (float)uniformRandom(0, extent.z));
+ }
+}
+
+
+Vector3 AABox::randomInteriorPoint() const {
+ return Vector3(
+ (float)uniformRandom(lo.x, hi.x),
+ (float)uniformRandom(lo.y, hi.y),
+ (float)uniformRandom(lo.z, hi.z));
+}
+
+
+bool AABox::intersects(const AABox& other) const {
+ // Must be overlap along all three axes.
+ // Try to find a separating axis.
+
+ for (int a = 0; a < 3; ++a) {
+
+ // |--------|
+ // |------|
+
+ if ((lo[a] > other.hi[a]) ||
+ (hi[a] < other.lo[a])) {
+ return false;
+ }
+ }
+
+ return true;
+}
+
+int AABox::dummy = 0;
+
+
+bool AABox::culledBy(
+ const Array<Plane>& plane,
+ int& cullingPlane,
+ const uint32 _inMask,
+ uint32& childMask) const {
+
+ uint32 inMask = _inMask;
+ assert(plane.size() < 31);
+
+ childMask = 0;
+
+ const bool finite =
+ (abs(lo.x) < G3D::inf()) &&
+ (abs(hi.x) < G3D::inf()) &&
+ (abs(lo.y) < G3D::inf()) &&
+ (abs(hi.y) < G3D::inf()) &&
+ (abs(lo.z) < G3D::inf()) &&
+ (abs(hi.z) < G3D::inf());
+
+ // See if there is one plane for which all of the
+ // vertices are in the negative half space.
+ for (int p = 0; p < plane.size(); ++p) {
+
+ // Only test planes that are not masked
+ if ((inMask & 1) != 0) {
+
+ Vector3 corner;
+
+ int numContained = 0;
+ int v = 0;
+
+ // We can early-out only if we have found one point on each
+ // side of the plane (i.e. if we are straddling). That
+ // occurs when (numContained < v) && (numContained > 0)
+ for (v = 0; (v < 8) && ((numContained == v) || (numContained == 0)); ++v) {
+ // Unrolling these 3 if's into a switch decreases performance
+ // by about 2x
+ corner.x = (v & 1) ? hi.x : lo.x;
+ corner.y = (v & 2) ? hi.y : lo.y;
+ corner.z = (v & 4) ? hi.z : lo.z;
+
+ if (finite) { // this branch is highly predictable
+ if (plane[p].halfSpaceContainsFinite(corner)) {
+ ++numContained;
+ }
+ } else {
+ if (plane[p].halfSpaceContains(corner)) {
+ ++numContained;
+ }
+ }
+ }
+
+ if (numContained == 0) {
+ // Plane p culled the box
+ cullingPlane = p;
+
+ // The caller should not recurse into the children,
+ // since the parent is culled. If they do recurse,
+ // make them only test against this one plane, which
+ // will immediately cull the volume.
+ childMask = 1 << p;
+ return true;
+
+ } else if (numContained < v) {
+ // The bounding volume straddled the plane; we have
+ // to keep testing against this plane
+ childMask |= (1 << p);
+ }
+ }
+
+ // Move on to the next bit.
+ inMask = inMask >> 1;
+ }
+
+ // None of the planes could cull this box
+ cullingPlane = -1;
+ return false;
+}
+
+
+bool AABox::culledBy(
+ const Array<Plane>& plane,
+ int& cullingPlane,
+ const uint32 _inMask) const {
+
+ uint32 inMask = _inMask;
+ assert(plane.size() < 31);
+
+ const bool finite =
+ (abs(lo.x) < G3D::inf()) &&
+ (abs(hi.x) < G3D::inf()) &&
+ (abs(lo.y) < G3D::inf()) &&
+ (abs(hi.y) < G3D::inf()) &&
+ (abs(lo.z) < G3D::inf()) &&
+ (abs(hi.z) < G3D::inf());
+
+ // See if there is one plane for which all of the
+ // vertices are in the negative half space.
+ for (int p = 0; p < plane.size(); ++p) {
+
+ // Only test planes that are not masked
+ if ((inMask & 1) != 0) {
+
+ bool culled = true;
+ Vector3 corner;
+
+ int v;
+
+ // Assume this plane culls all points. See if there is a point
+ // not culled by the plane... early out when at least one point
+ // is in the positive half space.
+ for (v = 0; (v < 8) && culled; ++v) {
+
+ // Unrolling these 3 if's into a switch decreases performance
+ // by about 2x
+ corner.x = (v & 1) ? hi.x : lo.x;
+ corner.y = (v & 2) ? hi.y : lo.y;
+ corner.z = (v & 4) ? hi.z : lo.z;
+
+ if (finite) { // this branch is highly predictable
+ culled = ! plane[p].halfSpaceContainsFinite(corner);
+ } else {
+ culled = ! plane[p].halfSpaceContains(corner);
+ }
+ }
+
+ if (culled) {
+ // Plane p culled the box
+ cullingPlane = p;
+
+ return true;
+ }
+ }
+
+ // Move on to the next bit.
+ inMask = inMask >> 1;
+ }
+
+ // None of the planes could cull this box
+ cullingPlane = -1;
+ return false;
+}
+
+
+bool AABox::intersects(const class Sphere& sphere) const {
+ double d = 0;
+
+ //find the square of the distance
+ //from the sphere to the box
+ for (int i = 0; i < 3; ++i) {
+ if (sphere.center[i] < lo[i]) {
+ d += square(sphere.center[i] - lo[i]);
+ } else if (sphere.center[i] > hi[i]) {
+ d += square(sphere.center[i] - hi[i]);
+ }
+ }
+
+ return d <= square(sphere.radius);
+}
+
+Vector3 AABox::corner(int index) const {
+
+ // default constructor inits all components to 0
+ Vector3 v;
+
+ switch (index)
+ {
+ case 0:
+ v.x = lo.x;
+ v.y = lo.y;
+ v.z = hi.z;
+ break;
+
+ case 1:
+ v.x = hi.x;
+ v.y = lo.y;
+ v.z = hi.z;
+ break;
+
+ case 2:
+ v.x = hi.x;
+ v.y = hi.y;
+ v.z = hi.z;
+ break;
+
+ case 3:
+ v.x = lo.x;
+ v.y = hi.y;
+ v.z = hi.z;
+ break;
+
+ case 4:
+ v.x = lo.x;
+ v.y = lo.y;
+ v.z = lo.z;
+ break;
+
+ case 5:
+ v.x = hi.x;
+ v.y = lo.y;
+ v.z = lo.z;
+ break;
+
+ case 6:
+ v.x = hi.x;
+ v.y = hi.y;
+ v.z = lo.z;
+ break;
+
+ case 7:
+ v.x = lo.x;
+ v.y = hi.y;
+ v.z = lo.z;
+ break;
+
+ default:
+ debugAssertM(false, "Invalid corner index");
+ break;
+ }
+
+ return v;
+}
+
+
+}
diff --git a/externals/g3dlite/G3D.lib/source/AnyVal.cpp b/externals/g3dlite/G3D.lib/source/AnyVal.cpp
new file mode 100644
index 00000000000..45fb41df534
--- /dev/null
+++ b/externals/g3dlite/G3D.lib/source/AnyVal.cpp
@@ -0,0 +1,1381 @@
+/**
+ @file AnyVal.cpp
+ @author Morgan McGuire
+ @maintainer Morgan McGuire
+ @created 2006-06-11
+ @edited 2008-07-14
+ */
+
+#include "G3D/AnyVal.h"
+#include "G3D/Array.h"
+#include "G3D/stringutils.h"
+#include "G3D/Table.h"
+#include "G3D/Vector2.h"
+#include "G3D/Vector3.h"
+#include "G3D/Vector4.h"
+#include "G3D/Color1.h"
+#include "G3D/Color3.h"
+#include "G3D/Color4.h"
+#include "G3D/Matrix2.h"
+#include "G3D/Matrix3.h"
+#include "G3D/Matrix4.h"
+#include "G3D/Rect2D.h"
+#include "G3D/AABox.h"
+#include "G3D/CoordinateFrame.h"
+#include "G3D/Quat.h"
+#include "G3D/TextInput.h"
+#include "G3D/TextOutput.h"
+#include "G3D/BinaryInput.h"
+#include "G3D/BinaryOutput.h"
+
+namespace G3D {
+
+AnyVal AnyVal::fromFile(const std::string& filename) {
+ TextInput t(filename);
+ return AnyVal(t);
+}
+
+
+void AnyVal::load(const std::string& filename) {
+ *this = fromFile(filename);
+}
+
+
+void AnyVal::save(const std::string& filename) const {
+ TextOutput t(filename);
+ serialize(t);
+ t.commit();
+}
+
+
+AnyVal::AnyVal() : m_type(NIL), m_value(NULL), m_referenceCount(NULL) {
+}
+
+
+AnyVal::AnyVal(bool b) : m_type(BOOLEAN), m_value(new bool(b)), m_referenceCount(NULL) {
+}
+
+
+AnyVal::AnyVal(G3D::TextInput& t) : m_type(NIL), m_value(NULL), m_referenceCount(NULL) {
+ deserialize(t);
+}
+
+
+/*AnyVal::AnyVal(G3D::BinaryInput& b) {
+ deserialize(b);
+}
+*/
+
+AnyVal::AnyVal(double v) : m_type(NUMBER), m_referenceCount(NULL) {
+ m_value = new double(v);
+}
+
+
+AnyVal::AnyVal(int v) : m_type(NUMBER), m_referenceCount(NULL) {
+ m_value = new double(v);
+}
+
+
+AnyVal::AnyVal(const Rect2D& v) : m_type(RECT2D), m_referenceCount(NULL) {
+ m_value = new Rect2D(v);
+}
+
+
+AnyVal::AnyVal(const AABox& v) : m_type(AABOX), m_referenceCount(NULL) {
+ m_value = new AABox(v);
+}
+
+
+AnyVal::AnyVal(const Vector2& v) : m_type(VECTOR2), m_referenceCount(NULL) {
+ m_value = new Vector2(v);
+}
+
+
+AnyVal::AnyVal(const Vector3& v) : m_type(VECTOR3), m_referenceCount(NULL) {
+ m_value = new Vector3(v);
+}
+
+
+AnyVal::AnyVal(const Vector4& v) : m_type(VECTOR4), m_referenceCount(NULL) {
+ m_value = new Vector4(v);
+}
+
+
+AnyVal::AnyVal(const Color1& v) : m_type(COLOR1), m_referenceCount(NULL) {
+ m_value = new Color1(v);
+}
+
+
+AnyVal::AnyVal(const Color3& v) : m_type(COLOR3), m_referenceCount(NULL) {
+ m_value = new Color3(v);
+}
+
+
+AnyVal::AnyVal(const Color4& v) : m_type(COLOR4), m_referenceCount(NULL) {
+ m_value = new Color4(v);
+}
+
+
+AnyVal::AnyVal(const std::string& v) : m_type(STRING), m_referenceCount(NULL) {
+ m_value = new std::string(v);
+}
+
+
+AnyVal::AnyVal(const char* v) : m_type(STRING), m_referenceCount(NULL) {
+ m_value = new std::string(v);
+}
+
+
+AnyVal::AnyVal(const Quat& v) : m_type(QUAT), m_referenceCount(NULL) {
+ m_value = new Quat(v);
+}
+
+
+AnyVal::AnyVal(const CoordinateFrame& v) : m_type(COORDINATEFRAME), m_referenceCount(NULL) {
+ m_value = new CoordinateFrame(v);
+}
+
+
+AnyVal::AnyVal(const Matrix2& v) : m_type(MATRIX2), m_referenceCount(NULL) {
+ m_value = new Matrix2(v);
+}
+
+AnyVal::AnyVal(const Matrix3& v) : m_type(MATRIX3), m_referenceCount(NULL) {
+ m_value = new Matrix3(v);
+}
+
+
+AnyVal::AnyVal(const Matrix4& v) : m_type(MATRIX4), m_referenceCount(NULL) {
+ m_value = new Matrix4(v);
+}
+
+
+AnyVal::AnyVal(const AnyVal& c) : m_type(NIL), m_value(NULL), m_referenceCount(NULL) {
+ *this = c;
+}
+
+
+AnyVal::AnyVal(Type arrayOrTable) : m_type(NIL), m_value(NULL), m_referenceCount(new int(1)) {
+ // TODO: make AnyVal::createArray()
+ switch (arrayOrTable) {
+ case ARRAY:
+ m_type = ARRAY;
+ m_value = new Array<AnyVal>();
+ break;
+
+ case TABLE:
+ m_type = TABLE;
+ m_value = new Table<std::string, AnyVal>();
+ break;
+
+ default:
+ debugAssertM(false, "Cannot construct AnyVal from constants except ARRAY or TABLE.");
+ }
+}
+
+
+AnyVal::~AnyVal() {
+ deleteValue();
+}
+
+
+void AnyVal::deleteValue() {
+ if (m_referenceCount) {
+ --(*m_referenceCount);
+ if (*m_referenceCount <= 0) {
+ delete m_referenceCount;
+ m_referenceCount = NULL;
+ // Pass through and delete the real object now
+ } else {
+ // Someone else is holding a reference, so we can't delete
+ // the object.
+ m_referenceCount = NULL;
+ return;
+ }
+ }
+
+ switch (m_type) {
+ case NIL:
+ // Nothing to do
+ break;
+
+ case NUMBER:
+ delete (double*)m_value;
+ break;
+
+ case BOOLEAN:
+ delete (bool*)m_value;
+ break;
+
+ case STRING:
+ delete (std::string*)m_value;
+ break;
+
+ case RECT2D:
+ delete (Rect2D*)m_value;
+ break;
+
+ case AABOX:
+ delete (AABox*)m_value;
+ break;
+
+ case VECTOR2:
+ delete (Vector2*)m_value;
+ break;
+
+ case VECTOR3:
+ delete (Vector3*)m_value;
+ break;
+
+ case VECTOR4:
+ delete (Vector4*)m_value;
+ break;
+
+ case MATRIX2:
+ delete (Matrix2*)m_value;
+ break;
+
+ case MATRIX3:
+ delete (Matrix3*)m_value;
+ break;
+
+ case MATRIX4:
+ delete (Matrix4*)m_value;
+ break;
+
+ case QUAT:
+ delete (Quat*)m_value;
+ break;
+
+ case COORDINATEFRAME:
+ delete (CoordinateFrame*)m_value;
+ break;
+
+ case COLOR1:
+ delete (Color1*)m_value;
+ break;
+
+ case COLOR3:
+ delete (Color3*)m_value;
+ break;
+
+ case COLOR4:
+ delete (Color4*)m_value;
+ break;
+
+ case ARRAY:
+ delete (Array<AnyVal>*)m_value;
+ break;
+
+ case TABLE:
+ delete (Table<std::string, AnyVal>*)m_value;
+ break;
+
+ default:
+ debugAssertM(false, "Internal error: no destructor for this type.");
+ }
+
+ m_value = NULL;
+}
+
+
+AnyVal& AnyVal::operator=(const AnyVal& v) {
+ deleteValue();
+
+ m_type = v.m_type;
+
+ m_referenceCount = v.m_referenceCount;
+
+ if (isSharedType()) {
+ ++(*m_referenceCount);
+ m_value = v.m_value;
+ } else {
+ m_value = v.copyValue();
+ }
+
+ return *this;
+}
+
+
+void* AnyVal::copyValue() const {
+ switch (m_type) {
+ case NIL:
+ return NULL;
+
+ case NUMBER:
+ return new double(*(double*)m_value);
+
+ case BOOLEAN:
+ return new bool(*(bool*)m_value);
+
+ case STRING:
+ return new std::string(*(std::string*)m_value);
+
+ case RECT2D:
+ return new Rect2D(*(Rect2D*)m_value);
+
+ case AABOX:
+ return new AABox(*(AABox*)m_value);
+
+ case VECTOR2:
+ return new Vector2(*(Vector2*)m_value);
+
+ case VECTOR3:
+ return new Vector3(*(Vector3*)m_value);
+
+ case VECTOR4:
+ return new Vector4(*(Vector4*)m_value);
+
+ case MATRIX2:
+ return new Matrix2(*(Matrix2*)m_value);
+
+ case MATRIX3:
+ return new Matrix3(*(Matrix3*)m_value);
+
+ case MATRIX4:
+ return new Matrix4(*(Matrix4*)m_value);
+
+ case QUAT:
+ return new Quat(*(Quat*)m_value);
+
+ case COORDINATEFRAME:
+ return new CoordinateFrame(*(CoordinateFrame*)m_value);
+
+ case COLOR1:
+ return new Color1(*(Color1*)m_value);
+
+ case COLOR3:
+ return new Color3(*(Color3*)m_value);
+
+ case COLOR4:
+ return new Color4(*(Color4*)m_value);
+
+ case ARRAY:
+ return new Array<AnyVal>(*(Array<AnyVal>*)m_value);
+
+ case TABLE:
+ return new Table<std::string, AnyVal>(*(Table<std::string, AnyVal>*)m_value);
+
+ default:
+ debugAssertM(false, "Internal error: no assignment operator for this type.");
+ return NULL;
+ }
+}
+
+AnyVal::Type AnyVal::type() const {
+ return m_type;
+}
+
+
+static bool legalIdentifier(const std::string& s) {
+ if (s.size() == 0) {
+ return false;
+ }
+
+ if (! isLetter(s[0]) || (s[0] == '_')) {
+ return false;
+ }
+
+ bool ok = true;
+
+ for (unsigned int i = 1; i < s.size(); ++i) {
+ ok &= isDigit(s[i]) || isLetter(s[i]) || (s[i] == '_');
+ }
+
+ return ok;
+}
+
+
+void AnyVal::serialize(G3D::TextOutput& t) const {
+ switch (m_type) {
+ case NIL:
+ t.writeSymbol("Nil");
+ break;
+
+ case NUMBER:
+ t.printf("%g", *(double*)m_value);
+ break;
+
+ case BOOLEAN:
+ t.writeBoolean(*(bool*)m_value);
+ break;
+
+ case STRING:
+ t.writeString(*(std::string*)m_value);
+ break;
+
+ case RECT2D:
+ t.printf("R(%g, %g, %g, %g)", ((Rect2D*)m_value)->x0(), ((Rect2D*)m_value)->y0(),
+ ((Rect2D*)m_value)->width(), ((Rect2D*)m_value)->height());
+ break;
+
+ case AABOX:
+ t.printf("AAB(V3(%g, %g, %g), V3(%g, %g, %g))",
+ aabox().low().x,
+ aabox().low().y,
+ aabox().low().z,
+ aabox().high().x,
+ aabox().high().y,
+ aabox().high().z);
+ break;
+
+ case VECTOR2:
+ t.printf("V2(%g, %g)", ((Vector2*)m_value)->x, ((Vector2*)m_value)->y);
+ break;
+
+ case VECTOR3:
+ t.printf("V3(%g, %g, %g)", ((Vector3*)m_value)->x, ((Vector3*)m_value)->y, ((Vector3*)m_value)->z);
+ break;
+
+ case VECTOR4:
+ t.printf("V4(%g, %g, %g, %g)", ((Vector4*)m_value)->x, ((Vector4*)m_value)->y, ((Vector4*)m_value)->z, ((Vector4*)m_value)->w);
+ break;
+
+ case MATRIX2:
+ {
+ const Matrix2& m = *(Matrix2*)m_value;
+ t.printf("M2(\n");
+ t.pushIndent();
+ t.printf("%10.5f, %10.5f,\n%10.5f, %10.5f)",
+ m[0][0], m[0][1],
+ m[1][0], m[1][1]);
+ t.popIndent();
+ }
+ break;
+
+ case MATRIX3:
+ {
+ const Matrix3& m = *(Matrix3*)m_value;
+ t.printf("M3(\n");
+ t.pushIndent();
+ t.printf("%10.5f, %10.5f, %10.5f,\n%10.5f, %10.5f, %10.5f,\n%10.5f, %10.5f, %10.5f)",
+ m[0][0], m[0][1], m[0][2],
+ m[1][0], m[1][1], m[1][2],
+ m[2][0], m[2][1], m[2][2]);
+ t.popIndent();
+ }
+ break;
+
+ case MATRIX4:
+ {
+ const Matrix4& m = *(Matrix4*)m_value;
+ t.printf("M4(\n");
+ t.pushIndent();
+ t.printf(
+ "%10.5f, %10.5f, %10.5f, %10.5f,\n"
+ "%10.5f, %10.5f, %10.5f, %10.5f,\n"
+ "%10.5f, %10.5f, %10.5f, %10.5f,\n"
+ "%10.5f, %10.5f, %10.5f, %10.5f)",
+ m[0][0], m[0][1], m[0][2], m[0][3],
+ m[1][0], m[1][1], m[1][2], m[1][3],
+ m[2][0], m[2][1], m[2][2], m[2][3],
+ m[3][0], m[3][1], m[3][2], m[3][3]);
+ t.popIndent();
+ }
+ break;
+
+ case QUAT:
+ t.printf("Q(%g, %g, %g, %g)", ((Quat*)m_value)->x, ((Quat*)m_value)->y, ((Quat*)m_value)->z, ((Quat*)m_value)->w);
+ break;
+
+ case COORDINATEFRAME:
+ {
+ const CoordinateFrame& c = *(CoordinateFrame*)m_value;
+ float x,y,z,yaw,pitch,roll;
+ c.getXYZYPRDegrees(x,y,z,yaw,pitch,roll);
+ t.printf("CF(V3(%g,%g,%g), %g, %g, %g)", x, y, z, yaw, pitch, roll);
+ /*
+ t.pushIndent();
+ t.printf(
+ "CF(\n%10.5f, %10.5f, %10.5f, %10.5f,\n"
+ "%10.5f, %10.5f, %10.5f, %10.5f,\n"
+ "%10.5f, %10.5f, %10.5f, %10.5f)",
+ c.rotation[0][0], c.rotation[0][1], c.rotation[0][2], c.translation.x,
+ c.rotation[1][0], c.rotation[1][1], c.rotation[1][2], c.translation.y,
+ c.rotation[2][0], c.rotation[2][1], c.rotation[2][2], c.translation.z);
+ t.popIndent();
+ */
+ }
+ break;
+
+ case COLOR1:
+ t.printf("C1(%g)", ((Color1*)m_value)->value);
+ break;
+
+ case COLOR3:
+ t.printf("C3(%g, %g, %g)", ((Color3*)m_value)->r, ((Color3*)m_value)->g, ((Color3*)m_value)->b);
+ break;
+
+ case COLOR4:
+ t.printf("C4(%g, %g, %g, %g)", ((Color4*)m_value)->r, ((Color4*)m_value)->g, ((Color4*)m_value)->b, ((Color4*)m_value)->a);
+ break;
+
+ case ARRAY:
+ {
+ const Array<AnyVal>& a = *(Array<AnyVal>*)m_value;
+ t.printf("[\n");
+ t.pushIndent();
+ for (int i = 0; i < a.size(); ++i) {
+ a[i].serialize(t);
+ if (i != a.size() - 1) {
+ t.printf(", \n");
+ }
+ }
+ t.printf("]");
+ t.popIndent();
+ }
+ break;
+
+ case TABLE:
+ {
+ const Table<std::string, AnyVal>& a = *(Table<std::string, AnyVal>*)m_value;
+ t.printf("{\n");
+ t.pushIndent();
+ Table<std::string, AnyVal>::Iterator i = a.begin();
+ const Table<std::string, AnyVal>::Iterator end = a.end();
+ while (i != end) {
+ // Quote names that are not legal C++ identifiers
+ if (! legalIdentifier(i->key)) {
+ t.printf("'%s' ", i->key.c_str());
+ } else {
+ t.writeSymbol(i->key);
+ }
+ t.printf("= ");
+
+ i->value.serialize(t);
+
+ if (i != end) {
+ t.printf("\n");
+ }
+ ++i;
+ }
+ t.popIndent();
+ t.printf("}");
+ }
+ break;
+
+ default:
+ debugAssertM(false, "Internal error: no serialize method for this type.");
+ }
+}
+
+
+std::string AnyVal::toString() const {
+ TextOutput t;
+ serialize(t);
+ std::string s;
+ t.commitString(s);
+ return s;
+}
+
+/*
+void AnyVal::serialize(G3D::BinaryOutput& t) const {
+ alwaysAssertM(false, "TODO");
+}
+*/
+
+void AnyVal::deserialize(G3D::TextInput& t) {
+ deleteValue();
+ m_type = NIL;
+ m_value = NULL;
+
+ if (! t.hasMore()) {
+ return;
+ }
+
+ switch (t.peek().type()) {
+ case Token::END:
+ // should never get here because of the hasMore check above
+ return;
+ break;
+
+ case Token::NUMBER:
+ m_type = NUMBER;
+ m_value = new double(t.readNumber());
+ break;
+
+ case Token::STRING:
+ m_type = STRING;
+ m_value = new std::string(t.readString());
+ break;
+
+ case Token::BOOLEAN:
+ m_type = BOOLEAN;
+ m_value = new bool(t.readBoolean());
+ break;
+
+ case Token::SYMBOL:
+ {
+ std::string s = t.readSymbol();
+ if (s == "NIL") {
+ break;
+
+ } else if (s == "true") {
+
+ m_type = BOOLEAN;
+ m_value = new bool(true);
+
+ } else if (s == "false") {
+
+ m_type = BOOLEAN;
+ m_value = new bool(false);
+
+ } else if (s == "R") {
+
+ m_type = RECT2D;
+ t.readSymbol("(");
+ float x,y,w,h;
+ x = (float)t.readNumber();
+ t.readSymbol(",");
+ y = (float)t.readNumber();
+ t.readSymbol(",");
+ w = (float)t.readNumber();
+ t.readSymbol(",");
+ h = (float)t.readNumber();
+ t.readSymbol(")");
+ m_value = new Rect2D(Rect2D::xywh(x, y, w, h));
+
+ } else if (s == "AAB") {
+
+ m_type = AABOX;
+ Vector3 v[2];
+ t.readSymbol("(");
+ for (int i = 0; i < 2; ++i) {
+ t.readSymbols("V3", "(");
+ v[i].x = (float)t.readNumber();
+ t.readSymbol(",");
+ v[i].y = (float)t.readNumber();
+ t.readSymbol(",");
+ v[i].z = (float)t.readNumber();
+ t.readSymbol(",");
+ if (i == 0) {
+ t.readSymbol(",");
+ }
+ }
+ t.readSymbol(")");
+ m_value = new AABox(v[0], v[1]);
+
+ } else if (s == "V2") {
+
+ t.readSymbol("(");
+ Vector2 v;
+ v.x = (float)t.readNumber();
+ t.readSymbol(",");
+ v.y = (float)t.readNumber();
+ t.readSymbol(")");
+ m_value = new Vector2(v);
+ m_type = VECTOR2;
+
+ } else if (s == "V3") {
+
+ t.readSymbol("(");
+ Vector3 v;
+ v.x = (float)t.readNumber();
+ t.readSymbol(",");
+ v.y = (float)t.readNumber();
+ t.readSymbol(",");
+ v.z = (float)t.readNumber();
+ t.readSymbol(")");
+ m_value = new Vector3(v);
+ m_type = VECTOR3;
+
+ } else if (s == "V4") {
+
+ t.readSymbol("(");
+ Vector4 v;
+ v.x = (float)t.readNumber();
+ t.readSymbol(",");
+ v.y = (float)t.readNumber();
+ t.readSymbol(",");
+ v.z = (float)t.readNumber();
+ t.readSymbol(",");
+ v.w = (float)t.readNumber();
+ t.readSymbol(")");
+ m_value = new Vector4(v);
+ m_type = VECTOR4;
+
+ } else if (s == "M2") {
+
+ t.readSymbol("(");
+ Matrix2 m;
+ for (int r = 0; r < 2; ++r) {
+ for (int c = 0; c < 2; ++c) {
+ m[r][c] = (float)t.readNumber();
+ if ((c != 1) || (r != 1)) {
+ t.readSymbol(",");
+ }
+ }
+ }
+ t.readSymbol(")");
+ m_value = new Matrix2(m);
+ m_type = MATRIX2;
+
+ } else if (s == "M3") {
+
+ t.readSymbol("(");
+ Matrix3 m;
+ for (int r = 0; r < 3; ++r) {
+ for (int c = 0; c < 3; ++c) {
+ m[r][c] = (float)t.readNumber();
+ if ((c != 2) || (r != 2)) {
+ t.readSymbol(",");
+ }
+ }
+ }
+ t.readSymbol(")");
+ m_value = new Matrix3(m);
+ m_type = MATRIX3;
+
+ } else if (s == "M4") {
+
+ t.readSymbol("(");
+ Matrix4 m;
+ for (int r = 0; r < 4; ++r) {
+ for (int c = 0; c < 4; ++c) {
+ m[r][c] = (float)t.readNumber();
+ if ((c != 3) || (r != 3)) {
+ t.readSymbol(",");
+ }
+ }
+ }
+ t.readSymbol(")");
+ m_value = new Matrix4(m);
+ m_type = MATRIX4;
+
+ } else if (s == "Q") {
+
+ t.readSymbol("(");
+ Quat q;
+ q.x = (float)t.readNumber();
+ t.readSymbol(",");
+ q.y = (float)t.readNumber();
+ t.readSymbol(",");
+ q.z = (float)t.readNumber();
+ t.readSymbol(",");
+ q.w = (float)t.readNumber();
+ t.readSymbol(")");
+ m_value = new Quat(q);
+ m_type = QUAT;
+
+ } else if (s == "CF") {
+
+ t.readSymbol("(");
+ CoordinateFrame m;
+ if (t.peek().type() == Token::SYMBOL) {
+ // Angle format
+ float x, y, z, yaw, roll, pitch;
+ t.readSymbols("V3", "(");
+ x = (float)t.readNumber();
+ t.readSymbol(",");
+ y = (float)t.readNumber();
+ t.readSymbol(",");
+ z = (float)t.readNumber();
+ t.readSymbols(")", ",");
+ yaw = (float)t.readNumber();
+ t.readSymbol(",");
+ pitch = (float)t.readNumber();
+ roll = 0;
+ if (t.peek().string() == ",") {
+ t.readSymbol(",");
+ roll = (float)t.readNumber();
+ }
+ m = CoordinateFrame::fromXYZYPRDegrees(x, y, z, yaw, pitch, roll);
+ } else {
+ // Matrix format
+ for (int r = 0; r < 3; ++r) {
+ for (int c = 0; c < 3; ++c) {
+ m.rotation[r][c] = (float)t.readNumber();
+ }
+ m.translation[r] = (float)t.readNumber();
+ if (r != 2) {
+ t.readSymbol(",");
+ }
+ }
+ }
+ t.readSymbol(")");
+ m_value = new CoordinateFrame(m);
+ m_type = COORDINATEFRAME;
+
+ } else if (s == "C1") {
+
+ t.readSymbol("(");
+ float v = (float)t.readNumber();
+ t.readSymbol(")");
+ m_value = new Color1(v);
+ m_type = COLOR1;
+
+ } else if (s == "C3") {
+
+ t.readSymbol("(");
+ Color3 c;
+ c.r = (float)t.readNumber();
+ t.readSymbol(",");
+ c.g = (float)t.readNumber();
+ t.readSymbol(",");
+ c.b = (float)t.readNumber();
+ t.readSymbol(")");
+ m_value = new Color3(c);
+ m_type = COLOR3;
+
+ } else if (s == "C4") {
+
+ t.readSymbol("(");
+ Color4 c;
+ c.r = (float)t.readNumber();
+ t.readSymbol(",");
+ c.g = (float)t.readNumber();
+ t.readSymbol(",");
+ c.b = (float)t.readNumber();
+ t.readSymbol(",");
+ c.a = (float)t.readNumber();
+ t.readSymbol(")");
+ m_value = new Color4(c);
+ m_type = COLOR4;
+
+ } else if (s == "[") {
+
+ // Array
+ m_type = ARRAY;
+ m_value = new Array<AnyVal>();
+ m_referenceCount = new int(1);
+ Array<AnyVal>& a = *(Array<AnyVal>*)m_value;
+
+ Token peek = t.peek();
+ while ((peek.type() != Token::SYMBOL) || (peek.string() != "]")) {
+ // Avoid copying large objects
+ a.next().deserialize(t);
+
+ peek = t.peek();
+ if (peek.type() != Token::SYMBOL) {
+ throw CorruptText("Expected ',' or ']'", peek);
+ } else if (peek.string() == ",") {
+ t.readSymbol(",");
+ } else if (peek.string() != "]") {
+ throw CorruptText("Missing ']'", peek);
+ }
+ }
+ t.readSymbol("]");
+
+ } else if (s == "{") {
+
+ // Table
+ m_type = TABLE;
+ m_value = new Table<std::string, AnyVal>();
+ m_referenceCount = new int(1);
+ Table<std::string, AnyVal>& a = *(Table<std::string, AnyVal>*)m_value;
+
+ Token peek = t.peek();
+ while ((peek.type() != Token::SYMBOL) || (peek.string() != "}")) {
+
+ std::string key;
+ // Get the name
+ if (peek.type() == Token::SYMBOL) {
+ key = t.readSymbol();
+ } else if (peek.extendedType() == Token::SINGLE_QUOTED_TYPE) {
+ key = t.readString();
+ } else {
+ throw CorruptText("Expected name inside table", peek);
+ }
+
+ t.readSymbol("=");
+
+ // Avoid copying large values
+ a.set(key, AnyVal());
+ a[key].deserialize(t);
+
+ peek = t.peek();
+ if ((peek.type() != Token::SYMBOL) && (peek.extendedType() == Token::SINGLE_QUOTED_TYPE)) {
+ throw CorruptText("Missing expected name or '}'", peek);
+ }
+ }
+ t.readSymbol("}");
+
+ } else {
+ throw CorruptText("Invalid value type.", t.peek());
+ } // dispatch on symbol type
+ } // scope
+ break;
+ }
+}
+
+/*
+void AnyVal::deserialize(G3D::BinaryInput& t) {
+ alwaysAssertM(false, "TODO");
+}
+*/
+
+
+AnyVal& AnyVal::operator[](const char* key) {
+ return this->operator[]((std::string)key);
+}
+
+
+const AnyVal& AnyVal::operator[](const char* key) const {
+ return this->operator[]((std::string)key);
+}
+
+
+AnyVal& AnyVal::operator[](const std::string& key) {
+ if (m_type != TABLE) {
+ throw WrongType(TABLE, m_type);
+ }
+
+ makeMutable();
+
+ Table<std::string, AnyVal>& t = *(Table<std::string, AnyVal>*)m_value;
+
+ if (! t.containsKey(key)) {
+ t.set(key, AnyVal());
+ }
+
+ return t[key];
+}
+
+
+const AnyVal& AnyVal::operator[](const std::string& key) const {
+ if (m_type != TABLE) {
+ throw WrongType(TABLE, m_type);
+ }
+
+ const Table<std::string, AnyVal>& t = *(const Table<std::string, AnyVal>*)m_value;
+
+ if (! t.containsKey(key)) {
+ throw KeyNotFound(key);
+ }
+
+ return t[key];
+}
+
+
+void AnyVal::append(const AnyVal& v) {
+ if (m_type != ARRAY) {
+ throw WrongType(ARRAY, m_type);
+ }
+ makeMutable();
+
+ Array<AnyVal>& a = *(Array<AnyVal>*)m_value;
+ a.append(v);
+}
+
+
+void AnyVal::getKeys(Array<std::string>& keys) const {
+ if (m_type != TABLE) {
+ throw WrongType(TABLE, m_type);
+ }
+
+ const Table<std::string, AnyVal>& t = *(const Table<std::string, AnyVal>*)m_value;
+ t.getKeys(keys);
+}
+
+
+int AnyVal::size() const {
+ switch (m_type) {
+ case TABLE:
+ {
+ const Table<std::string, AnyVal>& t = *(const Table<std::string, AnyVal>*)m_value;
+ return t.size();
+ }
+
+ case ARRAY:
+ {
+ const Array<AnyVal>& a = *(Array<AnyVal>*)m_value;
+ return a.size();
+ }
+
+ default:
+ throw WrongType(ARRAY, m_type);
+ }
+}
+
+
+AnyVal& AnyVal::operator[](int i) {
+ if (m_type != ARRAY) {
+ throw WrongType(ARRAY, m_type);
+ }
+ makeMutable();
+
+ Array<AnyVal>& a = *(Array<AnyVal>*)m_value;
+
+ if (i < 0) {
+ throw IndexOutOfBounds(i, a.size());
+ }
+
+ if (a.size() <= i) {
+ a.resize(i + 1);
+ }
+
+ return a[i];
+}
+
+
+const AnyVal& AnyVal::operator[](int i) const {
+ if (m_type != ARRAY) {
+ throw WrongType(ARRAY, m_type);
+ }
+
+ const Array<AnyVal>& a = *(Array<AnyVal>*)m_value;
+
+ if (a.size() <= i || i < 0) {
+ throw IndexOutOfBounds(i, a.size());
+ }
+
+ return a[i];
+}
+
+
+void AnyVal::makeMutable() {
+ if (*m_referenceCount > 1) {
+ // This is a shared instance
+ --(*m_referenceCount);
+ m_referenceCount = new int(1);
+ m_value = copyValue();
+ }
+}
+
+bool AnyVal::boolean() const {
+ if (m_type != BOOLEAN) {
+ throw WrongType(BOOLEAN, m_type);
+ }
+
+ return *(bool*)m_value;
+}
+
+
+bool AnyVal::boolean(bool defaultVal) const {
+ if (m_type != BOOLEAN) {
+ return defaultVal;
+ }
+
+ return *(bool*)m_value;
+}
+
+
+const std::string& AnyVal::string() const {
+ if (m_type != STRING) {
+ throw WrongType(STRING, m_type);
+ }
+
+ return *(std::string*)m_value;
+}
+
+
+const std::string& AnyVal::string(const std::string& defaultVal) const {
+ if (m_type != STRING) {
+ return defaultVal;
+ } else {
+ return *(std::string*)m_value;
+ }
+}
+
+
+double AnyVal::number() const {
+ if (m_type != NUMBER) {
+ throw WrongType(NUMBER, m_type);
+ }
+
+ return *(double*)m_value;
+}
+
+
+double AnyVal::number(double defaultVal) const {
+ if (m_type != NUMBER) {
+ return defaultVal;
+ } else {
+ return *(double*)m_value;
+ }
+}
+
+
+const Rect2D& AnyVal::rect2D() const {
+ if (m_type != RECT2D) {
+ throw WrongType(RECT2D, m_type);
+ }
+
+ return *(Rect2D*)m_value;
+}
+
+
+const Rect2D& AnyVal::rect2D(const Rect2D& defaultVal) const {
+ if (m_type != RECT2D) {
+ return defaultVal;
+ } else {
+ return *(Rect2D*)m_value;
+ }
+}
+
+
+const AABox& AnyVal::aabox() const {
+ if (m_type != AABOX) {
+ throw WrongType(AABOX, m_type);
+ }
+
+ return *(AABox*)m_value;
+}
+
+
+const AABox& AnyVal::aabox(const AABox& defaultVal) const {
+ if (m_type != AABOX) {
+ return defaultVal;
+ } else {
+ return *(AABox*)m_value;
+ }
+}
+
+
+const Color1& AnyVal::color1() const {
+ if (m_type != COLOR1) {
+ throw WrongType(COLOR1, m_type);
+ }
+
+ return *(Color1*)m_value;
+}
+
+
+const Color1& AnyVal::color1(const Color1& defaultVal) const {
+ if (m_type != COLOR1) {
+ return defaultVal;
+ } else {
+ return *(Color1*)m_value;
+ }
+}
+
+
+const Color3& AnyVal::color3() const {
+ if (m_type != COLOR3) {
+ throw WrongType(COLOR3, m_type);
+ }
+
+ return *(Color3*)m_value;
+}
+
+
+const Color3& AnyVal::color3(const Color3& defaultVal) const {
+ if (m_type != COLOR3) {
+ return defaultVal;
+ } else {
+ return *(Color3*)m_value;
+ }
+}
+
+
+const Color4& AnyVal::color4() const {
+ if (m_type != COLOR4) {
+ throw WrongType(COLOR4, m_type);
+ }
+
+ return *(Color4*)m_value;
+}
+
+
+const Color4& AnyVal::color4(const Color4& defaultVal) const {
+ if (m_type != COLOR4) {
+ return defaultVal;
+ } else {
+ return *(Color4*)m_value;
+ }
+}
+
+
+const Vector2& AnyVal::vector2() const {
+ if (m_type != VECTOR2) {
+ throw WrongType(VECTOR2, m_type);
+ }
+
+ return *(Vector2*)m_value;
+}
+
+
+const Vector2& AnyVal::vector2(const Vector2& defaultVal) const {
+ if (m_type != VECTOR2) {
+ return defaultVal;
+ } else {
+ return *(Vector2*)m_value;
+ }
+}
+
+
+const Vector3& AnyVal::vector3() const {
+ if (m_type != VECTOR3) {
+ throw WrongType(VECTOR3, m_type);
+ }
+
+ return *(Vector3*)m_value;
+}
+
+
+const Vector3& AnyVal::vector3(const Vector3& defaultVal) const {
+ if (m_type != VECTOR3) {
+ return defaultVal;
+ } else {
+ return *(Vector3*)m_value;
+ }
+}
+
+
+const Vector4& AnyVal::vector4() const {
+ if (m_type != VECTOR4) {
+ throw WrongType(VECTOR4, m_type);
+ }
+
+ return *(Vector4*)m_value;
+}
+
+
+const Vector4& AnyVal::vector4(const Vector4& defaultVal) const {
+ if (m_type != VECTOR4) {
+ return defaultVal;
+ } else {
+ return *(Vector4*)m_value;
+ }
+}
+
+
+const CoordinateFrame& AnyVal::coordinateFrame() const {
+ if (m_type != COORDINATEFRAME) {
+ throw WrongType(COORDINATEFRAME, m_type);
+ }
+
+ return *(CoordinateFrame*)m_value;
+}
+
+
+const CoordinateFrame& AnyVal::coordinateFrame(const CoordinateFrame& defaultVal) const {
+ if (m_type != COORDINATEFRAME) {
+ return defaultVal;
+ } else {
+ return *(CoordinateFrame*)m_value;
+ }
+}
+
+const Matrix2& AnyVal::matrix2(const Matrix2& defaultVal) const {
+ if (m_type != MATRIX2) {
+ return defaultVal;
+ } else {
+ return *(Matrix2*)m_value;
+ }
+}
+
+
+const Matrix2& AnyVal::matrix2() const {
+ if (m_type != MATRIX2) {
+ throw WrongType(MATRIX2, m_type);
+ }
+
+ return *(Matrix2*)m_value;
+}
+
+
+const Matrix3& AnyVal::matrix3(const Matrix3& defaultVal) const {
+ if (m_type != MATRIX3) {
+ return defaultVal;
+ } else {
+ return *(Matrix3*)m_value;
+ }
+}
+
+
+const Matrix3& AnyVal::matrix3() const {
+ if (m_type != MATRIX3) {
+ throw WrongType(MATRIX3, m_type);
+ }
+
+ return *(Matrix3*)m_value;
+}
+
+
+const Matrix4& AnyVal::matrix4(const Matrix4& defaultVal) const {
+ if (m_type != MATRIX4) {
+ return defaultVal;
+ } else {
+ return *(Matrix4*)m_value;
+ }
+}
+
+
+const Matrix4& AnyVal::matrix4() const {
+ if (m_type != MATRIX4) {
+ throw WrongType(MATRIX4, m_type);
+ }
+
+ return *(Matrix4*)m_value;
+}
+
+
+const Quat& AnyVal::quat(const Quat& defaultVal) const {
+ if (m_type != QUAT) {
+ return defaultVal;
+ } else {
+ return *(Quat*)m_value;
+ }
+}
+
+
+const Quat& AnyVal::quat() const {
+ if (m_type != QUAT) {
+ throw WrongType(QUAT, m_type);
+ }
+
+ return *(Quat*)m_value;
+}
+
+
+const AnyVal& AnyVal::get(const std::string& key, const AnyVal& defaultVal) const {
+ if (m_type != TABLE) {
+ return defaultVal;
+ }
+
+ const Table<std::string, AnyVal>& t = *(const Table<std::string, AnyVal>*)m_value;
+
+ if (t.containsKey(key)) {
+ return t[key];
+ } else {
+ return defaultVal;
+ }
+}
+
+
+const AnyVal& AnyVal::get(const std::string& key) const {
+ if (m_type != TABLE) {
+ throw WrongType(TABLE, m_type);
+ }
+
+ const Table<std::string, AnyVal>& t = *(const Table<std::string, AnyVal>*)m_value;
+
+ if (t.containsKey(key)) {
+ return t[key];
+ } else {
+ throw KeyNotFound(key);
+ }
+}
+
+
+const AnyVal& AnyVal::get(int i, const AnyVal& defaultVal) const {
+ if (m_type != ARRAY) {
+ return defaultVal;
+ }
+
+ const Array<AnyVal>& a = *(const Array<AnyVal>*)m_value;
+
+ if ((i >= 0) && (i < a.size())) {
+ return a[i];
+ } else {
+ return defaultVal;
+ }
+}
+
+
+const AnyVal& AnyVal::get(int i) const {
+ if (m_type != ARRAY) {
+ throw WrongType(ARRAY, m_type);
+ }
+
+ const Array<AnyVal>& a = *(const Array<AnyVal>*)m_value;
+
+ if ((i >= 0) && (i < a.size())) {
+ return a[i];
+ } else {
+ throw IndexOutOfBounds(i, a.size());
+ }
+}
+
+}
diff --git a/externals/g3dlite/G3D.lib/source/BinaryFormat.cpp b/externals/g3dlite/G3D.lib/source/BinaryFormat.cpp
new file mode 100644
index 00000000000..a30ec0644ea
--- /dev/null
+++ b/externals/g3dlite/G3D.lib/source/BinaryFormat.cpp
@@ -0,0 +1,81 @@
+/**
+ @file BinaryFormat.cpp
+ @maintainer Morgan McGuire, matrix@graphics3d.com
+
+ @created 2005-06-10
+ @edited 2005-06-10
+ */
+
+#include "G3D/BinaryFormat.h"
+
+namespace G3D {
+
+int32 byteSize(BinaryFormat f) {
+ switch (f) {
+ case BOOL8_BINFMT:
+ case UINT8_BINFMT:
+ case INT8_BINFMT:
+ return 1;
+
+ case UINT16_BINFMT:
+ case INT16_BINFMT:
+ return 2;
+
+ case FLOAT16_BINFMT:
+ return 2;
+
+ case UINT32_BINFMT:
+ case INT32_BINFMT:
+ case FLOAT32_BINFMT:
+ return 4;
+
+ case FLOAT64_BINFMT:
+ case UINT64_BINFMT:
+ case INT64_BINFMT:
+ return 8;
+
+ case INT128_BINFMT:
+ case UINT128_BINFMT:
+ return 16;
+
+ case VECTOR2_BINFMT:
+ return 2 * 4;
+
+ case VECTOR2INT16_BINFMT:
+ return 2 * 2;
+
+ case VECTOR3_BINFMT:
+ return 3 * 4;
+
+ case VECTOR3INT16_BINFMT:
+ return 3 * 2;
+
+ case VECTOR4_BINFMT:
+ return 4 * 4;
+
+ case VECTOR4INT16_BINFMT:
+ return 4 * 4;
+
+ case COLOR3_BINFMT:
+ return 3 * 4;
+
+ case COLOR3UINT8_BINFMT:
+ return 3 * 1;
+
+ case COLOR3INT16_BINFMT:
+ return 3 * 2;
+
+ case COLOR4_BINFMT:
+ return 4 * 4;
+
+ case COLOR4UINT8_BINFMT:
+ return 4 * 1;
+
+ case COLOR4INT16_BINFMT:
+ return 4 * 2;
+
+ default:
+ return -1;
+ }
+}
+}
diff --git a/externals/g3dlite/G3D.lib/source/BinaryInput.cpp b/externals/g3dlite/G3D.lib/source/BinaryInput.cpp
new file mode 100644
index 00000000000..65a9976fe04
--- /dev/null
+++ b/externals/g3dlite/G3D.lib/source/BinaryInput.cpp
@@ -0,0 +1,568 @@
+/**
+ @file BinaryInput.cpp
+
+ @author Morgan McGuire, graphics3d.com
+ Copyright 2001-2007, Morgan McGuire. All rights reserved.
+
+ @created 2001-08-09
+ @edited 2005-02-24
+
+
+ <PRE>
+ {
+ BinaryOutput b("c:/tmp/test.b", BinaryOutput::LITTLE_ENDIAN);
+
+ float f = 3.1415926;
+ int i = 1027221;
+ std::string s = "Hello World!";
+
+ b.writeFloat32(f);
+ b.writeInt32(i);
+ b.writeString(s);
+ b.commit();
+
+
+ BinaryInput in("c:/tmp/test.b", BinaryInput::LITTLE_ENDIAN);
+
+ debugAssert(f == in.readFloat32());
+ int ii = in.readInt32();
+ debugAssert(i == ii);
+ debugAssert(s == in.readString());
+ }
+ </PRE>
+ */
+
+#include "G3D/platform.h"
+#include "G3D/BinaryInput.h"
+#include "G3D/Array.h"
+#include "G3D/fileutils.h"
+#include "G3D/Log.h"
+#include <zlib.h>
+
+#include <cstring>
+
+namespace G3D {
+
+void BinaryInput::readBool8(std::vector<bool>& out, int64 n) {
+ out.resize((int)n);
+ // std::vector optimizes bool in a way that prevents fast reading
+ for (int64 i = 0; i < n ; ++i) {
+ out[i] = readBool8();
+ }
+}
+
+
+void BinaryInput::readBool8(Array<bool>& out, int64 n) {
+ out.resize(n);
+ readBool8(out.begin(), n);
+}
+
+
+#define IMPLEMENT_READER(ucase, lcase)\
+void BinaryInput::read##ucase(std::vector<lcase>& out, int64 n) {\
+ out.resize(n);\
+ read##ucase(&out[0], n);\
+}\
+\
+\
+void BinaryInput::read##ucase(Array<lcase>& out, int64 n) {\
+ out.resize(n);\
+ read##ucase(out.begin(), n);\
+}
+
+
+IMPLEMENT_READER(UInt8, uint8)
+IMPLEMENT_READER(Int8, int8)
+IMPLEMENT_READER(UInt16, uint16)
+IMPLEMENT_READER(Int16, int16)
+IMPLEMENT_READER(UInt32, uint32)
+IMPLEMENT_READER(Int32, int32)
+IMPLEMENT_READER(UInt64, uint64)
+IMPLEMENT_READER(Int64, int64)
+IMPLEMENT_READER(Float32, float32)
+IMPLEMENT_READER(Float64, float64)
+
+#undef IMPLEMENT_READER
+
+// Data structures that are one byte per element can be
+// directly copied, regardles of endian-ness.
+#define IMPLEMENT_READER(ucase, lcase)\
+void BinaryInput::read##ucase(lcase* out, int64 n) {\
+ if (sizeof(lcase) == 1) {\
+ readBytes(out, n);\
+ } else {\
+ for (int64 i = 0; i < n ; ++i) {\
+ out[i] = read##ucase();\
+ }\
+ }\
+}
+
+IMPLEMENT_READER(Bool8, bool)
+IMPLEMENT_READER(UInt8, uint8)
+IMPLEMENT_READER(Int8, int8)
+
+#undef IMPLEMENT_READER
+
+
+#define IMPLEMENT_READER(ucase, lcase)\
+void BinaryInput::read##ucase(lcase* out, int64 n) {\
+ if (m_swapBytes) {\
+ for (int64 i = 0; i < n; ++i) {\
+ out[i] = read##ucase();\
+ }\
+ } else {\
+ readBytes(out, sizeof(lcase) * n);\
+ }\
+}
+
+
+IMPLEMENT_READER(UInt16, uint16)
+IMPLEMENT_READER(Int16, int16)
+IMPLEMENT_READER(UInt32, uint32)
+IMPLEMENT_READER(Int32, int32)
+IMPLEMENT_READER(UInt64, uint64)
+IMPLEMENT_READER(Int64, int64)
+IMPLEMENT_READER(Float32, float32)
+IMPLEMENT_READER(Float64, float64)
+
+#undef IMPLEMENT_READER
+
+void BinaryInput::loadIntoMemory(int64 startPosition, int64 minLength) {
+ // Load the next section of the file
+ debugAssertM(m_filename != "<memory>", "Read past end of file.");
+
+ int64 absPos = m_alreadyRead + m_pos;
+
+ if (m_bufferLength < minLength) {
+ // The current buffer isn't big enough to hold the chunk we want to read.
+ // This happens if there was little memory available during the initial constructor
+ // read but more memory has since been freed.
+ m_bufferLength = minLength;
+ debugAssert(m_freeBuffer);
+ m_buffer = (uint8*)System::realloc(m_buffer, m_bufferLength);
+ if (m_buffer == NULL) {
+ throw "Tried to read a larger memory chunk than could fit in memory. (2)";
+ }
+ }
+
+ m_alreadyRead = startPosition;
+
+# ifdef G3D_WIN32
+ FILE* file = fopen(m_filename.c_str(), "rb");
+ debugAssert(file);
+ int ret = fseek(file, (off_t)m_alreadyRead, SEEK_SET);
+ debugAssert(ret == 0);
+ size_t toRead = (size_t)G3D::min(m_bufferLength, m_length - m_alreadyRead);
+ ret = fread(m_buffer, 1, toRead, file);
+ debugAssert(ret == toRead);
+ fclose(file);
+ file = NULL;
+
+# else
+ FILE* file = fopen(m_filename.c_str(), "rb");
+ debugAssert(file);
+ int ret = fseeko(file, (off_t)m_alreadyRead, SEEK_SET);
+ debugAssert(ret == 0);
+ size_t toRead = (size_t)G3D::min<int64>(m_bufferLength, m_length - m_alreadyRead);
+ ret = fread(m_buffer, 1, toRead, file);
+ debugAssert((size_t)ret == (size_t)toRead);
+ fclose(file);
+ file = NULL;
+# endif
+
+ m_pos = absPos - m_alreadyRead;
+ debugAssert(m_pos >= 0);
+}
+
+
+
+const bool BinaryInput::NO_COPY = false;
+
+static bool needSwapBytes(G3DEndian fileEndian) {
+ return (fileEndian != System::machineEndian());
+}
+
+
+/** Helper used by the constructors for decompression */
+static uint32 readUInt32(const uint8* data, bool swapBytes) {
+ if (swapBytes) {
+ uint8 out[4];
+ out[0] = data[3];
+ out[1] = data[2];
+ out[2] = data[1];
+ out[3] = data[0];
+ return *((uint32*)out);
+ } else {
+ return *((uint32*)data);
+ }
+}
+
+
+void BinaryInput::setEndian(G3DEndian e) {
+ m_fileEndian = e;
+ m_swapBytes = needSwapBytes(m_fileEndian);
+}
+
+
+BinaryInput::BinaryInput(
+ const uint8* data,
+ int64 dataLen,
+ G3DEndian dataEndian,
+ bool compressed,
+ bool copyMemory) :
+ m_filename("<memory>"),
+ m_bitPos(0),
+ m_bitString(0),
+ m_beginEndBits(0),
+ m_alreadyRead(0),
+ m_bufferLength(0),
+ m_pos(0) {
+
+ m_freeBuffer = copyMemory || compressed;
+
+ setEndian(dataEndian);
+
+ if (compressed) {
+ // Read the decompressed size from the first 4 bytes
+ m_length = G3D::readUInt32(data, m_swapBytes);
+
+ debugAssert(m_freeBuffer);
+ m_buffer = (uint8*)System::alignedMalloc(m_length, 16);
+
+ unsigned long L = m_length;
+ // Decompress with zlib
+ int64 result = uncompress(m_buffer, (unsigned long*)&L, data + 4, dataLen - 4);
+ m_length = L;
+ m_bufferLength = L;
+ debugAssert(result == Z_OK); (void)result;
+
+ } else {
+ m_length = dataLen;
+ m_bufferLength = m_length;
+ if (! copyMemory) {
+ debugAssert(!m_freeBuffer);
+ m_buffer = const_cast<uint8*>(data);
+ } else {
+ debugAssert(m_freeBuffer);
+ m_buffer = (uint8*)System::alignedMalloc(m_length, 16);
+ System::memcpy(m_buffer, data, dataLen);
+ }
+ }
+}
+
+
+BinaryInput::BinaryInput(
+ const std::string& filename,
+ G3DEndian fileEndian,
+ bool compressed) :
+ m_filename(filename),
+ m_bitPos(0),
+ m_bitString(0),
+ m_beginEndBits(0),
+ m_alreadyRead(0),
+ m_length(0),
+ m_bufferLength(0),
+ m_buffer(NULL),
+ m_pos(0),
+ m_freeBuffer(true) {
+
+ setEndian(fileEndian);
+
+ // Update global file tracker
+ _internal::currentFilesUsed.insert(m_filename);
+
+
+ if (! fileExists(m_filename, false)) {
+ std::string zipfile;
+ std::string internalfile;
+ if (zipfileExists(m_filename, zipfile, internalfile)) {
+ // Load from zipfile
+ void* v;
+ size_t s;
+ zipRead(filename, v, s);
+ m_buffer = reinterpret_cast<uint8*>(v);
+ m_bufferLength = m_length = s;
+ if (compressed) {
+ decompress();
+ }
+ m_freeBuffer = true;
+ } else {
+ Log::common()->printf("Warning: File not found: %s\n", m_filename.c_str());
+ }
+ return;
+ }
+
+ // Figure out how big the file is and verify that it exists.
+ m_length = fileLength(m_filename);
+
+ // Read the file into memory
+ FILE* file = fopen(m_filename.c_str(), "rb");
+
+ if (! file || (m_length == -1)) {
+ throw format("File not found: \"%s\"", m_filename.c_str());
+ return;
+ }
+
+ if (! compressed && (m_length > INITIAL_BUFFER_LENGTH)) {
+ // Read only a subset of the file so we don't consume
+ // all available memory.
+ m_bufferLength = INITIAL_BUFFER_LENGTH;
+ } else {
+ // Either the length is fine or the file is compressed
+ // and requires us to read the whole thing for zlib.
+ m_bufferLength = m_length;
+ }
+
+ debugAssert(m_freeBuffer);
+ m_buffer = (uint8*)System::alignedMalloc(m_bufferLength, 16);
+ if (m_buffer == NULL) {
+ if (compressed) {
+ throw "Not enough memory to load compressed file. (1)";
+ }
+
+ // Try to allocate a small array; not much memory is available.
+ // Give up if we can't allocate even 1k.
+ while ((m_buffer == NULL) && (m_bufferLength > 1024)) {
+ m_bufferLength /= 2;
+ m_buffer = (uint8*)System::alignedMalloc(m_bufferLength, 16);
+ }
+ }
+ debugAssert(m_buffer);
+
+ fread(m_buffer, m_bufferLength, sizeof(int8), file);
+ fclose(file);
+ file = NULL;
+
+ if (compressed) {
+ if (m_bufferLength != m_length) {
+ throw "Not enough memory to load compressed file. (2)";
+ }
+
+ decompress();
+ }
+}
+
+void BinaryInput::decompress() {
+ // Decompress
+ // Use the existing buffer as the source, allocate
+ // a new buffer to use as the destination.
+
+ int64 tempLength = m_length;
+ m_length = G3D::readUInt32(m_buffer, m_swapBytes);
+
+ // The file couldn't have better than 500:1 compression
+ alwaysAssertM(m_length < m_bufferLength * 500, "Compressed file header is corrupted");
+
+ uint8* tempBuffer = m_buffer;
+ m_buffer = (uint8*)System::alignedMalloc(m_length, 16);
+
+ debugAssert(m_buffer);
+ debugAssert(isValidHeapPointer(tempBuffer));
+ debugAssert(isValidHeapPointer(m_buffer));
+
+ unsigned long L = m_length;
+ int64 result = uncompress(m_buffer, &L, tempBuffer + 4, tempLength - 4);
+ m_length = L;
+ m_bufferLength = m_length;
+
+ debugAssertM(result == Z_OK, "BinaryInput/zlib detected corruption in " + m_filename);
+ (void)result;
+
+ System::alignedFree(tempBuffer);
+}
+
+
+void BinaryInput::readBytes(void* bytes, int64 n) {
+ prepareToRead(n);
+ debugAssert(isValidPointer(bytes));
+
+ memcpy(bytes, m_buffer + m_pos, n);
+ m_pos += n;
+}
+
+
+BinaryInput::~BinaryInput() {
+
+ if (m_freeBuffer) {
+ System::alignedFree(m_buffer);
+ }
+ m_buffer = NULL;
+}
+
+
+uint64 BinaryInput::readUInt64() {
+ prepareToRead(8);
+ uint8 out[8];
+
+ if (m_swapBytes) {
+ out[0] = m_buffer[m_pos + 7];
+ out[1] = m_buffer[m_pos + 6];
+ out[2] = m_buffer[m_pos + 5];
+ out[3] = m_buffer[m_pos + 4];
+ out[4] = m_buffer[m_pos + 3];
+ out[5] = m_buffer[m_pos + 2];
+ out[6] = m_buffer[m_pos + 1];
+ out[7] = m_buffer[m_pos + 0];
+ } else {
+ *(uint64*)out = *(uint64*)(m_buffer + m_pos);
+ }
+
+ m_pos += 8;
+ return *(uint64*)out;
+}
+
+
+std::string BinaryInput::readString(int64 n) {
+ prepareToRead(n);
+ debugAssertM((m_pos + n) <= m_length, "Read past end of file");
+
+ char *s = (char*)System::alignedMalloc(n + 1, 16);
+ assert(s != NULL);
+
+ memcpy(s, m_buffer + m_pos, n);
+ // There may not be a null, so make sure
+ // we add one.
+ s[n] = '\0';
+
+ std::string out = s;
+ System::alignedFree(s);
+ s = NULL;
+
+ m_pos += n;
+
+ return out;
+
+}
+
+
+std::string BinaryInput::readString() {
+ int64 n = 0;
+
+ if ((m_pos + m_alreadyRead + n) < (m_length - 1)) {
+ prepareToRead(1);
+ }
+
+ if ( ((m_pos + m_alreadyRead + n) < (m_length - 1)) &&
+ (m_buffer[m_pos + n] != '\0')) {
+
+ ++n;
+ while ( ((m_pos + m_alreadyRead + n) < (m_length - 1)) &&
+ (m_buffer[m_pos + n] != '\0')) {
+
+ prepareToRead(1);
+ ++n;
+ }
+ }
+
+ // Consume NULL
+ ++n;
+
+ return readString(n);
+}
+
+
+std::string BinaryInput::readStringEven() {
+ std::string x = readString();
+ if (hasMore() && (G3D::isOdd(x.length() + 1))) {
+ skip(1);
+ }
+ return x;
+}
+
+
+std::string BinaryInput::readString32() {
+ int len = readUInt32();
+ return readString(len);
+}
+
+
+Vector4 BinaryInput::readVector4() {
+ float x = readFloat32();
+ float y = readFloat32();
+ float z = readFloat32();
+ float w = readFloat32();
+ return Vector4(x, y, z, w);
+}
+
+
+Vector3 BinaryInput::readVector3() {
+ float x = readFloat32();
+ float y = readFloat32();
+ float z = readFloat32();
+ return Vector3(x, y, z);
+}
+
+
+Vector2 BinaryInput::readVector2() {
+ float x = readFloat32();
+ float y = readFloat32();
+ return Vector2(x, y);
+}
+
+
+Color4 BinaryInput::readColor4() {
+ float r = readFloat32();
+ float g = readFloat32();
+ float b = readFloat32();
+ float a = readFloat32();
+ return Color4(r, g, b, a);
+}
+
+
+Color3 BinaryInput::readColor3() {
+ float r = readFloat32();
+ float g = readFloat32();
+ float b = readFloat32();
+ return Color3(r, g, b);
+}
+
+
+void BinaryInput::beginBits() {
+ debugAssert(m_beginEndBits == 0);
+ m_beginEndBits = 1;
+ m_bitPos = 0;
+
+ debugAssertM(hasMore(), "Can't call beginBits when at the end of a file");
+ m_bitString = readUInt8();
+}
+
+
+uint32 BinaryInput::readBits(int numBits) {
+ debugAssert(m_beginEndBits == 1);
+
+ uint32 out = 0;
+
+ const int total = numBits;
+ while (numBits > 0) {
+ if (m_bitPos > 7) {
+ // Consume a new byte for reading. We do this at the beginning
+ // of the loop so that we don't try to read past the end of the file.
+ m_bitPos = 0;
+ m_bitString = readUInt8();
+ }
+
+ // Slide the lowest bit of the bitString into
+ // the correct position.
+ out |= (m_bitString & 1) << (total - numBits);
+
+ // Shift over to the next bit
+ m_bitString = m_bitString >> 1;
+ ++m_bitPos;
+ --numBits;
+ }
+
+ return out;
+}
+
+
+void BinaryInput::endBits() {
+ debugAssert(m_beginEndBits == 1);
+ if (m_bitPos == 0) {
+ // Put back the last byte we read
+ --m_pos;
+ }
+ m_beginEndBits = 0;
+ m_bitPos = 0;
+}
+
+}
diff --git a/externals/g3dlite/G3D.lib/source/BinaryOutput.cpp b/externals/g3dlite/G3D.lib/source/BinaryOutput.cpp
new file mode 100644
index 00000000000..8fcc30e548f
--- /dev/null
+++ b/externals/g3dlite/G3D.lib/source/BinaryOutput.cpp
@@ -0,0 +1,518 @@
+/**
+ @file BinaryOutput.cpp
+
+ @author Morgan McGuire, graphics3d.com
+ Copyright 2002-2007, Morgan McGuire, All rights reserved.
+
+ @created 2002-02-20
+ @edited 2008-01-07
+ */
+
+#include "G3D/platform.h"
+#include "G3D/BinaryOutput.h"
+#include "G3D/fileutils.h"
+#include "G3D/stringutils.h"
+#include "G3D/Array.h"
+#include <zlib.h>
+
+#include <cstring>
+
+// Largest memory buffer that the system will use for writing to
+// disk. After this (or if the system runs out of memory)
+// chunks of the file will be dumped to disk.
+//
+// Currently 400 MB
+#define MAX_BINARYOUTPUT_BUFFER_SIZE 400000000
+
+namespace G3D {
+
+void BinaryOutput::writeBool8(const std::vector<bool>& out, int n) {
+ for (int i = 0; i < n; ++i) {
+ writeBool8(out[i]);
+ }
+}
+
+
+void BinaryOutput::writeBool8(const Array<bool>& out, int n) {
+ writeBool8(out.getCArray(), n);
+}
+
+#define IMPLEMENT_WRITER(ucase, lcase)\
+void BinaryOutput::write##ucase(const std::vector<lcase>& out, int n) {\
+ write##ucase(&out[0], n);\
+}\
+\
+\
+void BinaryOutput::write##ucase(const Array<lcase>& out, int n) {\
+ write##ucase(out.getCArray(), n);\
+}
+
+
+IMPLEMENT_WRITER(UInt8, uint8)
+IMPLEMENT_WRITER(Int8, int8)
+IMPLEMENT_WRITER(UInt16, uint16)
+IMPLEMENT_WRITER(Int16, int16)
+IMPLEMENT_WRITER(UInt32, uint32)
+IMPLEMENT_WRITER(Int32, int32)
+IMPLEMENT_WRITER(UInt64, uint64)
+IMPLEMENT_WRITER(Int64, int64)
+IMPLEMENT_WRITER(Float32, float32)
+IMPLEMENT_WRITER(Float64, float64)
+
+#undef IMPLEMENT_WRITER
+
+// Data structures that are one byte per element can be
+// directly copied, regardles of endian-ness.
+#define IMPLEMENT_WRITER(ucase, lcase)\
+void BinaryOutput::write##ucase(const lcase* out, int n) {\
+ if (sizeof(lcase) == 1) {\
+ writeBytes((void*)out, n);\
+ } else {\
+ for (int i = 0; i < n ; ++i) {\
+ write##ucase(out[i]);\
+ }\
+ }\
+}
+
+IMPLEMENT_WRITER(Bool8, bool)
+IMPLEMENT_WRITER(UInt8, uint8)
+IMPLEMENT_WRITER(Int8, int8)
+
+#undef IMPLEMENT_WRITER
+
+
+#define IMPLEMENT_WRITER(ucase, lcase)\
+void BinaryOutput::write##ucase(const lcase* out, int n) {\
+ if (m_swapBytes) {\
+ for (int i = 0; i < n; ++i) {\
+ write##ucase(out[i]);\
+ }\
+ } else {\
+ writeBytes((const void*)out, sizeof(lcase) * n);\
+ }\
+}
+
+
+IMPLEMENT_WRITER(UInt16, uint16)
+IMPLEMENT_WRITER(Int16, int16)
+IMPLEMENT_WRITER(UInt32, uint32)
+IMPLEMENT_WRITER(Int32, int32)
+IMPLEMENT_WRITER(UInt64, uint64)
+IMPLEMENT_WRITER(Int64, int64)
+IMPLEMENT_WRITER(Float32, float32)
+IMPLEMENT_WRITER(Float64, float64)
+
+#undef IMPLEMENT_WRITER
+
+
+void BinaryOutput::reallocBuffer(size_t bytes, size_t oldBufferLen) {
+ //debugPrintf("reallocBuffer(%d, %d)\n", bytes, oldBufferLen);
+
+ size_t newBufferLen = (int)(m_bufferLen * 1.5) + 100;
+ uint8* newBuffer = NULL;
+
+ if ((m_filename == "<memory>") || (newBufferLen < MAX_BINARYOUTPUT_BUFFER_SIZE)) {
+ // We're either writing to memory (in which case we *have* to try and allocate)
+ // or we've been asked to allocate a reasonable size buffer.
+
+ //debugPrintf(" realloc(%d)\n", newBufferLen);
+ newBuffer = (uint8*)System::realloc(m_buffer, newBufferLen);
+ if (newBuffer != NULL) {
+ m_maxBufferLen = newBufferLen;
+ }
+ }
+
+ if ((newBuffer == NULL) && (bytes > 0)) {
+ // Realloc failed; we're probably out of memory. Back out
+ // the entire call and try to dump some data to disk.
+ m_bufferLen = oldBufferLen;
+ reserveBytesWhenOutOfMemory(bytes);
+ } else {
+ m_buffer = newBuffer;
+ debugAssert(isValidHeapPointer(m_buffer));
+ }
+}
+
+
+void BinaryOutput::reserveBytesWhenOutOfMemory(size_t bytes) {
+ if (m_filename == "<memory>") {
+ throw "Out of memory while writing to memory in BinaryOutput (no RAM left).";
+ } else if ((int)bytes > (int)m_maxBufferLen) {
+ throw "Out of memory while writing to disk in BinaryOutput (could not create a large enough buffer).";
+ } else {
+
+ // Dump the contents to disk. In order to enable seeking backwards,
+ // we keep the last 10 MB in memory.
+ int writeBytes = m_bufferLen - 10 * 1024 * 1024;
+
+ if (writeBytes < m_bufferLen / 3) {
+ // We're going to write less than 1/3 of the file;
+ // give up and just write the whole thing.
+ writeBytes = m_bufferLen;
+ }
+ debugAssert(writeBytes > 0);
+
+ //debugPrintf("Writing %d bytes to disk\n", writeBytes);
+
+ const char* mode = (m_alreadyWritten > 0) ? "ab" : "wb";
+ FILE* file = fopen(m_filename.c_str(), mode);
+ debugAssert(file);
+
+ size_t count = fwrite(m_buffer, 1, writeBytes, file);
+ debugAssert((int)count == writeBytes); (void)count;
+
+ fclose(file);
+ file = NULL;
+
+ // Record that we saved this data.
+ m_alreadyWritten += writeBytes;
+ m_bufferLen -= writeBytes;
+ m_pos -= writeBytes;
+
+ debugAssert(m_bufferLen < m_maxBufferLen);
+ debugAssert(m_bufferLen >= 0);
+ debugAssert(m_pos >= 0);
+ debugAssert(m_pos <= m_bufferLen);
+
+ // Shift the unwritten data back appropriately in the buffer.
+ debugAssert(isValidHeapPointer(m_buffer));
+ System::memcpy(m_buffer, m_buffer + writeBytes, m_bufferLen);
+ debugAssert(isValidHeapPointer(m_buffer));
+
+ // *now* we allocate bytes (there should presumably be enough
+ // space in the buffer; if not, we'll come back through this
+ // code and dump the last 10MB to disk as well. Note that the
+ // bytes > maxBufferLen case above would already have triggered
+ // if this call couldn't succeed.
+ reserveBytes(bytes);
+ }
+}
+
+
+BinaryOutput::BinaryOutput() {
+ m_alreadyWritten = 0;
+ m_swapBytes = false;
+ m_pos = 0;
+ m_filename = "<memory>";
+ m_buffer = NULL;
+ m_bufferLen = 0;
+ m_maxBufferLen = 0;
+ m_beginEndBits = 0;
+ m_bitString = 0;
+ m_bitPos = 0;
+ m_ok = true;
+ m_committed = false;
+}
+
+
+BinaryOutput::BinaryOutput(
+ const std::string& filename,
+ G3DEndian fileEndian) {
+
+ m_pos = 0;
+ m_alreadyWritten = 0;
+ setEndian(fileEndian);
+ m_filename = filename;
+ m_buffer = NULL;
+ m_bufferLen = 0;
+ m_maxBufferLen = 0;
+ m_beginEndBits = 0;
+ m_bitString = 0;
+ m_bitPos = 0;
+ m_committed = false;
+
+ m_ok = true;
+ /** Verify ability to write to disk */
+ commit(false);
+ m_committed = false;
+}
+
+
+void BinaryOutput::reset() {
+ debugAssert(m_beginEndBits == 0);
+ alwaysAssertM(m_filename == "<memory>",
+ "Can only reset a BinaryOutput that writes to memory.");
+
+ // Do not reallocate, just clear the size of the buffer.
+ m_pos = 0;
+ m_alreadyWritten = 0;
+ m_bufferLen = 0;
+ m_beginEndBits = 0;
+ m_bitString = 0;
+ m_bitPos = 0;
+ m_committed = false;
+}
+
+
+BinaryOutput::~BinaryOutput() {
+ debugAssert((m_buffer == NULL) || isValidHeapPointer(m_buffer));
+ System::free(m_buffer);
+ m_buffer = NULL;
+ m_bufferLen = 0;
+ m_maxBufferLen = 0;
+}
+
+
+void BinaryOutput::setEndian(G3DEndian fileEndian) {
+ m_fileEndian = fileEndian;
+ m_swapBytes = (fileEndian != System::machineEndian());
+}
+
+
+bool BinaryOutput::ok() const {
+ return m_ok;
+}
+
+
+void BinaryOutput::compress() {
+ if (m_alreadyWritten > 0) {
+ throw "Cannot compress huge files (part of this file has already been written to disk).";
+ }
+
+ // Old buffer size
+ int L = m_bufferLen;
+ uint8* convert = (uint8*)&L;
+
+ // Zlib requires the output buffer to be this big
+ unsigned long newSize = iCeil(m_bufferLen * 1.01) + 12;
+ uint8* temp = (uint8*)System::malloc(newSize);
+ int result = compress2(temp, &newSize, m_buffer, m_bufferLen, 9);
+
+ debugAssert(result == Z_OK); (void)result;
+
+ // Write the header
+ if (m_swapBytes) {
+ m_buffer[0] = convert[3];
+ m_buffer[1] = convert[2];
+ m_buffer[2] = convert[1];
+ m_buffer[3] = convert[0];
+ } else {
+ m_buffer[0] = convert[0];
+ m_buffer[1] = convert[1];
+ m_buffer[2] = convert[2];
+ m_buffer[3] = convert[3];
+ }
+
+ // Write the data
+ if ((int64)newSize + 4 > (int64)m_maxBufferLen) {
+ m_maxBufferLen = newSize + 4;
+ m_buffer = (uint8*)System::realloc(m_buffer, m_maxBufferLen);
+ }
+ m_bufferLen = newSize + 4;
+ System::memcpy(m_buffer + 4, temp, newSize);
+ m_pos = m_bufferLen;
+
+ System::free(temp);
+}
+
+
+void BinaryOutput::commit(bool flush) {
+ debugAssertM(! m_committed, "Cannot commit twice");
+ m_committed = true;
+ debugAssertM(m_beginEndBits == 0, "Missing endBits before commit");
+
+ // Make sure the directory exists.
+ std::string root, base, ext, path;
+ Array<std::string> pathArray;
+ parseFilename(m_filename, root, pathArray, base, ext);
+
+ path = root + stringJoin(pathArray, '/');
+ if (! fileExists(path, false)) {
+ createDirectory(path);
+ }
+
+ const char* mode = (m_alreadyWritten > 0) ? "ab" : "wb";
+
+ FILE* file = fopen(m_filename.c_str(), mode);
+
+ m_ok = (file != NULL) && m_ok;
+
+ if (m_ok) {
+ debugAssertM(file, std::string("Could not open '") + m_filename + "'");
+
+ m_alreadyWritten += m_bufferLen;
+
+ fwrite(m_buffer, m_bufferLen, 1, file);
+ if (flush) {
+ fflush(file);
+ }
+ fclose(file);
+ file = NULL;
+ }
+}
+
+
+void BinaryOutput::commit(
+ uint8* out) {
+ debugAssertM(! m_committed, "Cannot commit twice");
+ m_committed = true;
+
+ System::memcpy(out, m_buffer, m_bufferLen);
+}
+
+
+void BinaryOutput::writeUInt16(uint16 u) {
+ reserveBytes(2);
+
+ uint8* convert = (uint8*)&u;
+
+ if (m_swapBytes) {
+ m_buffer[m_pos] = convert[1];
+ m_buffer[m_pos + 1] = convert[0];
+ } else {
+ *(uint16*)(m_buffer + m_pos) = u;
+ }
+
+ m_pos += 2;
+}
+
+
+void BinaryOutput::writeUInt32(uint32 u) {
+ reserveBytes(4);
+
+ uint8* convert = (uint8*)&u;
+
+ debugAssert(m_beginEndBits == 0);
+
+ if (m_swapBytes) {
+ m_buffer[m_pos] = convert[3];
+ m_buffer[m_pos + 1] = convert[2];
+ m_buffer[m_pos + 2] = convert[1];
+ m_buffer[m_pos + 3] = convert[0];
+ } else {
+ *(uint32*)(m_buffer + m_pos) = u;
+ }
+
+ m_pos += 4;
+}
+
+
+void BinaryOutput::writeUInt64(uint64 u) {
+ reserveBytes(8);
+
+ uint8* convert = (uint8*)&u;
+
+ if (m_swapBytes) {
+ m_buffer[m_pos] = convert[7];
+ m_buffer[m_pos + 1] = convert[6];
+ m_buffer[m_pos + 2] = convert[5];
+ m_buffer[m_pos + 3] = convert[4];
+ m_buffer[m_pos + 4] = convert[3];
+ m_buffer[m_pos + 5] = convert[2];
+ m_buffer[m_pos + 6] = convert[1];
+ m_buffer[m_pos + 7] = convert[0];
+ } else {
+ *(uint64*)(m_buffer + m_pos) = u;
+ }
+
+ m_pos += 8;
+}
+
+
+void BinaryOutput::writeString(const char* s) {
+ // +1 is because strlen doesn't count the null
+ int len = strlen(s) + 1;
+
+ debugAssert(m_beginEndBits == 0);
+ reserveBytes(len);
+ System::memcpy(m_buffer + m_pos, s, len);
+ m_pos += len;
+}
+
+
+void BinaryOutput::writeStringEven(const char* s) {
+ // +1 is because strlen doesn't count the null
+ int len = strlen(s) + 1;
+
+ reserveBytes(len);
+ System::memcpy(m_buffer + m_pos, s, len);
+ m_pos += len;
+
+ // Pad with another NULL
+ if ((len % 2) == 1) {
+ writeUInt8(0);
+ }
+}
+
+
+void BinaryOutput::writeString32(const char* s) {
+ writeUInt32(strlen(s) + 1);
+ writeString(s);
+}
+
+
+void BinaryOutput::writeVector4(const Vector4& v) {
+ writeFloat32(v.x);
+ writeFloat32(v.y);
+ writeFloat32(v.z);
+ writeFloat32(v.w);
+}
+
+
+void BinaryOutput::writeVector3(const Vector3& v) {
+ writeFloat32(v.x);
+ writeFloat32(v.y);
+ writeFloat32(v.z);
+}
+
+
+void BinaryOutput::writeVector2(const Vector2& v) {
+ writeFloat32(v.x);
+ writeFloat32(v.y);
+}
+
+
+void BinaryOutput::writeColor4(const Color4& v) {
+ writeFloat32(v.r);
+ writeFloat32(v.g);
+ writeFloat32(v.b);
+ writeFloat32(v.a);
+}
+
+
+void BinaryOutput::writeColor3(const Color3& v) {
+ writeFloat32(v.r);
+ writeFloat32(v.g);
+ writeFloat32(v.b);
+}
+
+
+void BinaryOutput::beginBits() {
+ debugAssertM(m_beginEndBits == 0, "Already in beginBits...endBits");
+ m_bitString = 0x00;
+ m_bitPos = 0;
+ m_beginEndBits = 1;
+}
+
+
+void BinaryOutput::writeBits(uint32 value, int numBits) {
+
+ while (numBits > 0) {
+ // Extract the current bit of value and
+ // insert it into the current byte
+ m_bitString |= (value & 1) << m_bitPos;
+ ++m_bitPos;
+ value = value >> 1;
+ --numBits;
+
+ if (m_bitPos > 7) {
+ // We've reached the end of this byte
+ writeUInt8(m_bitString);
+ m_bitString = 0x00;
+ m_bitPos = 0;
+ }
+ }
+}
+
+
+void BinaryOutput::endBits() {
+ debugAssertM(m_beginEndBits == 1, "Not in beginBits...endBits");
+ if (m_bitPos > 0) {
+ writeUInt8(m_bitString);
+ }
+ m_bitString = 0;
+ m_bitPos = 0;
+ m_beginEndBits = 0;
+}
+
+}
diff --git a/externals/g3dlite/G3D.lib/source/Box.cpp b/externals/g3dlite/G3D.lib/source/Box.cpp
new file mode 100644
index 00000000000..4b6c56388ec
--- /dev/null
+++ b/externals/g3dlite/G3D.lib/source/Box.cpp
@@ -0,0 +1,393 @@
+/**
+ @file Box.cpp
+ Box class
+
+ @maintainer Morgan McGuire, matrix@graphics3d.com
+
+ @created 2001-06-02
+ @edited 2006-02-05
+*/
+
+#include "G3D/Box.h"
+#include "G3D/debug.h"
+#include "G3D/Plane.h"
+#include "G3D/AABox.h"
+#include "G3D/CoordinateFrame.h"
+
+namespace G3D {
+
+/**
+ Sets a field on four vertices. Used by the constructor.
+ */
+#define setMany(i0, i1, i2, i3, field, extreme) \
+ _corner[i0].field = _corner[i1].field = \
+ _corner[i2].field = _corner[i3].field = \
+ (extreme).field
+
+Box::Box() {
+}
+
+
+Box::Box(const AABox& b) {
+ init(b.low(), b.high());
+}
+
+Box::Box(class BinaryInput& b) {
+ deserialize(b);
+}
+
+
+void Box::serialize(class BinaryOutput& b) const {
+ int i;
+ for (i = 0; i < 8; ++i) {
+ _corner[i].serialize(b);
+ }
+
+ // Other state can be reconstructed
+}
+
+
+void Box::deserialize(class BinaryInput& b) {
+ int i;
+
+ _center = Vector3::zero();
+ for (i = 0; i < 8; ++i) {
+ _corner[i].deserialize(b);
+ _center += _corner[i];
+ }
+
+ _center = _center / 8;
+
+ // Reconstruct other state from the corners
+ _axis[0] = _corner[5] - _corner[4];
+ _axis[1] = _corner[7] - _corner[4];
+ _axis[2] = _corner[0] - _corner[4];
+
+ for (i = 0; i < 3; ++i) {
+ _extent[i] = _axis[i].magnitude();
+ _axis[i] /= _extent[i];
+ }
+
+ _volume = _extent.x * _extent.y * _extent.z;
+
+ _area = 2 *
+ (_extent.x * _extent.y +
+ _extent.y * _extent.z +
+ _extent.z * _extent.x);
+}
+
+
+Box::Box(
+ const Vector3& min,
+ const Vector3& max) {
+
+ init(min.min(max), min.max(max));
+
+}
+
+void Box::init(
+ const Vector3& min,
+ const Vector3& max) {
+
+ debugAssert(
+ (min.x <= max.x) &&
+ (min.y <= max.y) &&
+ (min.z <= max.z));
+
+ setMany(0, 1, 2, 3, z, max);
+ setMany(4, 5, 6, 7, z, min);
+
+ setMany(1, 2, 5, 6, x, max);
+ setMany(0, 3, 4, 7, x, min);
+
+ setMany(3, 2, 6, 7, y, max);
+ setMany(0, 1, 5, 4, y, min);
+
+ _extent = max - min;
+
+ _axis[0] = Vector3::unitX();
+ _axis[1] = Vector3::unitY();
+ _axis[2] = Vector3::unitZ();
+
+ if (_extent.isFinite()) {
+ _volume = _extent.x * _extent.y * _extent.z;
+ } else {
+ _volume = G3D::inf();
+ }
+
+ debugAssert(! isNaN(_extent.x));
+
+ _area = 2 *
+ (_extent.x * _extent.y +
+ _extent.y * _extent.z +
+ _extent.z * _extent.x);
+
+ _center = (max + min) / 2;
+
+ // If the extent is infinite along an axis, make the center zero to avoid NaNs
+ for (int i = 0; i < 3; ++i) {
+ if (! G3D::isFinite(_extent[i])) {
+ _center[i] = 0.0f;
+ }
+ }
+}
+
+
+float Box::volume() const {
+ return _volume;
+}
+
+
+float Box::area() const {
+ return _area;
+}
+
+
+void Box::getLocalFrame(CoordinateFrame& frame) const {
+
+ frame.rotation = Matrix3(
+ _axis[0][0], _axis[1][0], _axis[2][0],
+ _axis[0][1], _axis[1][1], _axis[2][1],
+ _axis[0][2], _axis[1][2], _axis[2][2]);
+
+ frame.translation = _center;
+}
+
+
+CoordinateFrame Box::localFrame() const {
+ CoordinateFrame out;
+ getLocalFrame(out);
+ return out;
+}
+
+
+void Box::getFaceCorners(int f, Vector3& v0, Vector3& v1, Vector3& v2, Vector3& v3) const {
+ switch (f) {
+ case 0:
+ v0 = _corner[0]; v1 = _corner[1]; v2 = _corner[2]; v3 = _corner[3];
+ break;
+
+ case 1:
+ v0 = _corner[1]; v1 = _corner[5]; v2 = _corner[6]; v3 = _corner[2];
+ break;
+
+ case 2:
+ v0 = _corner[7]; v1 = _corner[6]; v2 = _corner[5]; v3 = _corner[4];
+ break;
+
+ case 3:
+ v0 = _corner[2]; v1 = _corner[6]; v2 = _corner[7]; v3 = _corner[3];
+ break;
+
+ case 4:
+ v0 = _corner[3]; v1 = _corner[7]; v2 = _corner[4]; v3 = _corner[0];
+ break;
+
+ case 5:
+ v0 = _corner[1]; v1 = _corner[0]; v2 = _corner[4]; v3 = _corner[5];
+ break;
+
+ default:
+ debugAssert((f >= 0) && (f < 6));
+ }
+}
+
+
+
+int Box::dummy = 0;
+
+bool Box::culledBy(
+ const Array<Plane>& plane,
+ int& cullingPlane,
+ const uint32 _inMask,
+ uint32& childMask) const {
+
+ uint32 inMask = _inMask;
+ assert(plane.size() < 31);
+
+ childMask = 0;
+
+ // See if there is one plane for which all of the
+ // vertices are in the negative half space.
+ for (int p = 0; p < plane.size(); ++p) {
+
+ // Only test planes that are not masked
+ if ((inMask & 1) != 0) {
+
+ Vector3 corner;
+
+ int numContained = 0;
+ int v = 0;
+
+ // We can early-out only if we have found one point on each
+ // side of the plane (i.e. if we are straddling). That
+ // occurs when (numContained < v) && (numContained > 0)
+ for (v = 0; (v < 8) && ((numContained == v) || (numContained == 0)); ++v) {
+ if (plane[p].halfSpaceContains(_corner[v])) {
+ ++numContained;
+ }
+ }
+
+ if (numContained == 0) {
+ // Plane p culled the box
+ cullingPlane = p;
+
+ // The caller should not recurse into the children,
+ // since the parent is culled. If they do recurse,
+ // make them only test against this one plane, which
+ // will immediately cull the volume.
+ childMask = 1 << p;
+ return true;
+
+ } else if (numContained < v) {
+ // The bounding volume straddled the plane; we have
+ // to keep testing against this plane
+ childMask |= (1 << p);
+ }
+ }
+
+ // Move on to the next bit.
+ inMask = inMask >> 1;
+ }
+
+ // None of the planes could cull this box
+ cullingPlane = -1;
+ return false;
+}
+
+
+bool Box::culledBy(
+ const Array<Plane>& plane,
+ int& cullingPlane,
+ const uint32 _inMask) const {
+
+ uint32 inMask = _inMask;
+ assert(plane.size() < 31);
+
+ // See if there is one plane for which all of the
+ // vertices are in the negative half space.
+ for (int p = 0; p < plane.size(); ++p) {
+
+ // Only test planes that are not masked
+ if ((inMask & 1) != 0) {
+
+ bool culled = true;
+
+ int v;
+
+ // Assume this plane culls all points. See if there is a point
+ // not culled by the plane... early out when at least one point
+ // is in the positive half space.
+ for (v = 0; (v < 8) && culled; ++v) {
+ culled = ! plane[p].halfSpaceContains(corner(v));
+ }
+
+ if (culled) {
+ // Plane p culled the box
+ cullingPlane = p;
+
+ return true;
+ }
+ }
+
+ // Move on to the next bit.
+ inMask = inMask >> 1;
+ }
+
+ // None of the planes could cull this box
+ cullingPlane = -1;
+ return false;
+}
+
+
+bool Box::contains(
+ const Vector3& point) const {
+
+ // Form axes from three edges, transform the point into that
+ // space, and perform 3 interval tests
+
+ Vector3 u = _corner[4] - _corner[0];
+ Vector3 v = _corner[3] - _corner[0];
+ Vector3 w = _corner[1] - _corner[0];
+
+ Matrix3 M = Matrix3(u.x, v.x, w.x,
+ u.y, v.y, w.y,
+ u.z, v.z, w.z);
+
+ // M^-1 * (point - _corner[0]) = point in unit cube's object space
+ // compute the inverse of M
+ Vector3 osPoint = M.inverse() * (point - _corner[0]);
+
+ return
+ (osPoint.x >= 0) &&
+ (osPoint.y >= 0) &&
+ (osPoint.z >= 0) &&
+ (osPoint.x <= 1) &&
+ (osPoint.y <= 1) &&
+ (osPoint.z <= 1);
+}
+
+#undef setMany
+
+
+void Box::getRandomSurfacePoint(Vector3& P, Vector3& N) const {
+ float aXY = _extent.x * _extent.y;
+ float aYZ = _extent.y * _extent.z;
+ float aZX = _extent.z * _extent.x;
+
+ float r = (float)uniformRandom(0, aXY + aYZ + aZX);
+
+ // Choose evenly between positive and negative face planes
+ float d = (uniformRandom(0, 1) < 0.5f) ? -1.0f : 1.0f;
+
+ // The probability of choosing a given face is proportional to
+ // its area.
+ if (r < aXY) {
+ P = _axis[0] * (float)uniformRandom(-0.5, 0.5) * _extent.x +
+ _axis[1] * (float)uniformRandom(-0.5, 0.5) * _extent.y +
+ _center + _axis[2] * d * _extent.z * 0.5f;
+ N = _axis[2] * d;
+ } else if (r < aYZ) {
+ P = _axis[1] * (float)uniformRandom(-0.5, 0.5) * _extent.y +
+ _axis[2] * (float)uniformRandom(-0.5, 0.5) * _extent.z +
+ _center + _axis[0] * d * _extent.x * 0.5f;
+ N = _axis[0] * d;
+ } else {
+ P = _axis[2] * (float)uniformRandom(-0.5, 0.5) * _extent.z +
+ _axis[0] *(float) uniformRandom(-0.5, 0.5) * _extent.x +
+ _center + _axis[1] * d * _extent.y * 0.5f;
+ N = _axis[1] * d;
+ }
+}
+
+
+Vector3 Box::randomInteriorPoint() const {
+ Vector3 sum = _center;
+
+ for (int a = 0; a < 3; ++a) {
+ sum += _axis[a] * (float)uniformRandom(-0.5, 0.5) * _extent[a];
+ }
+
+ return sum;
+}
+
+Box Box::inf() {
+ return Box(-Vector3::inf(), Vector3::inf());
+}
+
+void Box::getBounds(class AABox& aabb) const {
+
+ Vector3 lo = _corner[0];
+ Vector3 hi = lo;
+
+ for (int v = 1; v < 8; ++v) {
+ const Vector3& C = _corner[v];
+ lo = lo.min(C);
+ hi = hi.max(C);
+ }
+
+ aabb = AABox(lo, hi);
+}
+
+
+} // namespace
diff --git a/externals/g3dlite/G3D.lib/source/Capsule.cpp b/externals/g3dlite/G3D.lib/source/Capsule.cpp
new file mode 100644
index 00000000000..fbcb56fe97b
--- /dev/null
+++ b/externals/g3dlite/G3D.lib/source/Capsule.cpp
@@ -0,0 +1,179 @@
+/**
+ @file Capsule.cpp
+
+ @maintainer Morgan McGuire, matrix@graphics3d.com
+
+ @created 2003-02-07
+ @edited 2005-08-18
+
+ Copyright 2000-2005, Morgan McGuire.
+ All rights reserved.
+ */
+
+#include "G3D/Capsule.h"
+#include "G3D/BinaryInput.h"
+#include "G3D/BinaryOutput.h"
+#include "G3D/LineSegment.h"
+#include "G3D/Sphere.h"
+#include "G3D/CoordinateFrame.h"
+#include "G3D/Line.h"
+#include "G3D/AABox.h"
+
+namespace G3D {
+
+Capsule::Capsule(class BinaryInput& b) {
+ deserialize(b);
+}
+
+
+Capsule::Capsule() {
+}
+
+
+Capsule::Capsule(const Vector3& _p1, const Vector3& _p2, float _r)
+ : p1(_p1), p2(_p2), _radius(_r) {
+}
+
+
+void Capsule::serialize(class BinaryOutput& b) const {
+ p1.serialize(b);
+ p2.serialize(b);
+ b.writeFloat64(_radius);
+}
+
+
+void Capsule::deserialize(class BinaryInput& b) {
+ p1.deserialize(b);
+ p2.deserialize(b);
+ _radius = b.readFloat64();
+}
+
+
+Line Capsule::axis() const {
+ return Line::fromTwoPoints(p1, p2);
+}
+
+
+float Capsule::volume() const {
+ return
+ // Sphere volume
+ pow(_radius, 3) * pi() * 4 / 3 +
+
+ // Cylinder volume
+ pow(_radius, 2) * (p1 - p2).magnitude();
+}
+
+
+float Capsule::area() const {
+
+ return
+ // Sphere area
+ pow(_radius, 2) * 4 * pi() +
+
+ // Cylinder area
+ twoPi() * _radius * (p1 - p2).magnitude();
+}
+
+
+void Capsule::getBounds(AABox& out) const {
+ Vector3 min = p1.min(p2) - (Vector3(1, 1, 1) * _radius);
+ Vector3 max = p1.max(p2) + (Vector3(1, 1, 1) * _radius);
+
+ out = AABox(min, max);
+}
+
+
+bool Capsule::contains(const Vector3& p) const {
+ return LineSegment::fromTwoPoints(p1, p2).distanceSquared(p) <= square(radius());
+}
+
+
+void Capsule::getRandomSurfacePoint(Vector3& p, Vector3& N) const {
+ float h = height();
+ float r = radius();
+
+ // Create a random point on a standard capsule and then rotate to the global frame.
+
+ // Relative areas
+ float capRelArea = sqrt(r) / 2.0f;
+ float sideRelArea = r * h;
+
+ float r1 = uniformRandom(0, capRelArea * 2 + sideRelArea);
+
+ if (r1 < capRelArea * 2) {
+
+ // Select a point uniformly at random on a sphere
+ N = Sphere(Vector3::zero(), 1).randomSurfacePoint();
+ p = N * r;
+ p.y += sign(p.y) * h / 2.0f;
+ } else {
+ // Side
+ float a = uniformRandom(0, (float)twoPi());
+ N.x = cos(a);
+ N.y = 0;
+ N.z = sin(a);
+ p.x = N.x * r;
+ p.z = N.y * r;
+ p.y = uniformRandom(-h / 2.0f, h / 2.0f);
+ }
+
+ // Transform to world space
+ CoordinateFrame cframe;
+ getReferenceFrame(cframe);
+
+ p = cframe.pointToWorldSpace(p);
+ N = cframe.normalToWorldSpace(N);
+}
+
+
+void Capsule::getReferenceFrame(CoordinateFrame& cframe) const {
+ cframe.translation = center();
+
+ Vector3 Y = (p1 - p2).direction();
+ Vector3 X = (abs(Y.dot(Vector3::unitX())) > 0.9) ? Vector3::unitY() : Vector3::unitX();
+ Vector3 Z = X.cross(Y).direction();
+ X = Y.cross(Z);
+ cframe.rotation.setColumn(0, X);
+ cframe.rotation.setColumn(1, Y);
+ cframe.rotation.setColumn(2, Z);
+}
+
+
+Vector3 Capsule::randomInteriorPoint() const {
+ float h = height();
+ float r = radius();
+
+ // Create a random point in a standard capsule and then rotate to the global frame.
+
+ Vector3 p;
+
+ float hemiVolume = pi() * (r*r*r) * 4 / 6.0;
+ float cylVolume = pi() * square(r) * h;
+
+ float r1 = uniformRandom(0, 2.0 * hemiVolume + cylVolume);
+
+ if (r1 < 2.0 * hemiVolume) {
+
+ p = Sphere(Vector3::zero(), r).randomInteriorPoint();
+
+ p.y += sign(p.y) * h / 2.0f;
+
+ } else {
+
+ // Select a point uniformly at random on a disk
+ float a = uniformRandom(0, (float)twoPi());
+ float r2 = sqrt(uniformRandom(0, 1)) * r;
+
+ p = Vector3(cos(a) * r2,
+ uniformRandom(-h / 2.0f, h / 2.0f),
+ sin(a) * r2);
+ }
+
+ // Transform to world space
+ CoordinateFrame cframe;
+ getReferenceFrame(cframe);
+
+ return cframe.pointToWorldSpace(p);
+}
+
+} // namespace
diff --git a/externals/g3dlite/G3D.lib/source/CollisionDetection.cpp b/externals/g3dlite/G3D.lib/source/CollisionDetection.cpp
new file mode 100644
index 00000000000..16650bc1a96
--- /dev/null
+++ b/externals/g3dlite/G3D.lib/source/CollisionDetection.cpp
@@ -0,0 +1,2152 @@
+/**
+ @file CollisionDetection.cpp
+
+ @maintainer Morgan McGuire, matrix@graphics3d.com
+
+ @cite Bounce direction based on Paul Nettle's ftp://ftp.3dmaileffects.com/pub/FluidStudios/CollisionDetection/Fluid_Studios_Generic_Collision_Detection_for_Games_Using_Ellipsoids.pdf and comments by Max McGuire. Ray-sphere code by Eric Haines.
+
+ @created 2001-11-24
+ @edited 2008-10-10
+ */
+
+#include "G3D/CoordinateFrame.h"
+#include "G3D/platform.h"
+#include "G3D/CollisionDetection.h"
+#include "G3D/debugAssert.h"
+#include "G3D/vectorMath.h"
+#include "G3D/Capsule.h"
+#include "G3D/Plane.h"
+#include "G3D/Line.h"
+#include "G3D/LineSegment.h"
+#include "G3D/Sphere.h"
+#include "G3D/Box.h"
+#include "G3D/Triangle.h"
+#include "G3D/Vector3.h"
+#include "G3D/AABox.h"
+
+namespace G3D {
+
+bool CollisionDetection::ignoreBool;
+Vector3 CollisionDetection::ignore;
+Array<Vector3> CollisionDetection::ignoreArray;
+
+
+
+Vector3 CollisionDetection::separatingAxisForSolidBoxSolidBox(
+ const int separatingAxisIndex,
+ const Box & box1,
+ const Box & box2) {
+ debugAssert(separatingAxisIndex >= 0);
+ debugAssert(separatingAxisIndex < 15);
+ Vector3 axis;
+ if (separatingAxisIndex < 3) {
+ axis = box1.axis(separatingAxisIndex);
+ } else if (separatingAxisIndex < 6) {
+ axis = box2.axis(separatingAxisIndex - 3);
+ } else {
+ int box1Index = (separatingAxisIndex - 6) / 3;
+ int box2Index = (separatingAxisIndex - 6) % 3;
+ axis = cross(box1.axis(box1Index), box2.axis(box2Index));
+ }
+ return axis;
+}
+
+#ifdef _MSC_VER
+# pragma warning (push)
+# pragma warning (disable : 4244)
+#endif
+
+float CollisionDetection::projectedDistanceForSolidBoxSolidBox(
+ const int separatingAxisIndex,
+ const Vector3 & a,
+ const Vector3 & b,
+ const Vector3 & D,
+ const double* c,
+ const double* ca,
+ const double* ad,
+ const double* bd)
+{
+ (void)D;
+
+ float R0 = 0.0f;
+ float R1 = 0.0f;
+ float R = 0.0f;
+ switch (separatingAxisIndex) {
+ case 0:
+ // A0
+ R0 = a[0];
+ R1 = b[0] * ca[0] + b[1] * ca[1] + b[2] * ca[2];
+ R = fabs(ad[0]);
+ break;
+ case 1:
+ // A1
+ R0 = a[1];
+ R1 = b[0] * ca[3] + b[1] * ca[4] + b[2] * ca[5];
+ R = fabs(ad[1]);
+ break;
+ case 2:
+ // A2
+ R0 = a[2];
+ R1 = b[0] * ca[6] + b[1] * ca[7] + b[2] * ca[8];
+ R = fabs(ad[2]);
+ break;
+ case 3:
+ // B0
+ R0 = a[0] * ca[0] + a[1] * ca[3] + a[2] * ca[6];
+ R1 = b[0];
+ R = fabs(bd[0]);
+ break;
+ case 4:
+ // B1
+ R0 = a[0] * ca[1] + a[1] * ca[4] + a[2] * ca[7];
+ R1 = b[1];
+ R = fabs(bd[1]);
+ break;
+ case 5:
+ // B2
+ R0 = a[0] * ca[2] + a[1] * ca[5] + a[2] * ca[8];
+ R1 = b[2];
+ R = fabs(bd[2]);
+ break;
+ case 6:
+ // A0 x B0
+ R0 = a[1] * ca[6] + a[2] * ca[3];
+ R1 = b[1] * ca[2] + b[2] * ca[1];
+ R = fabs(c[3] * ad[2] - c[6] * ad[1]);
+ break;
+ case 7:
+ // A0 x B1
+ R0 = a[1] * ca[7] + a[2] * ca[4];
+ R1 = b[0] * ca[2] + b[2] * ca[0];
+ R = fabs(c[4] * ad[2] - c[7] * ad[1]);
+ break;
+ case 8:
+ // A0 x B2
+ R0 = a[1] * ca[8] + a[2] * ca[5];
+ R1 = b[0] * ca[1] + b[1] * ca[0];
+ R = fabs(c[5] * ad[2] - c[8] * ad[1]);
+ break;
+ case 9:
+ // A1 x B0
+ R0 = a[0] * ca[6] + a[2] * ca[0];
+ R1 = b[1] * ca[5] + b[2] * ca[4];
+ R = fabs(c[6] * ad[0] - c[0] * ad[2]);
+ break;
+ case 10:
+ // A1 x B1
+ R0 = a[0] * ca[7] + a[2] * ca[1];
+ R1 = b[0] * ca[5] + b[2] * ca[3];
+ R = fabs(c[7] * ad[0] - c[1] * ad[2]);
+ break;
+ case 11:
+ // A1 x B2
+ R0 = a[0] * ca[8] + a[2] * ca[2];
+ R1 = b[0] * ca[4] + b[1] * ca[3];
+ R = fabs(c[8] * ad[0] - c[2] * ad[2]);
+ break;
+ case 12:
+ // A2 x B0
+ R0 = a[0] * ca[3] + a[1] * ca[0];
+ R1 = b[1] * ca[8] + b[2] * ca[7];
+ R = fabs(c[0] * ad[1] - c[3] * ad[0]);
+ break;
+ case 13:
+ // A2 x B1
+ R0 = a[0] * ca[4] + a[1] * ca[1];
+ R1 = b[0] * ca[8] + b[2] * ca[6];
+ R = fabs(c[1] * ad[1] - c[4] * ad[0]);
+ break;
+ case 14:
+ // A2 x B2
+ R0 = a[0] * ca[5] + a[1] * ca[2];
+ R1 = b[0] * ca[7] + b[1] * ca[6];
+ R = fabs(c[2] * ad[1] - c[5] * ad[0]);
+ break;
+ default:
+ debugAssertM(false, "fell through switch statement");
+ }
+
+ return (R - (R0 + R1));
+}
+
+
+bool CollisionDetection::parallelAxisForSolidBoxSolidBox(
+ const double* ca,
+ const double epsilon,
+ int & axis1,
+ int & axis2) {
+ const double parallelDot = 1.0 - epsilon;
+ for (int i = 0; i < 9; i++) {
+ if (ca[i] >= parallelDot) {
+ axis1 = i / 3;
+ axis2 = i % 3;
+ return true;
+ }
+ }
+ return false;
+}
+
+
+
+
+void CollisionDetection::fillSolidBoxSolidBoxInfo(
+ const Box & box1,
+ const Box & box2,
+ Vector3 & a,
+ Vector3 & b,
+ Vector3 & D,
+ double* c,
+ double* ca,
+ double* ad,
+ double* bd) {
+ // length between center and each side of box1 and box2
+ a = box1.extent() * 0.5;
+ b = box2.extent() * 0.5;
+
+ // difference between centers of box1 and box2
+ D = box2.center() - box1.center();
+
+ // store the value of all possible dot products between the
+ // axes of box1 and box2, c_{row, col} in the Eberly paper
+ // corresponds to c[row * 3 + col] for this 9 element array.
+ //
+ // c[] holds signed values, ca[] hold absolute values
+ for (int i = 0; i < 9; i++) {
+ c[i] = dot(box1.axis(i / 3), box2.axis(i % 3));
+ ca[i] = fabs(c[i]);
+ }
+
+ // store all possible dot products between the axes of box1 and D,
+ // as well as the axes of box2 and D
+ for (int i = 0; i < 3; i++) {
+ ad[i] = dot(box1.axis(i), D);
+ bd[i] = dot(box2.axis(i), D);
+ }
+}
+
+
+
+bool CollisionDetection::conservativeBoxBoxTest(
+ const Vector3 & a, const Vector3 & b, const Vector3 & D) {
+ // do a quick bounding sphere test because it is relatively
+ // cheap, (three dot products, two sqrts, and a few others)
+ double boxRadius1 = a.magnitude();
+ double boxRadius2 = b.magnitude();
+ return (D.squaredMagnitude() < square(boxRadius1 + boxRadius2));
+}
+
+
+
+
+bool CollisionDetection::fixedSolidBoxIntersectsFixedSolidBox(
+ const Box& box1,
+ const Box& box2,
+ const int lastSeparatingAxis) {
+ // for explanations of the variable please refer to the
+ // paper and fillSolidBoxSolidBoxInfo()
+ Vector3 a;
+ Vector3 b;
+ Vector3 D;
+ double c[9];
+ double ca[9];
+ double ad[3];
+ double bd[3];
+
+ fillSolidBoxSolidBoxInfo(box1, box2, a, b, D, c, ca, ad, bd);
+
+ int dummy1, dummy2;
+ bool parallelAxes = parallelAxisForSolidBoxSolidBox(ca, 0.00001,
+ dummy1, dummy2);
+
+ // check the separating axis from the last time step
+ if (lastSeparatingAxis != -1 &&
+ (lastSeparatingAxis < 6 || !parallelAxes)) {
+ double projectedDistance = projectedDistanceForSolidBoxSolidBox(
+ lastSeparatingAxis, a, b, D, c, ca, ad, bd);
+
+ // the separating axis from the last time step is still
+ // valid, the boxes do not intersect
+ if (projectedDistance > 0.0) {
+ return false;
+ }
+ }
+
+ // test if the boxes can be separated by a plane normal to
+ // any of the three axes of box1, any of the three axes of box2,
+ // or any of the 9 possible cross products of axes from box1
+ // and box2
+ for (int i = 0; i < 15; i++) {
+ // do not need to check edge-edge cases if any two of
+ // the axes are parallel
+ if (parallelAxes && i == 6) {
+ return true;
+ }
+
+ double projectedDistance =
+ projectedDistanceForSolidBoxSolidBox(i, a, b, D, c, ca, ad, bd);
+
+ // found a separating axis, the boxes do not intersect
+ if (projectedDistance > 0.0) {
+ return false;
+ }
+ }
+
+ return true;
+}
+
+
+
+void CollisionDetection::closestPointsBetweenLineAndLine(
+ const Line & line1,
+ const Line & line2,
+ Vector3 & closest1,
+ Vector3 & closest2) {
+ // TODO make accessors for Line that don't make a copy of data
+ Vector3 P0 = line1.point();
+ Vector3 u = line1.direction();
+ Vector3 Q0 = line2.point();
+ Vector3 v = line2.direction();
+ Vector3 w0 = P0 - Q0;
+
+ // a = 1.0, c = 1.0
+ double b = dot(u, v);
+ double d = dot(u, w0);
+ double e = dot(v, w0);
+ double D = 1.0 - b * b;
+ double sc, tc;
+
+ static const double epsilon = 0.00001;
+
+ if (D < epsilon) {
+ // lines are parallel, choose P0 as one point, find the point
+ // on line2 that is closest to P0
+ sc = 0.0;
+ tc = (b > 1.0) ? (d / b) : (e / 1.0);
+ } else {
+ // lines are not parallel
+ sc = (b * e - 1.0 * d) / D;
+ tc = (1.0 * e - b * d) / D;
+ }
+
+ closest1 = P0 + (sc * u);
+ closest2 = Q0 + (tc * v);
+}
+
+
+
+float CollisionDetection::penetrationDepthForFixedBoxFixedBox(
+ const Box& box1,
+ const Box& box2,
+ Array<Vector3>& contactPoints,
+ Array<Vector3>& contactNormals,
+ const int lastSeparatingAxis) {
+
+ contactPoints.resize(0, DONT_SHRINK_UNDERLYING_ARRAY);
+ contactNormals.resize(0, DONT_SHRINK_UNDERLYING_ARRAY);
+
+ Vector3 a;
+ Vector3 b;
+ Vector3 D;
+ double c[9];
+ double ca[9];
+ double ad[3];
+ double bd[3];
+
+ debugAssert(lastSeparatingAxis >= -1);
+ debugAssert(lastSeparatingAxis < 15);
+
+ fillSolidBoxSolidBoxInfo(box1, box2, a, b, D, c, ca, ad, bd);
+
+ int axis1, axis2;
+ bool parallelAxes = parallelAxisForSolidBoxSolidBox(ca, 0.00001,
+ axis1, axis2);
+
+
+ // check the separating axis from the last time step
+ if (lastSeparatingAxis != -1 &&
+ (lastSeparatingAxis < 6 || !parallelAxes)) {
+ float projectedDistance = projectedDistanceForSolidBoxSolidBox(
+ lastSeparatingAxis, a, b, D, c, ca, ad, bd);
+
+ // the separating axis from the last time step is still
+ // valid, the boxes do not intersect
+ if (projectedDistance > 0.0) {
+ return -projectedDistance;
+ }
+ }
+
+ // test if the boxes can be separated by a plane normal to
+ // any of the three axes of box1, any of the three axes of box2,
+ // (test 9 possible cross products later)
+ float penetration = -(float)G3D::inf();
+ int penetrationAxisIndex = -1;
+
+ for (int i = 0; i < 6; i++) {
+ float projectedDistance =
+ projectedDistanceForSolidBoxSolidBox(i, a, b, D, c, ca, ad, bd);
+
+ // found a separating axis, the boxes do not intersect
+ if (projectedDistance > 0.0) {
+ return -projectedDistance;
+ }
+
+ // keep track of the axis that is least violated
+ if (projectedDistance > penetration) {
+ penetration = projectedDistance;
+ penetrationAxisIndex = i;
+ }
+ }
+
+
+ // for each edge-edge case we have to adjust the magnitude of
+ // penetration since we did not include the dot(L, L) denominator
+ // that can be smaller than 1.0 for the edge-edge cases.
+ if (!parallelAxes) {
+ double edgeDistances[9];
+
+ // run through edge-edge cases to see if we can find a separating axis
+ for (int i = 6; i < 15; i++) {
+ float projectedDistance =
+ projectedDistanceForSolidBoxSolidBox(i, a, b, D, c, ca, ad, bd);
+
+ // found a separating axis, the boxes do not intersect,
+ // correct magnitude and return projected distance
+ if (projectedDistance > 0.0) {
+ Vector3 L = separatingAxisForSolidBoxSolidBox(i, box1, box2);
+ projectedDistance /= dot(L, L);
+ return -projectedDistance;
+ }
+
+ edgeDistances[i - 6] = projectedDistance;
+ }
+
+ // no separating axis found, the boxes do intersect,
+ // correct the magnitudes of the projectedDistance values
+ for (int i = 6; i < 15; i++) {
+ // find the negative penetration value with the smallest magnitude,
+ // the adjustment done for the edge-edge cases only increases
+ // magnitude by dividing by a number smaller than 1 and greater than 0
+ float projectedDistance = (float)edgeDistances[i - 6];
+ if (projectedDistance > penetration) {
+ Vector3 L = separatingAxisForSolidBoxSolidBox(i, box1, box2);
+ projectedDistance /= dot(L, L);
+ if (projectedDistance > penetration) {
+ penetration = projectedDistance;
+ penetrationAxisIndex = i;
+ }
+ }
+ }
+ }
+
+ // get final separating axis vector
+ Vector3 L = separatingAxisForSolidBoxSolidBox(penetrationAxisIndex,
+ box1, box2);
+
+ // set L to be the normal that faces away from box1
+ if (dot(L, D) < 0) {
+ L = -L;
+ }
+
+ Vector3 contactPoint;
+
+ if (penetrationAxisIndex < 6) {
+ // vertex to face collision, find deepest colliding vertex
+ const Box* vertexBox;
+ const Box* faceBox;
+ Vector3 faceNormal = L;
+
+ // L will be the outward facing normal for the faceBox
+ if (penetrationAxisIndex < 3) {
+ faceBox = & box1;
+ vertexBox = & box2;
+ if (dot(L, D) < 0) {
+ faceNormal = -L;
+ }
+ } else {
+ faceBox = & box2;
+ vertexBox = & box1;
+ if (dot(L, D) > 0) {
+ faceNormal = -L;
+ }
+ }
+
+ // find the vertex that is farthest away in the direction
+ // face normal direction
+ int deepestPointIndex = 0;
+ float deepestPointDot = dot(faceNormal, vertexBox->corner(0));
+ for (int i = 1; i < 8; i++) {
+ float dotProduct = dot(faceNormal, vertexBox->corner(i));
+ if (dotProduct < deepestPointDot) {
+ deepestPointDot = dotProduct;
+ deepestPointIndex = i;
+ }
+ }
+
+ // return the point half way between the deepest point and the
+ // contacting face
+ contactPoint = vertexBox->corner(deepestPointIndex) +
+ (-penetration * 0.5 * faceNormal);
+ } else {
+ // edge-edge case, find the two ege lines
+ int edge1 = (penetrationAxisIndex - 6) / 3;
+ int edge2 = (penetrationAxisIndex - 6) % 3;
+ Vector3 linePoint1 = box1.center();
+ Vector3 linePoint2 = box2.center();
+ Vector3 lineDir1;
+ Vector3 lineDir2;
+
+ // find edge line by finding the edge axis, and the
+ // other two axes that are closest to the other box
+ for (int i = 0; i < 3; i++ ) {
+ if (i == edge1) {
+ lineDir1 = box1.axis(i);
+ } else {
+ Vector3 axis = box1.axis(i);
+ if (dot(axis, L) < 0) {
+ axis = -axis;
+ }
+ linePoint1 += axis * a[i];
+ }
+
+ if (i == edge2) {
+ lineDir2 = box2.axis(i);
+ } else {
+ Vector3 axis = box2.axis(i);
+ if (dot(axis, L) > 0) {
+ axis = -axis;
+ }
+ linePoint2 += axis * b[i];
+ }
+ }
+
+ // make lines from the two closest edges, and find
+ // the points that on each line that are closest to the other
+ Line line1 = Line::fromPointAndDirection(linePoint1, lineDir1);
+ Line line2 = Line::fromPointAndDirection(linePoint2, lineDir2);
+ Vector3 closest1;
+ Vector3 closest2;
+
+ closestPointsBetweenLineAndLine(line1, line2, closest1, closest2);
+
+ // take the average of the two closest edge points for the final
+ // contact point
+ contactPoint = (closest1 + closest2) * 0.5;
+ }
+
+ contactPoints.push(contactPoint);
+ contactNormals.push(L);
+
+ return -penetration;
+
+}
+
+
+
+
+float CollisionDetection::penetrationDepthForFixedSphereFixedBox(
+ const Sphere& sphere,
+ const Box& box,
+ Array<Vector3>& contactPoints,
+ Array<Vector3>& contactNormals) {
+
+ contactPoints.resize(0, DONT_SHRINK_UNDERLYING_ARRAY);
+ contactNormals.resize(0, DONT_SHRINK_UNDERLYING_ARRAY);
+
+ // In its local coordinate frame, the box measures
+ // 2 * halfExtent[a] along dimesion a.
+ Vector3 halfExtent(box.extent(0), box.extent(1), box.extent(2));
+ halfExtent *= 0.5f;
+
+ CoordinateFrame boxFrame;
+ box.getLocalFrame(boxFrame);
+
+ // Transform the sphere to the box's coordinate frame.
+ Vector3 center = boxFrame.pointToObjectSpace(sphere.center);
+
+ // Find the square of the distance from the sphere to the box
+
+
+ // Distance along each axis from the closest side of the box
+ // to the sphere center. Negative values are *inside* the box.
+ Vector3 distOutsideBox;
+
+ // Divide space up into the 27 regions corresponding
+ // to {+|-|0}X, {+|-|0}Y, {+|-|0}Z and classify the
+ // sphere center into one of them.
+ Vector3 centerRegion;
+
+ // In the edge collision case, the edge is between vertices
+ // (constant + variable) and (constant - variable).
+ Vector3 constant, variable;
+
+ int numNonZero = 0;
+
+ // Iterate over axes
+ for (int a = 0; a < 3; ++a) {
+ // For each (box side), see which direction the sphere
+ // is outside the box (positive or negative). Add the
+ // square of that distance to the total distance from
+ // the box.
+
+ float distanceFromLow = -halfExtent[a] - center[a];
+ float distanceFromHigh = center[a] - halfExtent[a];
+
+ if (fabsf(distanceFromLow) < fabsf(distanceFromHigh)) {
+ distOutsideBox[a] = distanceFromLow;
+ } else {
+ distOutsideBox[a] = distanceFromHigh;
+ }
+
+ if (distanceFromLow < 0.0) {
+ if (distanceFromHigh < 0.0) {
+ // Inside the box
+ centerRegion[a] = 0.0;
+ variable[a] = 1.0;
+ } else {
+ // Off the high side
+ centerRegion[a] = 1.0;
+ constant[a] = halfExtent[a];
+ ++numNonZero;
+ }
+ } else if (distanceFromHigh < 0.0) {
+ // Off the low side
+ centerRegion[a] = -1.0;
+ constant[a] = -halfExtent[a];
+ ++numNonZero;
+ } else {
+ debugAssertM(false,
+ "distanceFromLow and distanceFromHigh cannot both be positive");
+ }
+ }
+
+ // Squared distance between the outside of the box and the
+ // sphere center.
+ float d2 = Vector3::zero().max(distOutsideBox).squaredMagnitude();
+
+ if (d2 > square(sphere.radius)) {
+ // There is no penetration because the distance is greater
+ // than the radius of the sphere. This is the common case
+ // and we quickly exit.
+ return -1;
+ }
+
+ // We know there is some penetration but need to classify it.
+ //
+ // Examine the region that contains the center of the sphere. If
+ // there is exactly one non-zero axis, the collision is with a
+ // plane. If there are exactly two non-zero axes, the collision
+ // is with an edge. If all three axes are non-zero, the collision is
+ // with a vertex. If there are no non-zero axes, the center is inside
+ // the box.
+
+ double depth = -1;
+ switch (numNonZero) {
+ case 3: // Vertex collision
+ // The collision point is the vertex at constant, the normal
+ // is the vector from there to the sphere center.
+ contactNormals.append(boxFrame.normalToWorldSpace(constant - center));
+ contactPoints.append(boxFrame.pointToWorldSpace(constant));
+ depth = sphere.radius - sqrt(d2);
+ break;
+
+ case 2: // Edge collision
+ {
+ // TODO: unwrapping the edge constructor and closest point
+ // code will probably make it faster.
+
+ // Determine the edge
+ Line line = Line::fromPointAndDirection(constant, variable);
+
+ // Penetration depth:
+ depth = sphere.radius - sqrt(d2);
+
+ // The contact point is the closes point to the sphere on the line
+ Vector3 X = line.closestPoint(center);
+ contactNormals.append(boxFrame.normalToWorldSpace(X - center).direction());
+ contactPoints.append(boxFrame.pointToWorldSpace(X));
+ }
+ break;
+
+ case 1: // Plane collision
+ {
+ // The plane normal is the centerRegion vector,
+ // so the sphere normal is the negative. Take
+ // it to world space from box-space.
+
+ // Center region doesn't need to be normalized because
+ // it is known to contain only one non-zero value
+ // and that value is +/- 1.
+ Vector3 N = boxFrame.normalToWorldSpace(-centerRegion);
+ contactNormals.append(N);
+
+ // Penetration depth:
+ depth = sphere.radius - sqrtf(d2);
+
+ // Compute the contact point from the penetration depth
+ contactPoints.append(sphere.center + N * (sphere.radius - depth));
+ }
+ break;
+
+ case 0: // Volume collision
+
+ // The sphere center is inside the box. This is an easy case
+ // to handle. Note that all axes of distOutsideBox must
+ // be negative.
+
+ // Arbitratily choose the sphere center as a contact point
+ contactPoints.append(sphere.center);
+
+ // Find the least-negative penetration axis.
+ //
+ // We could have computed this during the loop over the axes,
+ // but since volume collisions are rare (they only occur with
+ // large time steps), this case will seldom be executed and
+ // should not be optimized at the expense of the others.
+ if (distOutsideBox.x > distOutsideBox.y) {
+ if (distOutsideBox.x > distOutsideBox.z) {
+ // Smallest penetration on x-axis
+ // Chose normal based on which side we're closest to.
+ // Keep in mind that this is a normal to the sphere,
+ // so it is the inverse of the box normal.
+ if (center.x > 0) {
+ contactNormals.append(boxFrame.normalToWorldSpace(-Vector3::unitX()));
+ } else {
+ contactNormals.append(boxFrame.normalToWorldSpace(Vector3::unitX()));
+ }
+ depth = -distOutsideBox.x;
+ } else {
+ // Smallest penetration on z-axis
+ goto ZAXIS;
+ }
+ } else if (distOutsideBox.y > distOutsideBox.z) {
+ // Smallest penetration on y-axis
+ // Chose normal based on which side we're closest to.
+ // Keep in mind that this is a normal to the sphere,
+ // so it is the inverse of the box normal.
+ if (center.y > 0) {
+ contactNormals.append(boxFrame.normalToWorldSpace(-Vector3::unitY()));
+ } else {
+ contactNormals.append(boxFrame.normalToWorldSpace(Vector3::unitY()));
+ }
+ depth = -distOutsideBox.y;
+ } else {
+ // Smallest on z-axis
+ZAXIS:
+ // Chose normal based on which side we're closest to.
+ // Keep in mind that this is a normal to the sphere,
+ // so it is the inverse of the box normal.
+ if (center.z > 0) {
+ contactNormals.append(boxFrame.normalToWorldSpace(-Vector3::unitZ()));
+ } else {
+ contactNormals.append(boxFrame.normalToWorldSpace(Vector3::unitZ()));
+ }
+ depth = -distOutsideBox.z;
+ }
+ break;
+
+ default:
+ debugAssertM(false, "Fell through switch");
+ break;
+ }
+
+ return depth;
+}
+
+
+float CollisionDetection::penetrationDepthForFixedSphereFixedSphere(
+ const Sphere& sphereA,
+ const Sphere& sphereB,
+ Array<Vector3>& contactPoints,
+ Array<Vector3>& contactNormals) {
+
+ Vector3 axis = sphereB.center - sphereA.center;
+ double radius = sphereA.radius + sphereB.radius;
+ double mag = axis.magnitude();
+ axis /= mag;
+ double depth = -(mag - radius);
+
+ contactPoints.resize(0, DONT_SHRINK_UNDERLYING_ARRAY);
+ contactNormals.resize(0, DONT_SHRINK_UNDERLYING_ARRAY);
+
+ if (depth >= 0) {
+ contactPoints.append(sphereA.center + axis * (sphereA.radius - depth / 2));
+ contactNormals.append(axis);
+ }
+
+ return depth;
+}
+
+
+float CollisionDetection::penetrationDepthForFixedSphereFixedPlane(
+ const Sphere& sphereA,
+ const Plane& planeB,
+ Array<Vector3>& contactPoints,
+ Array<Vector3>& contactNormals) {
+
+ Vector3 N;
+ double d;
+
+ planeB.getEquation(N, d);
+
+ double depth = -(sphereA.center.dot(N) + d - sphereA.radius);
+
+ contactPoints.resize(0, DONT_SHRINK_UNDERLYING_ARRAY);
+ contactNormals.resize(0, DONT_SHRINK_UNDERLYING_ARRAY);
+
+ if (depth >= 0) {
+ contactPoints.append(N * (depth - sphereA.radius) + sphereA.center);
+ contactNormals.append(N);
+ }
+
+ return depth;
+}
+
+
+float CollisionDetection::penetrationDepthForFixedBoxFixedPlane(
+ const Box& box,
+ const Plane& plane,
+ Array<Vector3>& contactPoints,
+ Array<Vector3>& contactNormals) {
+
+ Vector3 N;
+ double d;
+
+ plane.getEquation(N, d);
+
+ contactPoints.resize(0, DONT_SHRINK_UNDERLYING_ARRAY);
+ contactNormals.resize(0, DONT_SHRINK_UNDERLYING_ARRAY);
+
+ float lowest = (float)inf();
+ for (int i = 0; i < 8; ++i) {
+ const Vector3 vertex = box.corner(i);
+
+ float x = vertex.dot(N) + (float)d;
+
+ if (x <= 0) {
+ // All vertices below the plane should be contact points.
+ contactPoints.append(vertex);
+ contactNormals.append(-N);
+ }
+
+ lowest = min(lowest, x);
+ }
+
+ // Depth should be a positive number
+ return -lowest;
+}
+
+
+float CollisionDetection::collisionTimeForMovingPointFixedPlane(
+ const Vector3& point,
+ const Vector3& velocity,
+ const Plane& plane,
+ Vector3& location,
+ Vector3& outNormal) {
+
+ // Solve for the time at which normal.dot(point + velocity) + d == 0.
+ double d;
+ Vector3 normal;
+ plane.getEquation(normal, d);
+
+ float vdotN = velocity.dot(normal);
+ float pdotN = point.dot(normal);
+
+ if (fuzzyEq(pdotN + d, 0)) {
+ // The point is *in* the plane.
+ location = point;
+ outNormal = normal;
+ return 0;
+ }
+
+ if (vdotN >= 0) {
+ // no collision will occur
+ location = Vector3::inf();
+ return (float)inf();
+ }
+
+ float t = -(pdotN + d) / vdotN;
+ if (t < 0) {
+ location = Vector3::inf();
+ return (float)inf();
+ } else {
+ location = point + velocity * t;
+ outNormal = normal;
+ return t;
+ }
+}
+
+
+float CollisionDetection::collisionTimeForMovingPointFixedSphere(
+ const Vector3& point,
+ const Vector3& velocity,
+ const Sphere& sphere,
+ Vector3& location,
+ Vector3& outNormal) {
+
+ double speed = velocity.magnitude();
+ Vector3 direction = velocity / speed;
+
+ Vector3 L = sphere.center - point;
+ double d = L.dot(direction);
+
+ double L2 = L.dot(L);
+ double R2 = sphere.radius * sphere.radius;
+ double D2 = d * d;
+
+ if ((d < 0) && (L2 > R2)) {
+ location = Vector3::inf();
+ return inf();
+ }
+
+ double M2 = L2 - D2;
+
+ if (M2 > R2) {
+ location = Vector3::inf();
+ return inf();
+ }
+
+ double q = sqrt(R2 - M2);
+ double time;
+
+ if (L2 > R2) {
+ time = d - q;
+ } else {
+ time = d + q;
+ }
+
+ time /= speed;
+
+ location = point + velocity * time;
+ outNormal = (location - sphere.center).direction();
+
+ return time;
+}
+
+
+float CollisionDetection::collisionTimeForMovingSphereFixedSphere(
+ const Sphere& movingSphere,
+ const Vector3& velocity,
+ const Sphere& fixedSphere,
+ Vector3& location,
+ Vector3& outNormal) {
+
+ double time = collisionTimeForMovingPointFixedSphere(movingSphere.center, velocity, Sphere(fixedSphere.center, fixedSphere.radius + movingSphere.radius), location, outNormal);
+
+ if (time < inf()) {
+ // Location is now the center of the moving sphere at the collision time.
+ // Adjust for the size of the moving sphere. Two spheres always collide
+ // along a line between their centers.
+ location += (location - fixedSphere.center) * movingSphere.radius / fixedSphere.radius;
+ }
+
+ return time;
+}
+
+
+/*
+float CollisionDetection::collisionTimeForMovingPointFixedTriangle(
+ const Vector3& point,
+ const Vector3& velocity,
+ const Triangle& triangle,
+ Vector3& outLocation,
+ Vector3& outNormal) {
+
+ double time = collisionTimeForMovingPointFixedPlane(point, velocity, triangle.plane(), outLocation, outNormal);
+
+ if (time == inf()) {
+ // No collision with the plane of the triangle.
+ return inf();
+ }
+
+ if (isPointInsideTriangle(triangle.vertex(0), triangle.vertex(1), triangle.vertex(2), triangle.normal(), outLocation, triangle.primaryAxis())) {
+ // Collision occured inside the triangle
+ return time;
+ } else {
+ // Missed the triangle
+ outLocation = Vector3::inf();
+ return inf();
+ }
+}*/
+
+/*
+float CollisionDetection::collisionTimeForMovingPointFixedTriangle(
+ const Vector3& orig,
+ const Vector3& dir,
+ const Vector3& vert0,
+ const Vector3& vert1,
+ const Vector3& vert2) {
+
+ // Barycenteric coords
+ double u, v;
+ #define EPSILON 0.000001
+ #define CROSS(dest,v1,v2) \
+ dest[0]=v1[1]*v2[2]-v1[2]*v2[1]; \
+ dest[1]=v1[2]*v2[0]-v1[0]*v2[2]; \
+ dest[2]=v1[0]*v2[1]-v1[1]*v2[0];
+
+ #define DOT(v1,v2) (v1[0]*v2[0]+v1[1]*v2[1]+v1[2]*v2[2])
+
+ #define SUB(dest,v1,v2) \
+ dest[0]=v1[0]-v2[0]; \
+ dest[1]=v1[1]-v2[1]; \
+ dest[2]=v1[2]-v2[2];
+
+ double edge1[3], edge2[3], tvec[3], pvec[3], qvec[3];
+
+ // find vectors for two edges sharing vert0
+ SUB(edge1, vert1, vert0);
+ SUB(edge2, vert2, vert0);
+
+ // begin calculating determinant - also used to calculate U parameter
+ CROSS(pvec, dir, edge2);
+
+ // if determinant is near zero, ray lies in plane of triangle
+ const double det = DOT(edge1, pvec);
+
+ if (det < EPSILON) {
+ return inf();
+ }
+
+ // calculate distance from vert0 to ray origin
+ SUB(tvec, orig, vert0);
+
+ // calculate U parameter and test bounds
+ u = DOT(tvec, pvec);
+ if ((u < 0.0) || (u > det)) {
+ // Hit the plane outside the triangle
+ return inf();
+ }
+
+ // prepare to test V parameter
+ CROSS(qvec, tvec, edge1);
+
+ // calculate V parameter and test bounds
+ v = DOT(dir, qvec);
+ if ((v < 0.0) || (u + v > det)) {
+ // Hit the plane outside the triangle
+ return inf();
+ }
+
+ // calculate t, scale parameters, ray intersects triangle
+ // If we want u,v, we can compute this
+ // double t = DOT(edge2, qvec);
+ //const double inv_det = 1.0 / det;
+ //t *= inv_det;
+ //u *= inv_det;
+ //v *= inv_det;
+ // return t;
+
+ // Case where we don't need correct (u, v):
+
+ const double t = DOT(edge2, qvec);
+
+ if (t >= 0) {
+ // Note that det must be positive
+ return t / det;
+ } else {
+ // We had to travel backwards in time to intersect
+ return inf();
+ }
+
+ #undef EPSILON
+ #undef CROSS
+ #undef DOT
+ #undef SUB
+}
+*/
+
+float CollisionDetection::collisionTimeForMovingPointFixedBox(
+ const Vector3& point,
+ const Vector3& velocity,
+ const Box& box,
+ Vector3& location,
+ Vector3& outNormal) {
+
+ double bestTime;
+
+ Vector3 normal;
+ Vector3 v[4];
+
+ // Prime the loop
+ int f = 0;
+ box.getFaceCorners(f, v[0], v[1], v[2], v[3]);
+ bestTime = collisionTimeForMovingPointFixedRectangle(point, velocity, v[0], v[1], v[2], v[3], location, normal);
+ outNormal = normal;
+
+ // Check other faces
+ for (f = 1; f < 6; ++f) {
+ Vector3 pos;
+ box.getFaceCorners(f, v[0], v[1], v[2], v[3]);
+ float time = collisionTimeForMovingPointFixedRectangle(point, velocity, v[0], v[1], v[2], v[3], pos, normal);
+ if (time < bestTime) {
+ bestTime = time;
+ outNormal = normal;
+ location = pos;
+ }
+ }
+
+ return bestTime;
+}
+
+
+float CollisionDetection::collisionTimeForMovingPointFixedAABox(
+ const Vector3& origin,
+ const Vector3& dir,
+ const AABox& box,
+ Vector3& location,
+ bool& Inside,
+ Vector3& normal) {
+
+ if (collisionLocationForMovingPointFixedAABox(origin, dir, box, location, Inside, normal)) {
+ return (location - origin).magnitude();
+ } else {
+ return (float)inf();
+ }
+}
+
+
+bool CollisionDetection::collisionLocationForMovingPointFixedAABox(
+ const Vector3& origin,
+ const Vector3& dir,
+ const AABox& box,
+ Vector3& location,
+ bool& Inside,
+ Vector3& normal) {
+
+ // Integer representation of a floating-point value.
+ #define IR(x) ((uint32&)x)
+
+ Inside = true;
+ const Vector3& MinB = box.low();
+ const Vector3& MaxB = box.high();
+ Vector3 MaxT(-1.0f, -1.0f, -1.0f);
+
+ // Find candidate planes.
+ for (int i = 0; i < 3; ++i) {
+ if (origin[i] < MinB[i]) {
+ location[i] = MinB[i];
+ Inside = false;
+
+ // Calculate T distances to candidate planes
+ if (IR(dir[i])) {
+ MaxT[i] = (MinB[i] - origin[i]) / dir[i];
+ }
+ } else if (origin[i] > MaxB[i]) {
+ location[i] = MaxB[i];
+ Inside = false;
+
+ // Calculate T distances to candidate planes
+ if (IR(dir[i])) {
+ MaxT[i] = (MaxB[i] - origin[i]) / dir[i];
+ }
+ }
+ }
+
+ if (Inside) {
+ // Ray origin inside bounding box
+ location = origin;
+ return false;
+ }
+
+ // Get largest of the maxT's for final choice of intersection
+ int WhichPlane = 0;
+ if (MaxT[1] > MaxT[WhichPlane]) {
+ WhichPlane = 1;
+ }
+
+ if (MaxT[2] > MaxT[WhichPlane]) {
+ WhichPlane = 2;
+ }
+
+ // Check final candidate actually inside box
+ if (IR(MaxT[WhichPlane]) & 0x80000000) {
+ // Miss the box
+ return false;
+ }
+
+ for (int i = 0; i < 3; ++i) {
+ if (i != WhichPlane) {
+ location[i] = origin[i] + MaxT[WhichPlane] * dir[i];
+ if ((location[i] < MinB[i]) ||
+ (location[i] > MaxB[i])) {
+ // On this plane we're outside the box extents, so
+ // we miss the box
+ return false;
+ }
+ }
+ }
+
+ // Choose the normal to be the plane normal facing into the ray
+ normal = Vector3::zero();
+ normal[WhichPlane] = (dir[WhichPlane] > 0) ? -1.0 : 1.0;
+
+ return true;
+
+ #undef IR
+}
+
+
+
+float CollisionDetection::collisionTimeForMovingPointFixedRectangle(
+ const Vector3& point,
+ const Vector3& velocity,
+ const Vector3& v0,
+ const Vector3& v1,
+ const Vector3& v2,
+ const Vector3& v3,
+ Vector3& location,
+ Vector3& outNormal) {
+
+ Plane plane = Plane(v0, v1, v2);
+
+ float time = collisionTimeForMovingPointFixedPlane(point, velocity, plane, location, outNormal);
+
+ if (time == inf()) {
+ // No collision is ever going to happen
+ return time;
+ }
+
+ if (isPointInsideRectangle(v0, v1, v2, v3, plane.normal(), location)) {
+ // The intersection point is inside the rectangle; that is the location where
+ // the point hits the rectangle.
+ return time;
+ } else {
+ return inf();
+ }
+}
+
+/** Used by findRayCapsuleIntersection.
+ @cite From magic software http://www.magic-software.com/Source/Intersection3D/MgcIntr3DLinCap.cpp */
+static int findRayCapsuleIntersectionAux(
+ const Vector3& rkOrigin,
+ const Vector3& rkDirection,
+ const Capsule& rkCapsule,
+ double afT[2]) {
+
+ Vector3 capsuleDirection = rkCapsule.point(1) - rkCapsule.point(0);
+
+ // set up quadratic Q(t) = a*t^2 + 2*b*t + c
+ Vector3 kU, kV, kW = capsuleDirection;
+ float fWLength = kW.unitize();
+ Vector3::generateOrthonormalBasis(kU, kV, kW);
+ Vector3 kD(kU.dot(rkDirection), kV.dot(rkDirection), kW.dot(rkDirection));
+ float fDLength = kD.unitize();
+
+ float fEpsilon = 1e-6f;
+
+ float fInvDLength = 1.0f/fDLength;
+ Vector3 kDiff = rkOrigin - rkCapsule.point(0);
+ Vector3 kP(kU.dot(kDiff),kV.dot(kDiff),kW.dot(kDiff));
+ float fRadiusSqr = square(rkCapsule.radius());
+
+ float fInv, fA, fB, fC, fDiscr, fRoot, fT, fTmp;
+
+ // Is the velocity parallel to the capsule direction? (or zero)
+ if ((abs(kD.z) >= 1.0f - fEpsilon) || (fDLength < fEpsilon)) {
+
+ float fAxisDir = rkDirection.dot(capsuleDirection);
+
+ fDiscr = fRadiusSqr - kP.x*kP.x - kP.y*kP.y;
+ if ((fAxisDir < 0) && (fDiscr >= 0.0f)) {
+ // Velocity anti-parallel to the capsule direction
+ fRoot = sqrt(fDiscr);
+ afT[0] = (kP.z + fRoot)*fInvDLength;
+ afT[1] = -(fWLength - kP.z + fRoot)*fInvDLength;
+ return 2;
+ } else if ((fAxisDir > 0) && (fDiscr >= 0.0f)) {
+ // Velocity parallel to the capsule direction
+ fRoot = sqrt(fDiscr);
+ afT[0] = -(kP.z + fRoot)*fInvDLength;
+ afT[1] = (fWLength - kP.z + fRoot)*fInvDLength;
+ return 2;
+ } else {
+ // sphere heading wrong direction, or no velocity at all
+ return 0;
+ }
+ }
+
+ // test intersection with infinite cylinder
+ fA = kD.x*kD.x + kD.y*kD.y;
+ fB = kP.x*kD.x + kP.y*kD.y;
+ fC = kP.x*kP.x + kP.y*kP.y - fRadiusSqr;
+ fDiscr = fB*fB - fA*fC;
+ if (fDiscr < 0.0f) {
+ // line does not intersect infinite cylinder
+ return 0;
+ }
+
+ int iQuantity = 0;
+
+ if (fDiscr > 0.0f) {
+ // line intersects infinite cylinder in two places
+ fRoot = sqrt(fDiscr);
+ fInv = 1.0f/fA;
+ fT = (-fB - fRoot)*fInv;
+ fTmp = kP.z + fT*kD.z;
+ if ((0.0f <= fTmp) && (fTmp <= fWLength)) {
+ afT[iQuantity] = fT * fInvDLength;
+ iQuantity++;
+ }
+
+ fT = (-fB + fRoot)*fInv;
+ fTmp = kP.z + fT*kD.z;
+
+ if ((0.0f <= fTmp) && (fTmp <= fWLength)) {
+ afT[iQuantity++] = fT*fInvDLength;
+ }
+
+ if (iQuantity == 2) {
+ // line intersects capsule wall in two places
+ return 2;
+ }
+ } else {
+ // line is tangent to infinite cylinder
+ fT = -fB/fA;
+ fTmp = kP.z + fT*kD.z;
+ if ((0.0f <= fTmp) && (fTmp <= fWLength)) {
+ afT[0] = fT*fInvDLength;
+ return 1;
+ }
+ }
+
+ // test intersection with bottom hemisphere
+ // fA = 1
+ fB += kP.z*kD.z;
+ fC += kP.z*kP.z;
+ fDiscr = fB*fB - fC;
+ if (fDiscr > 0.0f) {
+ fRoot = sqrt(fDiscr);
+ fT = -fB - fRoot;
+ fTmp = kP.z + fT*kD.z;
+ if (fTmp <= 0.0f) {
+ afT[iQuantity++] = fT*fInvDLength;
+ if (iQuantity == 2) {
+ return 2;
+ }
+ }
+
+ fT = -fB + fRoot;
+ fTmp = kP.z + fT*kD.z;
+ if (fTmp <= 0.0f) {
+ afT[iQuantity++] = fT*fInvDLength;
+ if (iQuantity == 2) {
+ return 2;
+ }
+ }
+ } else if (fDiscr == 0.0f) {
+ fT = -fB;
+ fTmp = kP.z + fT*kD.z;
+ if (fTmp <= 0.0f) {
+ afT[iQuantity++] = fT*fInvDLength;
+ if (iQuantity == 2) {
+ return 2;
+ }
+ }
+ }
+
+ // test intersection with top hemisphere
+ // fA = 1
+ fB -= kD.z*fWLength;
+ fC += fWLength*(fWLength - 2.0f*kP.z);
+
+ fDiscr = fB*fB - fC;
+ if (fDiscr > 0.0f) {
+ fRoot = sqrt(fDiscr);
+ fT = -fB - fRoot;
+ fTmp = kP.z + fT*kD.z;
+ if (fTmp >= fWLength) {
+ afT[iQuantity++] = fT*fInvDLength;
+ if (iQuantity == 2) {
+ return 2;
+ }
+ }
+
+ fT = -fB + fRoot;
+ fTmp = kP.z + fT*kD.z;
+ if (fTmp >= fWLength) {
+ afT[iQuantity++] = fT*fInvDLength;
+ if (iQuantity == 2) {
+ return 2;
+ }
+ }
+ } else if (fDiscr == 0.0f) {
+ fT = -fB;
+ fTmp = kP.z + fT*kD.z;
+ if (fTmp >= fWLength) {
+ afT[iQuantity++] = fT*fInvDLength;
+ if (iQuantity == 2) {
+ return 2;
+ }
+ }
+ }
+
+ return iQuantity;
+}
+
+
+/** Used by collisionTimeForMovingPointFixedCapsule.
+ @cite From magic software http://www.magic-software.com/Source/Intersection3D/MgcIntr3DLinCap.cpp
+
+ @param rkRay The ray
+ @param rkCapsule The capsule
+ @param riQuantity The number of intersections found
+ @param akPoint The intersections found
+ @return True if there is at least one intersection
+ */
+static bool findRayCapsuleIntersection(
+ const Ray& rkRay,
+ const Capsule& rkCapsule,
+ int& riQuantity,
+ Vector3 akPoint[2]) {
+
+ double afT[2];
+ riQuantity = findRayCapsuleIntersectionAux(rkRay.origin, rkRay.direction, rkCapsule, afT);
+
+ // Only return intersections that occur in the future
+ int iClipQuantity = 0;
+ int i;
+ for (i = 0; i < riQuantity; i++) {
+ if (afT[i] >= 0.0f) {
+ akPoint[iClipQuantity] = rkRay.origin + afT[i] * rkRay.direction;
+ iClipQuantity++;
+ }
+ }
+
+ riQuantity = iClipQuantity;
+ return (riQuantity > 0);
+}
+
+float CollisionDetection::collisionTimeForMovingPointFixedCapsule(
+ const Vector3& _point,
+ const Vector3& velocity,
+ const Capsule& capsule,
+ Vector3& location,
+ Vector3& outNormal) {
+
+ float timeScale = velocity.magnitude();
+
+ if (timeScale == 0.0f) {
+ timeScale = 1;
+ }
+
+ Vector3 direction = velocity / timeScale;
+ int numIntersections;
+ Vector3 intersection[2];
+ findRayCapsuleIntersection(Ray::fromOriginAndDirection(_point, direction), capsule, numIntersections, intersection);
+
+ if (numIntersections == 2) {
+ // A collision can only occur if there are two intersections. If there is one
+ // intersection, that one is exiting the capsule.
+
+ // Find the entering intersection (the first one that occurs).
+ float d0 = (intersection[0] - _point).squaredMagnitude();
+ float d1 = (intersection[1] - _point).squaredMagnitude();
+
+ // Compute the surface normal (if we aren't ignoring the result)
+ if (&outNormal != &ignore) {
+ Vector3 p2 = LineSegment::fromTwoPoints(capsule.point(0), capsule.point(1)).closestPoint(_point);
+ outNormal = (_point - p2).direction();
+ }
+
+ if (d0 > d1) {
+ location = intersection[1];
+ return sqrt(d1) / timeScale;
+ } else {
+ location = intersection[0];
+ return sqrt(d0) / timeScale;
+ }
+ } else {
+ // No entering intersection discovered; return no intersection.
+ location = Vector3::inf();
+ return inf();
+ }
+}
+
+
+float CollisionDetection::collisionTimeForMovingSphereFixedPlane(
+ const Sphere& sphere,
+ const Vector3& velocity,
+ const Plane& plane,
+ Vector3& location,
+ Vector3& outNormal) {
+
+ if (sphere.radius == 0) {
+ // Optimization for zero radius sphere
+ return collisionTimeForMovingPointFixedPlane(sphere.center, velocity, plane, location, outNormal);
+ }
+
+ // The collision point on the sphere will be the point at
+ // center - (radius * normal). Collisions only occur when
+ // the sphere is travelling into the plane.
+
+ double d;
+ plane.getEquation(outNormal, d);
+
+ double vdotN = velocity.dot(outNormal);
+
+ if (fuzzyGt(vdotN, 0)) {
+ // No collision when the sphere is moving towards a backface.
+ location = Vector3::inf();
+ return (float)inf();
+ }
+
+ float cdotN = sphere.center.dot(outNormal);
+
+ // Distance from the center to the plane
+ float distance = cdotN + (float)d;
+
+ // Where is the collision on the sphere?
+ Vector3 point = sphere.center - (sphere.radius * outNormal);
+
+ if (fuzzyLe(G3D::abs(distance), sphere.radius)) {
+ // Already interpenetrating
+ location = sphere.center - distance * outNormal;
+ return 0;
+ } else {
+ return collisionTimeForMovingPointFixedPlane(point, velocity, plane, location, outNormal);
+ }
+
+}
+
+
+float CollisionDetection::collisionTimeForMovingSphereFixedTriangle(
+ const class Sphere& sphere,
+ const Vector3& velocity,
+ const Triangle& triangle,
+ Vector3& outLocation,
+ float b[3]) {
+
+ Vector3 dummy;
+ float time = collisionTimeForMovingSphereFixedPlane(sphere, velocity, triangle.plane(),
+ outLocation, dummy);
+
+ if (time == inf()) {
+ // No collision is ever going to happen
+ return time;
+ }
+
+ // We will hit the plane of the triangle at *time*. See if
+ // the intersection point actually is within the triangle.
+
+ if (isPointInsideTriangle(triangle.vertex(0), triangle.vertex(1), triangle.vertex(2), triangle.normal(),
+ outLocation, b, triangle.primaryAxis())) {
+
+ // The intersection point is inside the triangle; that is the location where
+ // the sphere hits the triangle.
+
+# ifdef G3D_DEBUG
+ {
+ // Internal consistency checks
+ debugAssertM(b[0] >= 0.0 && b[0] <= 1.0f, "Intersection is outside triangle.");
+ debugAssertM(b[1] >= 0.0 && b[1] <= 1.0f, "Intersection is outside triangle.");
+ debugAssertM(b[2] >= 0.0 && b[2] <= 1.0f, "Intersection is outside triangle.");
+ Vector3 blend =
+ b[0] * triangle.vertex(0) +
+ b[1] * triangle.vertex(1) +
+ b[2] * triangle.vertex(2);
+ debugAssertM(blend.fuzzyEq(outLocation), "Barycentric coords don't match intersection.");
+ // Call again so that we can debug the problem
+ // isPointInsideTriangle(triangle.vertex(0), triangle.vertex(1), triangle.vertex(2), triangle.normal(),
+ // outLocation, b, triangle.primaryAxis());
+ }
+# endif
+
+ return time;
+ }
+
+ // The collision (if it exists) is with a point on the triangle perimeter.
+ // Switch over to moving the triangle towards a fixed sphere and see at what time
+ // they will hit.
+
+ // Closest point on the triangle to the sphere intersection with the plane.
+ int edgeIndex;
+ const Vector3& point = closestPointOnTrianglePerimeter(triangle._vertex, triangle.edgeDirection,
+ triangle.edgeMagnitude, outLocation, edgeIndex);
+
+ float t = 0;
+ if (! sphere.contains(point)) {
+ // The point is outside the sphere--see when it will hit
+ t = collisionTimeForMovingPointFixedSphere(point, -velocity, sphere, dummy, dummy);
+ }
+
+ if (t < inf()) {
+ outLocation = point;
+ // Compute Barycentric coords
+
+ // Index of the next vertex
+ static const int next[] = {1, 2, 0};
+
+ // Project along the edge in question.
+ // Avoid sqrt by taking advantage of the existing edgeDirection unit vector.
+ b[next[edgeIndex]] = (outLocation - triangle._vertex[edgeIndex]).dot
+ (triangle.edgeDirection[edgeIndex]) / triangle.edgeMagnitude[edgeIndex];
+
+ b[edgeIndex] = 1.0f - b[next[edgeIndex]];
+
+ b[next[next[edgeIndex]]] = 0.0f;
+
+# ifdef G3D_DEBUG
+ {
+ // Internal consistency checks
+ for (int i = 0; i < 3; ++i) {
+ debugAssertM(fuzzyGe(b[i], 0.0f) && fuzzyLe(b[i], 1.0f), "Intersection is outside triangle.");
+ }
+ Vector3 blend =
+ b[0] * triangle.vertex(0) +
+ b[1] * triangle.vertex(1) +
+ b[2] * triangle.vertex(2);
+ debugAssertM(blend.fuzzyEq(outLocation),
+ format("Barycentric coords don't match intersection. %s != %s",
+ blend.toString().c_str(),
+ outLocation.toString().c_str()));
+
+ // Call again so that we can debug the problem
+ collisionTimeForMovingPointFixedSphere(point, -velocity, sphere, dummy, dummy);
+ }
+# endif
+
+ // Due to tiny roundoffs, these values might be slightly out of bounds.
+ // Ensure that they are legal. Note that the above debugging code
+ // verifies that we are not clamping truly illegal values.
+ for (int i = 0; i < 3; ++i) {
+ b[i] = clamp(b[i], 0.0f, 1.0f);
+ }
+ }
+
+ // The collision occured at the point, if it occured. The normal
+ // was the plane normal, computed above.
+
+ return t;
+}
+
+
+float CollisionDetection::collisionTimeForMovingSphereFixedRectangle(
+ const Sphere& sphere,
+ const Vector3& velocity,
+ const Vector3& v0,
+ const Vector3& v1,
+ const Vector3& v2,
+ const Vector3& v3,
+ Vector3& location,
+ Vector3& outNormal) {
+
+ Plane plane(v0, v1, v2);
+
+ float time = collisionTimeForMovingSphereFixedPlane(sphere, velocity, plane, location, outNormal);
+
+ if (time == inf()) {
+ // No collision is ever going to happen
+ return time;
+ }
+
+ if (isPointInsideRectangle(v0, v1, v2, v3, plane.normal(), location)) {
+ // The intersection point is inside the rectangle; that is the location where
+ // the sphere hits the rectangle.
+ return time;
+ }
+
+ // Switch over to moving the rectangle towards a fixed sphere and see at what time
+ // they will hit.
+
+ Vector3 point = closestPointToRectanglePerimeter(v0, v1, v2, v3, sphere.center);
+
+ Vector3 dummy;
+ double t = collisionTimeForMovingPointFixedSphere(point, -velocity, sphere, location, dummy);
+
+ // Normal is the plane normal, location is the original location of the point.
+ location = point;
+
+ return t;
+}
+
+
+float CollisionDetection::collisionTimeForMovingSphereFixedBox(
+ const Sphere& sphere,
+ const Vector3& velocity,
+ const Box& box,
+ Vector3& location,
+ Vector3& outNormal) {
+
+ if (fixedSolidSphereIntersectsFixedSolidBox(sphere, box)) {
+ // TODO: Compute more useful location and normal?
+ location = sphere.center;
+ outNormal = Vector3::zero();
+ return 0;
+ }
+
+ float bestTime;
+
+ Vector3 v[4];
+ int f = 0;
+ box.getFaceCorners(f, v[0], v[1], v[2], v[3]);
+ bestTime = collisionTimeForMovingSphereFixedRectangle(sphere, velocity, v[0], v[1], v[2], v[3], location, outNormal);
+
+ for (f = 1; f < 6; ++f) {
+ Vector3 pos, normal;
+ box.getFaceCorners(f, v[0], v[1], v[2], v[3]);
+ float time = collisionTimeForMovingSphereFixedRectangle(sphere, velocity, v[0], v[1], v[2], v[3], pos, normal);
+ if (time < bestTime) {
+ bestTime = time;
+ location = pos;
+ outNormal = normal;
+ }
+ }
+
+ return bestTime;
+}
+
+
+float CollisionDetection::collisionTimeForMovingSphereFixedCapsule(
+ const Sphere& sphere,
+ const Vector3& velocity,
+ const Capsule& capsule,
+ Vector3& location,
+ Vector3& outNormal) {
+
+ (void)outNormal;
+
+ Capsule _capsule(capsule.point(0), capsule.point(1), capsule.radius() + sphere.radius);
+
+ Vector3 normal;
+ double time = collisionTimeForMovingPointFixedCapsule(sphere.center, velocity, _capsule, location, normal);
+
+ if (time < inf()) {
+ // Location is now the position of the center of the sphere at the time of collision.
+ // We have to adjust the collision location for the size of the sphere.
+ location -= sphere.radius * normal;
+ }
+
+ return time;
+}
+
+
+Vector3 CollisionDetection::bounceDirection(
+ const Sphere& sphere,
+ const Vector3& velocity,
+ const float collisionTime,
+ const Vector3& collisionLocation,
+ const Vector3& collisionNormal) {
+
+ // Location when the collision occurs
+ Vector3 sphereLocation = sphere.center + velocity * collisionTime;
+
+ Vector3 normal = (sphereLocation - collisionLocation);
+ if (fuzzyEq(normal.squaredMagnitude(), 0)) {
+ normal = collisionNormal;
+ } else {
+ normal.unitize();
+ }
+
+ Vector3 direction = velocity.direction();
+
+ // Reflect direction about the normal
+ return direction - 2.0 * normal * normal.dot(direction);
+}
+
+
+Vector3 CollisionDetection::slideDirection(
+ const Sphere& sphere,
+ const Vector3& velocity,
+ const float collisionTime,
+ const Vector3& collisionLocation) {
+
+ Vector3 sphereLocation = sphere.center + velocity * collisionTime;
+ Vector3 normal = (sphereLocation - collisionLocation).direction();
+ Vector3 direction = velocity.direction();
+
+ // subtract off the part in the direction away from the normal.
+ return direction - normal * normal.dot(direction);
+}
+
+
+Vector3 CollisionDetection::closestPointOnLineSegment(
+ const Vector3& v0,
+ const Vector3& v1,
+ const Vector3& point) {
+
+ const Vector3& edge = (v1 - v0);
+ float edgeLength = edge.magnitude();
+
+ if (edgeLength == 0) {
+ // The line segment is a point
+ return v0;
+ }
+
+ return closestPointOnLineSegment(v0, v1, edge / edgeLength, edgeLength, point);
+}
+
+
+Vector3 CollisionDetection::closestPointOnLineSegment(
+ const Vector3& v0,
+ const Vector3& v1,
+ const Vector3& edgeDirection,
+ const float edgeLength,
+ const Vector3& point) {
+
+ debugAssert((v1 - v0).direction().fuzzyEq(edgeDirection));
+ debugAssert(fuzzyEq((v1 - v0).magnitude(), edgeLength));
+
+ // Vector towards the point
+ const Vector3& c = point - v0;
+
+ // Projected onto the edge itself
+ float t = edgeDirection.dot(c);
+
+ if (t <= 0) {
+ // Before the start
+ return v0;
+ } else if (t >= edgeLength) {
+ // After the end
+ return v1;
+ } else {
+ // At distance t along the edge
+ return v0 + edgeDirection * t;
+ }
+}
+
+
+Vector3 CollisionDetection::closestPointOnTrianglePerimeter(
+ const Vector3& v0,
+ const Vector3& v1,
+ const Vector3& v2,
+ const Vector3& point) {
+
+ Vector3 v[3] = {v0, v1, v2};
+ Vector3 edgeDirection[3] = {(v1 - v0), (v2 - v1), (v0 - v2)};
+ float edgeLength[3];
+
+ for (int i = 0; i < 3; ++i) {
+ edgeLength[i] = edgeDirection[i].magnitude();
+ edgeDirection[i] /= edgeLength[i];
+ }
+
+ int edgeIndex;
+ return closestPointOnTrianglePerimeter(v, edgeDirection, edgeLength, point, edgeIndex);
+}
+
+
+Vector3 CollisionDetection::closestPointOnTrianglePerimeter(
+ const Vector3 v[3],
+ const Vector3 edgeDirection[3],
+ const float edgeLength[3],
+ const Vector3& point,
+ int& edgeIndex) {
+
+ // Closest point on segment from v[i] to v[i + 1]
+ Vector3 r[3];
+
+ // Distance squared from r[i] to point
+ float d[3];
+
+ // Index of the next point
+ static const int next[] = {1, 2, 0};
+
+ for (int i = 0; i < 3; ++i) {
+ r[i] = closestPointOnLineSegment(v[i], v[next[i]], edgeDirection[i], edgeLength[i], point);
+ d[i] = (r[i] - point).squaredMagnitude();
+ }
+
+ if (d[0] < d[1]) {
+ if (d[0] < d[2]) {
+ // Between v0 and v1
+ edgeIndex = 0;
+ } else {
+ // Between v2 and v0
+ edgeIndex = 2;
+ }
+ } else {
+ if (d[1] < d[2]) {
+ // Between v1 and v2
+ edgeIndex = 1;
+ } else {
+ // Between v2 and v0
+ edgeIndex = 2;
+ }
+ }
+
+# ifdef G3D_DEBUG
+ {
+ Vector3 diff = r[edgeIndex] - v[edgeIndex];
+ debugAssertM(fuzzyEq(diff.direction().dot(edgeDirection[edgeIndex]), 1.0f) ||
+ diff.fuzzyEq(Vector3::zero()), "Point not on correct triangle edge");
+ float frac = diff.dot(edgeDirection[edgeIndex])/edgeLength[edgeIndex];
+ debugAssertM(frac >= -0.000001, "Point off low side of edge.");
+ debugAssertM(frac <= 1.000001, "Point off high side of edge.");
+ }
+# endif
+
+ return r[edgeIndex];
+}
+
+
+bool CollisionDetection::isPointInsideTriangle(
+ const Vector3& v0,
+ const Vector3& v1,
+ const Vector3& v2,
+ const Vector3& normal,
+ const Vector3& point,
+ float b[3],
+ Vector3::Axis primaryAxis) {
+
+ if (primaryAxis == Vector3::DETECT_AXIS) {
+ primaryAxis = normal.primaryAxis();
+ }
+
+ // Check that the point is within the triangle using a Barycentric
+ // coordinate test on a two dimensional plane.
+ int i, j;
+
+ switch (primaryAxis) {
+ case Vector3::X_AXIS:
+ i = Vector3::Y_AXIS;
+ j = Vector3::Z_AXIS;
+ break;
+
+ case Vector3::Y_AXIS:
+ i = Vector3::Z_AXIS;
+ j = Vector3::X_AXIS;
+ break;
+
+ case Vector3::Z_AXIS:
+ i = Vector3::X_AXIS;
+ j = Vector3::Y_AXIS;
+ break;
+
+ default:
+ // This case is here to supress a warning on Linux
+ i = j = 0;
+ debugAssertM(false, "Should not get here.");
+ break;
+ }
+
+ // See if all barycentric coordinates are non-negative
+
+ // 2D area via cross product
+# define AREA2(d, e, f) (((e)[i] - (d)[i]) * ((f)[j] - (d)[j]) - ((f)[i] - (d)[i]) * ((e)[j] - (d)[j]))
+
+ // Area of the polygon
+ float area = AREA2(v0, v1, v2);
+ if (area == 0) {
+ // This triangle has zero area, so the point must not
+ // be in it unless the triangle point is the test point.
+ return (v0 == point);
+ }
+
+ debugAssert(area != 0);
+
+ float invArea = 1.0f / area;
+
+ // (avoid normalization until absolutely necessary)
+ b[0] = AREA2(point, v1, v2) * invArea;
+
+ if ((b[0] < 0.0f) || (b[0] > 1.0f)) {
+ return false;
+ }
+
+ b[1] = AREA2(v0, point, v2) * invArea;
+ if ((b[1] < 0.0f) || (b[1] > 1.0f)) {
+ return false;
+ }
+
+ b[2] = 1.0f - b[0] - b[1];
+
+# undef AREA2
+
+ return (b[2] >= 0.0f) && (b[2] <= 1.0f);
+}
+
+
+bool CollisionDetection::isPointInsideRectangle(
+ const Vector3& v0,
+ const Vector3& v1,
+ const Vector3& v2,
+ const Vector3& v3,
+ const Vector3& normal,
+ const Vector3& point) {
+
+ return isPointInsideTriangle(v0, v1, v2, normal, point) ||
+ isPointInsideTriangle(v2, v3, v0, normal, point);
+}
+
+
+Vector3 CollisionDetection::closestPointToRectanglePerimeter(
+ const Vector3& v0,
+ const Vector3& v1,
+ const Vector3& v2,
+ const Vector3& v3,
+ const Vector3& point) {
+
+ Vector3 r0 = closestPointOnLineSegment(v0, v1, point);
+ Vector3 r1 = closestPointOnLineSegment(v1, v2, point);
+ Vector3 r2 = closestPointOnLineSegment(v2, v3, point);
+ Vector3 r3 = closestPointOnLineSegment(v3, v0, point);
+
+ double d0 = (r0 - point).squaredMagnitude();
+ double d1 = (r1 - point).squaredMagnitude();
+ double d2 = (r2 - point).squaredMagnitude();
+ double d3 = (r3 - point).squaredMagnitude();
+
+ if (d0 < d1) {
+ if (d0 < d2) {
+ if (d0 < d3) {
+ return r0;
+ } else {
+ return r3;
+ }
+ } else {
+ if (d2 < d3) {
+ return r2;
+ } else {
+ return r3;
+ }
+ }
+ } else {
+ if (d1 < d2) {
+ if (d1 < d3) {
+ return r1;
+ } else {
+ return r3;
+ }
+ } else {
+ if (d2 < d3) {
+ return r2;
+ } else {
+ return r3;
+ }
+ }
+ }
+}
+
+
+Vector3 CollisionDetection::closestPointToRectangle(
+ const Vector3& v0,
+ const Vector3& v1,
+ const Vector3& v2,
+ const Vector3& v3,
+ const Vector3& point) {
+
+ Plane plane(v0, v1, v2);
+
+ // Project the point into the plane
+ double a, b, c, d;
+ plane.getEquation(a, b, c, d);
+
+ double distance = a*point.x + b*point.y + c*point.z + d;
+ Vector3 planePoint = point - distance * plane.normal();
+
+ if (isPointInsideRectangle(v0, v1, v2, v3, plane.normal(), planePoint)) {
+ return planePoint;
+ } else {
+ return closestPointToRectanglePerimeter(v0, v1, v2, v3, planePoint);
+ }
+}
+
+
+bool CollisionDetection::fixedSolidSphereIntersectsFixedSolidSphere(
+ const Sphere& sphere1,
+ const Sphere& sphere2) {
+
+ return (sphere1.center - sphere2.center).squaredMagnitude() < square(sphere1.radius + sphere2.radius);
+}
+
+
+bool CollisionDetection::fixedSolidSphereIntersectsFixedSolidBox(
+ const Sphere& sphere,
+ const Box& box) {
+
+ // If the center of the sphere is within the box, the whole
+ // sphere is within the box.
+ if (box.contains(sphere.center)) {
+ return true;
+ }
+
+ float r2 = square(sphere.radius);
+
+ // Find the closest point on the surface of the box to the sphere. If
+ // this point is within the sphere's radius, they intersect.
+ int f;
+ for (f = 0; f < 6; ++f) {
+ Vector3 v0, v1, v2, v3;
+ box.getFaceCorners(f, v0, v1, v2, v3);
+ if ((closestPointToRectangle(v0, v1, v2, v3, sphere.center) - sphere.center).squaredMagnitude() <= r2) {
+ return true;
+ }
+ }
+
+ return false;
+}
+
+
+bool CollisionDetection::movingSpherePassesThroughFixedBox(
+ const Sphere& sphere,
+ const Vector3& velocity,
+ const Box& box,
+ double timeLimit) {
+
+ // If they intersect originally, they definitely pass through each other.
+ if (fixedSolidSphereIntersectsFixedSolidBox(sphere, box)) {
+ return true;
+ }
+
+ // See if the sphere hits the box during the time period.
+ Vector3 dummy1, dummy2;
+
+ return (collisionTimeForMovingSphereFixedBox(sphere, velocity, box, dummy1, dummy2) < timeLimit);
+}
+
+
+bool CollisionDetection::movingSpherePassesThroughFixedSphere(
+ const Sphere& sphere,
+ const Vector3& velocity,
+ const Sphere& fixedSphere,
+ double timeLimit) {
+
+ if (fixedSolidSphereIntersectsFixedSolidSphere(sphere, fixedSphere)) {
+ return true;
+ }
+
+ // Extend the fixed sphere by the radius of the moving sphere
+ Sphere bigFixed(fixedSphere.center, fixedSphere.radius + sphere.radius);
+ Vector3 dummy1, dummy2;
+
+ // If the sphere collides with the other sphere during the time limit, it passes through
+ return (collisionTimeForMovingPointFixedSphere(sphere.center, velocity, bigFixed, dummy1, dummy2) < timeLimit);
+}
+
+
+
+bool CollisionDetection::fixedSolidSphereIntersectsFixedTriangle(
+ const Sphere& sphere,
+ const Triangle& triangle) {
+
+ // How far is the sphere from the plane of the triangle
+ const Plane& plane = triangle.plane();
+
+ // Does the closest point to the sphere center lie within the triangle?
+ Vector3 v = plane.closestPoint(sphere.center);
+
+ // Is the closest point to the plane within the sphere?
+ if ((v - sphere.center).squaredLength() <= square(sphere.radius)) {
+ // Is it also within the triangle?
+ float b[3];
+ if (isPointInsideTriangle(triangle.vertex(0), triangle.vertex(1), triangle.vertex(2), triangle.normal(),
+ v, b, triangle.primaryAxis())){
+ // The closest point is inside the triangle
+ return true;
+ }
+ }
+
+ // ignored
+ int edgeIndex;
+
+ v = closestPointOnTrianglePerimeter(triangle._vertex, triangle.edgeDirection, triangle.edgeMagnitude, sphere.center, edgeIndex);
+
+ // Is the closest point within the sphere?
+ return ((v - sphere.center).squaredLength() <= square(sphere.radius));
+}
+
+
+} // namespace
+#ifdef _MSC_VER
+#pragma warning (pop)
+#endif
diff --git a/externals/g3dlite/G3D.lib/source/Color1.cpp b/externals/g3dlite/G3D.lib/source/Color1.cpp
new file mode 100644
index 00000000000..7e058a27d1a
--- /dev/null
+++ b/externals/g3dlite/G3D.lib/source/Color1.cpp
@@ -0,0 +1,40 @@
+/**
+ @file Color1.cpp
+
+ Color class.
+
+ @author Morgan McGuire, matrix@graphics3d.com
+
+ @created 2007-01-30
+ @edited 2007-01-30
+ */
+
+#include "G3D/platform.h"
+#include "G3D/Color1.h"
+#include "G3D/Color1uint8.h"
+#include "G3D/BinaryInput.h"
+#include "G3D/BinaryOutput.h"
+
+namespace G3D {
+
+Color1::Color1(BinaryInput& bi) {
+ deserialize(bi);
+}
+
+
+void Color1::deserialize(BinaryInput& bi) {
+ value = bi.readFloat32();
+}
+
+
+void Color1::serialize(BinaryOutput& bo) const {
+ bo.writeFloat32(value);
+}
+
+
+Color1::Color1(const class Color1uint8& other) {
+ value = other.value / 255.0f;
+}
+
+} // namespace G3D
+
diff --git a/externals/g3dlite/G3D.lib/source/Color1uint8.cpp b/externals/g3dlite/G3D.lib/source/Color1uint8.cpp
new file mode 100644
index 00000000000..0fd00693d4c
--- /dev/null
+++ b/externals/g3dlite/G3D.lib/source/Color1uint8.cpp
@@ -0,0 +1,38 @@
+/**
+ @file Color1uint8.cpp
+
+ @author Morgan McGuire, matrix@graphics3d.com
+
+ @created 2007-01-30
+ @edited 2007-01-30
+ */
+
+#include "G3D/platform.h"
+#include "G3D/g3dmath.h"
+#include "G3D/Color1uint8.h"
+#include "G3D/Color1.h"
+#include "G3D/BinaryInput.h"
+#include "G3D/BinaryOutput.h"
+
+namespace G3D {
+
+Color1uint8::Color1uint8(const class Color1& c) : value(iClamp(iFloor(c.value * 256), 0, 255)) {
+}
+
+
+Color1uint8::Color1uint8(class BinaryInput& bi) {
+ deserialize(bi);
+}
+
+
+void Color1uint8::serialize(class BinaryOutput& bo) const {
+ bo.writeUInt8(value);
+}
+
+
+void Color1uint8::deserialize(class BinaryInput& bi) {
+ value = bi.readUInt8();
+}
+
+
+}
diff --git a/externals/g3dlite/G3D.lib/source/Color3.cpp b/externals/g3dlite/G3D.lib/source/Color3.cpp
new file mode 100644
index 00000000000..8183d8a0f62
--- /dev/null
+++ b/externals/g3dlite/G3D.lib/source/Color3.cpp
@@ -0,0 +1,321 @@
+/**
+ @file Color3.cpp
+
+ Color class.
+
+ @author Morgan McGuire, matrix@graphics3d.com
+ @cite Portions based on Dave Eberly's Magic Software Library at http://www.magic-software.com
+
+
+ @created 2001-06-02
+ @edited 2006-01-13
+ */
+
+#include "G3D/platform.h"
+#include <stdlib.h>
+#include "G3D/Color3.h"
+#include "G3D/Vector3.h"
+#include "G3D/format.h"
+#include "G3D/BinaryInput.h"
+#include "G3D/BinaryOutput.h"
+#include "G3D/Color3uint8.h"
+
+namespace G3D {
+
+const Color3& Color3::red() {
+ static Color3 c(1.0f, 0.0f, 0.0f);
+ return c;
+}
+
+
+const Color3& Color3::green() {
+ static Color3 c(0.0f, 1.0f, 0.0f);
+ return c;
+}
+
+
+const Color3& Color3::blue() {
+ static Color3 c(0.0f, 0.0f, 1.0f);
+ return c;
+}
+
+
+const Color3& Color3::purple() {
+ static Color3 c(0.7f, 0.0f, 1.0f);
+ return c;
+}
+
+
+const Color3& Color3::cyan() {
+ static Color3 c(0.0f, 0.7f, 1.0f);
+ return c;
+}
+
+
+const Color3& Color3::yellow() {
+ static Color3 c(1.0f, 1.0f, 0.0f);
+ return c;
+}
+
+
+const Color3& Color3::brown() {
+ static Color3 c(0.5f, 0.5f, 0.0f);
+ return c;
+}
+
+
+const Color3& Color3::orange() {
+ static Color3 c(1.0f, 0.5f, 0.0f);
+ return c;
+}
+
+
+const Color3& Color3::black() {
+ static Color3 c(0.0f, 0.0f, 0.0f);
+ return c;
+}
+
+const Color3& Color3::zero() {
+ static Color3 c(0.0f, 0.0f, 0.0f);
+ return c;
+}
+
+
+const Color3& Color3::one() {
+ static Color3 c(1.0f, 1.0f, 1.0f);
+ return c;
+}
+
+
+const Color3& Color3::gray() {
+ static Color3 c(0.7f, 0.7f, 0.7f);
+ return c;
+}
+
+
+const Color3& Color3::white() {
+ static Color3 c(1, 1, 1);
+ return c;
+}
+
+
+Color3::Color3(BinaryInput& bi) {
+ deserialize(bi);
+}
+
+
+void Color3::deserialize(BinaryInput& bi) {
+ r = bi.readFloat32();
+ g = bi.readFloat32();
+ b = bi.readFloat32();
+}
+
+
+void Color3::serialize(BinaryOutput& bo) const {
+ bo.writeFloat32(r);
+ bo.writeFloat32(g);
+ bo.writeFloat32(b);
+}
+
+
+const Color3& Color3::wheelRandom() {
+ static const Color3 colorArray[8] =
+ {Color3::blue(), Color3::red(), Color3::green(),
+ Color3::orange(), Color3::yellow(),
+ Color3::cyan(), Color3::purple(), Color3::brown()};
+
+ return colorArray[iRandom(0, 7)];
+}
+
+
+size_t Color3::hashCode() const {
+ unsigned int rhash = (*(int*)(void*)(&r));
+ unsigned int ghash = (*(int*)(void*)(&g));
+ unsigned int bhash = (*(int*)(void*)(&b));
+
+ return rhash + (ghash * 37) + (bhash * 101);
+}
+
+
+Color3::Color3(const Vector3& v) {
+ r = v.x;
+ g = v.y;
+ b = v.z;
+}
+
+
+Color3::Color3(const class Color3uint8& other) {
+ r = other.r / 255.0f;
+ g = other.g / 255.0f;
+ b = other.b / 255.0f;
+}
+
+
+Color3 Color3::fromARGB(uint32 x) {
+ return Color3((float)((x >> 16) & 0xFF), (float)((x >> 8) & 0xFF), (float)(x & 0xFF)) / 255.0f;
+}
+
+//----------------------------------------------------------------------------
+
+
+Color3 Color3::random() {
+ return Color3(uniformRandom(),
+ uniformRandom(),
+ uniformRandom()).direction();
+}
+
+//----------------------------------------------------------------------------
+Color3 Color3::operator/ (float fScalar) const {
+ Color3 kQuot;
+
+ if (fScalar != 0.0f) {
+ float fInvScalar = 1.0f / fScalar;
+ kQuot.r = fInvScalar * r;
+ kQuot.g = fInvScalar * g;
+ kQuot.b = fInvScalar * b;
+ return kQuot;
+
+ } else {
+
+ return Color3((float)G3D::inf(), (float)G3D::inf(), (float)G3D::inf());
+ }
+}
+
+//----------------------------------------------------------------------------
+Color3& Color3::operator/= (float fScalar) {
+ if (fScalar != 0.0f) {
+ float fInvScalar = 1.0f / fScalar;
+ r *= fInvScalar;
+ g *= fInvScalar;
+ b *= fInvScalar;
+ } else {
+ r = (float)G3D::inf();
+ g = (float)G3D::inf();
+ b = (float)G3D::inf();
+ }
+
+ return *this;
+}
+
+//----------------------------------------------------------------------------
+float Color3::unitize (float fTolerance) {
+ float fLength = length();
+
+ if ( fLength > fTolerance ) {
+ float fInvLength = 1.0f / fLength;
+ r *= fInvLength;
+ g *= fInvLength;
+ b *= fInvLength;
+ } else {
+ fLength = 0.0f;
+ }
+
+ return fLength;
+}
+
+//----------------------------------------------------------------------------
+Color3 Color3::fromHSV(const Vector3& _hsv) {
+ debugAssertM((_hsv.x <= 1.0f && _hsv.x >= 0.0f)
+ && (_hsv.y <= 1.0f && _hsv.y >= 0.0f)
+ && ( _hsv.z <= 1.0f && _hsv.z >= 0.0f), "H,S,V must be between [0,1]");
+ const int i = G3D::iFloor(6.0*_hsv.x);
+ const float f = 6.0f * _hsv.x - i;
+ const float m = _hsv.z * (1.0f - (_hsv.y));
+ const float n = _hsv.z * (1.0f - (_hsv.y * f));
+ const float k = _hsv.z * (1.0f - (_hsv.y * (1 - f)));
+ switch(i) {
+ case 0:
+ return Color3(_hsv.z, k, m);
+
+ case 1:
+ return Color3(n, _hsv.z, m);
+
+ case 2:
+ return Color3(m, _hsv.z, k);
+
+ case 3:
+ return Color3(m, n, _hsv.z);
+
+ case 4:
+ return Color3(k, m, _hsv.z);
+
+ case 5:
+ return Color3(_hsv.z, m, n);
+
+ default:
+ debugAssertM(false, "fell through switch..");
+ }
+ return Color3::black();
+}
+
+
+Vector3 Color3::toHSV(const Color3& _rgb) {
+ debugAssertM((_rgb.r <= 1.0f && _rgb.r >= 0.0f)
+ && (_rgb.g <= 1.0f && _rgb.g >= 0.0f)
+ && (_rgb.b <= 1.0f && _rgb.b >= 0.0f), "R,G,B must be between [0,1]");
+ Vector3 hsv = Vector3::zero();
+ hsv.z = G3D::max(G3D::max(_rgb.r, _rgb.g), _rgb.b);
+ if (G3D::fuzzyEq(hsv.z, 0.0f)) {
+ return hsv;
+ }
+
+ const float x = G3D::min(G3D::min(_rgb.r, _rgb.g), _rgb.b);
+ hsv.y = (hsv.z - x) / hsv.z;
+
+ if (G3D::fuzzyEq(hsv.y, 0.0f)) {
+ return hsv;
+ }
+
+ Vector3 rgbN;
+ rgbN.x = (hsv.z - _rgb.r) / (hsv.z - x);
+ rgbN.y = (hsv.z - _rgb.g) / (hsv.z - x);
+ rgbN.z = (hsv.z - _rgb.b) / (hsv.z - x);
+
+ if (_rgb.r == hsv.z) { // note from the max we know that it exactly equals one of the three.
+ hsv.x = (_rgb.g == x)? 5.0f + rgbN.z : 1.0f - rgbN.y;
+ } else if (_rgb.g == hsv.z) {
+ hsv.x = (_rgb.b == x)? 1.0f + rgbN.x : 3.0f - rgbN.z;
+ } else {
+ hsv.x = (_rgb.r == x)? 3.0f + rgbN.y : 5.0f - rgbN.x;
+ }
+
+ hsv.x /= 6.0f;
+
+ return hsv;
+}
+
+Color3 Color3::jetColorMap(const float& val) {
+ debugAssertM(val <= 1.0f && val >= 0.0f , "value should be in [0,1]");
+
+ //truncated triangles where sides have slope 4
+ Color3 jet;
+
+ jet.r = G3D::min(4.0f * val - 1.5f,-4.0f * val + 4.5f) ;
+ jet.g = G3D::min(4.0f * val - 0.5f,-4.0f * val + 3.5f) ;
+ jet.b = G3D::min(4.0f * val + 0.5f,-4.0f * val + 2.5f) ;
+
+
+ jet.r = G3D::clamp(jet.r, 0.0f, 1.0f);
+ jet.g = G3D::clamp(jet.g, 0.0f, 1.0f);
+ jet.b = G3D::clamp(jet.b, 0.0f, 1.0f);
+
+ return jet;
+}
+
+
+
+
+
+std::string Color3::toString() const {
+ return G3D::format("(%g, %g, %g)", r, g, b);
+}
+
+//----------------------------------------------------------------------------
+
+Color3 Color3::rainbowColorMap(float hue) {
+ return fromHSV(Vector3(hue, 1.0f, 1.0f));
+}
+
+
+}; // namespace
+
diff --git a/externals/g3dlite/G3D.lib/source/Color3uint8.cpp b/externals/g3dlite/G3D.lib/source/Color3uint8.cpp
new file mode 100644
index 00000000000..837bf1b2c8b
--- /dev/null
+++ b/externals/g3dlite/G3D.lib/source/Color3uint8.cpp
@@ -0,0 +1,45 @@
+/**
+ @file Color3uint8.cpp
+
+ @author Morgan McGuire, matrix@graphics3d.com
+
+ @created 2003-04-07
+ @edited 2006-01-07
+ */
+
+#include "G3D/platform.h"
+#include "G3D/g3dmath.h"
+#include "G3D/Color3uint8.h"
+#include "G3D/Color3.h"
+#include "G3D/BinaryInput.h"
+#include "G3D/BinaryOutput.h"
+
+namespace G3D {
+
+Color3uint8::Color3uint8(const class Color3& c) {
+ r = iMin(255, iFloor(c.r * 256));
+ g = iMin(255, iFloor(c.g * 256));
+ b = iMin(255, iFloor(c.b * 256));
+}
+
+
+Color3uint8::Color3uint8(class BinaryInput& bi) {
+ deserialize(bi);
+}
+
+
+void Color3uint8::serialize(class BinaryOutput& bo) const {
+ bo.writeUInt8(r);
+ bo.writeUInt8(g);
+ bo.writeUInt8(b);
+}
+
+
+void Color3uint8::deserialize(class BinaryInput& bi) {
+ r = bi.readUInt8();
+ g = bi.readUInt8();
+ b = bi.readUInt8();
+}
+
+
+}
diff --git a/externals/g3dlite/G3D.lib/source/Color4.cpp b/externals/g3dlite/G3D.lib/source/Color4.cpp
new file mode 100644
index 00000000000..ed2e91291a1
--- /dev/null
+++ b/externals/g3dlite/G3D.lib/source/Color4.cpp
@@ -0,0 +1,140 @@
+/**
+ @file Color4.cpp
+
+ Color class.
+
+ @author Morgan McGuire, matrix@graphics3d.com
+ @cite Portions by Laura Wollstadt, graphics3d.com
+ @cite Portions based on Dave Eberly's Magic Software Library at http://www.magic-software.com
+
+
+ @created 2002-06-25
+ @edited 2006-01-10
+ */
+
+#include <stdlib.h>
+#include "G3D/Color4.h"
+#include "G3D/Color4uint8.h"
+#include "G3D/Vector4.h"
+#include "G3D/format.h"
+#include "G3D/BinaryInput.h"
+#include "G3D/BinaryOutput.h"
+
+namespace G3D {
+
+const Color4& Color4::zero() {
+ static Color4 c(0.0f, 0.0f, 0.0f, 0.0f);
+ return c;
+}
+
+
+const Color4& Color4::inf() {
+ static Color4 c((float)G3D::inf(), (float)G3D::inf(), (float)G3D::inf(), (float)G3D::inf());
+ return c;
+}
+
+
+const Color4& Color4::clear() {
+ return Color4::zero();
+}
+
+
+Color4::Color4(const Vector4& v) {
+ r = v.x;
+ g = v.y;
+ b = v.z;
+ a = v.w;
+}
+
+
+Color4::Color4(const Color4uint8& c) : r(c.r), g(c.g), b(c.b), a(c.a) {
+ *this /= 255.0f;
+}
+
+size_t Color4::hashCode() const {
+ unsigned int rhash = (*(int*)(void*)(&r));
+ unsigned int ghash = (*(int*)(void*)(&g));
+ unsigned int bhash = (*(int*)(void*)(&b));
+ unsigned int ahash = (*(int*)(void*)(&a));
+
+ return rhash + (ghash * 37) + (bhash * 101) + (ahash * 241);
+}
+
+Color4 Color4::fromARGB(uint32 x) {
+ return Color4(
+ (float)((x >> 16) & 0xFF),
+ (float)((x >> 8) & 0xFF),
+ (float)(x & 0xFF),
+ (float)((x >> 24) & 0xFF)) / 255.0;
+}
+
+
+Color4::Color4(BinaryInput& bi) {
+ deserialize(bi);
+}
+
+
+void Color4::deserialize(BinaryInput& bi) {
+ r = bi.readFloat32();
+ g = bi.readFloat32();
+ b = bi.readFloat32();
+ a = bi.readFloat32();
+}
+
+
+void Color4::serialize(BinaryOutput& bo) const {
+ bo.writeFloat32(r);
+ bo.writeFloat32(g);
+ bo.writeFloat32(b);
+ bo.writeFloat32(a);
+}
+
+
+//----------------------------------------------------------------------------
+
+Color4 Color4::operator/ (float fScalar) const {
+ Color4 kQuot;
+
+ if (fScalar != 0.0f) {
+ float fInvScalar = 1.0f / fScalar;
+ kQuot.r = fInvScalar * r;
+ kQuot.g = fInvScalar * g;
+ kQuot.b = fInvScalar * b;
+ kQuot.a = fInvScalar * a;
+ return kQuot;
+
+ } else {
+
+ return Color4::inf();
+ }
+}
+
+//----------------------------------------------------------------------------
+
+Color4& Color4::operator/= (float fScalar) {
+ if (fScalar != 0.0f) {
+ float fInvScalar = 1.0f / fScalar;
+ r *= fInvScalar;
+ g *= fInvScalar;
+ b *= fInvScalar;
+ a *= fInvScalar;
+ } else {
+ r = (float)G3D::inf();
+ g = (float)G3D::inf();
+ b = (float)G3D::inf();
+ a = (float)G3D::inf();
+ }
+
+ return *this;
+}
+
+//----------------------------------------------------------------------------
+
+std::string Color4::toString() const {
+ return G3D::format("(%g, %g, %g, %g)", r, g, b, a);
+}
+
+//----------------------------------------------------------------------------
+
+}; // namespace
+
diff --git a/externals/g3dlite/G3D.lib/source/Color4uint8.cpp b/externals/g3dlite/G3D.lib/source/Color4uint8.cpp
new file mode 100644
index 00000000000..8c8636a742e
--- /dev/null
+++ b/externals/g3dlite/G3D.lib/source/Color4uint8.cpp
@@ -0,0 +1,47 @@
+/**
+ @file Color4uint8.cpp
+
+ @author Morgan McGuire, matrix@graphics3d.com
+
+ @created 2003-04-07
+ @edited 2006-01-07
+ */
+#include "G3D/platform.h"
+#include "G3D/g3dmath.h"
+#include "G3D/Color4uint8.h"
+#include "G3D/Color4.h"
+#include "G3D/BinaryInput.h"
+#include "G3D/BinaryOutput.h"
+
+namespace G3D {
+
+Color4uint8::Color4uint8(const class Color4& c) {
+ r = iMin(255, iFloor(c.r * 256));
+ g = iMin(255, iFloor(c.g * 256));
+ b = iMin(255, iFloor(c.b * 256));
+ a = iMin(255, iFloor(c.a * 256));
+}
+
+
+Color4uint8::Color4uint8(class BinaryInput& bi) {
+ deserialize(bi);
+}
+
+
+void Color4uint8::serialize(class BinaryOutput& bo) const {
+ bo.writeUInt8(r);
+ bo.writeUInt8(g);
+ bo.writeUInt8(b);
+ bo.writeUInt8(a);
+}
+
+
+void Color4uint8::deserialize(class BinaryInput& bi) {
+ r = bi.readUInt8();
+ g = bi.readUInt8();
+ b = bi.readUInt8();
+ a = bi.readUInt8();
+}
+
+
+}
diff --git a/externals/g3dlite/G3D.lib/source/Cone.cpp b/externals/g3dlite/G3D.lib/source/Cone.cpp
new file mode 100644
index 00000000000..99b29b5b0af
--- /dev/null
+++ b/externals/g3dlite/G3D.lib/source/Cone.cpp
@@ -0,0 +1,79 @@
+/**
+ @file Cone.cpp
+
+ Cone class
+
+ @maintainer Morgan McGuire, matrix@graphics3d.com
+
+ @created 2001-07-09
+ @edited 2006-01-29
+*/
+
+#include "G3D/platform.h"
+#include "G3D/Cone.h"
+#include "G3D/Line.h"
+#include "G3D/Sphere.h"
+#include "G3D/Box.h"
+
+namespace G3D {
+
+Cone::Cone(const Vector3 &tip, const Vector3 &direction, float angle) {
+ this->tip = tip;
+ this->direction = direction.direction();
+ this->angle = angle;
+
+ debugAssert(angle >= 0);
+ debugAssert(angle <= pi());
+}
+
+/**
+ Forms the smallest cone that contains the box. Undefined if
+ the tip is inside or on the box.
+ */
+Cone::Cone(const Vector3& tip, const Box& box) {
+ this->tip = tip;
+ this->direction = (box.center() - tip).direction();
+
+ // Find the biggest angle
+ float smallestDotProduct = direction.dot((box.corner(0) - tip).direction());
+
+ for (int i = 1; i < 8; ++i) {
+ float dp = direction.dot((box.corner(i) - tip).direction());
+
+ debugAssert(dp > 0);
+
+ if (dp < smallestDotProduct) {
+ smallestDotProduct = dp;
+ }
+ }
+
+ angle = acosf(smallestDotProduct);
+}
+
+
+bool Cone::intersects(const Sphere& b) const {
+ // If the bounding sphere contains the tip, then
+ // they definitely touch.
+ if (b.contains(this->tip)) {
+ return true;
+ }
+
+ // Move the tip backwards, effectively making the cone bigger
+ // to account for the radius of the sphere.
+
+ Vector3 tip = this->tip - direction * b.radius / sinf(angle);
+
+ return Cone(tip, direction, angle).contains(b.center);
+}
+
+
+bool Cone::contains(const Vector3& v) const {
+
+ Vector3 d = (v - tip).direction();
+
+ float x = d.dot(direction);
+
+ return (x > 0) && (x >= cosf(angle));
+}
+
+}; // namespace
diff --git a/externals/g3dlite/G3D.lib/source/ConvexPolyhedron.cpp b/externals/g3dlite/G3D.lib/source/ConvexPolyhedron.cpp
new file mode 100644
index 00000000000..76dbe21a7c4
--- /dev/null
+++ b/externals/g3dlite/G3D.lib/source/ConvexPolyhedron.cpp
@@ -0,0 +1,449 @@
+/**
+ @file ConvexPolyhedron.cpp
+
+ @author Morgan McGuire, morgan@graphics3d.com
+
+ @created 2001-11-11
+ @edited 2006-01-10
+
+ Copyright 2000-2006, Morgan McGuire.
+ All rights reserved.
+ */
+
+#include "G3D/platform.h"
+#include "G3D/ConvexPolyhedron.h"
+#include "G3D/debug.h"
+
+namespace G3D {
+
+ConvexPolygon::ConvexPolygon(const Array<Vector3>& __vertex) : _vertex(__vertex) {
+ // Intentionally empty
+}
+
+
+bool ConvexPolygon::isEmpty() const {
+ return (_vertex.length() == 0) || (getArea() <= fuzzyEpsilon);
+}
+
+
+float ConvexPolygon::getArea() const {
+
+ if (_vertex.length() < 3) {
+ return 0;
+ }
+
+ float sum = 0;
+
+ int length = _vertex.length();
+ // Split into triangle fan, compute individual area
+ for (int v = 2; v < length; v++) {
+ int i0 = 0;
+ int i1 = v - 1;
+ int i2 = v;
+
+ sum += (_vertex[i1] - _vertex[i0]).cross(_vertex[i2] - _vertex[i0]).magnitude() / 2;
+ }
+
+ return sum;
+}
+
+void ConvexPolygon::cut(const Plane& plane, ConvexPolygon &above, ConvexPolygon &below) {
+ DirectedEdge edge;
+ cut(plane, above, below, edge);
+}
+
+void ConvexPolygon::cut(const Plane& plane, ConvexPolygon &above, ConvexPolygon &below, DirectedEdge &newEdge) {
+ above._vertex.resize(0);
+ below._vertex.resize(0);
+
+ if (isEmpty()) {
+ //debugPrintf("Empty\n");
+ return;
+ }
+
+ int v = 0;
+ int length = _vertex.length();
+
+
+ Vector3 polyNormal = normal();
+ Vector3 planeNormal= plane.normal();
+
+ // See if the polygon is *in* the plane.
+ if (planeNormal.fuzzyEq(polyNormal) || planeNormal.fuzzyEq(-polyNormal)) {
+ // Polygon is parallel to the plane. It must be either above,
+ // below, or in the plane.
+
+ double a, b, c, d;
+ Vector3 pt = _vertex[0];
+
+ plane.getEquation(a,b,c,d);
+ float r = (float)(a * pt.x + b * pt.y + c * pt.z + d);
+
+ if (fuzzyGe(r, 0)) {
+ // The polygon is entirely in the plane.
+ //debugPrintf("Entirely above\n");
+ above = *this;
+ return;
+ } else {
+ //debugPrintf("Entirely below (1)\n");
+ below = *this;
+ return;
+ }
+ }
+
+
+ // Number of edges crossing the plane. Used for
+ // debug assertions.
+ int count = 0;
+
+ // True when the last _vertex we looked at was above the plane
+ bool lastAbove = plane.halfSpaceContains(_vertex[v]);
+
+ if (lastAbove) {
+ above._vertex.append(_vertex[v]);
+ } else {
+ below._vertex.append(_vertex[v]);
+ }
+
+ for (v = 1; v < length; v++) {
+ bool isAbove = plane.halfSpaceContains(_vertex[v]);
+
+ if (lastAbove ^ isAbove) {
+ // Switched sides.
+ // Create an interpolated point that lies
+ // in the plane, between the two points.
+ Line line = Line::fromTwoPoints(_vertex[v - 1], _vertex[v]);
+ Vector3 interp = line.intersection(plane);
+
+ if (! interp.isFinite()) {
+
+ // Since the polygon is not in the plane (we checked above),
+ // it must be the case that this edge (and only this edge)
+ // is in the plane. This only happens when the polygon is
+ // entirely below the plane except for one edge. This edge
+ // forms a degenerate polygon, so just treat the whole polygon
+ // as below the plane.
+ below = *this;
+ above._vertex.resize(0);
+ //debugPrintf("Entirely below\n");
+ return;
+ }
+
+ above._vertex.append(interp);
+ below._vertex.append(interp);
+ if (lastAbove) {
+ newEdge.stop = interp;
+ } else {
+ newEdge.start = interp;
+ }
+ count++;
+ }
+
+ lastAbove = isAbove;
+ if (lastAbove) {
+ above._vertex.append(_vertex[v]);
+ } else {
+ below._vertex.append(_vertex[v]);
+ }
+ }
+
+ // Loop back to the first point, seeing if an interpolated point is
+ // needed.
+ bool isAbove = plane.halfSpaceContains(_vertex[0]);
+ if (lastAbove ^ isAbove) {
+ Line line = Line::fromTwoPoints(_vertex[length - 1], _vertex[0]);
+ Vector3 interp = line.intersection(plane);
+ if (! interp.isFinite()) {
+ // Since the polygon is not in the plane (we checked above),
+ // it must be the case that this edge (and only this edge)
+ // is in the plane. This only happens when the polygon is
+ // entirely below the plane except for one edge. This edge
+ // forms a degenerate polygon, so just treat the whole polygon
+ // as below the plane.
+ below = *this;
+ above._vertex.resize(0);
+ //debugPrintf("Entirely below\n");
+ return;
+ }
+
+ above._vertex.append(interp);
+ below._vertex.append(interp);
+ debugAssertM(count < 2, "Convex polygons may only intersect planes at two edges.");
+ if (lastAbove) {
+ newEdge.stop = interp;
+ } else {
+ newEdge.start = interp;
+ }
+ count++;
+ }
+
+ debugAssertM((count == 2) || (count == 0), "Convex polygons may only intersect planes at two edges.");
+}
+
+ConvexPolygon ConvexPolygon::inverse() const {
+ ConvexPolygon result;
+ int length = _vertex.length();
+ result._vertex.resize(length);
+
+ for (int v = 0; v < length; v++) {
+ result._vertex[v] = _vertex[length - v - 1];
+ }
+
+ return result;
+}
+
+void ConvexPolygon::removeDuplicateVertices(){
+ // Any valid polygon should have 3 or more vertices, but why take chances?
+ if(_vertex.size() >= 2){
+
+ // Remove duplicate vertices.
+ for(int i=0;i<_vertex.size()-1;++i){
+ if(_vertex[i].fuzzyEq(_vertex[i+1])){
+ _vertex.remove(i+1);
+ --i; // Don't move forward.
+ }
+ }
+
+ // Check the last vertex against the first.
+ if(_vertex[_vertex.size()-1].fuzzyEq(_vertex[0])){
+ _vertex.pop();
+ }
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////////
+
+ConvexPolyhedron::ConvexPolyhedron(const Array<ConvexPolygon>& _face) : face(_face) {
+ // Intentionally empty
+}
+
+float ConvexPolyhedron::getVolume() const {
+
+ if (face.length() < 4) {
+ return 0;
+ }
+
+ // The volume of any pyramid is 1/3 * h * base area.
+ // Discussion at: http://nrich.maths.org/mathsf/journalf/oct01/art1/
+
+ float sum = 0;
+
+ // Choose the first _vertex of the first face as the origin.
+ // This lets us skip one face, too, and avoids negative heights.
+ Vector3 v0 = face[0]._vertex[0];
+ for (int f = 1; f < face.length(); f++) {
+ const ConvexPolygon& poly = face[f];
+
+ float height = (poly._vertex[0] - v0).dot(poly.normal());
+ float base = poly.getArea();
+
+ sum += height * base;
+ }
+
+ return sum / 3;
+}
+
+bool ConvexPolyhedron::isEmpty() const {
+ return (face.length() == 0) || (getVolume() <= fuzzyEpsilon);
+}
+
+void ConvexPolyhedron::cut(const Plane& plane, ConvexPolyhedron &above, ConvexPolyhedron &below) {
+ above.face.resize(0);
+ below.face.resize(0);
+
+ Array<DirectedEdge> edge;
+
+ int f;
+
+ // See if the plane cuts this polyhedron at all. Detect when
+ // the polyhedron is entirely to one side or the other.
+ //{
+ int numAbove = 0, numIn = 0, numBelow = 0;
+ bool ruledOut = false;
+ double d;
+ Vector3 abc;
+ plane.getEquation(abc, d);
+
+ // This number has to be fairly large to prevent precision problems down
+ // the road.
+ const float eps = 0.005f;
+ for (f = face.length() - 1; (f >= 0) && (!ruledOut); f--) {
+ const ConvexPolygon& poly = face[f];
+ for (int v = poly._vertex.length() - 1; (v >= 0) && (!ruledOut); v--) {
+ double r = abc.dot(poly._vertex[v]) + d;
+ if (r > eps) {
+ numAbove++;
+ } else if (r < -eps) {
+ numBelow++;
+ } else {
+ numIn++;
+ }
+
+ ruledOut = (numAbove != 0) && (numBelow !=0);
+ }
+ }
+
+ if (numBelow == 0) {
+ above = *this;
+ return;
+ } else if (numAbove == 0) {
+ below = *this;
+ return;
+ }
+ //}
+
+ // Clip each polygon, collecting split edges.
+ for (f = face.length() - 1; f >= 0; f--) {
+ ConvexPolygon a, b;
+ DirectedEdge e;
+ face[f].cut(plane, a, b, e);
+
+ bool aEmpty = a.isEmpty();
+ bool bEmpty = b.isEmpty();
+
+ //debugPrintf("\n");
+ if (! aEmpty) {
+ //debugPrintf(" Above %f\n", a.getArea());
+ above.face.append(a);
+ }
+
+ if (! bEmpty) {
+ //debugPrintf(" Below %f\n", b.getArea());
+ below.face.append(b);
+ }
+
+ if (! aEmpty && ! bEmpty) {
+ //debugPrintf(" == Split\n");
+ edge.append(e);
+ } else {
+ // Might be the case that the polygon is entirely on
+ // one side of the plane yet there is an edge we need
+ // because it touches the plane.
+ //
+ // Extract the non-empty _vertex list and examine it.
+ // If we find exactly one edge in the plane, add that edge.
+ const Array<Vector3>& _vertex = (aEmpty ? b._vertex : a._vertex);
+ int L = _vertex.length();
+ int count = 0;
+ for (int v = 0; v < L; v++) {
+ if (plane.fuzzyContains(_vertex[v]) && plane.fuzzyContains(_vertex[(v + 1) % L])) {
+ e.start = _vertex[v];
+ e.stop = _vertex[(v + 1) % L];
+ count++;
+ }
+ }
+
+ if (count == 1) {
+ edge.append(e);
+ }
+ }
+ }
+
+ if (above.face.length() == 1) {
+ // Only one face above means that this entire
+ // polyhedron is below the plane. Move that face over.
+ below.face.append(above.face[0]);
+ above.face.resize(0);
+ } else if (below.face.length() == 1) {
+ // This shouldn't happen, but it arises in practice
+ // from numerical imprecision.
+ above.face.append(below.face[0]);
+ below.face.resize(0);
+ }
+
+ if ((above.face.length() > 0) && (below.face.length() > 0)) {
+ // The polyhedron was actually cut; create a cap polygon
+ ConvexPolygon cap;
+
+ // Collect the final polgyon by sorting the edges
+ int numVertices = edge.length();
+/*debugPrintf("\n");
+for (int xx=0; xx < numVertices; xx++) {
+ std::string s1 = edge[xx].start.toString();
+ std::string s2 = edge[xx].stop.toString();
+ debugPrintf("%s -> %s\n", s1.c_str(), s2.c_str());
+}
+*/
+
+ // Need at least three points to make a polygon
+ debugAssert(numVertices >= 3);
+
+ Vector3 last_vertex = edge.last().stop;
+ cap._vertex.append(last_vertex);
+
+ // Search for the next _vertex. Because of accumulating
+ // numerical error, we have to find the closest match, not
+ // just the one we expect.
+ for (int v = numVertices - 1; v >= 0; v--) {
+ // matching edge index
+ int index = 0;
+ int num = edge.length();
+ double distance = (edge[index].start - last_vertex).squaredMagnitude();
+ for (int e = 1; e < num; e++) {
+ double d = (edge[e].start - last_vertex).squaredMagnitude();
+
+ if (d < distance) {
+ // This is the new closest one
+ index = e;
+ distance = d;
+ }
+ }
+
+ // Don't tolerate ridiculous error.
+ debugAssertM(distance < 0.02, "Edge missing while closing polygon.");
+
+ last_vertex = edge[index].stop;
+ cap._vertex.append(last_vertex);
+ }
+
+ //debugPrintf("\n");
+ //debugPrintf("Cap (both) %f\n", cap.getArea());
+ above.face.append(cap);
+ below.face.append(cap.inverse());
+ }
+
+ // Make sure we put enough faces on each polyhedra
+ debugAssert((above.face.length() == 0) || (above.face.length() >= 4));
+ debugAssert((below.face.length() == 0) || (below.face.length() >= 4));
+}
+
+///////////////////////////////////////////////
+
+ConvexPolygon2D::ConvexPolygon2D(const Array<Vector2>& pts, bool reverse) : m_vertex(pts) {
+ if (reverse) {
+ m_vertex.reverse();
+ }
+}
+
+
+bool ConvexPolygon2D::contains(const Vector2& p, bool reverse) const {
+ // Compute the signed area of each polygon from p to an edge.
+ // If the area is non-negative for all polygons then p is inside
+ // the polygon. (To adapt this algorithm for a concave polygon,
+ // the *sum* of the areas must be non-negative).
+
+ float r = reverse ? -1 : 1;
+
+ for (int i0 = 0; i0 < m_vertex.size(); ++i0) {
+ int i1 = (i0 + 1) % m_vertex.size();
+ const Vector2& v0 = m_vertex[i0];
+ const Vector2& v1 = m_vertex[i1];
+
+ Vector2 e0 = v0 - p;
+ Vector2 e1 = v1 - p;
+
+ // Area = (1/2) cross product, negated to be ccw in
+ // a 2D space; we neglect the 1/2
+ float area = -(e0.x * e1.y - e0.y * e1.x);
+
+ if (area * r < 0) {
+ return false;
+ }
+ }
+
+ return true;
+}
+
+
+}
+
diff --git a/externals/g3dlite/G3D.lib/source/CoordinateFrame.cpp b/externals/g3dlite/G3D.lib/source/CoordinateFrame.cpp
new file mode 100644
index 00000000000..b6e94fe5857
--- /dev/null
+++ b/externals/g3dlite/G3D.lib/source/CoordinateFrame.cpp
@@ -0,0 +1,381 @@
+/**
+ @file CoordinateFrame.cpp
+
+ Coordinate frame class
+
+ @maintainer Morgan McGuire, morgan@cs.williams.edu
+
+ @created 2001-06-02
+ @edited 2008-07-13
+*/
+
+#include "G3D/platform.h"
+#include "G3D/CoordinateFrame.h"
+#include "G3D/Quat.h"
+#include "G3D/Matrix4.h"
+#include "G3D/Box.h"
+#include "G3D/AABox.h"
+#include "G3D/Sphere.h"
+#include "G3D/Triangle.h"
+#include "G3D/Ray.h"
+#include "G3D/Capsule.h"
+#include "G3D/Cylinder.h"
+#include "G3D/UprightFrame.h"
+
+namespace G3D {
+
+CoordinateFrame::CoordinateFrame(const class UprightFrame& f) {
+ *this = f.toCoordinateFrame();
+}
+
+
+CoordinateFrame CoordinateFrame::fromXYZYPRRadians(float x, float y, float z, float yaw,
+ float pitch, float roll) {
+ Matrix3 rotation = Matrix3::fromAxisAngle(Vector3::unitY(), yaw);
+
+ rotation = Matrix3::fromAxisAngle(rotation.column(0), pitch) * rotation;
+ rotation = Matrix3::fromAxisAngle(rotation.column(2), roll) * rotation;
+
+ const Vector3 translation(x, y, z);
+
+ return CoordinateFrame(rotation, translation);
+}
+
+
+void CoordinateFrame::getXYZYPRRadians(float& x, float& y, float& z,
+ float& yaw, float& pitch, float& roll) const {
+ x = translation.x;
+ y = translation.y;
+ z = translation.z;
+
+ const Vector3& look = lookVector();
+
+ if (abs(look.y) > 0.99f) {
+ // Looking nearly straight up or down
+
+ yaw = G3D::pi() + atan2(look.x, look.z);
+ pitch = asin(look.y);
+ roll = 0.0f;
+
+ } else {
+
+ // Yaw cannot be affected by others, so pull it first
+ yaw = G3D::pi() + atan2(look.x, look.z);
+
+ // Pitch is the elevation of the yaw vector
+ pitch = asin(look.y);
+
+ Vector3 actualRight = rightVector();
+ Vector3 expectedRight = look.cross(Vector3::unitY());
+
+ roll = 0;//acos(actualRight.dot(expectedRight)); TODO
+ }
+}
+
+
+void CoordinateFrame::getXYZYPRDegrees(float& x, float& y, float& z,
+ float& yaw, float& pitch, float& roll) const {
+ getXYZYPRRadians(x, y, z, yaw, pitch, roll);
+ yaw = toDegrees(yaw);
+ pitch = toDegrees(pitch);
+ roll = toDegrees(roll);
+}
+
+
+CoordinateFrame CoordinateFrame::fromXYZYPRDegrees(float x, float y, float z,
+ float yaw, float pitch, float roll) {
+ return fromXYZYPRRadians(x, y, z, toRadians(yaw), toRadians(pitch), toRadians(roll));
+}
+
+
+Ray CoordinateFrame::lookRay() const {
+ return Ray::fromOriginAndDirection(translation, lookVector());
+}
+
+
+bool CoordinateFrame::fuzzyEq(const CoordinateFrame& other) const {
+
+ for (int c = 0; c < 3; ++c) {
+ for (int r = 0; r < 3; ++r) {
+ if (! G3D::fuzzyEq(other.rotation[r][c], rotation[r][c])) {
+ return false;
+ }
+ }
+ if (! G3D::fuzzyEq(translation[c], other.translation[c])) {
+ return false;
+ }
+ }
+
+ return true;
+}
+
+
+bool CoordinateFrame::fuzzyIsIdentity() const {
+ const Matrix3& I = Matrix3::identity();
+
+ for (int c = 0; c < 3; ++c) {
+ for (int r = 0; r < 3; ++r) {
+ if (fuzzyNe(I[r][c], rotation[r][c])) {
+ return false;
+ }
+ }
+ if (fuzzyNe(translation[c], 0)) {
+ return false;
+ }
+ }
+
+ return true;
+}
+
+
+bool CoordinateFrame::isIdentity() const {
+ return
+ (translation == Vector3::zero()) &&
+ (rotation == Matrix3::identity());
+}
+
+
+Matrix4 CoordinateFrame::toMatrix4() const {
+ return Matrix4(*this);
+}
+
+
+std::string CoordinateFrame::toXML() const {
+ return G3D::format(
+ "<COORDINATEFRAME>\n %lf,%lf,%lf,%lf,\n %lf,%lf,%lf,%lf,\n %lf,%lf,%lf,%lf,\n %lf,%lf,%lf,%lf\n</COORDINATEFRAME>\n",
+ rotation[0][0], rotation[0][1], rotation[0][2], translation.x,
+ rotation[1][0], rotation[1][1], rotation[1][2], translation.y,
+ rotation[2][0], rotation[2][1], rotation[2][2], translation.z,
+ 0.0, 0.0, 0.0, 1.0);
+}
+
+
+Plane CoordinateFrame::toObjectSpace(const Plane& p) const {
+ Vector3 N, P;
+ double d;
+ p.getEquation(N, d);
+ P = N * (float)d;
+ P = pointToObjectSpace(P);
+ N = normalToObjectSpace(N);
+ return Plane(N, P);
+}
+
+
+Plane CoordinateFrame::toWorldSpace(const Plane& p) const {
+ Vector3 N, P;
+ double d;
+ p.getEquation(N, d);
+ P = N * (float)d;
+ P = pointToWorldSpace(P);
+ N = normalToWorldSpace(N);
+ return Plane(N, P);
+}
+
+
+Triangle CoordinateFrame::toObjectSpace(const Triangle& t) const {
+ return Triangle(pointToObjectSpace(t.vertex(0)),
+ pointToObjectSpace(t.vertex(1)),
+ pointToObjectSpace(t.vertex(2)));
+}
+
+
+Triangle CoordinateFrame::toWorldSpace(const Triangle& t) const {
+ return Triangle(pointToWorldSpace(t.vertex(0)),
+ pointToWorldSpace(t.vertex(1)),
+ pointToWorldSpace(t.vertex(2)));
+}
+
+
+Cylinder CoordinateFrame::toWorldSpace(const Cylinder& c) const {
+ return Cylinder(
+ pointToWorldSpace(c.point(0)),
+ pointToWorldSpace(c.point(1)),
+ c.radius());
+}
+
+
+Capsule CoordinateFrame::toWorldSpace(const Capsule& c) const {
+ return Capsule(
+ pointToWorldSpace(c.point(0)),
+ pointToWorldSpace(c.point(1)),
+ c.radius());
+}
+
+
+Box CoordinateFrame::toWorldSpace(const AABox& b) const {
+ return toWorldSpace(Box(b));
+}
+
+
+Box CoordinateFrame::toWorldSpace(const Box& b) const {
+ Box out(b);
+
+ for (int i = 0; i < 8; ++i) {
+ out._corner[i] = pointToWorldSpace(out._corner[i]);
+ debugAssert(! isNaN(out._corner[i].x));
+ }
+
+ for (int i = 0; i < 3; ++i) {
+ out._axis[i] = vectorToWorldSpace(out._axis[i]);
+ }
+
+ out._center = pointToWorldSpace(out._center);
+
+ return out;
+}
+
+
+Box CoordinateFrame::toObjectSpace(const Box &b) const {
+ return inverse().toWorldSpace(b);
+}
+
+
+Box CoordinateFrame::toObjectSpace(const AABox& b) const {
+ return toObjectSpace(Box(b));
+}
+
+
+CoordinateFrame::CoordinateFrame(class BinaryInput& b) : rotation(Matrix3::zero()) {
+ deserialize(b);
+}
+
+
+void CoordinateFrame::deserialize(class BinaryInput& b) {
+ rotation.deserialize(b);
+ translation.deserialize(b);
+}
+
+
+void CoordinateFrame::serialize(class BinaryOutput& b) const {
+ rotation.serialize(b);
+ translation.serialize(b);
+}
+
+
+Sphere CoordinateFrame::toWorldSpace(const Sphere &b) const {
+ return Sphere(pointToWorldSpace(b.center), b.radius);
+}
+
+
+Sphere CoordinateFrame::toObjectSpace(const Sphere &b) const {
+ return Sphere(pointToObjectSpace(b.center), b.radius);
+}
+
+
+Ray CoordinateFrame::toWorldSpace(const Ray& r) const {
+ return Ray::fromOriginAndDirection(pointToWorldSpace(r.origin), vectorToWorldSpace(r.direction));
+}
+
+
+Ray CoordinateFrame::toObjectSpace(const Ray& r) const {
+ return Ray::fromOriginAndDirection(pointToObjectSpace(r.origin), vectorToObjectSpace(r.direction));
+}
+
+
+void CoordinateFrame::lookAt(const Vector3 &target) {
+ lookAt(target, Vector3::unitY());
+}
+
+
+void CoordinateFrame::lookAt(
+ const Vector3& target,
+ Vector3 up) {
+
+ up = up.direction();
+
+ Vector3 look = (target - translation).direction();
+ if (fabs(look.dot(up)) > .99f) {
+ up = Vector3::unitX();
+ if (fabs(look.dot(up)) > .99f) {
+ up = Vector3::unitY();
+ }
+ }
+
+ up -= look * look.dot(up);
+ up.unitize();
+
+ Vector3 z = -look;
+ Vector3 x = -z.cross(up);
+ x.unitize();
+
+ Vector3 y = z.cross(x);
+
+ rotation.setColumn(0, x);
+ rotation.setColumn(1, y);
+ rotation.setColumn(2, z);
+}
+
+
+CoordinateFrame CoordinateFrame::lerp(
+ const CoordinateFrame& other,
+ float alpha) const {
+
+ if (alpha == 1.0f) {
+ return other;
+ } else if (alpha == 0.0f) {
+ return *this;
+ } else {
+ Quat q1 = Quat(this->rotation);
+ Quat q2 = Quat(other.rotation);
+
+ return CoordinateFrame(
+ q1.slerp(q2, alpha).toRotationMatrix(),
+ this->translation * (1 - alpha) + other.translation * alpha);
+ }
+}
+
+
+void CoordinateFrame::pointToWorldSpace(const Array<Vector3>& v, Array<Vector3>& vout) const {
+ vout.resize(v.size());
+
+ for (int i = v.size() - 1; i >= 0; --i) {
+ vout[i] = pointToWorldSpace(v[i]);
+ }
+}
+
+
+void CoordinateFrame::normalToWorldSpace(const Array<Vector3>& v, Array<Vector3>& vout) const {
+ vout.resize(v.size());
+
+ for (int i = v.size() - 1; i >= 0; --i) {
+ vout[i] = normalToWorldSpace(v[i]);
+ }
+}
+
+
+void CoordinateFrame::vectorToWorldSpace(const Array<Vector3>& v, Array<Vector3>& vout) const {
+ vout.resize(v.size());
+
+ for (int i = v.size() - 1; i >= 0; --i) {
+ vout[i] = vectorToWorldSpace(v[i]);
+ }
+}
+
+
+void CoordinateFrame::pointToObjectSpace(const Array<Vector3>& v, Array<Vector3>& vout) const {
+ vout.resize(v.size());
+
+ for (int i = v.size() - 1; i >= 0; --i) {
+ vout[i] = pointToObjectSpace(v[i]);
+ }
+}
+
+
+void CoordinateFrame::normalToObjectSpace(const Array<Vector3>& v, Array<Vector3>& vout) const {
+ vout.resize(v.size());
+
+ for (int i = v.size() - 1; i >= 0; --i) {
+ vout[i] = normalToObjectSpace(v[i]);
+ }
+}
+
+
+void CoordinateFrame::vectorToObjectSpace(const Array<Vector3>& v, Array<Vector3>& vout) const {
+ vout.resize(v.size());
+
+ for (int i = v.size() - 1; i >= 0; --i) {
+ vout[i] = vectorToObjectSpace(v[i]);
+ }
+}
+
+} // namespace
diff --git a/externals/g3dlite/G3D.lib/source/Crypto.cpp b/externals/g3dlite/G3D.lib/source/Crypto.cpp
new file mode 100644
index 00000000000..09aee2c265d
--- /dev/null
+++ b/externals/g3dlite/G3D.lib/source/Crypto.cpp
@@ -0,0 +1,70 @@
+/**
+ @file Crypto.cpp
+
+ @author Morgan McGuire, matrix@graphics3d.com
+
+
+ @created 2006-03-28
+ @edited 2006-04-06
+ */
+
+#include "G3D/platform.h"
+#include "G3D/Crypto.h"
+#include "G3D/g3dmath.h"
+#include <zlib.h>
+
+namespace G3D {
+
+
+int Crypto::smallPrime(int n) {
+ debugAssert(n < numSmallPrimes() && n >= 0);
+
+ // From:
+ // http://primes.utm.edu/lists/small/1000.txt
+
+ static const int table[] = {
+ 2, 3, 5, 7, 11, 13, 17, 19, 23, 29,
+ 31, 37, 41, 43, 47, 53, 59, 61, 67, 71,
+ 73, 79, 83, 89, 97, 101, 103, 107, 109, 113,
+ 127, 131, 137, 139, 149, 151, 157, 163, 167, 173,
+ 179, 181, 191, 193, 197, 199, 211, 223, 227, 229,
+ 233, 239, 241, 251, 257, 263, 269, 271, 277, 281,
+ 283, 293, 307, 311, 313, 317, 331, 337, 347, 349,
+ 353, 359, 367, 373, 379, 383, 389, 397, 401, 409,
+ 419, 421, 431, 433, 439, 443, 449, 457, 461, 463,
+ 467, 479, 487, 491, 499, 503, 509, 521, 523, 541,
+ 547, 557, 563, 569, 571, 577, 587, 593, 599, 601,
+ 607, 613, 617, 619, 631, 641, 643, 647, 653, 659,
+ 661, 673, 677, 683, 691, 701, 709, 719, 727, 733,
+ 739, 743, 751, 757, 761, 769, 773, 787, 797, 809,
+ 811, 821, 823, 827, 829, 839, 853, 857, 859, 863,
+ 877, 881, 883, 887, 907, 911, 919, 929, 937, 941,
+ 947, 953, 967, 971, 977, 983, 991, 997, 1009, 1013,
+ 1019, 1021, 1031, 1033, 1039, 1049, 1051, 1061, 1063, 1069,
+ 1087, 1091, 1093, 1097, 1103, 1109, 1117, 1123, 1129, 1151,
+ 1153, 1163, 1171, 1181, 1187, 1193, 1201, 1213, 1217, 1223,
+ 1229, 1231, 1237, 1249, 1259, 1277, 1279, 1283, 1289, 1291,
+ 1297, 1301, 1303, 1307, 1319, 1321, 1327, 1361, 1367, 1373,
+ 1381, 1399, 1409, 1423, 1427, 1429, 1433, 1439, 1447, 1451,
+ 1453, 1459, 1471, 1481, 1483, 1487, 1489, 1493, 1499, 1511,
+ 1523, 1531, 1543, 1549, 1553, 1559, 1567, 1571, 1579, 1583,
+ 1597, 1601, 1607, 1609, 1613, 1619, 1621, 1627, 1637, 1657,
+ 1663, 1667, 1669, 1693, 1697, 1699, 1709, 1721, 1723, 1733,
+ 1741, 1747, 1753, 1759, 1777, 1783, 1787, 1789, 1801, 1811,
+ 1823, 1831, 1847, 1861, 1867, 1871, 1873, 1877, 1879, 1889,
+ 1901, 1907, 1913, 1931, 1933, 1949, 1951, 1973, 1979, 1987,
+ 1993, 1997, 1999};
+
+ return table[n];
+}
+
+
+int Crypto::numSmallPrimes() {
+ return 303;
+}
+
+uint32 Crypto::crc32(const void* byte, size_t numBytes) {
+ return ::crc32(::crc32(0, Z_NULL, 0), static_cast<const Bytef *>(byte), numBytes);
+}
+
+} // G3D
diff --git a/externals/g3dlite/G3D.lib/source/Crypto_md5.cpp b/externals/g3dlite/G3D.lib/source/Crypto_md5.cpp
new file mode 100644
index 00000000000..4aa24c39f9c
--- /dev/null
+++ b/externals/g3dlite/G3D.lib/source/Crypto_md5.cpp
@@ -0,0 +1,471 @@
+/**
+ @file Crypto_md5.cpp
+
+ @author Morgan McGuire, matrix@graphics3d.com
+ Copyright 2006-2007, Morgan McGuire. All rights reserved.
+
+ @created 2006-03-28
+ @edited 2006-04-06
+ */
+
+#include "G3D/platform.h"
+#include "G3D/Crypto.h"
+#include "G3D/BinaryInput.h"
+#include "G3D/BinaryOutput.h"
+
+#include <cstring>
+
+namespace G3D {
+
+
+MD5Hash::MD5Hash(class BinaryInput& b) {
+ deserialize(b);
+}
+
+
+void MD5Hash::deserialize(class BinaryInput& b) {
+ b.readBytes(value, 16);
+}
+
+
+void MD5Hash::serialize(class BinaryOutput& b) const {
+ b.writeBytes(value, 16);
+}
+
+
+typedef unsigned char md5_byte_t; /* 8-bit byte */
+typedef unsigned int md5_word_t; /* 32-bit word */
+
+/* Define the state of the MD5 Algorithm. */
+typedef struct md5_state_s {
+ md5_word_t count[2]; /* message length in bits, lsw first */
+ md5_word_t abcd[4]; /* digest buffer */
+ md5_byte_t buf[64]; /* accumulate block */
+} md5_state_t;
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+/* Initialize the algorithm. */
+static void md5_init(md5_state_t *pms);
+
+/* Append a string to the message. */
+static void md5_append(md5_state_t *pms, const md5_byte_t *data, int nbytes);
+
+/* Finish the message and return the digest. */
+void md5_finish(md5_state_t *pms, md5_byte_t digest[16]);
+#ifdef __cplusplus
+}
+#endif
+
+
+
+MD5Hash Crypto::md5(const void* data, size_t n) {
+ md5_state_t state;
+ md5_init(&state);
+ md5_append(&state, (const uint8*)data, (int)n);
+
+ MD5Hash h;
+ md5_finish(&state, &(h[0]));
+ return h;
+}
+
+/*
+ Copyright (C) 1999, 2000, 2002 Aladdin Enterprises. All rights reserved.
+
+ This software is provided 'as-is', without any express or implied
+ warranty. In no event will the authors be held liable for any damages
+ arising from the use of this software.
+
+ Permission is granted to anyone to use this software for any purpose,
+ including commercial applications, and to alter it and redistribute it
+ freely, subject to the following restrictions:
+
+ 1. The origin of this software must not be misrepresented; you must not
+ claim that you wrote the original software. If you use this software
+ in a product, an acknowledgment in the product documentation would be
+ appreciated but is not required.
+ 2. Altered source versions must be plainly marked as such, and must not be
+ misrepresented as being the original software.
+ 3. This notice may not be removed or altered from any source distribution.
+
+ L. Peter Deutsch
+ ghost@aladdin.com
+
+ */
+/*
+ Independent implementation of MD5 (RFC 1321).
+
+ This code implements the MD5 Algorithm defined in RFC 1321, whose
+ text is available at
+ http://www.ietf.org/rfc/rfc1321.txt
+ The code is derived from the text of the RFC, including the test suite
+ (section A.5) but excluding the rest of Appendix A. It does not include
+ any code or documentation that is identified in the RFC as being
+ copyrighted.
+
+ The original and principal author of md5.c is L. Peter Deutsch
+ <ghost@aladdin.com>. Other authors are noted in the change history
+ that follows (in reverse chronological order):
+
+ 2002-04-13 lpd Clarified derivation from RFC 1321; now handles byte order
+ either statically or dynamically; added missing #include <string.h>
+ in library.
+ 2002-03-11 lpd Corrected argument list for main(), and added int return
+ type, in test program and T value program.
+ 2002-02-21 lpd Added missing #include <stdio.h> in test program.
+ 2000-07-03 lpd Patched to eliminate warnings about "constant is
+ unsigned in ANSI C, signed in traditional"; made test program
+ self-checking.
+ 1999-11-04 lpd Edited comments slightly for automatic TOC extraction.
+ 1999-10-18 lpd Fixed typo in header comment (ansi2knr rather than md5).
+ 1999-05-03 lpd Original version.
+ */
+
+/*
+ * This package supports both compile-time and run-time determination of CPU
+ * byte order. If ARCH_IS_BIG_ENDIAN is defined as 0, the code will be
+ * compiled to run only on little-endian CPUs; if ARCH_IS_BIG_ENDIAN is
+ * defined as non-zero, the code will be compiled to run only on big-endian
+ * CPUs; if ARCH_IS_BIG_ENDIAN is not defined, the code will be compiled to
+ * run on either big- or little-endian CPUs, but will run slightly less
+ * efficiently on either one than if ARCH_IS_BIG_ENDIAN is defined.
+ */
+
+
+#if defined(G3D_LINUX) || defined(G3D_OSX)
+# if defined(G3D_OSX_PPC)
+# include <ppc/endian.h>
+# elif defined(G3D_OSX_INTEL)
+# include <i386/endian.h>
+# elif defined(__linux__)
+# include <endian.h>
+# elif defined(__FreeBSD__)
+# include <sys/endian.h>
+# endif
+#else
+# define BYTE_ORDER 0
+#endif
+
+#define T_MASK ((md5_word_t)~0)
+#define T1 /* 0xd76aa478 */ (T_MASK ^ 0x28955b87)
+#define T2 /* 0xe8c7b756 */ (T_MASK ^ 0x173848a9)
+#define T3 0x242070db
+#define T4 /* 0xc1bdceee */ (T_MASK ^ 0x3e423111)
+#define T5 /* 0xf57c0faf */ (T_MASK ^ 0x0a83f050)
+#define T6 0x4787c62a
+#define T7 /* 0xa8304613 */ (T_MASK ^ 0x57cfb9ec)
+#define T8 /* 0xfd469501 */ (T_MASK ^ 0x02b96afe)
+#define T9 0x698098d8
+#define T10 /* 0x8b44f7af */ (T_MASK ^ 0x74bb0850)
+#define T11 /* 0xffff5bb1 */ (T_MASK ^ 0x0000a44e)
+#define T12 /* 0x895cd7be */ (T_MASK ^ 0x76a32841)
+#define T13 0x6b901122
+#define T14 /* 0xfd987193 */ (T_MASK ^ 0x02678e6c)
+#define T15 /* 0xa679438e */ (T_MASK ^ 0x5986bc71)
+#define T16 0x49b40821
+#define T17 /* 0xf61e2562 */ (T_MASK ^ 0x09e1da9d)
+#define T18 /* 0xc040b340 */ (T_MASK ^ 0x3fbf4cbf)
+#define T19 0x265e5a51
+#define T20 /* 0xe9b6c7aa */ (T_MASK ^ 0x16493855)
+#define T21 /* 0xd62f105d */ (T_MASK ^ 0x29d0efa2)
+#define T22 0x02441453
+#define T23 /* 0xd8a1e681 */ (T_MASK ^ 0x275e197e)
+#define T24 /* 0xe7d3fbc8 */ (T_MASK ^ 0x182c0437)
+#define T25 0x21e1cde6
+#define T26 /* 0xc33707d6 */ (T_MASK ^ 0x3cc8f829)
+#define T27 /* 0xf4d50d87 */ (T_MASK ^ 0x0b2af278)
+#define T28 0x455a14ed
+#define T29 /* 0xa9e3e905 */ (T_MASK ^ 0x561c16fa)
+#define T30 /* 0xfcefa3f8 */ (T_MASK ^ 0x03105c07)
+#define T31 0x676f02d9
+#define T32 /* 0x8d2a4c8a */ (T_MASK ^ 0x72d5b375)
+#define T33 /* 0xfffa3942 */ (T_MASK ^ 0x0005c6bd)
+#define T34 /* 0x8771f681 */ (T_MASK ^ 0x788e097e)
+#define T35 0x6d9d6122
+#define T36 /* 0xfde5380c */ (T_MASK ^ 0x021ac7f3)
+#define T37 /* 0xa4beea44 */ (T_MASK ^ 0x5b4115bb)
+#define T38 0x4bdecfa9
+#define T39 /* 0xf6bb4b60 */ (T_MASK ^ 0x0944b49f)
+#define T40 /* 0xbebfbc70 */ (T_MASK ^ 0x4140438f)
+#define T41 0x289b7ec6
+#define T42 /* 0xeaa127fa */ (T_MASK ^ 0x155ed805)
+#define T43 /* 0xd4ef3085 */ (T_MASK ^ 0x2b10cf7a)
+#define T44 0x04881d05
+#define T45 /* 0xd9d4d039 */ (T_MASK ^ 0x262b2fc6)
+#define T46 /* 0xe6db99e5 */ (T_MASK ^ 0x1924661a)
+#define T47 0x1fa27cf8
+#define T48 /* 0xc4ac5665 */ (T_MASK ^ 0x3b53a99a)
+#define T49 /* 0xf4292244 */ (T_MASK ^ 0x0bd6ddbb)
+#define T50 0x432aff97
+#define T51 /* 0xab9423a7 */ (T_MASK ^ 0x546bdc58)
+#define T52 /* 0xfc93a039 */ (T_MASK ^ 0x036c5fc6)
+#define T53 0x655b59c3
+#define T54 /* 0x8f0ccc92 */ (T_MASK ^ 0x70f3336d)
+#define T55 /* 0xffeff47d */ (T_MASK ^ 0x00100b82)
+#define T56 /* 0x85845dd1 */ (T_MASK ^ 0x7a7ba22e)
+#define T57 0x6fa87e4f
+#define T58 /* 0xfe2ce6e0 */ (T_MASK ^ 0x01d3191f)
+#define T59 /* 0xa3014314 */ (T_MASK ^ 0x5cfebceb)
+#define T60 0x4e0811a1
+#define T61 /* 0xf7537e82 */ (T_MASK ^ 0x08ac817d)
+#define T62 /* 0xbd3af235 */ (T_MASK ^ 0x42c50dca)
+#define T63 0x2ad7d2bb
+#define T64 /* 0xeb86d391 */ (T_MASK ^ 0x14792c6e)
+
+
+static void
+md5_process(md5_state_t *pms, const md5_byte_t *data /*[64]*/)
+{
+ md5_word_t
+ a = pms->abcd[0], b = pms->abcd[1],
+ c = pms->abcd[2], d = pms->abcd[3];
+ md5_word_t t;
+#if BYTE_ORDER > 0
+ /* Define storage only for big-endian CPUs. */
+ md5_word_t X[16];
+#else
+ /* Define storage for little-endian or both types of CPUs. */
+ md5_word_t xbuf[16];
+ const md5_word_t *X;
+#endif
+
+ {
+#if BYTE_ORDER == 0
+ /*
+ * Determine dynamically whether this is a big-endian or
+ * little-endian machine, since we can use a more efficient
+ * algorithm on the latter.
+ */
+ static const int w = 1;
+
+ if (*((const md5_byte_t *)&w)) /* dynamic little-endian */
+#endif
+#if BYTE_ORDER <= 0 /* little-endian */
+ {
+ /*
+ * On little-endian machines, we can process properly aligned
+ * data without copying it.
+ */
+ if (!((data - (const md5_byte_t *)0) & 3)) {
+ /* data are properly aligned */
+ X = (const md5_word_t *)data;
+ } else {
+ /* not aligned */
+ memcpy(xbuf, data, 64);
+ X = xbuf;
+ }
+ }
+#endif
+#if BYTE_ORDER == 0
+ else /* dynamic big-endian */
+#endif
+#if BYTE_ORDER >= 0 /* big-endian */
+ {
+ /*
+ * On big-endian machines, we must arrange the bytes in the
+ * right order.
+ */
+ const md5_byte_t *xp = data;
+ int i;
+
+# if BYTE_ORDER == 0
+ X = xbuf; /* (dynamic only) */
+# else
+# define xbuf X /* (static only) */
+# endif
+ for (i = 0; i < 16; ++i, xp += 4)
+ xbuf[i] = xp[0] + (xp[1] << 8) + (xp[2] << 16) + (xp[3] << 24);
+ }
+#endif
+ }
+
+#define ROTATE_LEFT(x, n) (((x) << (n)) | ((x) >> (32 - (n))))
+
+ /* Round 1. */
+ /* Let [abcd k s i] denote the operation
+ a = b + ((a + F(b,c,d) + X[k] + T[i]) <<< s). */
+#define F(x, y, z) (((x) & (y)) | (~(x) & (z)))
+#define SET(a, b, c, d, k, s, Ti)\
+ t = a + F(b,c,d) + X[k] + Ti;\
+ a = ROTATE_LEFT(t, s) + b
+ /* Do the following 16 operations. */
+ SET(a, b, c, d, 0, 7, T1);
+ SET(d, a, b, c, 1, 12, T2);
+ SET(c, d, a, b, 2, 17, T3);
+ SET(b, c, d, a, 3, 22, T4);
+ SET(a, b, c, d, 4, 7, T5);
+ SET(d, a, b, c, 5, 12, T6);
+ SET(c, d, a, b, 6, 17, T7);
+ SET(b, c, d, a, 7, 22, T8);
+ SET(a, b, c, d, 8, 7, T9);
+ SET(d, a, b, c, 9, 12, T10);
+ SET(c, d, a, b, 10, 17, T11);
+ SET(b, c, d, a, 11, 22, T12);
+ SET(a, b, c, d, 12, 7, T13);
+ SET(d, a, b, c, 13, 12, T14);
+ SET(c, d, a, b, 14, 17, T15);
+ SET(b, c, d, a, 15, 22, T16);
+#undef SET
+
+ /* Round 2. */
+ /* Let [abcd k s i] denote the operation
+ a = b + ((a + G(b,c,d) + X[k] + T[i]) <<< s). */
+#define G(x, y, z) (((x) & (z)) | ((y) & ~(z)))
+#define SET(a, b, c, d, k, s, Ti)\
+ t = a + G(b,c,d) + X[k] + Ti;\
+ a = ROTATE_LEFT(t, s) + b
+ /* Do the following 16 operations. */
+ SET(a, b, c, d, 1, 5, T17);
+ SET(d, a, b, c, 6, 9, T18);
+ SET(c, d, a, b, 11, 14, T19);
+ SET(b, c, d, a, 0, 20, T20);
+ SET(a, b, c, d, 5, 5, T21);
+ SET(d, a, b, c, 10, 9, T22);
+ SET(c, d, a, b, 15, 14, T23);
+ SET(b, c, d, a, 4, 20, T24);
+ SET(a, b, c, d, 9, 5, T25);
+ SET(d, a, b, c, 14, 9, T26);
+ SET(c, d, a, b, 3, 14, T27);
+ SET(b, c, d, a, 8, 20, T28);
+ SET(a, b, c, d, 13, 5, T29);
+ SET(d, a, b, c, 2, 9, T30);
+ SET(c, d, a, b, 7, 14, T31);
+ SET(b, c, d, a, 12, 20, T32);
+#undef SET
+
+ /* Round 3. */
+ /* Let [abcd k s t] denote the operation
+ a = b + ((a + H(b,c,d) + X[k] + T[i]) <<< s). */
+#define H(x, y, z) ((x) ^ (y) ^ (z))
+#define SET(a, b, c, d, k, s, Ti)\
+ t = a + H(b,c,d) + X[k] + Ti;\
+ a = ROTATE_LEFT(t, s) + b
+ /* Do the following 16 operations. */
+ SET(a, b, c, d, 5, 4, T33);
+ SET(d, a, b, c, 8, 11, T34);
+ SET(c, d, a, b, 11, 16, T35);
+ SET(b, c, d, a, 14, 23, T36);
+ SET(a, b, c, d, 1, 4, T37);
+ SET(d, a, b, c, 4, 11, T38);
+ SET(c, d, a, b, 7, 16, T39);
+ SET(b, c, d, a, 10, 23, T40);
+ SET(a, b, c, d, 13, 4, T41);
+ SET(d, a, b, c, 0, 11, T42);
+ SET(c, d, a, b, 3, 16, T43);
+ SET(b, c, d, a, 6, 23, T44);
+ SET(a, b, c, d, 9, 4, T45);
+ SET(d, a, b, c, 12, 11, T46);
+ SET(c, d, a, b, 15, 16, T47);
+ SET(b, c, d, a, 2, 23, T48);
+#undef SET
+
+ /* Round 4. */
+ /* Let [abcd k s t] denote the operation
+ a = b + ((a + I(b,c,d) + X[k] + T[i]) <<< s). */
+#define I(x, y, z) ((y) ^ ((x) | ~(z)))
+#define SET(a, b, c, d, k, s, Ti)\
+ t = a + I(b,c,d) + X[k] + Ti;\
+ a = ROTATE_LEFT(t, s) + b
+ /* Do the following 16 operations. */
+ SET(a, b, c, d, 0, 6, T49);
+ SET(d, a, b, c, 7, 10, T50);
+ SET(c, d, a, b, 14, 15, T51);
+ SET(b, c, d, a, 5, 21, T52);
+ SET(a, b, c, d, 12, 6, T53);
+ SET(d, a, b, c, 3, 10, T54);
+ SET(c, d, a, b, 10, 15, T55);
+ SET(b, c, d, a, 1, 21, T56);
+ SET(a, b, c, d, 8, 6, T57);
+ SET(d, a, b, c, 15, 10, T58);
+ SET(c, d, a, b, 6, 15, T59);
+ SET(b, c, d, a, 13, 21, T60);
+ SET(a, b, c, d, 4, 6, T61);
+ SET(d, a, b, c, 11, 10, T62);
+ SET(c, d, a, b, 2, 15, T63);
+ SET(b, c, d, a, 9, 21, T64);
+#undef SET
+
+ /* Then perform the following additions. (That is increment each
+ of the four registers by the value it had before this block
+ was started.) */
+ pms->abcd[0] += a;
+ pms->abcd[1] += b;
+ pms->abcd[2] += c;
+ pms->abcd[3] += d;
+}
+
+void
+md5_init(md5_state_t *pms)
+{
+ pms->count[0] = pms->count[1] = 0;
+ pms->abcd[0] = 0x67452301;
+ pms->abcd[1] = /*0xefcdab89*/ T_MASK ^ 0x10325476;
+ pms->abcd[2] = /*0x98badcfe*/ T_MASK ^ 0x67452301;
+ pms->abcd[3] = 0x10325476;
+}
+
+void
+md5_append(md5_state_t *pms, const md5_byte_t *data, int nbytes)
+{
+ const md5_byte_t *p = data;
+ int left = nbytes;
+ int offset = (pms->count[0] >> 3) & 63;
+ md5_word_t nbits = (md5_word_t)(nbytes << 3);
+
+ if (nbytes <= 0)
+ return;
+
+ /* Update the message length. */
+ pms->count[1] += nbytes >> 29;
+ pms->count[0] += nbits;
+ if (pms->count[0] < nbits)
+ pms->count[1]++;
+
+ /* Process an initial partial block. */
+ if (offset) {
+ int copy = (offset + nbytes > 64 ? 64 - offset : nbytes);
+
+ memcpy(pms->buf + offset, p, copy);
+ if (offset + copy < 64)
+ return;
+ p += copy;
+ left -= copy;
+ md5_process(pms, pms->buf);
+ }
+
+ /* Process full blocks. */
+ for (; left >= 64; p += 64, left -= 64)
+ md5_process(pms, p);
+
+ /* Process a final partial block. */
+ if (left)
+ memcpy(pms->buf, p, left);
+}
+
+void
+md5_finish(md5_state_t *pms, md5_byte_t digest[16])
+{
+ static const md5_byte_t pad[64] = {
+ 0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
+ };
+ md5_byte_t data[8];
+ int i;
+
+ /* Save the length before padding. */
+ for (i = 0; i < 8; ++i)
+ data[i] = (md5_byte_t)(pms->count[i >> 2] >> ((i & 3) << 3));
+ /* Pad to 56 bytes mod 64. */
+ md5_append(pms, pad, ((55 - (pms->count[0] >> 3)) & 63) + 1);
+ /* Append the length. */
+ md5_append(pms, data, 8);
+ for (i = 0; i < 16; ++i)
+ digest[i] = (md5_byte_t)(pms->abcd[i >> 2] >> ((i & 3) << 3));
+}
+
+}
diff --git a/externals/g3dlite/G3D.lib/source/Cylinder.cpp b/externals/g3dlite/G3D.lib/source/Cylinder.cpp
new file mode 100644
index 00000000000..bdc7d56be23
--- /dev/null
+++ b/externals/g3dlite/G3D.lib/source/Cylinder.cpp
@@ -0,0 +1,176 @@
+/**
+ @file Cylinder.cpp
+
+ @maintainer Morgan McGuire, matrix@graphics3d.com
+
+ @created 2003-02-07
+ @edited 2006-02-18
+
+ Copyright 2000-2006, Morgan McGuire.
+ All rights reserved.
+ */
+
+#include "G3D/platform.h"
+#include "G3D/Cylinder.h"
+#include "G3D/BinaryInput.h"
+#include "G3D/BinaryOutput.h"
+#include "G3D/LineSegment.h"
+#include "G3D/CoordinateFrame.h"
+#include "G3D/Line.h"
+#include "G3D/AABox.h"
+
+namespace G3D {
+
+Cylinder::Cylinder(class BinaryInput& b) {
+ deserialize(b);
+}
+
+
+Cylinder::Cylinder() {
+}
+
+
+Cylinder::Cylinder(const Vector3& _p1, const Vector3& _p2, float _r)
+ : p1(_p1), p2(_p2), mRadius(_r) {
+}
+
+
+void Cylinder::serialize(class BinaryOutput& b) const {
+ p1.serialize(b);
+ p2.serialize(b);
+ b.writeFloat64(mRadius);
+}
+
+
+void Cylinder::deserialize(class BinaryInput& b) {
+ p1.deserialize(b);
+ p2.deserialize(b);
+ mRadius = b.readFloat64();
+}
+
+
+Line Cylinder::axis() const {
+ return Line::fromTwoPoints(p1, p2);
+}
+
+
+
+float Cylinder::radius() const {
+ return mRadius;
+}
+
+
+float Cylinder::volume() const {
+ return
+ (float)pi() * square(mRadius) * (p1 - p2).magnitude();
+}
+
+
+float Cylinder::area() const {
+ return
+ // Sides
+ (twoPi() * mRadius) * height() +
+
+ // Caps
+ twoPi() * square(mRadius);
+}
+
+void Cylinder::getBounds(AABox& out) const {
+ Vector3 min = p1.min(p2) - (Vector3(1, 1, 1) * mRadius);
+ Vector3 max = p1.max(p2) + (Vector3(1, 1, 1) * mRadius);
+ out = AABox(min, max);
+}
+
+bool Cylinder::contains(const Vector3& p) const {
+ return LineSegment::fromTwoPoints(p1, p2).distanceSquared(p) <= square(mRadius);
+}
+
+
+void Cylinder::getReferenceFrame(CoordinateFrame& cframe) const {
+ cframe.translation = center();
+
+ Vector3 Y = (p1 - p2).direction();
+ Vector3 X = (abs(Y.dot(Vector3::unitX())) > 0.9) ? Vector3::unitY() : Vector3::unitX();
+ Vector3 Z = X.cross(Y).direction();
+ X = Y.cross(Z);
+ cframe.rotation.setColumn(0, X);
+ cframe.rotation.setColumn(1, Y);
+ cframe.rotation.setColumn(2, Z);
+}
+
+
+void Cylinder::getRandomSurfacePoint(Vector3& p, Vector3& N) const {
+ float h = height();
+ float r = radius();
+
+ // Create a random point on a standard cylinder and then rotate to the global frame.
+
+ // Relative areas (factor of 2PI already taken out)
+ float capRelArea = square(r) / 2.0f;
+ float sideRelArea = r * h;
+
+ float r1 = uniformRandom(0, capRelArea * 2 + sideRelArea);
+
+ if (r1 < capRelArea * 2) {
+
+ // Select a point uniformly at random on a disk
+ // @cite http://mathworld.wolfram.com/DiskPointPicking.html
+ float a = uniformRandom(0, (float)twoPi());
+ float r2 = sqrt(uniformRandom(0, 1)) * r;
+ p.x = cos(a) * r2;
+ p.z = sin(a) * r2;
+
+ N.x = 0;
+ N.z = 0;
+ if (r1 < capRelArea) {
+ // Top
+ p.y = h / 2.0f;
+ N.y = 1;
+ } else {
+ // Bottom
+ p.y = -h / 2.0f;
+ N.y = -1;
+ }
+ } else {
+ // Side
+ float a = uniformRandom(0, (float)twoPi());
+ N.x = cos(a);
+ N.y = 0;
+ N.z = sin(a);
+ p.x = N.x * r;
+ p.z = N.y * r;
+ p.y = uniformRandom(-h / 2.0f, h / 2.0f);
+ }
+
+ // Transform to world space
+ CoordinateFrame cframe;
+ getReferenceFrame(cframe);
+
+ p = cframe.pointToWorldSpace(p);
+ N = cframe.normalToWorldSpace(N);
+}
+
+
+Vector3 Cylinder::randomInteriorPoint() const {
+ float h = height();
+ float r = radius();
+
+ // Create a random point in a standard cylinder and then rotate to the global frame.
+
+ // Select a point uniformly at random on a disk
+ // @cite http://mathworld.wolfram.com/DiskPointPicking.html
+ float a = uniformRandom(0, (float)twoPi());
+ float r2 = sqrt(uniformRandom(0, 1)) * r;
+
+ Vector3 p( cos(a) * r2,
+ uniformRandom(-h / 2.0f, h / 2.0f),
+ sin(a) * r2);
+
+ // Transform to world space
+ CoordinateFrame cframe;
+ getReferenceFrame(cframe);
+
+ return cframe.pointToWorldSpace(p);
+}
+
+} // namespace
diff --git a/externals/g3dlite/G3D.lib/source/Discovery.cpp b/externals/g3dlite/G3D.lib/source/Discovery.cpp
new file mode 100644
index 00000000000..2c27c5bf2b9
--- /dev/null
+++ b/externals/g3dlite/G3D.lib/source/Discovery.cpp
@@ -0,0 +1,170 @@
+/**
+ @file Discovery.cpp
+
+ @maintainer Morgan McGuire, matrix@graphics3d.com
+
+ @created 2003-06-26
+ @edited 2005-02-24
+ */
+
+#include "G3D/Discovery.h"
+#include "G3D/BinaryInput.h"
+#include "G3D/BinaryOutput.h"
+
+#include <cstring>
+
+namespace G3D {
+
+///////////////////////////////////////////////////////////////////////////////////////////
+
+void DiscoveryAdvertisement::serialize(BinaryOutput& b) const {
+ address.serialize(b);
+}
+
+void DiscoveryAdvertisement::deserialize(BinaryInput& b) {
+ address.deserialize(b);
+ lastUpdateTime = System::time();
+}
+
+
+///////////////////////////////////////////////////////////////////////////////////////////
+
+void DiscoveryServerAddressMessage::serialize(BinaryOutput& b) const {
+ b.writeString(G3D_DISCOVERY_PROTOCOL_NAME);
+ b.writeInt32(G3D_DISCOVERY_PROTOCOL_VERSION);
+ b.writeString(settings->appProtocolName);
+ b.writeInt32(settings->appProtocolVersion);
+
+ // Send addresses
+ b.writeInt32(address.size());
+ for (int i = 0; i < address.size(); ++i) {
+ address[i].serialize(b);
+ }
+}
+
+
+void DiscoveryServerAddressMessage::deserialize(BinaryInput& b) {
+ address.clear();
+ correctProtocol = false;
+ serverProtocolVersion[0] = 0;
+ serverProtocolVersion[1] = 0;
+
+ // Verify that we are on the same protocol
+ if (b.readString(strlen(G3D_DISCOVERY_PROTOCOL_NAME) + 1) != G3D_DISCOVERY_PROTOCOL_NAME) {
+ return;
+ }
+
+ serverProtocolVersion[0] = b.readInt32();
+ if (serverProtocolVersion[0] != G3D_DISCOVERY_PROTOCOL_VERSION) {
+ return;
+ }
+
+ if (b.readString() != settings->appProtocolName) {
+ return;
+ }
+
+ serverProtocolVersion[1] = b.readInt32();
+ if (serverProtocolVersion[1] != settings->appProtocolVersion) {
+ return;
+ }
+
+ correctProtocol = true;
+
+ address.resize(b.readInt32());
+ for (int i = 0; i < address.size(); ++i) {
+ address[i].deserialize(b);
+ }
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////
+
+void DiscoveryServer::sendAnnouncement() const {
+ NetAddress broadcast(NetworkDevice::instance()->broadcastAddressArray()[0], settings->serverBroadcastPort);
+
+ net->send(broadcast, SERVER_BROADCAST_MESSAGE, addressMessage);
+
+ const_cast<DiscoveryServer*>(this)->lastBroadcast = System::time();
+}
+
+
+void DiscoveryServer::sendShutDown() const {
+ NetAddress broadcast(NetworkDevice::instance()->broadcastAddressArray()[0], settings->serverBroadcastPort);
+ ShutdownMessage s;
+ net->send(broadcast, SERVER_SHUTDOWN_MESSAGE, s);
+}
+
+
+bool DiscoveryServer::ok() const {
+ return listener->ok() && net->ok();
+}
+
+
+void DiscoveryServer::init(
+ const DiscoverySettings* _settings,
+ DiscoveryAdvertisement* _advertisement) {
+
+ Discovery::init(_settings);
+
+ advertisement = _advertisement;
+ addressMessage.settings = settings;
+ NetworkDevice::instance()->localHostAddresses(addressMessage.address);
+
+ // Set the port number
+ for (int i = 0; i < addressMessage.address.size(); ++i) {
+ addressMessage.address[i] =
+ NetAddress(addressMessage.address[i].ip(),
+ settings->serverAdvertisementPort);
+ }
+
+ net = LightweightConduit::create(settings->clientBroadcastPort, true, true);
+
+ listener = NetListener::create(settings->serverAdvertisementPort);
+
+ // Send initial announcement
+ sendAnnouncement();
+}
+
+
+void DiscoveryServer::doNetwork() {
+ const RealTime UNSOLICITED_BROADCAST_PERIOD = 60;
+
+ // Check for client broadcast requests
+
+ if (net->messageWaiting()) {
+ // Some client broadcast that it is looking for servers.
+ // Respond by sending out our announcement to everyone
+ // (avoids having to figure out if the message return address
+ // is correct).
+ NetAddress dummy;
+ net->receive(dummy);
+ sendAnnouncement();
+ } else if (System::time() > lastBroadcast + UNSOLICITED_BROADCAST_PERIOD) {
+ sendAnnouncement();
+ }
+
+ // Handle incoming connections
+
+ if (listener->clientWaiting()) {
+ // Respond to this client
+ ReliableConduitRef client = listener->waitForConnection();
+ client->send(SERVER_BROADCAST_MESSAGE, *advertisement);
+ }
+}
+
+
+void DiscoveryServer::cleanup() {
+ sendShutDown();
+}
+
+//////////////////////////////////////////////////////////////////////////////////
+
+std::string IncompatibleServerDescription::toString() const {
+ return std::string("Incompatible server at ") + address.toString() +
+ format(", version %d.%d", protocolVersion[0], protocolVersion[1]);
+}
+
+//////////////////////////////////////////////////////////////////////////////////
+
+
+}
+
diff --git a/externals/g3dlite/G3D.lib/source/GCamera.cpp b/externals/g3dlite/G3D.lib/source/GCamera.cpp
new file mode 100644
index 00000000000..e70188377e6
--- /dev/null
+++ b/externals/g3dlite/G3D.lib/source/GCamera.cpp
@@ -0,0 +1,399 @@
+/**
+ @file GCamera.cpp
+
+ @author Morgan McGuire, matrix@graphics3d.com
+ @author Jeff Marsceill, 08jcm@williams.edu
+
+ @created 2005-07-20
+ @edited 2007-07-24
+*/
+#include "G3D/GCamera.h"
+#include "G3D/platform.h"
+#include "G3D/Rect2D.h"
+#include "G3D/BinaryInput.h"
+#include "G3D/BinaryOutput.h"
+#include "G3D/Ray.h"
+#include "G3D/Matrix4.h"
+
+namespace G3D {
+
+GCamera::GCamera() {
+ setNearPlaneZ(-0.1f);
+ setFarPlaneZ(-(float)inf());
+ setFieldOfView((float)toRadians(55.0f), VERTICAL);
+}
+
+
+GCamera::~GCamera() {
+}
+
+void GCamera::getCoordinateFrame(CoordinateFrame& c) const {
+ c = m_cframe;
+}
+
+void GCamera::setCoordinateFrame(const CoordinateFrame& c) {
+ m_cframe = c;
+}
+
+void GCamera::setFieldOfView(float angle, FOVDirection dir) {
+ debugAssert((angle < pi()) && (angle > 0));
+
+ m_fieldOfView = angle;
+ m_direction = dir;
+}
+
+float GCamera::imagePlaneDepth() const{
+ return -m_nearPlaneZ;
+}
+
+float GCamera::viewportWidth(const Rect2D& viewport) const {
+ // Compute the side of a square at the near plane based on our field of view
+ float s = 2.0f * -m_nearPlaneZ * tan(m_fieldOfView * 0.5f);
+
+ if (m_direction == VERTICAL) {
+ s *= viewport.width() / viewport.height();
+ }
+
+ return s;
+}
+
+float GCamera::viewportHeight(const Rect2D& viewport) const {
+ // Compute the side of a square at the near plane based on our field of view
+ float s = 2.0f * -m_nearPlaneZ * tan(m_fieldOfView * 0.5f);
+
+ if (m_direction == HORIZONTAL) {
+ s *= viewport.height() / viewport.width();
+ }
+
+ return s;
+}
+
+Ray GCamera::worldRay(float x, float y, const Rect2D& viewport) const {
+
+ int screenWidth = iFloor(viewport.width());
+ int screenHeight = iFloor(viewport.height());
+
+ Ray out;
+ out.origin = m_cframe.translation;
+
+ float cx = screenWidth / 2.0f;
+ float cy = screenHeight / 2.0f;
+
+ out.direction = Vector3( (x - cx) * viewportWidth(viewport) / screenWidth,
+ -(y - cy) * viewportHeight(viewport) / screenHeight,
+ (m_nearPlaneZ) );
+
+ out.direction = m_cframe.vectorToWorldSpace(out.direction);
+
+ // Normalize the direction (we didn't do it before)
+ out.direction = out.direction.direction();
+
+ return out;
+}
+
+/**
+This is the matrix that a RenderDevice (or OpenGL) uses as the projection matrix.
+@sa RenderDevice::setProjectionAndCameraMatrix, RenderDevice::setProjectionMatrix, Matrix4::perspectiveProjection
+*/
+void GCamera::getProjectUnitMatrix(const Rect2D& viewport, Matrix4& P) const{
+
+ float screenWidth = viewport.width();
+ float screenHeight = viewport.height();
+
+ float r, l, t, b, n, f, x, y;
+
+ if(m_direction == VERTICAL){
+ y = -m_nearPlaneZ * tan(m_fieldOfView / 2);
+ x = y * (screenWidth / screenHeight);
+ }
+ else{ //m_direction == HORIZONTAL
+ x = -m_nearPlaneZ * tan(m_fieldOfView / 2);
+ y = x * (screenHeight / screenWidth);
+ }
+
+ n = -m_nearPlaneZ;
+ f = -m_farPlaneZ;
+ r = x;
+ l = -x;
+ t = y;
+ b = -y;
+
+ P = Matrix4::perspectiveProjection(l, r, b, t, n, f);
+}
+
+Vector3 GCamera::projectUnit(const Vector3& point, const Rect2D& viewport) const {
+ Matrix4 M;
+ getProjectUnitMatrix(viewport, M);
+
+ Vector4 cameraSpacePoint(coordinateFrame().pointToObjectSpace(point), 1.0f);
+ const Vector4& screenSpacePoint = M * cameraSpacePoint;
+
+ return Vector3(screenSpacePoint.xyz() / screenSpacePoint.w);
+}
+
+Vector3 GCamera::project(const Vector3& point,
+ const Rect2D& viewport) const {
+
+ // Find the point in the homogeneous cube
+ const Vector3& cube = projectUnit(point, viewport);
+
+ return convertFromUnitToNormal(cube, viewport);
+}
+
+Vector3 GCamera::unprojectUnit(const Vector3& v, const Rect2D& viewport) const {
+
+ const Vector3& projectedPoint = convertFromUnitToNormal(v, viewport);
+
+ return unproject(projectedPoint, viewport);
+}
+
+
+Vector3 GCamera::unproject(const Vector3& v, const Rect2D& viewport) const {
+
+ const float n = m_nearPlaneZ;
+ const float f = m_farPlaneZ;
+
+ float z;
+
+ if (-f >= inf()) {
+ // Infinite far plane
+ z = 1.0f / (((-1.0f / n) * v.z) + 1.0f / n);
+ } else {
+ z = 1.0f / ((((1.0f / f) - (1.0f / n)) * v.z) + 1.0f / n);
+ }
+
+ const Ray& ray = worldRay(v.x, v.y, viewport);
+
+ // Find out where the ray reaches the specified depth.
+ const Vector3& out = ray.origin + ray.direction * -z / (ray.direction.dot(m_cframe.lookVector()));
+
+ return out;
+}
+
+float GCamera::worldToScreenSpaceArea(float area, float z, const Rect2D& viewport) const {
+ if (z >= 0) {
+ return (float)inf();
+ }
+ return area * (float)square(imagePlaneDepth() / z);
+}
+
+
+void GCamera::getClipPlanes(
+ const Rect2D& viewport,
+ Array<Plane>& clip) const {
+
+ Frustum fr;
+ frustum(viewport, fr);
+ clip.resize(fr.faceArray.size(), DONT_SHRINK_UNDERLYING_ARRAY);
+ for (int f = 0; f < clip.size(); ++f) {
+ clip[f] = fr.faceArray[f].plane;
+ }
+}
+
+GCamera::Frustum GCamera::frustum(const Rect2D& viewport) const {
+ Frustum f;
+ frustum(viewport, f);
+ return f;
+}
+
+void GCamera::frustum(const Rect2D& viewport, Frustum& fr) const {
+
+ // The volume is the convex hull of the vertices definining the view
+ // frustum and the light source point at infinity.
+
+ const float x = viewportWidth(viewport) / 2;
+ const float y = viewportHeight(viewport) / 2;
+ const float z = m_nearPlaneZ;
+ const float w = z / -m_farPlaneZ;
+ float fovx;
+
+ fovx = m_fieldOfView;
+ if (m_direction == VERTICAL) {
+ fovx *= x / y;
+ }
+
+ // Near face (ccw from UR)
+ fr.vertexPos.append(
+ Vector4( x, y, z, 1),
+ Vector4(-x, y, z, 1),
+ Vector4(-x, -y, z, 1),
+ Vector4( x, -y, z, 1));
+
+ // Far face (ccw from UR, from origin)
+ fr.vertexPos.append(
+ Vector4( x, y, z, w),
+ Vector4(-x, y, z, w),
+ Vector4(-x, -y, z, w),
+ Vector4( x, -y, z, w));
+
+ Frustum::Face face;
+
+ // Near plane (wind backwards so normal faces into frustum)
+ // Recall that nearPlane, farPlane are positive numbers, so
+ // we need to negate them to produce actual z values.
+ face.plane = Plane(Vector3(0,0,-1), Vector3(0,0,m_nearPlaneZ));
+ face.vertexIndex[0] = 3;
+ face.vertexIndex[1] = 2;
+ face.vertexIndex[2] = 1;
+ face.vertexIndex[3] = 0;
+ fr.faceArray.append(face);
+
+ // Right plane
+ face.plane = Plane(Vector3(-cosf(fovx/2), 0, -sinf(fovx/2)), Vector3::zero());
+ face.vertexIndex[0] = 0;
+ face.vertexIndex[1] = 4;
+ face.vertexIndex[2] = 7;
+ face.vertexIndex[3] = 3;
+ fr.faceArray.append(face);
+
+ // Left plane
+ face.plane = Plane(Vector3(-fr.faceArray.last().plane.normal().x, 0, fr.faceArray.last().plane.normal().z), Vector3::zero());
+ face.vertexIndex[0] = 5;
+ face.vertexIndex[1] = 1;
+ face.vertexIndex[2] = 2;
+ face.vertexIndex[3] = 6;
+ fr.faceArray.append(face);
+
+ // Top plane
+ face.plane = Plane(Vector3(0, -cosf(m_fieldOfView/2.0f), -sinf(m_fieldOfView/2.0f)), Vector3::zero());
+ face.vertexIndex[0] = 1;
+ face.vertexIndex[1] = 5;
+ face.vertexIndex[2] = 4;
+ face.vertexIndex[3] = 0;
+ fr.faceArray.append(face);
+
+ // Bottom plane
+ face.plane = Plane(Vector3(0, -fr.faceArray.last().plane.normal().y, fr.faceArray.last().plane.normal().z), Vector3::zero());
+ face.vertexIndex[0] = 2;
+ face.vertexIndex[1] = 3;
+ face.vertexIndex[2] = 7;
+ face.vertexIndex[3] = 6;
+ fr.faceArray.append(face);
+
+ // Far plane
+ if (-m_farPlaneZ < inf()) {
+ face.plane = Plane(Vector3(0, 0, 1), Vector3(0, 0, m_farPlaneZ));
+ face.vertexIndex[0] = 4;
+ face.vertexIndex[1] = 5;
+ face.vertexIndex[2] = 6;
+ face.vertexIndex[3] = 7;
+ fr.faceArray.append(face);
+ }
+
+ // Transform vertices to world space
+ for (int v = 0; v < fr.vertexPos.size(); ++v) {
+ fr.vertexPos[v] = m_cframe.toWorldSpace(fr.vertexPos[v]);
+ }
+
+ // Transform planes to world space
+ for (int p = 0; p < fr.faceArray.size(); ++p) {
+ // Since there is no scale factor, we don't have to
+ // worry about the inverse transpose of the normal.
+ Vector3 normal;
+ float d;
+
+ fr.faceArray[p].plane.getEquation(normal, d);
+
+ Vector3 newNormal = m_cframe.rotation * normal;
+
+ if (isFinite(d)) {
+ d = (newNormal * -d + m_cframe.translation).dot(newNormal);
+ fr.faceArray[p].plane = Plane(newNormal, newNormal * d);
+ } else {
+ // When d is infinite, we can't multiply 0's by it without
+ // generating NaNs.
+ fr.faceArray[p].plane = Plane::fromEquation(newNormal.x, newNormal.y, newNormal.z, d);
+ }
+ }
+}
+
+void GCamera::getNearViewportCorners(
+ const Rect2D& viewport,
+ Vector3& outUR,
+ Vector3& outUL,
+ Vector3& outLL,
+ Vector3& outLR) const {
+
+ // Must be kept in sync with getFrustum()
+ const float w = viewportWidth(viewport) / 2.0f;
+ const float h = viewportHeight(viewport) / 2.0f;
+ const float z = nearPlaneZ();
+
+ // Compute the points
+ outUR = Vector3( w, h, z);
+ outUL = Vector3(-w, h, z);
+ outLL = Vector3(-w, -h, z);
+ outLR = Vector3( w, -h, z);
+
+ // Take to world space
+ outUR = m_cframe.pointToWorldSpace(outUR);
+ outUL = m_cframe.pointToWorldSpace(outUL);
+ outLR = m_cframe.pointToWorldSpace(outLR);
+ outLL = m_cframe.pointToWorldSpace(outLL);
+}
+
+void GCamera::getFarViewportCorners(
+ const Rect2D& viewport,
+ Vector3& outUR,
+ Vector3& outUL,
+ Vector3& outLL,
+ Vector3& outLR) const {
+
+ // Must be kept in sync with getFrustum()
+ const float w = viewportWidth(viewport) * m_farPlaneZ / m_nearPlaneZ;
+ const float h = viewportHeight(viewport) * m_farPlaneZ / m_nearPlaneZ;
+ const float z = m_farPlaneZ;
+
+ // Compute the points
+ outUR = Vector3( w, h, z);
+ outUL = Vector3(-w, h, z);
+ outLL = Vector3(-w, -h, z);
+ outLR = Vector3( w, -h, z);
+
+ // Take to world space
+ outUR = m_cframe.pointToWorldSpace(outUR);
+ outUL = m_cframe.pointToWorldSpace(outUL);
+ outLR = m_cframe.pointToWorldSpace(outLR);
+ outLL = m_cframe.pointToWorldSpace(outLL);
+}
+
+
+
+void GCamera::setPosition(const Vector3& t) {
+ m_cframe.translation = t;
+}
+
+
+void GCamera::lookAt(const Vector3& position, const Vector3& up) {
+ m_cframe.lookAt(position, up);
+}
+
+
+void GCamera::serialize(BinaryOutput& bo) const {
+ bo.writeFloat32(m_fieldOfView);
+ bo.writeFloat32(imagePlaneDepth());
+ debugAssert(nearPlaneZ() < 0.0f);
+ bo.writeFloat32(nearPlaneZ());
+ debugAssert(farPlaneZ() < 0.0f);
+ bo.writeFloat32(farPlaneZ());
+ m_cframe.serialize(bo);
+ bo.writeInt8(m_direction);
+}
+
+
+void GCamera::deserialize(BinaryInput& bi) {
+ m_fieldOfView = bi.readFloat32();
+ m_nearPlaneZ = bi.readFloat32();
+ debugAssert(m_nearPlaneZ < 0.0f);
+ m_farPlaneZ = bi.readFloat32();
+ debugAssert(m_farPlaneZ < 0.0f);
+ m_cframe.deserialize(bi);
+ m_direction = (FOVDirection)bi.readInt8();
+}
+
+
+Vector3 GCamera::convertFromUnitToNormal(const Vector3& in, const Rect2D& viewport) const{
+ return (in + Vector3(1,1,1)) * 0.5 * Vector3(viewport.width(), -viewport.height(), 1) +
+ Vector3(viewport.x0(), viewport.y1(), 0);
+}
+} // namespace
diff --git a/externals/g3dlite/G3D.lib/source/GImage.cpp b/externals/g3dlite/G3D.lib/source/GImage.cpp
new file mode 100644
index 00000000000..c7abf982553
--- /dev/null
+++ b/externals/g3dlite/G3D.lib/source/GImage.cpp
@@ -0,0 +1,1065 @@
+/**
+ @file GImage.cpp
+ @author Morgan McGuire, morgan@graphics3d.com
+ Copyright 2002-2007, Morgan McGuire
+
+ @created 2002-05-27
+ @edited 2006-10-10
+ */
+#include "G3D/platform.h"
+#include "G3D/GImage.h"
+#include "G3D/debug.h"
+#include "G3D/stringutils.h"
+#include "G3D/TextInput.h"
+#include "G3D/TextOutput.h"
+#include "G3D/BinaryInput.h"
+#include "G3D/BinaryOutput.h"
+#include "G3D/Log.h"
+#include <png.h>
+#include <sys/stat.h>
+#include <assert.h>
+#include <sys/types.h>
+
+//////////////////////////////////////////////////////////////////////////////////////////////
+
+namespace G3D {
+
+void GImage::RGBtoRGBA(
+ const uint8* in,
+ uint8* out,
+ int numPixels) {
+
+ for (int i = 0; i < numPixels; ++i) {
+ int i3 = i * 3;
+ int i4 = i3 + i;
+
+ out[i4 + 0] = in[i3 + 0];
+ out[i4 + 1] = in[i3 + 1];
+ out[i4 + 2] = in[i3 + 2];
+ out[i4 + 3] = 255;
+ }
+}
+
+
+void GImage::RGBAtoRGB(
+ const uint8* in,
+ uint8* out,
+ int numPixels) {
+
+ for (int i = 0; i < numPixels; ++i) {
+ int i3 = i * 3;
+ int i4 = i3 + i;
+
+ out[i3 + 0] = in[i4 + 0];
+ out[i3 + 1] = in[i4 + 1];
+ out[i3 + 2] = in[i4 + 2];
+ }
+}
+
+
+void GImage::RGBtoBGRA(
+ const uint8* in,
+ uint8* out,
+ int numPixels) {
+
+ for (int i = 0; i < numPixels; ++i) {
+ int i3 = i * 3;
+ int i4 = i3 + i;
+
+ out[i4 + 2] = in[i3 + 0];
+ out[i4 + 1] = in[i3 + 1];
+ out[i4 + 0] = in[i3 + 2];
+ out[i4 + 3] = 255;
+ }
+}
+
+
+
+void GImage::RGBtoBGR(
+ const uint8* in,
+ uint8* out,
+ int numPixels) {
+
+ for (int i = 0; i < numPixels; ++i) {
+ int i3 = i * 3;
+
+ int r = in[i3 + 0];
+ int g = in[i3 + 1];
+ int b = in[i3 + 2];
+
+ out[i3 + 2] = r;
+ out[i3 + 1] = g;
+ out[i3 + 0] = b;
+ }
+}
+
+
+void GImage::RGBxRGBtoRGBA(
+ const uint8* colorRGB,
+ const uint8* alphaRGB,
+ uint8* out,
+ int numPixels) {
+
+ for (int i = numPixels - 1; i >= 0; --i) {
+ int i3 = i * 3;
+ int i4 = i3 + i;
+
+ out[i4 + 0] = colorRGB[i3 + 0];
+ out[i4 + 1] = colorRGB[i3 + 1];
+ out[i4 + 2] = colorRGB[i3 + 2];
+ out[i4 + 3] = alphaRGB[i3 + 0];
+ }
+}
+
+
+void GImage::RGBtoARGB(
+ const uint8* in,
+ uint8* out,
+ int numPixels) {
+
+ for (int i = 0; i < numPixels; ++i) {
+ int i3 = i * 3;
+ int i4 = i3 + i;
+
+ out[i4 + 0] = 255;
+ out[i4 + 1] = in[i3 + 0];
+ out[i4 + 2] = in[i3 + 1];
+ out[i4 + 3] = in[i3 + 2];
+ }
+}
+
+
+void GImage::flipRGBVertical(
+ const uint8* in,
+ uint8* out,
+ int width,
+ int height) {
+
+
+ // Allocate a temp row so the operation
+ // is still safe if in == out
+ uint8* temp = (uint8*)System::malloc(width * 3);
+ alwaysAssertM(temp != NULL, "Out of memory");
+
+ int oneRow = width * 3;
+ int N = height / 2;
+
+ // if height is an odd value, don't swap odd middle row
+ for (int i = 0; i < N; ++i) {
+ int topOff = i * oneRow;
+ int botOff = (height - i - 1) * oneRow;
+ System::memcpy(temp, in + topOff, oneRow);
+ System::memcpy(out + topOff, in + botOff, oneRow);
+ System::memcpy(out + botOff, temp, oneRow);
+ }
+
+ System::free(temp);
+}
+
+
+void GImage::flipRGBAVertical(
+ const uint8* in,
+ uint8* out,
+ int width,
+ int height) {
+
+
+ // Allocate a temp row so the operation
+ // is still safe if in == out
+ uint8* temp = (uint8*)System::malloc(width * 4);
+ alwaysAssertM(temp != NULL, "Out of memory");
+
+ int oneRow = width * 4;
+
+ // if height is an odd value, don't swap odd middle row
+ for (int i = 0; i < height / 2; ++i) {
+ int topOff = i * oneRow;
+ int botOff = (height - i - 1) * oneRow;
+ System::memcpy(temp, in + topOff, oneRow);
+ System::memcpy(out + topOff, in + botOff, oneRow);
+ System::memcpy(out + botOff, temp, oneRow);
+ }
+
+ System::free(temp);
+}
+
+////////////////////////////////////////////////////////////////////////////////////////
+
+void GImage::decode(
+ BinaryInput& input,
+ Format format) {
+
+ switch (format) {
+ case PPM_ASCII:
+ decodePPMASCII(input);
+ break;
+
+ case PPM:
+ decodePPM(input);
+ break;
+
+ case PNG:
+ decodePNG(input);
+ break;
+
+ case JPEG:
+ decodeJPEG(input);
+ break;
+
+ case TGA:
+ decodeTGA(input);
+ break;
+
+ case BMP:
+ decodeBMP(input);
+ break;
+
+ case ICO:
+ decodeICO(input);
+ break;
+
+ case PCX:
+ decodePCX(input);
+ break;
+
+ default:
+ debugAssert(false);
+ }
+
+ debugAssert(width >= 0);
+ debugAssert(height >= 0);
+ debugAssert(channels == 1 || channels == 3 || channels == 4);
+ debugAssert(_byte != NULL);
+}
+
+
+void GImage::decodePCX(
+ BinaryInput& input) {
+
+ uint8 manufacturer = input.readUInt8();
+ uint8 version = input.readUInt8();
+ uint8 encoding = input.readUInt8();
+ uint8 bitsPerPixel = input.readUInt8();
+
+ uint16 xmin = input.readUInt16();
+ uint16 ymin = input.readUInt16();
+ uint16 xmax = input.readUInt16();
+ uint16 ymax = input.readUInt16();
+
+ uint16 horizDPI = input.readUInt16();
+ uint16 vertDPI = input.readUInt16();
+
+ Color3uint8 colorMap[16];
+ input.readBytes(colorMap, 48);
+
+ input.skip(1);
+
+ uint8 planes = input.readUInt8();
+ uint16 bytesPerLine = input.readUInt16();
+ uint16 paletteType = input.readUInt16();
+ input.skip(4 + 54);
+
+ (void)bytesPerLine;
+
+ width = xmax - xmin + 1;
+ height = ymax - ymin + 1;
+ channels = 3;
+
+ if ((manufacturer != 0x0A) || (encoding != 0x01)) {
+ throw GImage::Error("PCX file is corrupted", input.getFilename());
+ }
+
+ (void)version;
+ (void)vertDPI;
+ (void)horizDPI;
+
+ if ((bitsPerPixel != 8) || ((planes != 1) && (planes != 3))) {
+ throw GImage::Error("Only 8-bit paletted and 24-bit PCX files supported.", input.getFilename());
+ }
+
+ // Prepare the pointer object for the pixel data
+ _byte = (uint8*)System::malloc(width * height * 3);
+
+ if ((paletteType == 1) && (planes == 3)) {
+
+ Color3uint8* pixel = pixel3();
+
+ // Iterate over each scan line
+ for (int row = 0; row < height; ++row) {
+ // Read each scan line once per plane
+ for (int plane = 0; plane < planes; ++plane) {
+ int p = row * width;
+ int p1 = p + width;
+ while (p < p1) {
+ uint8 value = input.readUInt8();
+ int length = 1;
+
+ if (value >= 192) {
+ // This is the length, not the value. Mask off
+ // the two high bits and read the true index.
+ length = value & 0x3F;
+ value = input.readUInt8();
+ }
+
+ // Set the whole run
+ for (int i = length - 1; i >= 0; --i, ++p) {
+ debugAssert(p < width * height);
+ pixel[p][plane] = value;
+ }
+ }
+ }
+ }
+
+ } else if (planes == 1) {
+
+ Color3uint8 palette[256];
+
+ int imageBeginning = input.getPosition();
+ int paletteBeginning = input.getLength() - 769;
+
+ input.setPosition(paletteBeginning);
+
+ uint8 dummy = input.readUInt8();
+
+ if (dummy != 12) {
+ Log::common()->println("\n*********************");
+ Log::common()->printf("Warning: Corrupted PCX file (palette marker byte was missing) \"%s\"\nLoading anyway\n\n", input.getFilename().c_str());
+ }
+
+ input.readBytes(palette, sizeof(palette));
+ input.setPosition(imageBeginning);
+
+ Color3uint8* pixel = pixel3();
+
+ // The palette indices are run length encoded.
+ int p = 0;
+ while (p < width * height) {
+ uint8 index = input.readUInt8();
+ uint8 length = 1;
+
+ if (index >= 192) {
+ // This is the length, not the index. Mask off
+ // the two high bits and read the true index.
+ length = index & 0x3F;
+ index = input.readUInt8();
+ }
+
+ Color3uint8 color = palette[index];
+
+ // Set the whole run
+ for (int i = length - 1; i >= 0; --i, ++p) {
+ if (p > width * height) {
+ break;
+ }
+ pixel[p] = color;
+ }
+
+ }
+
+ } else {
+ throw GImage::Error("Unsupported PCX file type.", input.getFilename());
+ }
+}
+
+
+GImage::Format GImage::resolveFormat(const std::string& filename) {
+ BinaryInput b(filename, G3D_LITTLE_ENDIAN);
+ if (b.size() <= 0) {
+ throw Error("File not found.", filename);
+ }
+
+ return resolveFormat(filename, b.getCArray(), b.size(), AUTODETECT);
+}
+
+
+GImage::Format GImage::resolveFormat(
+ const std::string& filename,
+ const uint8* data,
+ int dataLen,
+ Format maybeFormat) {
+
+ // Return the provided format if it is specified.
+ if (maybeFormat != AUTODETECT) {
+ return maybeFormat;
+ }
+
+ std::string extension;
+
+ // Try to detect from the filename's extension
+ if (filename.size() >= 5) {
+ int n = iMax(filename.size() - 1, 5);
+ // Search backwards for the "."
+ for (int i = 1; i <= n; ++i) {
+ if (filename[filename.size() - i] == '.') {
+ // Upper case
+ extension = toUpper(filename.substr(filename.size() - i + 1));
+ break;
+ }
+ }
+ }
+
+ if (extension == "PPM") {
+ // There are two PPM formats; we handle them differently
+ if (dataLen > 3) {
+ if (!memcmp(data, "P6", 2)) {
+ return PPM;
+ } else {
+ return PPM_ASCII;
+ }
+ }
+ }
+
+ Format tmp = stringToFormat(extension);
+ if ((tmp != AUTODETECT) && (tmp != UNKNOWN)) {
+ return tmp;
+ }
+
+ // Try and autodetect from the file itself by looking at the first
+ // character.
+
+ // We can't look at the character if it is null.
+ debugAssert(data != NULL);
+
+ if ((dataLen > 3) && (!memcmp(data, "P3", 2) || !memcmp(data, "P2", 2) || !memcmp(data, "P1", 2))) {
+ return PPM_ASCII;
+ }
+
+ if ((dataLen > 3) && !memcmp(data, "P6", 2)) {
+ return PPM;
+ }
+
+ if (dataLen > 8) {
+ if (!png_sig_cmp((png_bytep)data, 0, 8))
+ return PNG;
+ }
+
+ if ((dataLen > 0) && (data[0] == 'B')) {
+ return BMP;
+ }
+
+ if (dataLen > 10) {
+ if ((dataLen > 11) && (data[0] == 0xFF) &&
+ (memcmp(&data[6], "JFIF", 4) == 0)) {
+ return JPEG;
+ }
+ }
+
+ if (dataLen > 40) {
+ if (memcmp(&data[dataLen - 18], "TRUEVISION-XFILE", 16) == 0) {
+ return TGA;
+ }
+ }
+
+ if ((dataLen > 4) && (data[0] == 0) && (data[1] == 0) && (data[2] == 0) && (data[3] == 1)) {
+ return ICO;
+ }
+
+ if ((dataLen > 0) && (data[0] == 10)) {
+ return PCX;
+ }
+
+ return UNKNOWN;
+}
+
+
+GImage::GImage(
+ const std::string& filename,
+ Format format) :
+ _byte(NULL),
+ width(0),
+ height(0),
+ channels(0){
+
+ load(filename, format);
+}
+
+
+void GImage::load(
+ const std::string& filename,
+ Format format) {
+
+ clear();
+
+ try {
+ BinaryInput b(filename, G3D_LITTLE_ENDIAN);
+ if (b.size() <= 0) {
+ throw Error("File not found.", filename);
+ }
+
+ alwaysAssertM(this != NULL, "Corrupt GImage");
+ decode(b, resolveFormat(filename, b.getCArray(), b.size(), format));
+ } catch (const std::string& error) {
+ throw Error(error, filename);
+ }
+}
+
+
+GImage::GImage(
+ const uint8* data,
+ int length,
+ Format format) {
+
+ BinaryInput b(data, length, G3D_LITTLE_ENDIAN);
+ // It is safe to cast away the const because we
+ // know we don't corrupt the data.
+
+ decode(b, resolveFormat("", data, length, format));
+}
+
+
+GImage::GImage(
+ int width,
+ int height,
+ int channels) {
+
+ _byte = NULL;
+ resize(width, height, channels);
+}
+
+
+void GImage::resize(
+ int width,
+ int height,
+ int channels) {
+ debugAssert(width >= 0);
+ debugAssert(height >= 0);
+ debugAssert(channels >= 1);
+
+ clear();
+
+ this->width = width;
+ this->height = height;
+ this->channels = channels;
+ size_t sz = width * height * channels;
+
+ _byte = (uint8*)System::calloc(sz, sizeof(uint8));
+ debugAssert(isValidHeapPointer(_byte));
+}
+
+
+void GImage::_copy(
+ const GImage& other) {
+
+ clear();
+
+ width = other.width;
+ height = other.height;
+ channels = other.channels;
+ int s = width * height * channels * sizeof(uint8);
+ _byte = (uint8*)System::malloc(s);
+ debugAssert(isValidHeapPointer(_byte));
+ memcpy(_byte, other._byte, s);
+}
+
+
+GImage::GImage(
+ const GImage& other) : _byte(NULL) {
+
+ _copy(other);
+}
+
+
+GImage::~GImage() {
+ clear();
+}
+
+
+void GImage::clear() {
+ width = 0;
+ height = 0;
+ System::free(_byte);
+ _byte = NULL;
+}
+
+
+GImage& GImage::operator=(const GImage& other) {
+ _copy(other);
+ return *this;
+}
+
+
+bool GImage::copySubImage(
+ GImage & dest, const GImage & src,
+ int srcX, int srcY, int srcWidth, int srcHeight) {
+ if ((src.width < srcX + srcWidth) ||
+ (src.height < srcY + srcHeight) ||
+ (srcY < 0) ||
+ (srcX < 0)) {
+
+ return false;
+ }
+
+ dest.resize(srcWidth, srcHeight, src.channels);
+
+ bool ret;
+ ret = pasteSubImage(dest, src, 0, 0, srcX, srcY, srcWidth, srcHeight);
+ debugAssert(ret);
+
+ return true;
+}
+
+
+bool GImage::pasteSubImage(
+ GImage & dest, const GImage & src,
+ int destX, int destY,
+ int srcX, int srcY, int srcWidth, int srcHeight) {
+ if ((src.width < srcX + srcWidth) ||
+ (src.height < srcY + srcHeight) ||
+ (dest.width < destX + srcWidth) ||
+ (dest.height < destY + srcHeight) ||
+ (srcY < 0) ||
+ (srcX < 0) ||
+ (destY < 0) ||
+ (destX < 0) ||
+ (src.channels != dest.channels)) {
+
+ return false;
+ }
+
+ for (int i = 0; i < srcHeight; i++) {
+ const uint8* srcRow = src.byte() +
+ ((i + srcY) * src.width + srcX) * src.channels;
+ uint8* destRow = dest.byte() +
+ ((i + destY) * dest.width + destX) * dest.channels;
+ memcpy(destRow, srcRow, srcWidth * src.channels);
+ }
+
+ return true;
+}
+
+
+bool GImage::supportedFormat(
+ const std::string& format) {
+
+ return (stringToFormat(format) != UNKNOWN);
+}
+
+
+GImage::Format GImage::stringToFormat(
+ const std::string& format) {
+
+ std::string extension = toUpper(format);
+
+ if ((extension == "JPG") || (extension == "JPEG")) {
+ return JPEG;
+ } else if (extension == "TGA") {
+ return TGA;
+ } else if (extension == "BMP") {
+ return BMP;
+ } else if (extension == "PCX") {
+ return PCX;
+ } else if (extension == "ICO") {
+ return ICO;
+ } else if (extension == "PNG") {
+ return PNG;
+ } else if (extension == "PPM") {
+ return PPM;
+ } else {
+ return UNKNOWN;
+ }
+}
+
+
+void GImage::save(
+ const std::string& filename,
+ Format format) const {
+
+ BinaryOutput b(filename, G3D_LITTLE_ENDIAN);
+ encode(resolveFormat(filename, NULL, 0, format), b);
+ b.commit(false);
+}
+
+
+void GImage::encode(
+ Format format,
+ uint8*& outData,
+ int& outLength) const {
+
+ BinaryOutput out;
+
+ encode(format, out);
+
+ outData = (uint8*)System::malloc(out.size());
+ debugAssert(outData);
+ outLength = out.size();
+
+ out.commit(outData);
+}
+
+
+void GImage::encode(
+ Format format,
+ BinaryOutput& out) const {
+
+ switch (format) {
+ case PPM_ASCII:
+ encodePPMASCII(out);
+ break;
+
+ case PPM:
+ encodePPM(out);
+ break;
+
+ case PNG:
+ encodePNG(out);
+ break;
+
+ case JPEG:
+ encodeJPEG(out);
+ break;
+
+ case BMP:
+ encodeBMP(out);
+ break;
+
+ case TGA:
+ encodeTGA(out);
+ break;
+
+ default:
+ debugAssert(false);
+ }
+}
+
+void GImage::insertRedAsAlpha(const GImage& alpha, GImage& output) const {
+ debugAssert(alpha.width == width);
+ debugAssert(alpha.height == height);
+
+ // make sure output GImage is valid
+ if (output.width != width || output.height != height || output.channels != 4) {
+ output.resize(width, height, 4);
+ }
+
+ for (int i = 0; i < width * height; ++i) {
+ output.byte()[i * 4 + 0] = byte()[i * channels + 0];
+ output.byte()[i * 4 + 1] = byte()[i * channels + 1];
+ output.byte()[i * 4 + 2] = byte()[i * channels + 2];
+ output.byte()[i * 4 + 3] = alpha.byte()[i * alpha.channels];
+ }
+}
+
+GImage GImage::insertRedAsAlpha(const GImage& alpha) const {
+ debugAssert(alpha.width == width);
+ debugAssert(alpha.height == height);
+
+ GImage out(width, height, 4);
+
+ insertRedAsAlpha(alpha, out);
+
+ return out;
+}
+
+
+void GImage::stripAlpha(GImage& output) const {
+
+ if (output.width != width || output.height != height || output.channels != 3)
+ {
+ output.resize(width, height, 3);
+ }
+
+ for (int i = 0; i < width * height; ++i) {
+ output.byte()[i * 3 + 0] = byte()[i * channels + 0];
+ output.byte()[i * 3 + 1] = byte()[i * channels + 1];
+ output.byte()[i * 3 + 2] = byte()[i * channels + 2];
+ }
+}
+
+GImage GImage::stripAlpha() const {
+ GImage out(width, height, 3);
+
+ stripAlpha(out);
+
+ return out;
+}
+
+
+int GImage::sizeInMemory() const {
+ return sizeof(GImage) + width * height * channels;
+}
+
+
+void GImage::computeNormalMap(
+ const GImage& bump,
+ GImage& normal,
+ float whiteHeightInPixels,
+ bool lowPassBump,
+ bool scaleHeightByNz) {
+ computeNormalMap(bump.width, bump.height, bump.channels, bump.byte(), normal, whiteHeightInPixels, lowPassBump, scaleHeightByNz);
+}
+
+void GImage::computeNormalMap(
+ int width,
+ int height,
+ int channels,
+ const uint8* src,
+ GImage& normal,
+ float whiteHeightInPixels,
+ bool lowPassBump,
+ bool scaleHeightByNz) {
+
+ if (whiteHeightInPixels == -1.0f) {
+ // Default setting scales so that a gradient ramp
+ // over the whole image becomes a 45-degree angle
+
+ // Account for potentially non-square aspect ratios
+ whiteHeightInPixels = max(width, height);
+ }
+
+ debugAssert(whiteHeightInPixels >= 0);
+
+ const int w = width;
+ const int h = height;
+ const int stride = channels;
+
+ normal.resize(w, h, 4);
+
+ const uint8* const B = src;
+ Color4uint8* const N = normal.pixel4();
+
+ for (int y = 0; y < h; ++y) {
+ for (int x = 0; x < w; ++x) {
+ // Index into normal map pixel
+ int i = x + y * w;
+
+ // Index into bump map *byte*
+ int j = stride * i;
+
+ Vector3 delta;
+
+ // Get a value from B (with wrapping lookup) relative to (x, y)
+ // and divide by 255
+ #define height(DX, DY) ((int)B[(((DX + x + w) % w) + \
+ ((DY + y + h) % h) * w) * stride])
+
+
+ // Sobel filter to compute the normal.
+ //
+ // Y Filter (X filter is the transpose)
+ // [ -1 -2 -1 ]
+ // [ 0 0 0 ]
+ // [ 1 2 1 ]
+
+ // Write the Y value directly into the x-component so we don't have
+ // to explicitly compute a cross product at the end.
+ delta.y = -(height(-1, -1) * 1 + height( 0, -1) * 2 + height( 1, -1) * 1 +
+ height(-1, 1) * -1 + height( 0, 1) * -2 + height( 1, 1) * -1);
+
+ delta.x = -(height(-1, -1) * -1 + height( 1, -1) * 1 +
+ height(-1, 0) * -2 + height( 1, 0) * 2 +
+ height(-1, 1) * -1 + height( 1, 1) * 1);
+
+ // The scale of each filter row is 4, the filter width is two pixels,
+ // and the "normal" range is 0-255.
+ delta.z = 4 * 2 * (whiteHeightInPixels / 255.0f);
+
+ // Delta is now scaled in pixels; normalize
+ delta = delta.direction();
+
+ // Copy over the bump value into the alpha channel.
+ float H = B[j] / 255.0;
+
+ if (lowPassBump) {
+ H = (height(-1, -1) + height( 0, -1) + height(1, -1) +
+ height(-1, 0) + height( 0, 0) + height(1, 0) +
+ height(-1, 1) + height( 0, 1) + height(1, 1)) / (255.0f * 9.0f);
+ }
+ #undef height
+
+ if (scaleHeightByNz) {
+ // delta.z can't possibly be negative, so we avoid actually
+ // computing the absolute value.
+ H *= delta.z;
+ }
+
+ N[i].a = iRound(H * 255.0);
+
+ // Pack into byte range
+ delta = delta * 127.5 + Vector3(127.5, 127.5, 127.5);
+ N[i].r = iClamp(iRound(delta.x), 0, 255);
+ N[i].g = iClamp(iRound(delta.y), 0, 255);
+ N[i].b = iClamp(iRound(delta.z), 0, 255);
+ }
+ }
+}
+
+
+/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+void GImage::convertToL8() {
+ switch(channels) {
+ case 1:
+ return;
+
+ case 3:
+ {
+ // Average
+ Color3uint8* src = (Color3uint8*)_byte;
+ _byte = NULL;
+ resize(width, height, 1);
+ for (int i = width * height - 1; i >= 0; --i) {
+ const Color3uint8 s = src[i];
+ uint8& d = _byte[i];
+ d = ((int)s.r + (int)s.g + (int)s.b) / 3;
+ }
+ System::free(src);
+ }
+ break;
+
+ case 4:
+ {
+ // Average
+ Color4uint8* src = (Color4uint8*)_byte;
+ _byte = NULL;
+ resize(width, height, 1);
+ for (int i = width * height - 1; i >= 0; --i) {
+ const Color4uint8 s = src[i];
+ uint8& d = _byte[i];
+ d = ((int)s.r + (int)s.g + (int)s.b) / 3;
+ }
+ System::free(src);
+ }
+ return;
+
+ default:
+ alwaysAssertM(false, "Bad number of channels in input image");
+ }
+}
+
+
+void GImage::convertToRGBA() {
+ switch(channels) {
+ case 1:
+ {
+ // Spread
+ uint8* old = _byte;
+ _byte = NULL;
+ resize(width, height, 4);
+ for (int i = width * height - 1; i >= 0; --i) {
+ const uint8 s = old[i];
+ Color4uint8& d = ((Color4uint8*)_byte)[i];
+ d.r = d.g = d.b = s;
+ d.a = 255;
+ }
+ System::free(_byte);
+ }
+ break;
+
+ case 3:
+ {
+ // Add alpha
+ Color3uint8* old = (Color3uint8*)_byte;
+ _byte = NULL;
+ resize(width, height, 4);
+ for (int i = width * height - 1; i >= 0; --i) {
+ const Color3uint8 s = old[i];
+ Color4uint8& d = ((Color4uint8*)_byte)[i];
+ d.r = s.r;
+ d.g = s.g;
+ d.b = s.b;
+ d.a = 255;
+ }
+ System::free(old);
+ }
+ break;
+
+ case 4:
+ // Already RGBA
+ return;
+
+ default:
+ alwaysAssertM(false, "Bad number of channels in input image");
+ }
+}
+
+
+void GImage::convertToRGB() {
+ switch(channels) {
+ case 1:
+ {
+ // Spread
+ uint8* old = _byte;
+ _byte = NULL;
+ resize(width, height, 3);
+ for (int i = width * height - 1; i >= 0; --i) {
+ const uint8 s = old[i];
+ Color3uint8& d = ((Color3uint8*)_byte)[i];
+ d.r = d.g = d.b = s;
+ }
+ System::free(old);
+ }
+ break;
+
+ case 3:
+ return;
+
+ case 4:
+ // Strip alpha
+ {
+ Color4uint8* old = (Color4uint8*)_byte;
+ _byte = NULL;
+ resize(width, height, 3);
+ for (int i = width * height - 1; i >= 0; --i) {
+ const Color4uint8 s = old[i];
+ Color3uint8& d = ((Color3uint8*)_byte)[i];
+ d.r = s.r;
+ d.g = s.g;
+ d.b = s.b;
+ }
+ System::free(old);
+ }
+ break;
+
+ default:
+ alwaysAssertM(false, "Bad number of channels in input image");
+ }
+}
+
+
+void GImage::R8G8B8_to_Y8U8V8(int width, int height, const uint8* _in, uint8* _out) {
+ const Color3uint8* in = reinterpret_cast<const Color3uint8*>(_in);
+ Color3uint8* out = reinterpret_cast<Color3uint8*>(_out);
+
+ Color3uint8 p;
+ for (int i = width * height - 1; i >= 0; --i) {
+ p.r = iClamp(iRound(in->r * 0.229 + in->g * 0.587 + in->b * 0.114), 0, 255);
+ p.g = iClamp(iRound(in->r * -0.147 + in->g * -0.289 + in->b * 0.436) + 127, 0, 255);
+ p.b = iClamp(iRound(in->r * 0.615 + in->g * -0.515 + in->b * -0.100) + 127, 0, 255);
+ *out = p;
+ ++in;
+ ++out;
+ }
+}
+
+
+
+void GImage::Y8U8V8_to_R8G8B8(int width, int height, const uint8* _in, uint8* _out) {
+ const Color3uint8* in = reinterpret_cast<const Color3uint8*>(_in);
+ Color3uint8* out = reinterpret_cast<Color3uint8*>(_out);
+
+ Color3uint8 p;
+ for (int i = width * height - 1; i >= 0; --i) {
+ p.r = iClamp(iRound(in->r * 1.0753 + (in->b - 127) * 1.2256), 0, 255);
+ p.g = iClamp(iRound(in->r * 1.0753 + (in->g - 127) * -0.3946 + (in->b - 127) * -0.4947), 0, 255);
+ p.b = iClamp(iRound(in->r * 1.0753 + (in->g - 127) * 2.0320 + (in->b - 127) * 0.0853), 0, 255);
+ *out = p;
+ ++in;
+ ++out;
+ }
+}
+
+
+void GImage::makeCheckerboard(GImage& im, int checkerSize, const Color4uint8& A, const Color4uint8& B) {
+ for (int y = 0; y < im.height; ++y) {
+ for (int x = 0; x < im.width; ++x) {
+ bool checker = isOdd((x / checkerSize) + (y / checkerSize));
+ const Color4uint8& color = checker ? A : B;
+ for (int c = 0; c < im.channels; ++c) {
+ uint8* v = im.byte() + (x + y * im.width) * im.channels + c;
+ *v = color[c];
+ }
+ }
+ }
+}
+
+}
+
diff --git a/externals/g3dlite/G3D.lib/source/GImage_bayer.cpp b/externals/g3dlite/G3D.lib/source/GImage_bayer.cpp
new file mode 100644
index 00000000000..16499af15f6
--- /dev/null
+++ b/externals/g3dlite/G3D.lib/source/GImage_bayer.cpp
@@ -0,0 +1,298 @@
+/**
+ @file GImage_bayer.cpp
+ @author Morgan McGuire, morgan@graphics3d.com
+ @created 2002-05-27
+ @edited 2006-05-10
+ */
+#include "G3D/platform.h"
+#include "G3D/GImage.h"
+
+namespace G3D {
+
+void GImage::BAYER_G8B8_R8G8_to_Quarter_R8G8B8(int width, int height, const uint8* in, uint8* out) {
+ debugAssert(in != out);
+
+ int halfHeight = height / 2;
+ int halfWidth = width / 2;
+
+ int dst_off = 0;
+ for (int y = 0; y < halfHeight; ++y) {
+ for (int x = 0; x < halfWidth; ++x) {
+ // GBRG
+ int src_off = x*2 + y*2*width;
+ out[dst_off] = in[src_off+width]; // red
+ out[dst_off+1] = ((int)in[src_off] + (int)in[src_off+width+1])/2; // green
+ out[dst_off+2] = in[src_off+1]; // blue
+
+ dst_off = dst_off + 3;
+ }
+ }
+}
+
+
+void GImage::Quarter_R8G8B8_to_BAYER_G8B8_R8G8(int inWidth, int inHeight, const uint8* in, uint8* out) {
+ // Undo quarter-size Bayer as best we can. This code isn't very efficient, but it
+ // also isn't used very frequently.
+
+ debugAssert(out != in);
+
+ int outWidth = 2 * inWidth;
+ int outHeight = 2 * inHeight;
+
+ for (int y = 0; y < outHeight; ++y) {
+ for (int x = 0; x < outWidth; ++x) {
+ const Color3uint8* inp = ((const Color3uint8*)in) + ((x/2) + (y/2)* inWidth);
+ uint8* outp = out + x + y * outWidth;
+
+ if (isEven(y)) {
+ // GB row
+ if (isEven(x)) {
+ // Green
+ *outp = inp->g;
+ } else {
+ // Blue
+ *outp = inp->b;
+ }
+ } else {
+ // RG row
+ if (isEven(x)) {
+ // Red
+ *outp = inp->r;
+ } else {
+ // Green
+ *outp = inp->g;
+ }
+ }
+ }
+ }
+}
+
+
+/** Applies a 5x5 filter to monochrome image I (wrapping at the boundaries) */
+static uint8 applyFilter(
+ const uint8* I,
+ int x,
+ int y,
+ int w,
+ int h,
+ const float filter[5][5]) {
+
+ debugAssert(isEven(w));
+ debugAssert(isEven(h));
+
+ float sum = 0.0f;
+ float denom = 0.0f;
+
+ for (int dy = 0; dy < 5; ++dy) {
+ int offset = ((y + dy + h - 2) % h) * w;
+
+ for (int dx = 0; dx < 5; ++dx) {
+ float f = filter[dy][dx];
+ sum += f * I[((x + dx + w - 2) % w) + offset];
+ denom += f;
+ }
+ }
+
+ return (uint8)iClamp(iRound(sum / denom), 0, 255);
+}
+
+////////////////////////////////////////////////////////////////////////////////////////////////
+//
+// Bayer conversions
+//
+
+// There are two kinds of rows (GR and BG).
+// In each row, there are two kinds of pixels (G/R, B/G).
+// We express the four kinds of INPUT pixels as:
+// GRG, GRG, BGB, BGG
+//
+// There are three kinds of OUTPUT pixels: R, G, B.
+// Thus there are nominally 12 different I/O combinations,
+// but several are impulses because needed output at that
+// location *is* the input (e.g., G_GRG and G_BGG).
+//
+// The following 5x5 row-major filters are named as output_input.
+
+// Green
+static const float G_GRR[5][5] =
+{{ 0.0f, 0.0f, -1.0f, 0.0f, 0.0f},
+{ 0.0f, 0.0f, 2.0f, 0.0f, 0.0f},
+{ -1.0f, 2.0f, 4.0f, 2.0f, -1.0f},
+{ 0.0f, 0.0f, 2.0f, 0.0f, 0.0f},
+{ 0.0f, 0.0f, -1.0f, 0.0f, 0.0f}};
+
+static const float G_BGB[5][5] =
+{{ 0.0f, 0.0f, -1.0f, 0.0f, 0.0f},
+{ 0.0f, 0.0f, 2.0f, 0.0f, 0.0f},
+{ -1.0f, 2.0f, 4.0f, 2.0f, -1.0f},
+{ 0.0f, 0.0f, 2.0f, 0.0f, 0.0f},
+{ 0.0f, 0.0f, -1.0f, 0.0f, 0.0f}};
+
+// Red
+//(the caption in the paper is wrong for this case:
+// "R row B column really means R row G column"
+static const float R_GRG[5][5] =
+{{ 0.0f, 0.0f, 0.5f, 0.0f, 0.0f},
+{ 0.0f, -1.0f, 0.0f, -1.0f, 0.0f},
+{ -1.0f, 4.0f, 5.0f, 4.0f, -1.0f},
+{ 0.0f, -1.0f, 0.0f, -1.0f, 0.0f},
+{ 0.0f, 0.0f, 0.5f, 0.0f, 0.0f}};
+
+static const float R_BGG[5][5] =
+{{ 0.0f, 0.0f, -1.0f, 0.0f, 0.0f},
+{ 0.0f, -1.0f, 4.0f, -1.0f, 0.0f},
+{ 0.5f, 0.0f, 5.0f, 0.0f, 0.5f},
+{ 0.0f, -1.0f, 4.0f, -1.0f, 0.0f},
+{ 0.0f, 0.0f, -1.0f, 0.0f, 0.0f}};
+
+static const float R_BGB[5][5] =
+{{ 0.0f, 0.0f, -3.0f/2.0f, 0.0f, 0.0f},
+{ 0.0f, 2.0f, 0.0f, 2.0f, 0.0f},
+{-3.0f/2.0f, 0.0f, 6.0f, 0.0f, -3.0f/2.0f},
+{ 0.0f, 2.0f, 0.0f, 2.0f, 0.0f},
+{ 0.0f, 0.0f, -3.0f/2.0f, 0.0f, 0.0f}};
+
+
+// Blue
+//(the caption in the paper is wrong for this case:
+// "B row R column really means B row G column")
+#define B_BGG R_GRG
+#define B_GRG R_BGG
+#define B_GRR R_BGB
+
+
+void GImage::BAYER_R8G8_G8B8_to_R8G8B8_MHC(int w, int h, const uint8* in, uint8* _out) {
+ debugAssert(in != _out);
+
+ Color3uint8* out = (Color3uint8*)_out;
+
+ for (int y = 0; y < h; ++y) {
+
+ // Row beginning in the input array.
+ int offset = y * w;
+
+ // RG row
+ for (int x = 0; x < w; ++x, ++out) {
+ // R pixel
+ {
+ out->r = in[x + offset];
+ out->g = applyFilter(in, x, y, w, h, G_GRR);
+ out->b = applyFilter(in, x, y, w, h, B_GRR);
+ }
+ ++x; ++out;
+
+ // G pixel
+ {
+ out->r = applyFilter(in, x, y, w, h, R_GRG);
+ out->g = in[x + offset];
+ out->b = applyFilter(in, x, y, w, h, B_GRG);
+ }
+ }
+
+ ++y;
+ offset += w;
+
+ // GB row
+ for (int x = 0; x < w; ++x, ++out) {
+ // G pixel
+ {
+ out->r = applyFilter(in, x, y, w, h, R_BGG);
+ out->g = in[x + offset];
+ out->b = applyFilter(in, x, y, w, h, B_BGG);
+ }
+ ++x; ++out;
+
+ // B pixel
+ {
+ out->r = applyFilter(in, x, y, w, h, R_BGB);
+ out->g = applyFilter(in, x, y, w, h, G_BGB);
+ out->b = in[x + offset];
+ }
+ }
+ }
+}
+
+static void swapRedAndBlue(int N, Color3uint8* out) {
+ for (int i = N - 1; i >= 0; --i) {
+ uint8 tmp = out[i].r;
+ out[i].r = out[i].b;
+ out[i].b = tmp;
+ }
+}
+
+void GImage::BAYER_G8R8_B8G8_to_R8G8B8_MHC(int w, int h, const uint8* in, uint8* _out) {
+ // Run the equivalent function for red
+ BAYER_G8B8_R8G8_to_R8G8B8_MHC(w, h, in, _out);
+
+ // Now swap red and blue
+ swapRedAndBlue(w * h, (Color3uint8*)_out);
+}
+
+
+void GImage::BAYER_B8G8_G8R8_to_R8G8B8_MHC(int w, int h, const uint8* in, uint8* _out) {
+ // Run the equivalent function for red
+ BAYER_R8G8_G8B8_to_R8G8B8_MHC(w, h, in, _out);
+
+ // Now swap red and blue
+ swapRedAndBlue(w * h, (Color3uint8*)_out);
+}
+
+
+void GImage::BAYER_G8B8_R8G8_to_R8G8B8_MHC(int w, int h, const uint8* in, uint8* _out) {
+
+ debugAssert(in != _out);
+
+ Color3uint8* out = (Color3uint8*)_out;
+
+ for (int y = 0; y < h; ++y) {
+
+ // Row beginning in the input array.
+ int offset = y * w;
+
+ // GB row
+ for (int x = 0; x < w; ++x, ++out) {
+ // G pixel
+ {
+ out->r = applyFilter(in, x, y, w, h, R_BGG);
+ out->g = in[x + offset];
+ out->b = applyFilter(in, x, y, w, h, B_BGG);
+ }
+ ++x; ++out;
+
+ // B pixel
+ {
+ out->r = applyFilter(in, x, y, w, h, R_BGB);
+ out->g = applyFilter(in, x, y, w, h, G_BGB);
+ out->b = in[x + offset];
+ }
+ }
+
+ ++y;
+ offset += w;
+
+ // RG row
+ for (int x = 0; x < w; ++x, ++out) {
+ // R pixel
+ {
+ out->r = in[x + offset];
+ out->g = applyFilter(in, x, y, w, h, G_GRR);
+ out->b = applyFilter(in, x, y, w, h, B_GRR);
+ }
+ ++x; ++out;
+
+ // G pixel
+ {
+ out->r = applyFilter(in, x, y, w, h, R_GRG);
+ out->g = in[x + offset];
+ out->b = applyFilter(in, x, y, w, h, B_GRG);
+ }
+ }
+ }
+
+}
+
+#undef B_BGG
+#undef B_GRG
+#undef B_GRR
+
+}
diff --git a/externals/g3dlite/G3D.lib/source/GImage_bmp.cpp b/externals/g3dlite/G3D.lib/source/GImage_bmp.cpp
new file mode 100644
index 00000000000..598ba730d0b
--- /dev/null
+++ b/externals/g3dlite/G3D.lib/source/GImage_bmp.cpp
@@ -0,0 +1,716 @@
+/**
+ @file GImage_bmp.cpp
+ @author Morgan McGuire, morgan@graphics3d.com
+ @created 2002-05-27
+ @edited 2006-05-10
+ */
+#include "G3D/platform.h"
+#include "G3D/GImage.h"
+#include "G3D/BinaryInput.h"
+#include "G3D/BinaryOutput.h"
+#include "G3D/Log.h"
+
+namespace G3D {
+
+#ifndef G3D_WIN32
+/**
+ This is used by the Windows bitmap I/O.
+ */
+static const int BI_RGB = 0;
+#endif
+
+void GImage::encodeBMP(
+ BinaryOutput& out) const {
+
+ debugAssert(channels == 1 || channels == 3);
+ out.setEndian(G3D_LITTLE_ENDIAN);
+
+ uint8 red;
+ uint8 green;
+ uint8 blue;
+ int pixelBufferSize = width * height * 3;
+ int fileHeaderSize = 14;
+ int infoHeaderSize = 40;
+ int BMScanWidth;
+ int BMPadding;
+
+ // First write the BITMAPFILEHEADER
+ //
+ // WORD bfType;
+ // DWORD bfSize;
+ // WORD bfReserved1;
+ // WORD bfReserved2;
+ // DWORD bfOffBits;
+
+ // Type
+ out.writeUInt8('B');
+ out.writeUInt8('M');
+
+ // File size
+ out.writeUInt32(fileHeaderSize + infoHeaderSize + pixelBufferSize);
+
+ // Two reserved fields set to zero
+ out.writeUInt16(0);
+ out.writeUInt16(0);
+
+ // The offset, in bytes, from the BITMAPFILEHEADER structure
+ // to the bitmap bits.
+ out.writeUInt32(infoHeaderSize + fileHeaderSize);
+
+ // Now the BITMAPINFOHEADER
+ //
+ // DWORD biSize;
+ // LONG biWidth;
+ // LONG biHeight;
+ // WORD biPlanes;
+ // WORD biBitCount
+ // DWORD biCompression;
+ // DWORD biSizeImage;
+ // LONG biXPelsPerMeter;
+ // LONG biYPelsPerMeter;
+ // DWORD biClrUsed;
+ // DWORD biClrImportant;
+
+ // Size of the info header
+ out.writeUInt32(infoHeaderSize);
+
+ // Width and height of the image
+ out.writeUInt32(width);
+ out.writeUInt32(height);
+
+ // Planes ("must be set to 1")
+ out.writeUInt16(1);
+
+ // BitCount and CompressionType
+ out.writeUInt16(24);
+ out.writeUInt32(BI_RGB);
+
+ // Image size ("may be zero for BI_RGB bitmaps")
+ out.writeUInt32(0);
+
+ // biXPelsPerMeter
+ out.writeUInt32(0);
+ // biYPelsPerMeter
+ out.writeUInt32(0);
+
+ // biClrUsed
+ out.writeUInt32(0);
+
+ // biClrImportant
+ out.writeUInt32(0);
+
+ BMScanWidth = width * 3;
+
+ if (BMScanWidth & 3) {
+ BMPadding = 4 - (BMScanWidth & 3);
+ } else {
+ BMPadding = 0;
+ }
+
+ int hStart = height - 1;
+ int hEnd = -1;
+ int hDir = -1;
+ int dest;
+
+ // Write the pixel data
+ for (int h = hStart; h != hEnd; h += hDir) {
+ dest = channels * h * width;
+ for (int w = 0; w < width; ++w) {
+
+ if (channels == 3) {
+ red = _byte[dest];
+ green = _byte[dest + 1];
+ blue = _byte[dest + 2];
+ } else {
+ red = _byte[dest];
+ green = _byte[dest];
+ blue = _byte[dest];
+ }
+
+ out.writeUInt8(blue);
+ out.writeUInt8(green);
+ out.writeUInt8(red);
+
+ dest += channels;
+ }
+
+ if (BMPadding > 0) {
+ out.skip(BMPadding);
+ }
+ }
+}
+
+
+void GImage::decodeBMP(
+ BinaryInput& input) {
+
+ // The BMP decoding uses these flags.
+ static const uint16 PICTURE_NONE = 0x0000;
+ static const uint16 PICTURE_BITMAP = 0x1000;
+
+ // Compression Flags
+ static const uint16 PICTURE_UNCOMPRESSED = 0x0100;
+ static const uint16 PICTURE_MONOCHROME = 0x0001;
+ static const uint16 PICTURE_4BIT = 0x0002;
+ static const uint16 PICTURE_8BIT = 0x0004;
+ static const uint16 PICTURE_16BIT = 0x0008;
+ static const uint16 PICTURE_24BIT = 0x0010;
+ static const uint16 PICTURE_32BIT = 0x0020;
+
+ (void)PICTURE_16BIT;
+ (void)PICTURE_32BIT;
+
+ // This is a simple BMP loader that can handle uncompressed BMP files.
+ // Verify this is a BMP file by looking for the BM tag.
+ input.reset();
+ std::string tag = input.readString(2);
+ if (tag != "BM") {
+ throw Error("Not a BMP file", input.getFilename());
+ }
+
+ channels = 3;
+ // Skip to the BITMAPINFOHEADER's width and height
+ input.skip(16);
+
+ width = input.readUInt32();
+ height = input.readUInt32();
+
+ // Skip to the bit count and compression type
+ input.skip(2);
+
+ uint16 bitCount = input.readUInt16();
+ uint32 compressionType = input.readUInt32();
+
+ uint8 red;
+ uint8 green;
+ uint8 blue;
+ uint8 blank;
+
+ // Only uncompressed bitmaps are supported by this code
+ if ((int32)compressionType != BI_RGB) {
+ throw Error("BMP images must be uncompressed", input.getFilename());
+ }
+
+ uint8* palette = NULL;
+
+ // Create the palette if needed
+ if (bitCount <= 8) {
+
+ // Skip to the palette color count in the header
+ input.skip(12);
+
+ int numColors = input.readUInt32();
+
+ palette = (uint8*)System::malloc(numColors * 3);
+ debugAssert(palette);
+
+ // Skip past the end of the header to the palette info
+ input.skip(4);
+
+ int c;
+ for(c = 0; c < numColors * 3; c += 3) {
+ // Palette information in bitmaps is stored in BGR_ format.
+ // That means it's blue-green-red-blank, for each entry.
+ blue = input.readUInt8();
+ green = input.readUInt8();
+ red = input.readUInt8();
+ blank = input.readUInt8();
+
+ palette[c] = red;
+ palette[c + 1] = green;
+ palette[c + 2] = blue;
+ }
+ }
+
+ int hStart = 0;
+ int hEnd = 0;
+ int hDir = 0;
+
+ if (height < 0) {
+ height = -height;
+ hStart = 0;
+ hEnd = height;
+ hDir = 1;
+ } else {
+ //height = height;
+ hStart = height - 1;
+ hEnd = -1;
+ hDir = -1;
+ }
+
+ _byte = (uint8*)System::malloc(width * height * 3);
+ debugAssert(_byte);
+
+ int BMScanWidth;
+ int BMPadding;
+ uint8 BMGroup;
+ uint8 BMPixel8;
+ int currPixel;
+ int dest;
+ int flags = PICTURE_NONE;
+
+ if (bitCount == 1) {
+ // Note that this file is not necessarily grayscale, since it's possible
+ // the palette is blue-and-white, or whatever. But of course most image
+ // programs only write 1-bit images if they're black-and-white.
+ flags = PICTURE_BITMAP | PICTURE_UNCOMPRESSED | PICTURE_MONOCHROME;
+
+ // For bitmaps, each scanline is dword-aligned.
+ BMScanWidth = (width + 7) >> 3;
+ if (BMScanWidth & 3) {
+ BMScanWidth += 4 - (BMScanWidth & 3);
+ }
+
+ // Powers of 2
+ int pow2[8] = {0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80};
+
+ for (int h = hStart; h != hEnd; h += hDir) {
+
+ currPixel = 0;
+ dest = 3 * h * width;
+
+ for (int w = 0; w < BMScanWidth; ++w) {
+
+ BMGroup = input.readUInt8();
+
+ // Now we read the pixels. Usually there are eight pixels per byte,
+ // since each pixel is represented by one bit, but if the width
+ // is not a multiple of eight, the last byte will have some bits
+ // set, with the others just being extra. Plus there's the
+ // dword-alignment padding. So we keep checking to see if we've
+ // already read "width" number of pixels.
+ for (int i = 7; i >= 0; --i) {
+ if (currPixel < width) {
+ int src = 3 * ((BMGroup & pow2[i]) >> i);
+
+ _byte[dest] = palette[src];
+ _byte[dest + 1] = palette[src + 1];
+ _byte[dest + 2] = palette[src + 2];
+
+ ++currPixel;
+ dest += 3;
+ }
+ }
+ }
+ }
+
+ } else if (bitCount == 4) {
+
+ flags = PICTURE_BITMAP | PICTURE_UNCOMPRESSED | PICTURE_4BIT;
+
+ // For bitmaps, each scanline is dword-aligned.
+ int BMScanWidth = (width+1) >> 1;
+ if (BMScanWidth & 3) {
+ BMScanWidth += 4 - (BMScanWidth & 3);
+ }
+
+ for (int h = hStart; h != hEnd; h += hDir) {
+
+ currPixel = 0;
+ dest = 3 * h * width;
+
+ for (int w = 0; w < BMScanWidth; w++) {
+
+ BMGroup = input.readUInt8();
+ int src[2];
+ src[0] = 3 * ((BMGroup & 0xF0) >> 4);
+ src[1] = 3 * (BMGroup & 0x0F);
+
+ // Now we read the pixels. Usually there are two pixels per byte,
+ // since each pixel is represented by four bits, but if the width
+ // is not a multiple of two, the last byte will have only four bits
+ // set, with the others just being extra. Plus there's the
+ // dword-alignment padding. So we keep checking to see if we've
+ // already read "Width" number of pixels.
+
+ for (int i = 0; i < 2; ++i) {
+ if (currPixel < width) {
+ int tsrc = src[i];
+
+ _byte[dest] = palette[tsrc];
+ _byte[dest + 1] = palette[tsrc + 1];
+ _byte[dest + 2] = palette[tsrc + 2];
+
+ ++currPixel;
+ dest += 3;
+ }
+ }
+ }
+ }
+
+ } else if (bitCount == 8) {
+
+ flags = PICTURE_BITMAP | PICTURE_UNCOMPRESSED | PICTURE_8BIT;
+
+ // For bitmaps, each scanline is dword-aligned.
+ BMScanWidth = width;
+ if (BMScanWidth & 3) {
+ BMScanWidth += 4 - (BMScanWidth & 3);
+ }
+
+ for (int h = hStart; h != hEnd; h += hDir) {
+
+ currPixel = 0;
+
+ for (int w = 0; w < BMScanWidth; ++w) {
+
+ BMPixel8 = input.readUInt8();
+
+ if (currPixel < width) {
+ dest = 3 * ((h * width) + currPixel);
+ int src = 3 * BMPixel8;
+
+ _byte[dest] = palette[src];
+ _byte[dest + 1] = palette[src + 1];
+ _byte[dest + 2] = palette[src + 2];
+
+ ++currPixel;
+ }
+ }
+ }
+
+ } else if (bitCount == 16) {
+
+ System::free(_byte);
+ _byte = NULL;
+ System::free(palette);
+ palette = NULL;
+ throw Error("16-bit bitmaps not supported", input.getFilename());
+
+ } else if (bitCount == 24) {
+ input.skip(20);
+
+ flags = PICTURE_BITMAP | PICTURE_UNCOMPRESSED | PICTURE_24BIT;
+
+ // For bitmaps, each scanline is dword-aligned.
+ BMScanWidth = width * 3;
+
+ if (BMScanWidth & 3) {
+ BMPadding = 4 - (BMScanWidth & 3);
+ } else {
+ BMPadding = 0;
+ }
+
+ for (int h = hStart; h != hEnd; h += hDir) {
+ dest = 3 * h * width;
+ for (int w = 0; w < width; ++w) {
+
+ blue = input.readUInt8();
+ green = input.readUInt8();
+ red = input.readUInt8();
+
+ _byte[dest] = red;
+ _byte[dest + 1] = green;
+ _byte[dest + 2] = blue;
+
+ dest += 3;
+ }
+
+ if (BMPadding) {
+ input.skip(2);
+ }
+ }
+
+ } else if (bitCount == 32) {
+
+ System::free(_byte);
+ _byte = NULL;
+ System::free(palette);
+ palette = NULL;
+ throw Error("32 bit bitmaps not supported", input.getFilename());
+
+ } else {
+ // We support all possible bit depths, so if the
+ // code gets here, it's not even a real bitmap.
+ System::free(_byte);
+ _byte = NULL;
+ throw Error("Not a bitmap!", input.getFilename());
+ }
+
+ System::free(palette);
+ palette = NULL;
+}
+
+
+void GImage::decodeICO(
+ BinaryInput& input) {
+
+ // Header
+ uint16 r = input.readUInt16();
+ debugAssert(r == 0);
+ r = input.readUInt16();
+ debugAssert(r == 1);
+
+ // Read the number of icons, although we'll only load the
+ // first one.
+ int count = input.readUInt16();
+
+ channels = 4;
+
+ debugAssert(count > 0);
+
+ const uint8* headerBuffer = input.getCArray() + input.getPosition();
+ int maxWidth = 0, maxHeight = 0;
+ int maxHeaderNum = 0;
+ for (int currentHeader = 0; currentHeader < count; ++currentHeader) {
+
+ const uint8* curHeaderBuffer = headerBuffer + (currentHeader * 16);
+ int tmpWidth = curHeaderBuffer[0];
+ int tmpHeight = curHeaderBuffer[1];
+ // Just in case there is a non-square icon, checking area
+ if ((tmpWidth * tmpHeight) > (maxWidth * maxHeight)) {
+ maxWidth = tmpWidth;
+ maxHeight = tmpHeight;
+ maxHeaderNum = currentHeader;
+ }
+ }
+
+ input.skip(maxHeaderNum * 16);
+
+ width = input.readUInt8();
+ height = input.readUInt8();
+ int numColors = input.readUInt8();
+
+ _byte = (uint8*)System::malloc(width * height * channels);
+ debugAssert(_byte);
+
+ // Bit mask for packed bits
+ int mask = 0;
+
+ int bitsPerPixel = 8;
+
+ switch (numColors) {
+ case 2:
+ mask = 0x01;
+ bitsPerPixel = 1;
+ break;
+
+ case 16:
+ mask = 0x0F;
+ bitsPerPixel = 4;
+ break;
+
+ case 0:
+ numColors = 256;
+ mask = 0xFF;
+ bitsPerPixel = 8;
+ break;
+ default:
+ throw Error("Unsupported ICO color count.", input.getFilename());
+ }
+
+ input.skip(5);
+ // Skip 'size' unused
+ input.skip(4);
+
+ int offset = input.readUInt32();
+
+ // Skip over any other icon descriptions
+ input.setPosition(offset);
+
+ // Skip over bitmap header; it is redundant
+ input.skip(40);
+
+ Array<Color4uint8> palette;
+ palette.resize(numColors, true);
+ for (int c = 0; c < numColors; ++c) {
+ palette[c].b = input.readUInt8();
+ palette[c].g = input.readUInt8();
+ palette[c].r = input.readUInt8();
+ palette[c].a = input.readUInt8();
+ }
+
+ // The actual image and mask follow
+
+ // The XOR Bitmap is stored as 1-bit, 4-bit or 8-bit uncompressed Bitmap
+ // using the same encoding as BMP files. The AND Bitmap is stored in as
+ // 1-bit uncompressed Bitmap.
+ //
+ // Pixels are stored bottom-up, left-to-right. Pixel lines are padded
+ // with zeros to end on a 32bit (4byte) boundary. Every line will have the
+ // same number of bytes. Color indices are zero based, meaning a pixel color
+ // of 0 represents the first color table entry, a pixel color of 255 (if there
+ // are that many) represents the 256th entry.
+/*
+ int bitsPerRow = width * bitsPerPixel;
+ int bytesPerRow = iCeil((double)bitsPerRow / 8);
+ // Rows are padded to 32-bit boundaries
+ bytesPerRow += bytesPerRow % 4;
+
+ // Read the XOR values into the color channel
+ for (int y = height - 1; y >= 0; --y) {
+ int x = 0;
+ // Read the row
+ for (int i = 0; i < bytesPerRow; ++i) {
+ uint8 byte = input.readUInt8();
+ for (int j = 0; (j < 8) && (x < width); ++x, j += bitsPerPixel) {
+ int bit = ((byte << j) >> (8 - bitsPerPixel)) & mask;
+ pixel4(x, y) = colorTable[bit];
+ }
+ }
+ }
+*/
+ int hStart = 0;
+ int hEnd = 0;
+ int hDir = 0;
+
+ if (height < 0) {
+ height = -height;
+ hStart = 0;
+ hEnd = height;
+ hDir = 1;
+ } else {
+ //height = height;
+ hStart = height - 1;
+ hEnd = -1;
+ hDir = -1;
+ }
+
+ int BMScanWidth;
+ uint8 BMGroup;
+ uint8 BMPixel8;
+ int currPixel;
+ int dest;
+
+ if (bitsPerPixel == 1) {
+ // Note that this file is not necessarily grayscale, since it's possible
+ // the palette is blue-and-white, or whatever. But of course most image
+ // programs only write 1-bit images if they're black-and-white.
+
+ // For bitmaps, each scanline is dword-aligned.
+ BMScanWidth = (width + 7) >> 3;
+ if (BMScanWidth & 3) {
+ BMScanWidth += 4 - (BMScanWidth & 3);
+ }
+
+ // Powers of 2
+ int pow2[8] = {0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80};
+
+ for (int h = hStart; h != hEnd; h += hDir) {
+
+ currPixel = 0;
+ dest = 3 * h * width;
+
+ for (int w = 0; w < BMScanWidth; ++w) {
+
+ BMGroup = input.readUInt8();
+
+ // Now we read the pixels. Usually there are eight pixels per byte,
+ // since each pixel is represented by one bit, but if the width
+ // is not a multiple of eight, the last byte will have some bits
+ // set, with the others just being extra. Plus there's the
+ // dword-alignment padding. So we keep checking to see if we've
+ // already read "width" number of pixels.
+ for (int i = 7; i >= 0; --i) {
+ if (currPixel < width) {
+ int src = ((BMGroup & pow2[i]) >> i);
+
+ _byte[dest] = palette[src].r;
+ _byte[dest + 1] = palette[src].g;
+ _byte[dest + 2] = palette[src].b;
+
+ ++currPixel;
+ dest += 4;
+ }
+ }
+ }
+ }
+
+ } else if (bitsPerPixel == 4) {
+
+ // For bitmaps, each scanline is dword-aligned.
+ int BMScanWidth = (width+1) >> 1;
+ if (BMScanWidth & 3) {
+ BMScanWidth += 4 - (BMScanWidth & 3);
+ }
+
+ for (int h = hStart; h != hEnd; h += hDir) {
+
+ currPixel = 0;
+ dest = 4 * h * width;
+
+ for (int w = 0; w < BMScanWidth; w++) {
+
+ BMGroup = input.readUInt8();
+ int src[2];
+ src[0] = ((BMGroup & 0xF0) >> 4);
+ src[1] = (BMGroup & 0x0F);
+
+ // Now we read the pixels. Usually there are two pixels per byte,
+ // since each pixel is represented by four bits, but if the width
+ // is not a multiple of two, the last byte will have only four bits
+ // set, with the others just being extra. Plus there's the
+ // dword-alignment padding. So we keep checking to see if we've
+ // already read "Width" number of pixels.
+
+ for (int i = 0; i < 2; ++i) {
+ if (currPixel < width) {
+ int tsrc = src[i];
+
+ _byte[dest] = palette[tsrc].r;
+ _byte[dest + 1] = palette[tsrc].g;
+ _byte[dest + 2] = palette[tsrc].b;
+
+ ++currPixel;
+ dest += 4;
+ }
+ }
+ }
+ }
+
+ } else if (bitsPerPixel == 8) {
+
+ // For bitmaps, each scanline is dword-aligned.
+ BMScanWidth = width;
+ if (BMScanWidth & 3) {
+ BMScanWidth += 4 - (BMScanWidth & 3);
+ }
+
+ for (int h = hStart; h != hEnd; h += hDir) {
+
+ currPixel = 0;
+
+ for (int w = 0; w < BMScanWidth; ++w) {
+
+ BMPixel8 = input.readUInt8();
+
+ if (currPixel < width) {
+ dest = 4 * ((h * width) + currPixel);
+ int src = BMPixel8;
+
+ _byte[dest] = palette[src].r;
+ _byte[dest + 1] = palette[src].g;
+ _byte[dest + 2] = palette[src].b;
+
+ ++currPixel;
+ }
+ }
+ }
+ }
+
+ // Read the mask into the alpha channel
+ int bitsPerRow = width;
+ int bytesPerRow = iCeil((double)bitsPerRow / 8);
+
+ // For bitmaps, each scanline is dword-aligned.
+ //BMScanWidth = (width + 1) >> 1;
+ if (bytesPerRow & 3) {
+ bytesPerRow += 4 - (bytesPerRow & 3);
+ }
+
+ for (int y = height - 1; y >= 0; --y) {
+ int x = 0;
+ // Read the row
+ for (int i = 0; i < bytesPerRow; ++i) {
+ uint8 byte = input.readUInt8();
+ for (int j = 0; (j < 8) && (x < width); ++x, ++j) {
+ int bit = (byte >> (7 - j)) & 0x01;
+ pixel4(x, y).a = (1 - bit) * 0xFF;
+ }
+ }
+ }
+
+}
+
+
+}
diff --git a/externals/g3dlite/G3D.lib/source/GImage_jpeg.cpp b/externals/g3dlite/G3D.lib/source/GImage_jpeg.cpp
new file mode 100644
index 00000000000..fe2812cf3b2
--- /dev/null
+++ b/externals/g3dlite/G3D.lib/source/GImage_jpeg.cpp
@@ -0,0 +1,445 @@
+/**
+ @file GImage_jpeg.cpp
+ @author Morgan McGuire, morgan@graphics3d.com
+ @created 2002-05-27
+ @edited 2006-10-10
+ */
+#include "G3D/platform.h"
+#include "G3D/GImage.h"
+#include "G3D/BinaryInput.h"
+#include "G3D/BinaryOutput.h"
+
+#include <cstring>
+
+/**
+ Pick up libjpeg headers locally on Windows, but from the system on all other platforms.
+*/
+extern "C" {
+#include <jconfig.h>
+#include <jpeglib.h>
+}
+
+namespace G3D {
+
+
+const int jpegQuality = 96;
+
+/**
+ The IJG library needs special setup for compress/decompressing
+ from memory. These classes provide them.
+
+ The format of this class is defined by the IJG library; do not
+ change it.
+ */
+class memory_destination_mgr {
+public:
+ struct jpeg_destination_mgr pub;
+ JOCTET* buffer;
+ int size;
+ int count;
+};
+
+typedef memory_destination_mgr* mem_dest_ptr;
+
+/**
+ Signature dictated by IJG.
+ */
+static void init_destination (
+ j_compress_ptr cinfo) {
+
+ mem_dest_ptr dest = (mem_dest_ptr) cinfo->dest;
+
+ dest->pub.next_output_byte = dest->buffer;
+ dest->pub.free_in_buffer = dest->size;
+ dest->count=0;
+}
+
+/**
+ Signature dictated by IJG.
+ */
+static boolean empty_output_buffer (
+ j_compress_ptr cinfo) {
+
+ mem_dest_ptr dest = (mem_dest_ptr) cinfo->dest;
+
+ dest->pub.next_output_byte = dest->buffer;
+ dest->pub.free_in_buffer = dest->size;
+
+ return TRUE;
+}
+
+/**
+ Signature dictated by IJG.
+ */
+static void term_destination (
+ j_compress_ptr cinfo) {
+
+ mem_dest_ptr dest = (mem_dest_ptr) cinfo->dest;
+ dest->count = dest->size - dest->pub.free_in_buffer;
+}
+
+/**
+ Signature dictated by IJG.
+ */
+static void jpeg_memory_dest (
+ j_compress_ptr cinfo,
+ JOCTET* buffer,
+ int size) {
+
+ mem_dest_ptr dest;
+
+ if (cinfo->dest == NULL) {
+ // First time for this JPEG object; call the
+ // IJG allocator to get space.
+ cinfo->dest = (struct jpeg_destination_mgr*)
+ (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo,
+ JPOOL_PERMANENT,
+ sizeof(memory_destination_mgr));
+ }
+
+ dest = (mem_dest_ptr) cinfo->dest;
+ dest->size = size;
+ dest->buffer = buffer;
+ dest->pub.init_destination = init_destination;
+ dest->pub.empty_output_buffer = empty_output_buffer;
+ dest->pub.term_destination = term_destination;
+}
+
+////////////////////////////////////////////////////////////////////////////////////////
+
+#define INPUT_BUF_SIZE 4096
+
+/**
+ Structure dictated by IJG.
+ */
+class memory_source_mgr {
+public:
+ struct jpeg_source_mgr pub;
+ int source_size;
+ unsigned char* source_data;
+ boolean start_of_data;
+ JOCTET* buffer;
+};
+
+
+typedef memory_source_mgr* mem_src_ptr;
+
+
+/**
+ Signature dictated by IJG.
+ */
+static void init_source(
+ j_decompress_ptr cinfo) {
+
+ mem_src_ptr src = (mem_src_ptr) cinfo->src;
+
+ src->start_of_data = TRUE;
+}
+
+
+/**
+ Signature dictated by IJG.
+ */
+static boolean fill_input_buffer(
+ j_decompress_ptr cinfo) {
+
+ mem_src_ptr src = (mem_src_ptr) cinfo->src;
+
+ size_t bytes_read = 0;
+
+ if (src->source_size > INPUT_BUF_SIZE)
+ bytes_read = INPUT_BUF_SIZE;
+ else
+ bytes_read = src->source_size;
+
+ memcpy (src->buffer, src->source_data, bytes_read);
+
+ src->source_data += bytes_read;
+ src->source_size -= bytes_read;
+
+ src->pub.next_input_byte = src->buffer;
+ src->pub.bytes_in_buffer = bytes_read;
+ src->start_of_data = FALSE;
+
+
+ return TRUE;
+}
+
+
+/**
+ Signature dictated by IJG.
+ */
+static void skip_input_data(
+ j_decompress_ptr cinfo,
+ long num_bytes) {
+
+ mem_src_ptr src = (mem_src_ptr)cinfo->src;
+
+ if (num_bytes > 0) {
+ while (num_bytes > (long) src->pub.bytes_in_buffer) {
+ num_bytes -= (long) src->pub.bytes_in_buffer;
+ boolean s = fill_input_buffer(cinfo);
+ debugAssert(s); (void)s;
+ }
+
+ src->pub.next_input_byte += (size_t) num_bytes;
+ src->pub.bytes_in_buffer -= (size_t) num_bytes;
+ }
+}
+
+
+/**
+ Signature dictated by IJG.
+ */
+static void term_source (
+ j_decompress_ptr cinfo) {
+ (void)cinfo;
+ // Intentionally empty
+}
+
+
+/**
+ Signature dictated by IJG.
+ */
+static void jpeg_memory_src (
+ j_decompress_ptr cinfo,
+ JOCTET* buffer,
+ int size) {
+
+ mem_src_ptr src;
+
+ if (cinfo->src == NULL) {
+ // First time for this JPEG object
+ cinfo->src = (struct jpeg_source_mgr*)
+ (*cinfo->mem->alloc_small)(
+ (j_common_ptr) cinfo,
+ JPOOL_PERMANENT,
+ sizeof(memory_source_mgr));
+
+ src = (mem_src_ptr)cinfo->src;
+
+ src->buffer = (JOCTET*)
+ (*cinfo->mem->alloc_small)(
+ (j_common_ptr) cinfo,
+ JPOOL_PERMANENT,
+ INPUT_BUF_SIZE * sizeof(JOCTET));
+ }
+
+ src = (mem_src_ptr)cinfo->src;
+ src->pub.init_source = init_source;
+ src->pub.fill_input_buffer = fill_input_buffer;
+ src->pub.skip_input_data = skip_input_data;
+
+ // use default method
+ src->pub.resync_to_restart = jpeg_resync_to_restart;
+ src->pub.term_source = term_source;
+ src->source_data = buffer;
+ src->source_size = size;
+
+ // forces fill_input_buffer on first read
+ src->pub.bytes_in_buffer = 0;
+
+ // until buffer loaded
+ src->pub.next_input_byte = NULL;
+}
+
+
+void GImage::encodeJPEG(
+ BinaryOutput& out) const {
+
+ if (channels != 3) {
+ // Convert to three channel
+ GImage tmp = *this;
+ tmp.convertToRGB();
+ tmp.encodeJPEG(out);
+ return;
+ }
+
+ debugAssert(channels == 3);
+ out.setEndian(G3D_LITTLE_ENDIAN);
+
+ // Allocate and initialize a compression object
+ jpeg_compress_struct cinfo;
+ jpeg_error_mgr jerr;
+
+ cinfo.err = jpeg_std_error(&jerr);
+ jpeg_create_compress(&cinfo);
+
+ // Specify the destination for the compressed data.
+ // (Overestimate the size)
+ int buffer_size = width * height * 3 + 200;
+ JOCTET* compressed_data = (JOCTET*)System::malloc(buffer_size);
+ jpeg_memory_dest(&cinfo, compressed_data, buffer_size);
+
+
+ cinfo.image_width = width;
+ cinfo.image_height = height;
+
+ // # of color components per pixel
+ cinfo.input_components = 3;
+
+ // colorspace of input image
+ cinfo.in_color_space = JCS_RGB;
+ cinfo.input_gamma = 1.0;
+
+ // Set parameters for compression, including image size & colorspace
+ jpeg_set_defaults(&cinfo);
+ jpeg_set_quality(&cinfo, jpegQuality, false);
+ cinfo.smoothing_factor = 0;
+ cinfo.optimize_coding = TRUE;
+// cinfo.dct_method = JDCT_FLOAT;
+ cinfo.dct_method = JDCT_ISLOW;
+ cinfo.jpeg_color_space = JCS_YCbCr;
+
+ // Initialize the compressor
+ jpeg_start_compress(&cinfo, TRUE);
+
+ // Iterate over all scanlines from top to bottom
+ // pointer to a single row
+ JSAMPROW row_pointer[1];
+
+ // JSAMPLEs per row in image_buffer
+ int row_stride = cinfo.image_width * 3;
+ while (cinfo.next_scanline < cinfo.image_height) {
+ row_pointer[0] = &(_byte[cinfo.next_scanline * row_stride]);
+ jpeg_write_scanlines(&cinfo, row_pointer, 1);
+ }
+
+ // Shut down the compressor
+ jpeg_finish_compress(&cinfo);
+
+ // Figure out how big the result was.
+ int outLength = ((mem_dest_ptr)cinfo.dest)->count;
+
+ // Release the JPEG compression object
+ jpeg_destroy_compress(&cinfo);
+
+ // Copy into an appropriately sized output buffer.
+ out.writeBytes(compressed_data, outLength);
+
+ // Free the conservative buffer.
+ System::free(compressed_data);
+ compressed_data = NULL;
+}
+
+
+
+void GImage::decodeJPEG(
+ BinaryInput& input) {
+
+ struct jpeg_decompress_struct cinfo;
+ struct jpeg_error_mgr jerr;
+ int loc = 0;
+
+ channels = 3;
+ // We have to set up the error handler, in case initialization fails.
+ cinfo.err = jpeg_std_error(&jerr);
+
+ // Initialize the JPEG decompression object.
+ jpeg_create_decompress(&cinfo);
+
+ // Specify data source (eg, a file, for us, memory)
+ jpeg_memory_src(&cinfo, const_cast<uint8*>(input.getCArray()), input.size());
+
+ // Read the parameters with jpeg_read_header()
+ jpeg_read_header(&cinfo, TRUE);
+
+ // Set parameters for decompression
+ // (We do nothing here since the defaults are fine)
+
+ // Start decompressor
+ jpeg_start_decompress(&cinfo);
+
+ // Get and set the values of interest to this object
+ this->width = cinfo.output_width;
+ this->height = cinfo.output_height;
+
+ // Prepare the pointer object for the pixel data
+ _byte = (uint8*)System::malloc(width * height * 3);
+
+ // JSAMPLEs per row in output buffer
+ int bpp = cinfo.output_components;
+ int row_stride = cinfo.output_width * bpp;
+
+ // Make a one-row-high sample array that will go away when done with image
+ JSAMPARRAY temp = (*cinfo.mem->alloc_sarray)
+ ((j_common_ptr) &cinfo, JPOOL_IMAGE, row_stride, 1);
+
+ // Read data on a scanline by scanline basis
+ while (cinfo.output_scanline < cinfo.output_height) {
+
+ // We may need to adjust the output based on the
+ // number of channels it has.
+ switch (bpp) {
+ case 1:
+ // Grayscale; decompress to temp.
+ jpeg_read_scanlines(&cinfo, temp, 1);
+
+ // Expand to three channels
+ {
+ uint8* scan = &(_byte[loc * 3]);
+ uint8* endScan = scan + (width * 3);
+ uint8* t = *temp;
+
+ while (scan < endScan) {
+ uint8 value = t[0];
+
+ // Spread the value 3x.
+ scan[0] = value;
+ scan[1] = value;
+ scan[2] = value;
+
+ scan += 3;
+ t += 1;
+ }
+ }
+ break;
+
+ case 3:
+ // Read directly into the array
+ {
+ // Need one extra level of indirection.
+ uint8* scan = _byte + loc;
+ JSAMPARRAY ptr = &scan;
+ jpeg_read_scanlines(&cinfo, ptr, 1);
+ }
+ break;
+
+ case 4:
+ // RGBA; decompress to temp.
+ jpeg_read_scanlines(&cinfo, temp, 1);
+
+ // Drop the 3rd channel
+ {
+ uint8* scan = &(_byte[loc * 3]);
+ uint8* endScan = scan + width * 3;
+ uint8* t = *temp;
+
+ while (scan < endScan) {
+ scan[0] = t[0];
+ scan[1] = t[1];
+ scan[2] = t[2];
+
+ scan += 3;
+ t += 4;
+ }
+ }
+ break;
+
+ default:
+ throw Error("Unexpected number of channels.", input.getFilename());
+ }
+
+ loc += row_stride;
+ }
+
+ // Finish decompression
+ jpeg_finish_decompress(&cinfo);
+
+ alwaysAssertM(this, "Corrupt GImage");
+ // Release JPEG decompression object
+ jpeg_destroy_decompress(&cinfo);
+}
+
+
+}
diff --git a/externals/g3dlite/G3D.lib/source/GImage_png.cpp b/externals/g3dlite/G3D.lib/source/GImage_png.cpp
new file mode 100644
index 00000000000..9fd6a743e8e
--- /dev/null
+++ b/externals/g3dlite/G3D.lib/source/GImage_png.cpp
@@ -0,0 +1,245 @@
+/**
+ @file GImage_png.cpp
+ @author Morgan McGuire, morgan@graphics3d.com
+ @created 2002-05-27
+ @edited 2006-05-10
+ */
+#include "G3D/platform.h"
+#include "G3D/GImage.h"
+#include "G3D/BinaryInput.h"
+#include "G3D/BinaryOutput.h"
+#include "G3D/Log.h"
+#include <png.h>
+
+namespace G3D {
+
+
+//libpng required function signature
+static void png_read_data(
+ png_structp png_ptr,
+ png_bytep data,
+ png_size_t length) {
+
+
+ debugAssert( png_ptr->io_ptr != NULL );
+ debugAssert( length >= 0 );
+ debugAssert( data != NULL );
+
+ ((BinaryInput*)png_ptr->io_ptr)->readBytes(data, length);
+}
+
+//libpng required function signature
+static void png_write_data(png_structp png_ptr,
+ png_bytep data,
+ png_size_t length) {
+
+ debugAssert( png_ptr->io_ptr != NULL );
+ debugAssert( data != NULL );
+
+ ((BinaryOutput*)png_ptr->io_ptr)->writeBytes(data, length);
+}
+
+//libpng required function signature
+static void png_flush_data(
+ png_structp png_ptr) {
+ (void)png_ptr;
+ //Do nothing.
+}
+
+//libpng required function signature
+static void png_error(
+ png_structp png_ptr,
+ png_const_charp error_msg) {
+
+ (void)png_ptr;
+ debugAssert( error_msg != NULL );
+ throw GImage::Error(error_msg, "PNG");
+}
+
+//libpng required function signature
+void png_warning(
+ png_structp png_ptr,
+ png_const_charp warning_msg) {
+
+ (void)png_ptr;
+ debugAssert( warning_msg != NULL );
+ Log::common()->println(warning_msg);
+}
+
+void GImage::encodePNG(
+ BinaryOutput& out) const {
+
+ debugAssert( channels == 1 || channels == 3 || channels == 4 );
+
+ if (this->height > (int)(PNG_UINT_32_MAX/png_sizeof(png_bytep)))
+ throw GImage::Error("Unsupported PNG height.", out.getFilename());
+
+ out.setEndian(G3D_LITTLE_ENDIAN);
+
+ png_structp png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, png_error, png_warning);
+ if (!png_ptr)
+ throw GImage::Error("Unable to initialize PNG encoder.", out.getFilename());
+
+ png_infop info_ptr = png_create_info_struct(png_ptr);
+ if (!info_ptr) {
+ png_destroy_write_struct(&png_ptr, &info_ptr);
+ throw GImage::Error("Unable to initialize PNG encoder.", out.getFilename());
+ }
+
+ //setup libpng write handler so can use BinaryOutput
+ png_set_write_fn(png_ptr, (void*)&out, png_write_data, png_flush_data);
+
+ if (channels == 3) {
+ png_set_IHDR(png_ptr, info_ptr, this->width, this->height, 8, PNG_COLOR_TYPE_RGB,
+ PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_BASE, PNG_FILTER_TYPE_BASE);
+ }
+ else if (channels == 4) {
+ png_set_IHDR(png_ptr, info_ptr, this->width, this->height, 8, PNG_COLOR_TYPE_RGBA,
+ PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_BASE, PNG_FILTER_TYPE_BASE);
+ }
+ else if (channels == 1) {
+ png_set_IHDR(png_ptr, info_ptr, this->width, this->height, 8, PNG_COLOR_TYPE_GRAY,
+ PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_BASE, PNG_FILTER_TYPE_BASE);
+ }
+ else {
+ png_destroy_write_struct(&png_ptr, &info_ptr);
+ throw GImage::Error("Unsupported number of channels for PNG.", out.getFilename());
+ }
+
+ png_color_8_struct sig_bit;
+ sig_bit.red = 8;
+ sig_bit.green = 8;
+ sig_bit.blue = 8;
+ if (channels == 4)
+ sig_bit.alpha = 8;
+ else
+ sig_bit.alpha = 0;
+ png_set_sBIT(png_ptr, info_ptr, &sig_bit);
+
+ //write the png header
+ png_write_info(png_ptr, info_ptr);
+
+ png_bytepp row_pointers = new png_bytep[this->height];
+
+ for (int i=0; i < this->height; ++i) {
+ row_pointers[i] = (png_bytep)&this->_byte[(this->width * this->channels * i)];
+ }
+
+ png_write_image(png_ptr, row_pointers);
+
+ png_write_end(png_ptr, info_ptr);
+
+ delete[] row_pointers;
+
+ png_destroy_write_struct(&png_ptr, &info_ptr);
+}
+
+
+void GImage::decodePNG(
+ BinaryInput& input) {
+
+ png_structp png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, png_error, png_warning);
+ if (!png_ptr)
+ throw GImage::Error("Unable to initialize PNG decoder.", input.getFilename());
+
+ png_infop info_ptr = png_create_info_struct(png_ptr);
+ if (!info_ptr) {
+ png_destroy_read_struct(&png_ptr, (png_infopp)NULL, (png_infopp)NULL);
+ throw GImage::Error("Unable to initialize PNG decoder.", input.getFilename());
+ }
+
+ png_infop end_info = png_create_info_struct(png_ptr);
+ if (!end_info) {
+ png_destroy_read_struct(&png_ptr, &info_ptr, (png_infopp)NULL);
+ throw GImage::Error("Unable to initialize PNG decoder.", input.getFilename());
+ }
+
+ //now that the libpng structures are setup, change the error handlers and read routines
+ //to use G3D functions so that BinaryInput can be used.
+
+ png_set_read_fn(png_ptr, (png_voidp)&input, png_read_data);
+
+ //read in sequentially so that three copies of the file are not in memory at once
+ png_read_info(png_ptr, info_ptr);
+
+ png_uint_32 png_width, png_height;
+ int bit_depth, color_type, interlace_type;
+ //this will validate the data it extracts from info_ptr
+ png_get_IHDR(png_ptr, info_ptr, &png_width, &png_height, &bit_depth, &color_type,
+ &interlace_type, int_p_NULL, int_p_NULL);
+
+ if (color_type == PNG_COLOR_TYPE_GRAY_ALPHA) {
+ png_destroy_read_struct(&png_ptr, &info_ptr, &end_info);
+ throw GImage::Error("Unsupported PNG color type - PNG_COLOR_TYPE_GRAY_ALPHA.", input.getFilename());
+ }
+
+ this->width = static_cast<uint32>(png_width);
+ this->height = static_cast<uint32>(png_height);
+
+ //swap bytes of 16 bit files to least significant byte first
+ png_set_swap(png_ptr);
+
+ png_set_strip_16(png_ptr);
+
+ //Expand paletted colors into true RGB triplets
+ if (color_type == PNG_COLOR_TYPE_PALETTE) {
+ png_set_palette_to_rgb(png_ptr);
+ }
+
+ //Expand grayscale images to the full 8 bits from 1, 2, or 4 bits/pixel
+ if (color_type == PNG_COLOR_TYPE_GRAY && bit_depth < 8) {
+ png_set_gray_1_2_4_to_8(png_ptr);
+ }
+
+ //Expand paletted or RGB images with transparency to full alpha channels
+ //so the data will be available as RGBA quartets.
+ if (png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS)) {
+ png_set_tRNS_to_alpha(png_ptr);
+ }
+
+ // Fix sub-8 bit_depth to 8bit
+ if (bit_depth < 8) {
+ png_set_packing(png_ptr);
+ }
+
+ if ((color_type == PNG_COLOR_TYPE_RGBA) ||
+ ((color_type == PNG_COLOR_TYPE_PALETTE) && (png_ptr->num_trans > 0)) ) {
+
+ this->channels = 4;
+ this->_byte = (uint8*)System::malloc(width * height * 4);
+
+ } else if ((color_type == PNG_COLOR_TYPE_RGB) ||
+ (color_type == PNG_COLOR_TYPE_PALETTE)) {
+
+ this->channels = 3;
+ this->_byte = (uint8*)System::malloc(width * height * 3);
+
+ } else if (color_type == PNG_COLOR_TYPE_GRAY) {
+
+ this->channels = 1;
+ this->_byte = (uint8*)System::malloc(width * height);
+
+ } else {
+ throw GImage::Error("Unsupported PNG bit-depth or type.", input.getFilename());
+ }
+
+ //since we are reading row by row, required to handle interlacing
+ uint32 number_passes = png_set_interlace_handling(png_ptr);
+
+ png_read_update_info(png_ptr, info_ptr);
+
+ for (uint32 pass = 0; pass < number_passes; ++pass) {
+ for (uint32 y = 0; y < (uint32)height; ++y) {
+ png_bytep rowPointer = &this->_byte[width * this->channels * y];
+ png_read_rows(png_ptr, &rowPointer, png_bytepp_NULL, 1);
+ }
+ }
+
+// png_read_image(png_ptr, &_byte);
+ png_read_end(png_ptr, info_ptr);
+
+ png_destroy_read_struct(&png_ptr, &info_ptr, &end_info);
+}
+
+
+}
diff --git a/externals/g3dlite/G3D.lib/source/GImage_ppm.cpp b/externals/g3dlite/G3D.lib/source/GImage_ppm.cpp
new file mode 100644
index 00000000000..bfe5c8305e8
--- /dev/null
+++ b/externals/g3dlite/G3D.lib/source/GImage_ppm.cpp
@@ -0,0 +1,185 @@
+/**
+ @file GImage_ppm.cpp
+ @author Morgan McGuire, morgan@graphics3d.com
+ @created 2002-05-27
+ @edited 2006-05-10
+ */
+#include "G3D/platform.h"
+#include "G3D/GImage.h"
+#include "G3D/BinaryInput.h"
+#include "G3D/BinaryOutput.h"
+#include "G3D/TextInput.h"
+#include "G3D/TextOutput.h"
+#include "G3D/Log.h"
+
+namespace G3D {
+
+void GImage::encodePPMASCII(
+ BinaryOutput& out) const {
+
+ debugAssert(channels == 3);
+
+ TextOutput::Settings ppmOptions;
+ ppmOptions.convertNewlines = false;
+ ppmOptions.numColumns = 70;
+ ppmOptions.wordWrap = TextOutput::Settings::WRAP_WITHOUT_BREAKING;
+ TextOutput ppm(ppmOptions);
+ // Always write out a full-color ppm
+ ppm.printf("P3\n%d %d\n255\n", width, height);
+
+ const Color3uint8* c = this->pixel3();
+ for (uint32 i = 0; i < (uint32)(width * height); ++i) {
+ ppm.printf("%d %d %d%c", c[i].r, c[i].g, c[i].b,
+ ((i % ((width * 3) - 1)) == 0) ?
+ '\n' : ' ');
+ }
+
+ out.writeString(ppm.commitString());
+}
+
+
+void GImage::encodePPM(
+ BinaryOutput& out) const {
+
+ // http://netpbm.sourceforge.net/doc/ppm.html
+ debugAssert(channels == 3);
+
+ std::string header = format("P6 %d %d 255 ", width, height);
+
+ out.writeBytes(header.c_str(), header.size());
+
+ out.writeBytes(this->pixel3(), width * height * 3);
+}
+
+void GImage::decodePPMASCII(
+ BinaryInput& input) {
+
+ int ppmWidth;
+ int ppmHeight;
+
+ double maxColor;
+
+ // Create a TextInput object to parse ascii format
+ // Mixed binary/ascii formats will require more
+
+ const std::string inputStr = input.readString();
+
+ TextInput::Settings ppmOptions;
+ ppmOptions.cppComments = false;
+ ppmOptions.otherCommentCharacter = '#';
+ ppmOptions.signedNumbers = true;
+ ppmOptions.singleQuotedStrings = false;
+
+ TextInput ppmInput(TextInput::FROM_STRING, inputStr, ppmOptions);
+
+ //Skip first line in header P#
+ std::string ppmType = ppmInput.readSymbol();
+
+ ppmWidth = (int)ppmInput.readNumber();
+ ppmHeight = (int)ppmInput.readNumber();
+
+ // Everything but a PBM will have a max color value
+ if (ppmType != "P2") {
+ maxColor = ppmInput.readNumber();
+ } else {
+ maxColor = 255;
+ }
+
+ if ((ppmWidth < 0) ||
+ (ppmHeight < 0) ||
+ (maxColor <= 0)) {
+ throw GImage::Error("Invalid PPM Header.", input.getFilename());
+ }
+
+ // I don't think it's proper to scale values less than 255
+ if (maxColor <= 255.0) {
+ maxColor = 255.0;
+ }
+
+ this->width = ppmWidth;
+ this->height = ppmHeight;
+ this->channels = 3;
+ // always scale down to 1 byte per channel
+ this->_byte = (uint8*)System::malloc(width * height * 3);
+
+ // Read in the image data. I am not validating if the values match the maxColor
+ // requirements. I only scale if needed to fit within the byte available.
+ for (uint32 i = 0; i < (uint32)(width * height); ++i) {
+ // read in color and scale to max pixel defined in header
+ // A max color less than 255 might need to be left alone and not scaled.
+ Color3uint8& curPixel = *(this->pixel3() + i);
+
+ if (ppmType == "P3") {
+ curPixel.r = (uint8)(ppmInput.readNumber() * (255.0 / maxColor));
+ curPixel.g = (uint8)(ppmInput.readNumber() * (255.0 / maxColor));
+ curPixel.b = (uint8)(ppmInput.readNumber() * (255.0 / maxColor));
+ } else if (ppmType == "P2") {
+ uint8 pixel = (uint8)(ppmInput.readNumber() * (255.0 / maxColor));
+ curPixel.r = pixel;
+ curPixel.g = pixel;
+ curPixel.b = pixel;
+ } else if (ppmType == "P1") {
+ int pixel = (uint8)(ppmInput.readNumber() * maxColor);
+ curPixel.r = pixel;
+ curPixel.g = pixel;
+ curPixel.b = pixel;
+ }
+ }
+}
+
+/** Consumes whitespace up to and including a number, but not the following character */
+static int scanUInt(BinaryInput& input) {
+ char c = input.readUInt8();
+ while (isWhiteSpace(c)) {
+ c = input.readUInt8();
+ }
+
+ std::string s;
+ s += c;
+ c = input.readUInt8();
+ while (!isWhiteSpace(c)) {
+ s += c;
+ c = input.readUInt8();
+ }
+
+ // Back up one to avoid consuming the last character
+ input.setPosition(input.getPosition() - 1);
+
+ int x;
+ sscanf(s.c_str(), "%d", &x);
+ return x;
+}
+
+void GImage::decodePPM(
+ BinaryInput& input) {
+
+ char head[2];
+ int w, h;
+
+ input.readBytes(head, 2);
+ if (head[0] != 'P' || head[1] != '6') {
+ throw GImage::Error("Invalid PPM Header.", input.getFilename());
+ }
+
+ w = scanUInt(input);
+ h = scanUInt(input);
+
+ // Skip the max color specifier
+ scanUInt(input);
+
+ if ((w < 0) ||
+ (h < 0) ||
+ (w > 100000) ||
+ (h > 100000)) {
+ throw GImage::Error("Invalid PPM size in header.", input.getFilename());
+ }
+
+ // Trailing whitespace
+ input.readUInt8();
+
+ resize(w, h, 3);
+
+ input.readBytes(_byte, width * height * 3);
+}
+
+}
diff --git a/externals/g3dlite/G3D.lib/source/GImage_tga.cpp b/externals/g3dlite/G3D.lib/source/GImage_tga.cpp
new file mode 100644
index 00000000000..d84feedecc9
--- /dev/null
+++ b/externals/g3dlite/G3D.lib/source/GImage_tga.cpp
@@ -0,0 +1,179 @@
+/**
+ @file GImage_tga.cpp
+ @author Morgan McGuire, morgan@graphics3d.com
+ @created 2002-05-27
+ @edited 2006-05-10
+ */
+#include "G3D/platform.h"
+#include "G3D/GImage.h"
+#include "G3D/BinaryInput.h"
+#include "G3D/BinaryOutput.h"
+#include "G3D/Log.h"
+
+namespace G3D {
+
+void GImage::encodeTGA(
+ BinaryOutput& out) const {
+
+ out.setEndian(G3D_LITTLE_ENDIAN);
+
+ // ID length
+ out.writeUInt8(0);
+
+ // Color map Type
+ out.writeUInt8(0);
+
+ // Type
+ out.writeUInt8(2);
+
+ // Color map
+ out.skip(5);
+
+ // x, y offsets
+ out.writeUInt16(0);
+ out.writeUInt16(0);
+
+ // Width & height
+ out.writeUInt16(width);
+ out.writeUInt16(height);
+
+ // Color depth
+ out.writeUInt8(8 * channels);
+
+ // Image descriptor
+ if (channels == 3) {
+ // 0 alpha bits
+ out.writeUInt8(0);
+ }
+ else {
+ // 8 alpha bits
+ out.writeUInt8(8);
+ }
+
+ // Image ID (zero length)
+
+ if (channels == 3) {
+ // Pixels are upside down in BGR format.
+ for (int y = height - 1; y >= 0; y--) {
+ for (int x = 0; x < width; x++) {
+ uint8* p = &(_byte[3 * (y * width + x)]);
+ out.writeUInt8(p[2]);
+ out.writeUInt8(p[1]);
+ out.writeUInt8(p[0]);
+ }
+ }
+ } else {
+ // Pixels are upside down in BGRA format.
+ for (int y = height - 1; y >= 0; y--) {
+ for (int x = 0; x < width; x++) {
+ uint8* p = &(_byte[4 * (y * width + x)]);
+ out.writeUInt8(p[2]);
+ out.writeUInt8(p[1]);
+ out.writeUInt8(p[0]);
+ out.writeUInt8(p[3]);
+ }
+ }
+ }
+
+ // Write "TRUEVISION-XFILE " 18 bytes from the end
+ // (with null termination)
+ out.writeString("TRUEVISION-XFILE ");
+}
+
+
+void GImage::decodeTGA(
+ BinaryInput& input) {
+
+ // This is a simple TGA loader that can handle uncompressed
+ // truecolor TGA files (TGA type 2).
+ // Verify this is a TGA file by looking for the TRUEVISION tag.
+ int pos = input.getPosition();
+ input.setPosition(input.size() - 18);
+ std::string tag = input.readString(16);
+ if (tag != "TRUEVISION-XFILE") {
+ throw Error("Not a TGA file", input.getFilename());
+ }
+
+ input.setPosition(pos);
+
+ int IDLength = input.readUInt8();
+ int colorMapType = input.readUInt8();
+ int imageType = input.readUInt8();
+
+ (void)colorMapType;
+
+ // 2 is the type supported by this routine.
+ if (imageType != 2) {
+ throw Error("TGA images must be type 2 (Uncompressed truecolor)", input.getFilename());
+ }
+
+ // Color map specification
+ input.skip(5);
+
+ // Image specification
+
+ // Skip x and y offsets
+ input.skip(4);
+
+ width = input.readInt16();
+ height = input.readInt16();
+
+ int colorDepth = input.readUInt8();
+
+ if ((colorDepth != 24) && (colorDepth != 32)) {
+ throw Error("TGA files must be 24 or 32 bit.", input.getFilename());
+ }
+
+ if (colorDepth == 32) {
+ channels = 4;
+ } else {
+ channels = 3;
+ }
+
+ // Image descriptor contains overlay data as well
+ // as data indicating where the origin is
+ int imageDescriptor = input.readUInt8();
+ (void)imageDescriptor;
+
+ // Image ID
+ input.skip(IDLength);
+
+ _byte = (uint8*)System::malloc(width * height * channels);
+ debugAssert(_byte);
+
+ // Pixel data
+ int x;
+ int y;
+
+ if (channels == 3) {
+ for (y = height - 1; y >= 0; y--) {
+ for (x = 0; x < width; x++) {
+ int b = input.readUInt8();
+ int g = input.readUInt8();
+ int r = input.readUInt8();
+
+ int i = (x + y * width) * 3;
+ _byte[i + 0] = r;
+ _byte[i + 1] = g;
+ _byte[i + 2] = b;
+ }
+ }
+ } else {
+ for (y = height - 1; y >= 0; y--) {
+ for (x = 0; x < width; x++) {
+ int b = input.readUInt8();
+ int g = input.readUInt8();
+ int r = input.readUInt8();
+ int a = input.readUInt8();
+
+ int i = (x + y * width) * 4;
+ _byte[i + 0] = r;
+ _byte[i + 1] = g;
+ _byte[i + 2] = b;
+ _byte[i + 3] = a;
+ }
+ }
+ }
+}
+
+}
diff --git a/externals/g3dlite/G3D.lib/source/GLight.cpp b/externals/g3dlite/G3D.lib/source/GLight.cpp
new file mode 100644
index 00000000000..8acb066ef54
--- /dev/null
+++ b/externals/g3dlite/G3D.lib/source/GLight.cpp
@@ -0,0 +1,143 @@
+/**
+ @file GLight.cpp
+
+ @maintainer Morgan McGuire, matrix@graphics3d.com
+
+ @created 2003-11-12
+ @edited 2007-10-22
+*/
+
+#include "G3D/GLight.h"
+#include "G3D/Sphere.h"
+
+namespace G3D {
+
+GLight::GLight() {
+ position = Vector4(0, 0, 0, 0);
+ color = Color3::white();
+ spotDirection = Vector3(0, 0, -1);
+ spotCutoff = 180;
+ enabled = false;
+ attenuation[0] = 1.0;
+ attenuation[1] = 0.0;
+ attenuation[2] = 0.0;
+ specular = true;
+ diffuse = true;
+}
+
+
+GLight GLight::directional(const Vector3& toLight, const Color3& color, bool s, bool d) {
+ GLight L;
+ L.position = Vector4(toLight.direction(), 0);
+ L.color = color;
+ L.specular = s;
+ L.diffuse = d;
+ return L;
+}
+
+
+GLight GLight::point(const Vector3& pos, const Color3& color, float constAtt, float linAtt, float quadAtt, bool s, bool d) {
+ GLight L;
+ L.position = Vector4(pos, 1);
+ L.color = color;
+ L.attenuation[0] = constAtt;
+ L.attenuation[1] = linAtt;
+ L.attenuation[2] = quadAtt;
+ L.specular = s;
+ L.diffuse = d;
+ return L;
+}
+
+
+GLight GLight::spot(const Vector3& pos, const Vector3& pointDirection, float cutOffAngleDegrees, const Color3& color, float constAtt, float linAtt, float quadAtt, bool s, bool d) {
+ GLight L;
+ L.position = Vector4(pos, 1.0f);
+ L.spotDirection = pointDirection.direction();
+ debugAssert(cutOffAngleDegrees <= 90);
+ L.spotCutoff = cutOffAngleDegrees;
+ L.color = color;
+ L.attenuation[0] = constAtt;
+ L.attenuation[1] = linAtt;
+ L.attenuation[2] = quadAtt;
+ L.specular = s;
+ L.diffuse = d;
+ return L;
+}
+
+
+bool GLight::operator==(const GLight& other) const {
+ return (position == other.position) &&
+ (spotDirection == other.spotDirection) &&
+ (spotCutoff == other.spotCutoff) &&
+ (attenuation[0] == other.attenuation[0]) &&
+ (attenuation[1] == other.attenuation[1]) &&
+ (attenuation[2] == other.attenuation[2]) &&
+ (color == other.color) &&
+ (enabled == other.enabled) &&
+ (specular == other.specular) &&
+ (diffuse == other.diffuse);
+}
+
+bool GLight::operator!=(const GLight& other) const {
+ return !(*this == other);
+}
+
+
+Sphere GLight::effectSphere(float cutoff) const {
+ if (position.w == 0) {
+ // Directional light
+ return Sphere(Vector3::zero(), (float)inf());
+ } else {
+ // Avoid divide by zero
+ cutoff = max(cutoff, 0.0001f);
+ float maxIntensity = max(color.r, max(color.g, color.b));
+
+ float radius = (float)inf();
+
+ if (attenuation[2] != 0) {
+
+ // Solve I / attenuation.dot(1, r, r^2) < cutoff for r
+ //
+ // a[0] + a[1] r + a[2] r^2 > I/cutoff
+ //
+
+ float a = attenuation[2];
+ float b = attenuation[1];
+ float c = attenuation[0] - maxIntensity / cutoff;
+
+ float discrim = square(b) - 4 * a * c;
+
+ if (discrim >= 0) {
+ discrim = sqrt(discrim);
+
+ float r1 = (-b + discrim) / (2 * a);
+ float r2 = (-b - discrim) / (2 * a);
+
+ if (r1 < 0) {
+ if (r2 > 0) {
+ radius = r2;
+ }
+ } else if (r2 > 0) {
+ radius = min(r1, r2);
+ } else {
+ radius = r1;
+ }
+ }
+
+ } else if (attenuation[1] != 0) {
+
+ // Solve I / attenuation.dot(1, r) < cutoff for r
+ //
+ // r * a[1] + a[0] = I / cutoff
+ // r = (I / cutoff - a[0]) / a[1]
+
+ float radius = (maxIntensity / cutoff - attenuation[0]) / attenuation[1];
+ radius = max(radius, 0.0f);
+ }
+
+ return Sphere(position.xyz(), radius);
+
+ }
+}
+
+}
diff --git a/externals/g3dlite/G3D.lib/source/GThread.cpp b/externals/g3dlite/G3D.lib/source/GThread.cpp
new file mode 100644
index 00000000000..090370436c4
--- /dev/null
+++ b/externals/g3dlite/G3D.lib/source/GThread.cpp
@@ -0,0 +1,203 @@
+/**
+ @file GThread.cpp
+
+ GThread class.
+
+ @created 2005-09-24
+ @edited 2005-10-22
+ */
+
+#include "G3D/GThread.h"
+#include "G3D/System.h"
+#include "G3D/debugAssert.h"
+
+
+namespace G3D {
+
+namespace _internal {
+
+class BasicThread: public GThread {
+public:
+ BasicThread(const std::string& name, void (*proc)(void*), void* param):
+ GThread(name), m_wrapperProc(proc), m_param(param) { }
+protected:
+ virtual void threadMain() {
+ m_wrapperProc(m_param);
+ }
+
+private:
+ void (*m_wrapperProc)(void*);
+
+ void* m_param;
+};
+
+} // namespace _internal
+
+
+GThread::GThread(const std::string& name):
+ m_status(STATUS_CREATED),
+ m_name(name) {
+
+#ifdef G3D_WIN32
+ m_event = NULL;
+#endif
+
+ // system-independent clear of handle
+ System::memset(&m_handle, 0, sizeof(m_handle));
+}
+
+GThread::~GThread() {
+#ifdef _MSC_VER
+# pragma warning( push )
+# pragma warning( disable : 4127 )
+#endif
+ alwaysAssertM(m_status != STATUS_RUNNING, "Deleting thread while running.");
+#ifdef _MSC_VER
+# pragma warning( pop )
+#endif
+
+#ifdef G3D_WIN32
+ if (m_event) {
+ ::CloseHandle(m_event);
+ }
+#endif
+}
+
+GThreadRef GThread::create(const std::string& name, void (*proc)(void*), void* param) {
+ return new _internal::BasicThread(name, proc, param);
+}
+
+
+bool GThread::started() const {
+ return m_status != STATUS_CREATED;
+}
+
+bool GThread::start() {
+
+ debugAssertM(! started(), "Thread has already executed.");
+ if (started()) {
+ return false;
+ }
+
+ m_status = STATUS_STARTED;
+
+# ifdef G3D_WIN32
+ DWORD threadId;
+
+ m_event = ::CreateEvent(NULL, TRUE, FALSE, NULL);
+ debugAssert(m_event);
+
+ m_handle = ::CreateThread(NULL, 0, &internalThreadProc, this, 0, &threadId);
+
+ if (m_handle == NULL) {
+ ::CloseHandle(m_event);
+ m_event = NULL;
+ }
+
+ return (m_handle != NULL);
+# else
+ if (!pthread_create(&m_handle, NULL, &internalThreadProc, this)) {
+ return true;
+ } else {
+ // system-independent clear of handle
+ System::memset(&m_handle, 0, sizeof(m_handle));
+
+ return false;
+ }
+# endif
+}
+
+void GThread::terminate() {
+ if (m_handle) {
+# ifdef G3D_WIN32
+ ::TerminateThread(m_handle, 0);
+# else
+ pthread_kill(m_handle, SIGSTOP);
+# endif
+ // system-independent clear of handle
+ System::memset(&m_handle, 0, sizeof(m_handle));
+ }
+}
+
+bool GThread::running() const{
+ return (m_status == STATUS_RUNNING);
+}
+
+bool GThread::completed() const {
+ return (m_status == STATUS_COMPLETED);
+}
+
+void GThread::waitForCompletion() {
+# ifdef G3D_WIN32
+ debugAssert(m_event);
+ ::WaitForSingleObject(m_event, INFINITE);
+# else
+ debugAssert(m_handle);
+ pthread_join(m_handle, NULL);
+# endif
+}
+
+#ifdef G3D_WIN32
+DWORD WINAPI GThread::internalThreadProc(LPVOID param) {
+ GThread* current = reinterpret_cast<GThread*>(param);
+ debugAssert(current->m_event);
+ current->m_status = STATUS_RUNNING;
+ current->threadMain();
+ current->m_status = STATUS_COMPLETED;
+ ::SetEvent(current->m_event);
+ return 0;
+}
+#else
+void* GThread::internalThreadProc(void* param) {
+ GThread* current = reinterpret_cast<GThread*>(param);
+ current->m_status = STATUS_RUNNING;
+ current->threadMain();
+ current->m_status = STATUS_COMPLETED;
+ return (void*)NULL;
+}
+#endif
+
+
+
+GMutex::GMutex() {
+# ifdef G3D_WIN32
+ ::InitializeCriticalSection(&m_handle);
+# else
+ pthread_mutex_init(&m_handle, NULL);
+# endif
+}
+
+GMutex::~GMutex() {
+ //TODO: Debug check for locked
+# ifdef G3D_WIN32
+ ::DeleteCriticalSection(&m_handle);
+# else
+ pthread_mutex_destroy(&m_handle);
+# endif
+}
+
+//bool GMutex::tryLock() {
+//# ifdef G3D_WIN32
+// return ::TryEnterCriticalSection(&m_handle);
+//# else
+// return pthread_mutex_trylock(&m_handle);
+//# endif
+//}
+
+void GMutex::lock() {
+# ifdef G3D_WIN32
+ ::EnterCriticalSection(&m_handle);
+# else
+ pthread_mutex_lock(&m_handle);
+# endif
+}
+
+void GMutex::unlock() {
+# ifdef G3D_WIN32
+ ::LeaveCriticalSection(&m_handle);
+# else
+ pthread_mutex_unlock(&m_handle);
+# endif
+}
+
+} // namespace G3D
diff --git a/externals/g3dlite/G3D.lib/source/GUniqueID.cpp b/externals/g3dlite/G3D.lib/source/GUniqueID.cpp
new file mode 100644
index 00000000000..25c757b70e4
--- /dev/null
+++ b/externals/g3dlite/G3D.lib/source/GUniqueID.cpp
@@ -0,0 +1,78 @@
+/**
+ @file GUniqueID.cpp
+ @author Morgan McGuire, morgan@cs.williams.edu
+ */
+#include "G3D/GUniqueID.h"
+#include "G3D/BinaryInput.h"
+#include "G3D/TextInput.h"
+#include "G3D/BinaryOutput.h"
+#include "G3D/TextOutput.h"
+#include "G3D/NetworkDevice.h"
+
+namespace G3D {
+
+void GUniqueID::serialize(BinaryOutput& b) const {
+ b.writeUInt64(id);
+}
+
+
+void GUniqueID::deserialize(BinaryInput& b) {
+ id = b.readUInt64();
+}
+
+void GUniqueID::serialize(TextOutput& t) const {
+ t.writeSymbol("(");
+ t.writeNumber((double)(id >> 32));
+ t.writeNumber((double)(id & 0xFFFFFFFF));
+ t.writeSymbol(")");
+}
+
+void GUniqueID::deserialize(TextInput& t) {
+ t.readSymbol("(");
+ id = (((uint64)t.readNumber()) << 32) + (uint64)t.readNumber();
+ t.readSymbol(")");
+}
+
+
+GUniqueID GUniqueID::create(uint16 tag) {
+ static uint64 counter = 0;
+ static uint64 systemID = 0;
+
+ if (systemID == 0) {
+ // Create a unique ID for this machine/program instance
+
+ // TODO: see ioctl(skfd, SIOCGIFHWADDR, &if_hwaddr)
+ Array<NetAddress> addr;
+ NetworkDevice::instance()->localHostAddresses(addr);
+ if (addr.size() > 0) {
+ systemID |= addr[0].ip();
+ }
+
+ union {
+ float64 ft;
+ uint64 ut;
+ };
+ ft = System::time();
+ systemID = ut << 22;
+ systemID ^= ((uint64)iRandom(0, 32768)) << 8;
+
+ systemID &= ~((uint64)1023 << 54);
+
+ // Ensure that the systemID is non-zero (vanishingly small probability)
+ if (systemID == 0) {
+ systemID = 1;
+ }
+ }
+
+ // No need for modulo; we'll all be dead before this counter
+ // overflows 54 bits
+ ++counter;
+
+ GUniqueID i;
+
+ i.id = (((uint64)(tag & 1023)) << 54) | (counter ^ systemID);
+
+ return i;
+}
+
+} // G3D
diff --git a/externals/g3dlite/G3D.lib/source/Image1.cpp b/externals/g3dlite/G3D.lib/source/Image1.cpp
new file mode 100644
index 00000000000..30841d01c6b
--- /dev/null
+++ b/externals/g3dlite/G3D.lib/source/Image1.cpp
@@ -0,0 +1,224 @@
+/**
+ @file Image1.cpp
+
+ @maintainer Morgan McGuire, matrix@graphics3d.com
+
+ @created 2007-01-31
+ @edited 2007-01-31
+*/
+
+
+#include "G3D/Image1.h"
+#include "G3D/Image1uint8.h"
+#include "G3D/GImage.h"
+#include "G3D/Color4.h"
+#include "G3D/Color4uint8.h"
+#include "G3D/Color1.h"
+#include "G3D/Color1uint8.h"
+#include "G3D/ImageFormat.h"
+
+namespace G3D {
+
+Image1::Image1(int w, int h, WrapMode wrap) : Map2D<Color1, Color1>(w, h, wrap) {
+ setAll(Color1(0.0f));
+}
+
+
+Image1::Ref Image1::fromGImage(const GImage& im, WrapMode wrap) {
+ switch (im.channels) {
+ case 1:
+ return fromArray(im.pixel1(), im.width, im.height, wrap);
+
+ case 3:
+ return fromArray(im.pixel3(), im.width, im.height, wrap);
+
+ case 4:
+ return fromArray(im.pixel4(), im.width, im.height, wrap);
+
+ default:
+ debugAssertM(false, "Input GImage must have 1, 3, or 4 channels.");
+ return NULL;
+ }
+}
+
+
+Image1::Ref Image1::fromImage1uint8(const ReferenceCountedPointer<Image1uint8>& im) {
+ Ref out = createEmpty(static_cast<WrapMode>(im->wrapMode()));
+ out->resize(im->width(), im->height());
+
+ int N = im->width() * im->height();
+ const Color1uint8* src = reinterpret_cast<Color1uint8*>(im->getCArray());
+ for (int i = 0; i < N; ++i) {
+ out->data[i] = Color1(src[i]);
+ }
+
+ return out;
+}
+
+
+Image1::Ref Image1::createEmpty(int width, int height, WrapMode wrap) {
+ return new Type(width, height, wrap);
+}
+
+
+Image1::Ref Image1::createEmpty(WrapMode wrap) {
+ return createEmpty(0, 0, wrap);
+}
+
+
+Image1::Ref Image1::fromFile(const std::string& filename, WrapMode wrap, GImage::Format fmt) {
+ Ref out = createEmpty(wrap);
+ out->load(filename);
+ return out;
+}
+
+
+void Image1::load(const std::string& filename, GImage::Format fmt) {
+ copyGImage(GImage(filename, fmt));
+ setChanged(true);
+}
+
+
+Image1::Ref Image1::fromArray(const class Color3uint8* ptr, int w, int h, WrapMode wrap) {
+ Ref out = createEmpty(wrap);
+ out->copyArray(ptr, w, h);
+ return out;
+}
+
+
+Image1::Ref Image1::fromArray(const class Color1* ptr, int w, int h, WrapMode wrap) {
+ Ref out = createEmpty(wrap);
+ out->copyArray(ptr, w, h);
+ return out;
+}
+
+
+Image1::Ref Image1::fromArray(const class Color1uint8* ptr, int w, int h, WrapMode wrap) {
+ Ref out = createEmpty(wrap);
+ out->copyArray(ptr, w, h);
+ return out;
+}
+
+
+Image1::Ref Image1::fromArray(const class Color3* ptr, int w, int h, WrapMode wrap) {
+ Ref out = createEmpty(wrap);
+ out->copyArray(ptr, w, h);
+ return out;
+}
+
+
+Image1::Ref Image1::fromArray(const class Color4uint8* ptr, int w, int h, WrapMode wrap) {
+ Ref out = createEmpty(wrap);
+ out->copyArray(ptr, w, h);
+ return out;
+}
+
+
+Image1::Ref Image1::fromArray(const class Color4* ptr, int w, int h, WrapMode wrap) {
+ Ref out = createEmpty(wrap);
+ out->copyArray(ptr, w, h);
+ return out;
+}
+
+void Image1::copyGImage(const GImage& im) {
+ switch (im.channels) {
+ case 1:
+ copyArray(im.pixel1(), im.width, im.height);
+ break;
+
+ case 3:
+ copyArray(im.pixel3(), im.width, im.height);
+ break;
+
+ case 4:
+ copyArray(im.pixel4(), im.width, im.height);
+ break;
+ }
+}
+
+
+void Image1::copyArray(const Color3uint8* src, int w, int h) {
+ resize(w, h);
+
+ int N = w * h;
+ Color1* dst = data.getCArray();
+ // Convert int8 -> float
+ for (int i = 0; i < N; ++i) {
+ dst[i] = Color1(Color3(src[i]).average());
+ }
+}
+
+
+void Image1::copyArray(const Color4uint8* src, int w, int h) {
+ resize(w, h);
+
+ int N = w * h;
+ Color1* dst = data.getCArray();
+
+ // Strip alpha and convert
+ for (int i = 0; i < N; ++i) {
+ dst[i] = Color1(Color3(src[i].rgb()).average());
+ }
+}
+
+
+void Image1::copyArray(const Color1* src, int w, int h) {
+ resize(w, h);
+ System::memcpy(getCArray(), src, w * h * sizeof(Color1));
+}
+
+
+void Image1::copyArray(const Color4* src, int w, int h) {
+ resize(w, h);
+
+ int N = w * h;
+ Color1* dst = data.getCArray();
+
+ // Strip alpha
+ for (int i = 0; i < N; ++i) {
+ dst[i] = Color1(src[i].rgb().average());
+ }
+}
+
+
+void Image1::copyArray(const Color1uint8* src, int w, int h) {
+ resize(w, h);
+ int N = w * h;
+
+ Color1* dst = getCArray();
+ for (int i = 0; i < N; ++i) {
+ dst[i]= Color1(src[i]);
+ }
+}
+
+
+void Image1::copyArray(const Color3* src, int w, int h) {
+ resize(w, h);
+ int N = w * h;
+
+ Color1* dst = getCArray();
+ for (int i = 0; i < N; ++i) {
+ dst[i] = Color1(src[i].average());
+ }
+}
+
+
+/** Saves in any of the formats supported by G3D::GImage. */
+void Image1::save(const std::string& filename, GImage::Format fmt) {
+ GImage im(width(), height(), 1);
+
+ int N = im.width * im.height;
+ Color1uint8* dst = im.pixel1();
+ for (int i = 0; i < N; ++i) {
+ dst[i] = Color1uint8(data[i]);
+ }
+
+ im.save(filename, fmt);
+}
+
+
+const ImageFormat* Image1::format() const {
+ return ImageFormat::L32F();
+}
+
+} // G3D
diff --git a/externals/g3dlite/G3D.lib/source/Image1uint8.cpp b/externals/g3dlite/G3D.lib/source/Image1uint8.cpp
new file mode 100644
index 00000000000..c43e7194d7d
--- /dev/null
+++ b/externals/g3dlite/G3D.lib/source/Image1uint8.cpp
@@ -0,0 +1,212 @@
+/**
+ @file Image1uint8.cpp
+
+ @maintainer Morgan McGuire, matrix@graphics3d.com
+
+ @created 2007-01-31
+ @edited 2008-01-13
+*/
+
+#include "G3D/Image1uint8.h"
+#include "G3D/Image3uint8.h"
+#include "G3D/Image1.h"
+#include "G3D/GImage.h"
+#include "G3D/Color1.h"
+#include "G3D/Color1uint8.h"
+#include "G3D/Color4.h"
+#include "G3D/Color4uint8.h"
+#include "G3D/ImageFormat.h"
+
+namespace G3D {
+
+Image1uint8::Image1uint8(int w, int h, WrapMode wrap) : Map2D<Color1uint8, Color1>(w, h, wrap) {
+ setAll(Color1uint8(0));
+}
+
+
+Image1uint8::Ref Image1uint8::fromImage3uint8(const ReferenceCountedPointer<class Image3uint8>& im) {
+ return fromArray(im->getCArray(), im->width(), im->height(), im->wrapMode());
+}
+
+
+Image1uint8::Ref Image1uint8::fromGImage(const GImage& im, WrapMode wrap) {
+ switch (im.channels) {
+ case 1:
+ return fromArray(im.pixel1(), im.width, im.height, wrap);
+
+ case 3:
+ return fromArray(im.pixel3(), im.width, im.height, wrap);
+
+ case 4:
+ return fromArray(im.pixel4(), im.width, im.height, wrap);
+
+ default:
+ debugAssertM(false, "Input GImage must have 1, 3, or 4 channels.");
+ return NULL;
+ }
+}
+
+
+Image1uint8::Ref Image1uint8::fromImage1(const ReferenceCountedPointer<Image1>& im) {
+ Ref out = createEmpty(static_cast<WrapMode>(im->wrapMode()));
+ out->copyArray(im->getCArray(), im->width(), im->height());
+
+ return out;
+}
+
+
+Image1uint8::Ref Image1uint8::createEmpty(int width, int height, WrapMode wrap) {
+ return new Type(width, height, wrap);
+}
+
+
+Image1uint8::Ref Image1uint8::createEmpty(WrapMode wrap) {
+ return createEmpty(0, 0, wrap);
+}
+
+
+Image1uint8::Ref Image1uint8::fromFile(const std::string& filename, WrapMode wrap, GImage::Format fmt) {
+ Ref out = createEmpty(wrap);
+ out->load(filename);
+ return out;
+}
+
+
+Image1uint8::Ref Image1uint8::fromArray(const class Color3uint8* ptr, int w, int h, WrapMode wrap) {
+ Ref out = createEmpty(wrap);
+ out->copyArray(ptr, w, h);
+ return out;
+}
+
+
+Image1uint8::Ref Image1uint8::fromArray(const class Color1* ptr, int w, int h, WrapMode wrap) {
+ Ref out = createEmpty(wrap);
+ out->copyArray(ptr, w, h);
+ return out;
+}
+
+
+Image1uint8::Ref Image1uint8::fromArray(const class Color1uint8* ptr, int w, int h, WrapMode wrap) {
+ Ref out = createEmpty(wrap);
+ out->copyArray(ptr, w, h);
+ return out;
+}
+
+
+Image1uint8::Ref Image1uint8::fromArray(const class Color3* ptr, int w, int h, WrapMode wrap) {
+ Ref out = createEmpty(wrap);
+ out->copyArray(ptr, w, h);
+ return out;
+}
+
+
+Image1uint8::Ref Image1uint8::fromArray(const class Color4uint8* ptr, int w, int h, WrapMode wrap) {
+ Ref out = createEmpty(wrap);
+ out->copyArray(ptr, w, h);
+ return out;
+}
+
+
+Image1uint8::Ref Image1uint8::fromArray(const class Color4* ptr, int w, int h, WrapMode wrap) {
+ Ref out = createEmpty(wrap);
+ out->copyArray(ptr, w, h);
+ return out;
+}
+
+
+void Image1uint8::load(const std::string& filename, GImage::Format fmt) {
+ copyGImage(GImage(filename, fmt));
+ setChanged(true);
+}
+
+
+void Image1uint8::copyGImage(const GImage& im) {
+ switch (im.channels) {
+ case 1:
+ copyArray(im.pixel1(), im.width, im.height);
+ break;
+
+ case 3:
+ copyArray(im.pixel3(), im.width, im.height);
+ break;
+
+ case 4:
+ copyArray(im.pixel4(), im.width, im.height);
+ break;
+ }
+}
+
+
+void Image1uint8::copyArray(const Color3uint8* src, int w, int h) {
+ resize(w, h);
+ int N = w * h;
+
+ Color1uint8* dst = getCArray();
+ for (int i = 0; i < N; ++i) {
+ dst[i].value = (src[i].r + src[i].g + src[i].b) / 3;
+ }
+}
+
+void Image1uint8::copyArray(const Color3* src, int w, int h) {
+ resize(w, h);
+ int N = w * h;
+
+ Color1uint8* dst = getCArray();
+ for (int i = 0; i < N; ++i) {
+ dst[i] = Color1uint8(Color1(src[i].average()));
+ }
+}
+
+
+void Image1uint8::copyArray(const Color1uint8* ptr, int w, int h) {
+ resize(w, h);
+ System::memcpy(getCArray(), ptr, w * h);
+}
+
+
+void Image1uint8::copyArray(const Color1* src, int w, int h) {
+ resize(w, h);
+ int N = w * h;
+
+ Color1uint8* dst = getCArray();
+ for (int i = 0; i < N; ++i) {
+ dst[i] = Color1uint8(src[i]);
+ }
+}
+
+
+void Image1uint8::copyArray(const Color4uint8* ptr, int w, int h) {
+ resize(w, h);
+ int N = w * h;
+
+ Color1uint8* dst = getCArray();
+ for (int i = 0; i < N; ++i) {
+ dst[i].value = (ptr[i].r + ptr[i].g + ptr[i].b) / 3;
+ }
+}
+
+
+void Image1uint8::copyArray(const Color4* src, int w, int h) {
+ resize(w, h);
+ int N = w * h;
+
+ Color1uint8* dst = getCArray();
+ for (int i = 0; i < N; ++i) {
+ dst[i] = Color1uint8(Color1(src[i].rgb().average()));
+ }
+}
+
+
+/** Saves in any of the formats supported by G3D::GImage. */
+void Image1uint8::save(const std::string& filename, GImage::Format fmt) {
+ GImage im(width(), height(), 1);
+ System::memcpy(im.byte(), getCArray(), width() * height());
+ im.save(filename, fmt);
+}
+
+
+const ImageFormat* Image1uint8::format() const {
+ return ImageFormat::L8();
+}
+
+} // G3D
diff --git a/externals/g3dlite/G3D.lib/source/Image3.cpp b/externals/g3dlite/G3D.lib/source/Image3.cpp
new file mode 100644
index 00000000000..aa2ac6098dc
--- /dev/null
+++ b/externals/g3dlite/G3D.lib/source/Image3.cpp
@@ -0,0 +1,224 @@
+/**
+ @file Image3.cpp
+
+ @maintainer Morgan McGuire, matrix@graphics3d.com
+
+ @created 2007-01-31
+ @edited 2007-01-31
+*/
+
+
+#include "G3D/Image3.h"
+#include "G3D/Image3uint8.h"
+#include "G3D/GImage.h"
+#include "G3D/Color4.h"
+#include "G3D/Color4uint8.h"
+#include "G3D/Color1.h"
+#include "G3D/Color1uint8.h"
+#include "G3D/ImageFormat.h"
+
+namespace G3D {
+
+Image3::Image3(int w, int h, WrapMode wrap) : Map2D<Color3, Color3>(w, h, wrap) {
+ setAll(Color3::black());
+}
+
+
+Image3::Ref Image3::fromGImage(const GImage& im, WrapMode wrap) {
+ switch (im.channels) {
+ case 1:
+ return fromArray(im.pixel1(), im.width, im.height, wrap);
+
+ case 3:
+ return fromArray(im.pixel3(), im.width, im.height, wrap);
+
+ case 4:
+ return fromArray(im.pixel4(), im.width, im.height, wrap);
+
+ default:
+ debugAssertM(false, "Input GImage must have 1, 3, or 4 channels.");
+ return NULL;
+ }
+}
+
+
+Image3::Ref Image3::fromImage3uint8(const ReferenceCountedPointer<Image3uint8>& im) {
+ Ref out = createEmpty(im->wrapMode());
+ out->resize(im->width(), im->height());
+
+ int N = im->width() * im->height();
+ const Color3uint8* src = reinterpret_cast<Color3uint8*>(im->getCArray());
+ for (int i = 0; i < N; ++i) {
+ out->data[i] = Color3(src[i]);
+ }
+
+ return out;
+}
+
+
+Image3::Ref Image3::createEmpty(int width, int height, WrapMode wrap) {
+ return new Image3(width, height, wrap);
+}
+
+
+Image3::Ref Image3::createEmpty(WrapMode wrap) {
+ return createEmpty(0, 0, wrap);
+}
+
+
+Image3::Ref Image3::fromFile(const std::string& filename, WrapMode wrap, GImage::Format fmt) {
+ Ref out = createEmpty(wrap);
+ out->load(filename);
+ return out;
+}
+
+
+void Image3::load(const std::string& filename, GImage::Format fmt) {
+ copyGImage(GImage(filename, fmt));
+ setChanged(true);
+}
+
+
+Image3::Ref Image3::fromArray(const class Color3uint8* ptr, int w, int h, WrapMode wrap) {
+ Ref out = createEmpty(wrap);
+ out->copyArray(ptr, w, h);
+ return out;
+}
+
+
+Image3::Ref Image3::fromArray(const class Color1* ptr, int w, int h, WrapMode wrap) {
+ Ref out = createEmpty(wrap);
+ out->copyArray(ptr, w, h);
+ return out;
+}
+
+
+Image3::Ref Image3::fromArray(const class Color1uint8* ptr, int w, int h, WrapMode wrap) {
+ Ref out = createEmpty(wrap);
+ out->copyArray(ptr, w, h);
+ return out;
+}
+
+
+Image3::Ref Image3::fromArray(const class Color3* ptr, int w, int h, WrapMode wrap) {
+ Ref out = createEmpty(wrap);
+ out->copyArray(ptr, w, h);
+ return out;
+}
+
+
+Image3::Ref Image3::fromArray(const class Color4uint8* ptr, int w, int h, WrapMode wrap) {
+ Ref out = createEmpty(wrap);
+ out->copyArray(ptr, w, h);
+ return out;
+}
+
+
+Image3::Ref Image3::fromArray(const class Color4* ptr, int w, int h, WrapMode wrap) {
+ Ref out = createEmpty(wrap);
+ out->copyArray(ptr, w, h);
+ return out;
+}
+
+void Image3::copyGImage(const GImage& im) {
+ switch (im.channels) {
+ case 1:
+ copyArray(im.pixel1(), im.width, im.height);
+ break;
+
+ case 3:
+ copyArray(im.pixel3(), im.width, im.height);
+ break;
+
+ case 4:
+ copyArray(im.pixel4(), im.width, im.height);
+ break;
+ }
+}
+
+
+void Image3::copyArray(const Color3uint8* src, int w, int h) {
+ resize(w, h);
+
+ int N = w * h;
+ Color3* dst = data.getCArray();
+ // Convert int8 -> float
+ for (int i = 0; i < N; ++i) {
+ dst[i] = Color3(src[i]);
+ }
+}
+
+
+void Image3::copyArray(const Color4uint8* src, int w, int h) {
+ resize(w, h);
+
+ int N = w * h;
+ Color3* dst = data.getCArray();
+
+ // Strip alpha and convert
+ for (int i = 0; i < N; ++i) {
+ dst[i] = Color3(src[i].rgb());
+ }
+}
+
+
+void Image3::copyArray(const Color3* src, int w, int h) {
+ resize(w, h);
+ System::memcpy(getCArray(), src, w * h * sizeof(Color3));
+}
+
+
+void Image3::copyArray(const Color4* src, int w, int h) {
+ resize(w, h);
+
+ int N = w * h;
+ Color3* dst = data.getCArray();
+
+ // Strip alpha
+ for (int i = 0; i < N; ++i) {
+ dst[i] = src[i].rgb();
+ }
+}
+
+
+void Image3::copyArray(const Color1uint8* src, int w, int h) {
+ resize(w, h);
+ int N = w * h;
+
+ Color3* dst = getCArray();
+ for (int i = 0; i < N; ++i) {
+ dst[i].r = dst[i].g = dst[i].b = Color1(src[i]).value;
+ }
+}
+
+
+void Image3::copyArray(const Color1* src, int w, int h) {
+ resize(w, h);
+ int N = w * h;
+
+ Color3* dst = getCArray();
+ for (int i = 0; i < N; ++i) {
+ dst[i].r = dst[i].g = dst[i].b = src[i].value;
+ }
+}
+
+
+/** Saves in any of the formats supported by G3D::GImage. */
+void Image3::save(const std::string& filename, GImage::Format fmt) {
+ GImage im(width(), height(), 3);
+
+ int N = im.width * im.height;
+ Color3uint8* dst = im.pixel3();
+ for (int i = 0; i < N; ++i) {
+ dst[i] = Color3uint8(data[i]);
+ }
+
+ im.save(filename, fmt);
+}
+
+
+const ImageFormat* Image3::format() const {
+ return ImageFormat::RGB32F();
+}
+
+} // G3D
diff --git a/externals/g3dlite/G3D.lib/source/Image3uint8.cpp b/externals/g3dlite/G3D.lib/source/Image3uint8.cpp
new file mode 100644
index 00000000000..2de32b6009e
--- /dev/null
+++ b/externals/g3dlite/G3D.lib/source/Image3uint8.cpp
@@ -0,0 +1,225 @@
+/**
+ @file Image3uint8.cpp
+
+ @maintainer Morgan McGuire, matrix@graphics3d.com
+
+ @created 2007-01-31
+ @edited 2008-01-08
+*/
+
+#include "G3D/Image1uint8.h"
+#include "G3D/Image3uint8.h"
+#include "G3D/Image3.h"
+#include "G3D/GImage.h"
+#include "G3D/Color1.h"
+#include "G3D/Color1uint8.h"
+#include "G3D/Color4.h"
+#include "G3D/Color4uint8.h"
+#include "G3D/ImageFormat.h"
+
+namespace G3D {
+
+Image3uint8::Ref Image3uint8::fromImage1uint8(const ReferenceCountedPointer<class Image1uint8>& im) {
+ return fromArray(im->getCArray(), im->width(), im->height(), im->wrapMode());
+}
+
+
+Image3uint8::Image3uint8(int w, int h, WrapMode wrap) : Map2D<Color3uint8>(w, h, wrap) {
+ setAll(Color3::black());
+}
+
+
+Image3uint8::Ref Image3uint8::fromGImage(const GImage& im, WrapMode wrap) {
+ switch (im.channels) {
+ case 1:
+ return fromArray(im.pixel1(), im.width, im.height, wrap);
+
+ case 3:
+ return fromArray(im.pixel3(), im.width, im.height, wrap);
+
+ case 4:
+ return fromArray(im.pixel4(), im.width, im.height, wrap);
+
+ default:
+ debugAssertM(false, "Input GImage must have 1, 3, or 4 channels.");
+ return NULL;
+ }
+}
+
+
+Image3uint8::Ref Image3uint8::fromImage3(const ReferenceCountedPointer<Image3>& im) {
+ Ref out = createEmpty(static_cast<WrapMode>(im->wrapMode()));
+ out->copyArray(im->getCArray(), im->width(), im->height());
+
+ return out;
+}
+
+
+Image3uint8::Ref Image3uint8::createEmpty(int width, int height, WrapMode wrap) {
+ return new Type(width, height, wrap);
+}
+
+
+Image3uint8::Ref Image3uint8::createEmpty(WrapMode wrap) {
+ return createEmpty(0, 0, wrap);
+}
+
+
+Image3uint8::Ref Image3uint8::fromFile(const std::string& filename, WrapMode wrap, GImage::Format fmt) {
+ Ref out = createEmpty(wrap);
+ out->load(filename);
+ return out;
+}
+
+
+Image3uint8::Ref Image3uint8::fromArray(const class Color3uint8* ptr, int w, int h, WrapMode wrap) {
+ Ref out = createEmpty(wrap);
+ out->copyArray(ptr, w, h);
+ return out;
+}
+
+
+Image3uint8::Ref Image3uint8::fromArray(const class Color1* ptr, int w, int h, WrapMode wrap) {
+ Ref out = createEmpty(wrap);
+ out->copyArray(ptr, w, h);
+ return out;
+}
+
+
+Image3uint8::Ref Image3uint8::fromArray(const class Color1uint8* ptr, int w, int h, WrapMode wrap) {
+ Ref out = createEmpty(wrap);
+ out->copyArray(ptr, w, h);
+ return out;
+}
+
+
+Image3uint8::Ref Image3uint8::fromArray(const class Color3* ptr, int w, int h, WrapMode wrap) {
+ Ref out = createEmpty(wrap);
+ out->copyArray(ptr, w, h);
+ return out;
+}
+
+
+Image3uint8::Ref Image3uint8::fromArray(const class Color4uint8* ptr, int w, int h, WrapMode wrap) {
+ Ref out = createEmpty(wrap);
+ out->copyArray(ptr, w, h);
+ return out;
+}
+
+
+Image3uint8::Ref Image3uint8::fromArray(const class Color4* ptr, int w, int h, WrapMode wrap) {
+ Ref out = createEmpty(wrap);
+ out->copyArray(ptr, w, h);
+ return out;
+}
+
+
+void Image3uint8::load(const std::string& filename, GImage::Format fmt) {
+ copyGImage(GImage(filename, fmt));
+ setChanged(true);
+}
+
+
+void Image3uint8::copyGImage(const GImage& im) {
+ switch (im.channels) {
+ case 1:
+ copyArray(im.pixel1(), im.width, im.height);
+ break;
+
+ case 3:
+ copyArray(im.pixel3(), im.width, im.height);
+ break;
+
+ case 4:
+ copyArray(im.pixel4(), im.width, im.height);
+ break;
+ }
+}
+
+
+void Image3uint8::copyArray(const Color1uint8* src, int w, int h) {
+ resize(w, h);
+ int N = w * h;
+
+ Color3uint8* dst = getCArray();
+ for (int i = 0; i < N; ++i) {
+ dst[i].r = dst[i].g = dst[i].b = src[i].value;
+ }
+}
+
+void Image3uint8::copyArray(const Color1* src, int w, int h) {
+ resize(w, h);
+ int N = w * h;
+
+ Color3uint8* dst = getCArray();
+ for (int i = 0; i < N; ++i) {
+ dst[i].r = dst[i].g = dst[i].b = Color1uint8(src[i]).value;
+ }
+}
+
+
+void Image3uint8::copyArray(const Color3uint8* ptr, int w, int h) {
+ resize(w, h);
+ System::memcpy(getCArray(), ptr, w * h * 3);
+}
+
+
+void Image3uint8::copyArray(const Color3* src, int w, int h) {
+ resize(w, h);
+ int N = w * h;
+
+ Color3uint8* dst = getCArray();
+ for (int i = 0; i < N; ++i) {
+ dst[i] = Color3uint8(src[i]);
+ }
+}
+
+
+void Image3uint8::copyArray(const Color4uint8* ptr, int w, int h) {
+ resize(w, h);
+
+ // Copy 3/4 bytes
+ GImage::RGBAtoRGB((const uint8*)ptr, (uint8*)getCArray(), w * h);
+}
+
+
+void Image3uint8::copyArray(const Color4* src, int w, int h) {
+ resize(w, h);
+ int N = w * h;
+
+ Color3uint8* dst = getCArray();
+ for (int i = 0; i < N; ++i) {
+ dst[i] = Color3uint8(src[i].rgb());
+ }
+}
+
+
+/** Saves in any of the formats supported by G3D::GImage. */
+void Image3uint8::save(const std::string& filename, GImage::Format fmt) {
+ GImage im(width(), height(), 3);
+ System::memcpy(im.byte(), getCArray(), width() * height() * 3);
+ im.save(filename, fmt);
+}
+
+
+ReferenceCountedPointer<class Image1uint8> Image3uint8::getChannel(int c) const {
+ debugAssert(c >= 0 && c <= 2);
+
+ Image1uint8Ref dst = Image1uint8::createEmpty(width(), height(), wrapMode());
+ const Color3uint8* srcArray = getCArray();
+ Color1uint8* dstArray = dst->getCArray();
+
+ const int N = width() * height();
+ for (int i = 0; i < N; ++i) {
+ dstArray[i] = Color1uint8(srcArray[i][c]);
+ }
+
+ return dst;
+}
+
+
+const ImageFormat* Image3uint8::format() const {
+ return ImageFormat::RGB8();
+}
+
+} // G3D
diff --git a/externals/g3dlite/G3D.lib/source/Image4.cpp b/externals/g3dlite/G3D.lib/source/Image4.cpp
new file mode 100644
index 00000000000..84a1cd650b6
--- /dev/null
+++ b/externals/g3dlite/G3D.lib/source/Image4.cpp
@@ -0,0 +1,226 @@
+/**
+ @file Image4.cpp
+
+ @maintainer Morgan McGuire, matrix@graphics3d.com
+
+ @created 2007-01-31
+ @edited 2008-07-27
+*/
+
+
+#include "G3D/Image4.h"
+#include "G3D/Image4uint8.h"
+#include "G3D/GImage.h"
+#include "G3D/Color3.h"
+#include "G3D/Color3uint8.h"
+#include "G3D/Color1.h"
+#include "G3D/Color1uint8.h"
+#include "G3D/ImageFormat.h"
+
+namespace G3D {
+
+Image4::Image4(int w, int h, WrapMode wrap) : Map2D<Color4, Color4>(w, h, wrap) {
+ setAll(Color4::zero());
+}
+
+
+Image4::Ref Image4::fromGImage(const GImage& im, WrapMode wrap) {
+ switch (im.channels) {
+ case 1:
+ return fromArray(im.pixel1(), im.width, im.height, wrap);
+
+ case 3:
+ return fromArray(im.pixel3(), im.width, im.height, wrap);
+
+ case 4:
+ return fromArray(im.pixel4(), im.width, im.height, wrap);
+
+ default:
+ debugAssertM(false, "Input GImage must have 1, 3, or 4 channels.");
+ return NULL;
+ }
+}
+
+
+Image4::Ref Image4::fromImage4uint8(const ReferenceCountedPointer<Image4uint8>& im) {
+ Ref out = createEmpty(static_cast<WrapMode>(im->wrapMode()));
+ out->resize(im->width(), im->height());
+
+ int N = im->width() * im->height();
+ const Color4uint8* src = reinterpret_cast<Color4uint8*>(im->getCArray());
+ for (int i = 0; i < N; ++i) {
+ out->data[i] = Color4(src[i]);
+ }
+
+ return out;
+}
+
+
+Image4::Ref Image4::createEmpty(int width, int height, WrapMode wrap) {
+ return new Type(width, height, WrapMode::ERROR);
+}
+
+
+Image4::Ref Image4::createEmpty(WrapMode wrap) {
+ return createEmpty(0, 0, wrap);
+}
+
+
+Image4::Ref Image4::fromFile(const std::string& filename, WrapMode wrap, GImage::Format fmt) {
+ Ref out = createEmpty(wrap);
+ out->load(filename);
+ return out;
+}
+
+
+void Image4::load(const std::string& filename, GImage::Format fmt) {
+ copyGImage(GImage(filename, fmt));
+ setChanged(true);
+}
+
+
+Image4::Ref Image4::fromArray(const class Color3uint8* ptr, int w, int h, WrapMode wrap) {
+ Ref out = createEmpty(wrap);
+ out->copyArray(ptr, w, h);
+ return out;
+}
+
+
+Image4::Ref Image4::fromArray(const class Color1* ptr, int w, int h, WrapMode wrap) {
+ Ref out = createEmpty(wrap);
+ out->copyArray(ptr, w, h);
+ return out;
+}
+
+
+Image4::Ref Image4::fromArray(const class Color1uint8* ptr, int w, int h, WrapMode wrap) {
+ Ref out = createEmpty(wrap);
+ out->copyArray(ptr, w, h);
+ return out;
+}
+
+
+Image4::Ref Image4::fromArray(const class Color3* ptr, int w, int h, WrapMode wrap) {
+ Ref out = createEmpty(wrap);
+ out->copyArray(ptr, w, h);
+ return out;
+}
+
+
+Image4::Ref Image4::fromArray(const class Color4uint8* ptr, int w, int h, WrapMode wrap) {
+ Ref out = createEmpty(wrap);
+ out->copyArray(ptr, w, h);
+ return out;
+}
+
+
+Image4::Ref Image4::fromArray(const class Color4* ptr, int w, int h, WrapMode wrap) {
+ Ref out = createEmpty(wrap);
+ out->copyArray(ptr, w, h);
+ return out;
+}
+
+
+void Image4::copyGImage(const GImage& im) {
+ switch (im.channels) {
+ case 1:
+ copyArray(im.pixel1(), im.width, im.height);
+ break;
+
+ case 3:
+ copyArray(im.pixel3(), im.width, im.height);
+ break;
+
+ case 4:
+ copyArray(im.pixel4(), im.width, im.height);
+ break;
+ }
+}
+
+
+void Image4::copyArray(const Color4uint8* src, int w, int h) {
+ resize(w, h);
+
+ int N = w * h;
+ Color4* dst = data.getCArray();
+ // Convert int8 -> float
+ for (int i = 0; i < N; ++i) {
+ dst[i] = Color4(src[i]);
+ }
+}
+
+
+void Image4::copyArray(const Color3uint8* src, int w, int h) {
+ resize(w, h);
+
+ int N = w * h;
+ Color4* dst = data.getCArray();
+
+ // Add alpha and convert
+ for (int i = 0; i < N; ++i) {
+ dst[i] = Color4(Color3(src[i]), 1.0f);
+ }
+}
+
+
+void Image4::copyArray(const Color4* src, int w, int h) {
+ resize(w, h);
+ System::memcpy(getCArray(), src, w * h * sizeof(Color4));
+}
+
+
+void Image4::copyArray(const Color3* src, int w, int h) {
+ resize(w, h);
+
+ int N = w * h;
+ Color4* dst = data.getCArray();
+
+ // Add alpha
+ for (int i = 0; i < N; ++i) {
+ dst[i] = Color4(src[i], 1.0f);
+ }
+}
+
+
+void Image4::copyArray(const Color1uint8* src, int w, int h) {
+ resize(w, h);
+ int N = w * h;
+
+ Color4* dst = getCArray();
+ for (int i = 0; i < N; ++i) {
+ dst[i].r = dst[i].g = dst[i].b = Color1(src[i]).value;
+ dst[i].a = 1.0f;
+ }
+}
+
+
+void Image4::copyArray(const Color1* src, int w, int h) {
+ resize(w, h);
+ int N = w * h;
+
+ Color4* dst = getCArray();
+ for (int i = 0; i < N; ++i) {
+ dst[i].r = dst[i].g = dst[i].b = src[i].value;
+ dst[i].a = 1.0f;
+ }
+}
+
+
+/** Saves in any of the formats supported by G3D::GImage. */
+void Image4::save(const std::string& filename, GImage::Format fmt) {
+ GImage im(width(), height(), 4);
+
+ int N = im.width * im.height;
+ Color4uint8* dst = im.pixel4();
+ for (int i = 0; i < N; ++i) {
+ dst[i] = Color4uint8(data[i]);
+ }
+
+ im.save(filename, fmt);
+}
+
+const ImageFormat* Image4::format() const {
+ return ImageFormat::RGBA32F();
+}
+
+} // G3D
diff --git a/externals/g3dlite/G3D.lib/source/Image4uint8.cpp b/externals/g3dlite/G3D.lib/source/Image4uint8.cpp
new file mode 100644
index 00000000000..dee1cba14ba
--- /dev/null
+++ b/externals/g3dlite/G3D.lib/source/Image4uint8.cpp
@@ -0,0 +1,222 @@
+/**
+ @file Image4uint8.cpp
+
+ @maintainer Morgan McGuire, matrix@graphics3d.com
+
+ @created 2007-01-31
+ @edited 2008-07-31
+*/
+
+#include "G3D/Image4uint8.h"
+#include "G3D/Image4.h"
+#include "G3D/Image3uint8.h"
+#include "G3D/Image3.h"
+#include "G3D/GImage.h"
+#include "G3D/Color1.h"
+#include "G3D/Color1uint8.h"
+#include "G3D/Color4.h"
+#include "G3D/Color4uint8.h"
+#include "G3D/ImageFormat.h"
+
+namespace G3D {
+
+Image4uint8::Image4uint8(int w, int h, WrapMode wrap) : Map2D<Color4uint8, Color4>(w, h, wrap) {
+ setAll(Color4::zero());
+}
+
+
+Image4uint8::Ref Image4uint8::fromGImage(const GImage& im, WrapMode wrap) {
+ switch (im.channels) {
+ case 1:
+ return fromArray(im.pixel1(), im.width, im.height, wrap);
+
+ case 3:
+ return fromArray(im.pixel3(), im.width, im.height, wrap);
+
+ case 4:
+ return fromArray(im.pixel4(), im.width, im.height, wrap);
+
+ default:
+ debugAssertM(false, "Input GImage must have 1, 3, or 4 channels.");
+ return NULL;
+ }
+}
+
+
+Image4uint8::Ref Image4uint8::fromImage4(const ReferenceCountedPointer<Image4>& im) {
+ Ref out = createEmpty(static_cast<WrapMode>(im->wrapMode()));
+ out->copyArray(im->getCArray(), im->width(), im->height());
+
+ return out;
+}
+
+
+Image4uint8::Ref Image4uint8::createEmpty(int width, int height, WrapMode wrap) {
+ return new Type(width, height, wrap);
+}
+
+
+Image4uint8::Ref Image4uint8::createEmpty(WrapMode wrap) {
+ return createEmpty(0, 0, wrap);
+}
+
+
+Image4uint8::Ref Image4uint8::fromFile(const std::string& filename, WrapMode wrap, GImage::Format fmt) {
+ Ref out = createEmpty(wrap);
+ out->load(filename);
+ return out;
+}
+
+
+Image4uint8::Ref Image4uint8::fromArray(const class Color3uint8* ptr, int w, int h, WrapMode wrap) {
+ Ref out = createEmpty(wrap);
+ out->copyArray(ptr, w, h);
+ return out;
+}
+
+
+Image4uint8::Ref Image4uint8::fromArray(const class Color1* ptr, int w, int h, WrapMode wrap) {
+ Ref out = createEmpty(wrap);
+ out->copyArray(ptr, w, h);
+ return out;
+}
+
+
+Image4uint8::Ref Image4uint8::fromArray(const class Color1uint8* ptr, int w, int h, WrapMode wrap) {
+ Ref out = createEmpty(wrap);
+ out->copyArray(ptr, w, h);
+ return out;
+}
+
+
+Image4uint8::Ref Image4uint8::fromArray(const class Color3* ptr, int w, int h, WrapMode wrap) {
+ Ref out = createEmpty(wrap);
+ out->copyArray(ptr, w, h);
+ return out;
+}
+
+
+Image4uint8::Ref Image4uint8::fromArray(const class Color4uint8* ptr, int w, int h, WrapMode wrap) {
+ Ref out = createEmpty(wrap);
+ out->copyArray(ptr, w, h);
+ return out;
+}
+
+
+Image4uint8::Ref Image4uint8::fromArray(const class Color4* ptr, int w, int h, WrapMode wrap) {
+ Ref out = createEmpty(wrap);
+ out->copyArray(ptr, w, h);
+ return out;
+}
+
+
+void Image4uint8::load(const std::string& filename, GImage::Format fmt) {
+ copyGImage(GImage(filename, fmt));
+ setChanged(true);
+}
+
+
+void Image4uint8::copyGImage(const GImage& im) {
+ switch (im.channels) {
+ case 1:
+ copyArray(im.pixel1(), im.width, im.height);
+ break;
+
+ case 3:
+ copyArray(im.pixel3(), im.width, im.height);
+ break;
+
+ case 4:
+ copyArray(im.pixel4(), im.width, im.height);
+ break;
+ }
+}
+
+
+void Image4uint8::copyArray(const Color1uint8* src, int w, int h) {
+ resize(w, h);
+ int N = w * h;
+
+ Color4uint8* dst = getCArray();
+ for (int i = 0; i < N; ++i) {
+ dst[i].r = dst[i].g = dst[i].b = src[i].value;
+ dst[i].a = 255;
+ }
+}
+
+void Image4uint8::copyArray(const Color1* src, int w, int h) {
+ resize(w, h);
+ int N = w * h;
+
+ Color4uint8* dst = getCArray();
+ for (int i = 0; i < N; ++i) {
+ dst[i].r = dst[i].g = dst[i].b = Color1uint8(src[i]).value;
+ dst[i].a = 255;
+ }
+}
+
+
+void Image4uint8::copyArray(const Color4uint8* ptr, int w, int h) {
+ resize(w, h);
+ System::memcpy(getCArray(), ptr, w * h * 4);
+}
+
+
+void Image4uint8::copyArray(const Color4* src, int w, int h) {
+ resize(w, h);
+ int N = w * h;
+
+ Color4uint8* dst = getCArray();
+ for (int i = 0; i < N; ++i) {
+ dst[i] = Color4uint8(src[i]);
+ }
+}
+
+
+void Image4uint8::copyArray(const Color3uint8* ptr, int w, int h) {
+ resize(w, h);
+
+ GImage::RGBtoRGBA((const uint8*)ptr, (uint8*)getCArray(), w * h);
+}
+
+
+void Image4uint8::copyArray(const Color3* src, int w, int h) {
+ resize(w, h);
+ int N = w * h;
+
+ Color4uint8* dst = getCArray();
+ for (int i = 0; i < N; ++i) {
+ dst[i] = Color4uint8(Color4(src[i], 1.0f));
+ }
+}
+
+
+/** Saves in any of the formats supported by G3D::GImage. */
+void Image4uint8::save(const std::string& filename, GImage::Format fmt) {
+ GImage im(width(), height(), 4);
+ System::memcpy(im.byte(), getCArray(), width() * height() * 4);
+ im.save(filename, fmt);
+}
+
+
+ReferenceCountedPointer<class Image1uint8> Image4uint8::getChannel(int c) const {
+ debugAssert(c >= 0 && c <= 3);
+
+ Image1uint8Ref dst = Image1uint8::createEmpty(width(), height(), wrapMode());
+ const Color4uint8* srcArray = getCArray();
+ Color1uint8* dstArray = dst->getCArray();
+
+ const int N = width() * height();
+ for (int i = 0; i < N; ++i) {
+ dstArray[i] = Color1uint8(srcArray[i][c]);
+ }
+
+ return dst;
+}
+
+
+const ImageFormat* Image4uint8::format() const {
+ return ImageFormat::RGBA8();
+}
+
+} // G3D
diff --git a/externals/g3dlite/G3D.lib/source/ImageFormat.cpp b/externals/g3dlite/G3D.lib/source/ImageFormat.cpp
new file mode 100644
index 00000000000..3cbf6ad711e
--- /dev/null
+++ b/externals/g3dlite/G3D.lib/source/ImageFormat.cpp
@@ -0,0 +1,440 @@
+/**
+ @file ImageFormat.cpp
+
+ @maintainer Morgan McGuire, morgan@graphics3d.com
+
+ @created 2003-05-23
+ @edited 2006-01-11
+ */
+
+#include "../../GLG3D.lib/include/GLG3D/glheaders.h"
+#include "../../GLG3D.lib/include/GLG3D/glcalls.h"
+#include "G3D/ImageFormat.h"
+
+namespace G3D {
+
+ImageFormat::ImageFormat(
+ int _numComponents,
+ bool _compressed,
+ int _glFormat,
+ int _glBaseFormat,
+ int _luminanceBits,
+ int _alphaBits,
+ int _redBits,
+ int _greenBits,
+ int _blueBits,
+ int _depthBits,
+ int _stencilBits,
+ int _hardwareBitsPerTexel,
+ int _packedBitsPerTexel,
+ int glDataFormat,
+ bool _opaque,
+ bool _floatingPoint,
+ Code _code,
+ ColorSpace _colorSpace,
+ BayerPattern _bayerPattern) :
+
+ numComponents(_numComponents),
+ compressed(_compressed),
+ code(_code),
+ colorSpace(_colorSpace),
+ bayerPattern(_bayerPattern),
+ openGLFormat(_glFormat),
+ openGLBaseFormat(_glBaseFormat),
+ luminanceBits(_luminanceBits),
+ alphaBits(_alphaBits),
+ redBits(_redBits),
+ greenBits(_greenBits),
+ blueBits(_blueBits),
+ stencilBits(_stencilBits),
+ depthBits(_depthBits),
+ cpuBitsPerPixel(_packedBitsPerTexel),
+ packedBitsPerTexel(_packedBitsPerTexel),
+ openGLBitsPerPixel(_hardwareBitsPerTexel),
+ hardwareBitsPerTexel(_hardwareBitsPerTexel),
+ openGLDataFormat(glDataFormat),
+ opaque(_opaque),
+ floatingPoint(_floatingPoint) {
+
+ debugAssert(_packedBitsPerTexel <= _hardwareBitsPerTexel);
+}
+
+const ImageFormat* ImageFormat::depth(int depthBits) {
+
+ switch (depthBits) {
+ case 16:
+ return DEPTH16();
+
+ case 24:
+ return DEPTH24();
+
+ case 32:
+ return DEPTH32();
+
+ default:
+ debugAssertM(false, "Depth must be 16, 24, or 32.");
+ return DEPTH32();
+ }
+}
+
+
+const ImageFormat* ImageFormat::stencil(int bits) {
+ switch (bits) {
+ case 1:
+ return STENCIL1();
+
+ case 4:
+ return STENCIL4();
+
+ case 8:
+ return STENCIL8();
+
+ case 16:
+ return STENCIL16();
+
+ default:
+ debugAssertM(false, "Stencil must be 1, 4, 8 or 16.");
+ return STENCIL16();
+ }
+}
+
+
+std::string ImageFormat::name() const {
+
+ static const std::string nameArray[] =
+ {
+ "L8",
+ "L16",
+ "L16F",
+ "L32F",
+
+ "A8",
+ "A16",
+ "A16F",
+ "A32F",
+
+ "LA4",
+ "LA8",
+ "LA16",
+ "LA16F",
+ "LA32F",
+
+ "RGB5",
+ "RGB5A1",
+ "RGB8",
+ "RGB10",
+ "RGB10A2",
+ "RGB16",
+ "RGB16F",
+ "RGB32F",
+
+ "ARGB8",
+ "BGR8",
+
+ "RGBA8",
+ "RGBA16",
+ "RGBA16F",
+ "RGBA32F",
+
+ "BAYER_RGGB8",
+ "BAYER_GRBG8",
+ "BAYER_GBRG8",
+ "BAYER_BGGR8",
+ "BAYER_RGGB32F",
+ "BAYER_GRBG32F",
+ "BAYER_GBRG32F",
+ "BAYER_BGGR32F",
+
+ "HSV8",
+ "HSV32F",
+
+ "YUV420_PLANAR",
+ "YUV422",
+ "YUV444",
+
+ "RGB_DXT1",
+ "RGBA_DXT1",
+ "RGBA_DXT3",
+ "RGBA_DXT5",
+
+ "DEPTH16",
+ "DEPTH24",
+ "DEPTH32",
+ "DEPTH32F",
+
+ "STENCIL1",
+ "STENCIL4",
+ "STENCIL8",
+ "STENCIL16",
+
+ "DEPTH24_STENCIL8"
+ };
+
+ debugAssert(code < CODE_NUM);
+ return nameArray[code];
+}
+
+
+const ImageFormat* ImageFormat::fromCode(ImageFormat::Code code) {
+ switch (code) {
+ case ImageFormat::CODE_L8:
+ return ImageFormat::L8();
+ break;
+ case ImageFormat::CODE_L16:
+ return ImageFormat::L16();
+ break;
+ case ImageFormat::CODE_L16F:
+ return ImageFormat::L16F();
+ break;
+ case ImageFormat::CODE_L32F:
+ return ImageFormat::L32F();
+ break;
+
+ case ImageFormat::CODE_A8:
+ return ImageFormat::A8();
+ break;
+ case ImageFormat::CODE_A16:
+ return ImageFormat::A16();
+ break;
+ case ImageFormat::CODE_A16F:
+ return ImageFormat::A16F();
+ break;
+ case ImageFormat::CODE_A32F:
+ return ImageFormat::A32F();
+ break;
+
+ case ImageFormat::CODE_LA4:
+ return ImageFormat::LA4();
+ break;
+ case ImageFormat::CODE_LA8:
+ return ImageFormat::LA8();
+ break;
+ case ImageFormat::CODE_LA16:
+ return ImageFormat::LA16();
+ break;
+ case ImageFormat::CODE_LA16F:
+ return ImageFormat::LA16F();
+ break;
+ case ImageFormat::CODE_LA32F:
+ return ImageFormat::LA32F();
+ break;
+
+ case ImageFormat::CODE_RGB5:
+ return ImageFormat::RGB5();
+ break;
+ case ImageFormat::CODE_RGB5A1:
+ return ImageFormat::RGB5A1();
+ break;
+ case ImageFormat::CODE_RGB8:
+ return ImageFormat::RGB8();
+ break;
+ case ImageFormat::CODE_RGB10:
+ return ImageFormat::RGB10();
+ break;
+ case ImageFormat::CODE_RGB10A2:
+ return ImageFormat::RGB10A2();
+ break;
+ case ImageFormat::CODE_RGB16:
+ return ImageFormat::RGB16();
+ break;
+ case ImageFormat::CODE_RGB16F:
+ return ImageFormat::RGB16F();
+ break;
+ case ImageFormat::CODE_RGB32F:
+ return ImageFormat::RGB32F();
+ break;
+
+ case ImageFormat::CODE_ARGB8:
+ return NULL;
+
+ case ImageFormat::CODE_BGR8:
+ return NULL;
+
+ case ImageFormat::CODE_RGBA8:
+ return ImageFormat::RGBA8();
+ break;
+ case ImageFormat::CODE_RGBA16:
+ return ImageFormat::RGBA16();
+ break;
+ case ImageFormat::CODE_RGBA16F:
+ return ImageFormat::RGBA16F();
+ break;
+ case ImageFormat::CODE_RGBA32F:
+ return ImageFormat::RGBA32F();
+ break;
+
+ case ImageFormat::CODE_BAYER_RGGB8:
+ case ImageFormat::CODE_BAYER_GRBG8:
+ case ImageFormat::CODE_BAYER_GBRG8:
+ case ImageFormat::CODE_BAYER_BGGR8:
+ case ImageFormat::CODE_BAYER_RGGB32F:
+ case ImageFormat::CODE_BAYER_GRBG32F:
+ case ImageFormat::CODE_BAYER_GBRG32F:
+ case ImageFormat::CODE_BAYER_BGGR32F:
+
+ case ImageFormat::CODE_HSV8:
+ case ImageFormat::CODE_HSV32F:
+
+ case ImageFormat::CODE_RGB_DXT1:
+ return ImageFormat::RGB_DXT1();
+ break;
+ case ImageFormat::CODE_RGBA_DXT1:
+ return ImageFormat::RGBA_DXT1();
+ break;
+ case ImageFormat::CODE_RGBA_DXT3:
+ return ImageFormat::RGBA_DXT3();
+ break;
+ case ImageFormat::CODE_RGBA_DXT5:
+ return ImageFormat::RGBA_DXT5();
+ break;
+
+ case ImageFormat::CODE_DEPTH16:
+ return ImageFormat::DEPTH16();
+ break;
+ case ImageFormat::CODE_DEPTH24:
+ return ImageFormat::DEPTH24();
+ break;
+ case ImageFormat::CODE_DEPTH32:
+ return ImageFormat::DEPTH32();
+ break;
+ case ImageFormat::CODE_DEPTH32F:
+ return ImageFormat::DEPTH32F();
+ break;
+
+ case ImageFormat::CODE_STENCIL1:
+ return ImageFormat::STENCIL1();
+ break;
+ case ImageFormat::CODE_STENCIL4:
+ return ImageFormat::STENCIL4();
+ break;
+ case ImageFormat::CODE_STENCIL8:
+ return ImageFormat::STENCIL8();
+ break;
+ case ImageFormat::CODE_STENCIL16:
+ return ImageFormat::STENCIL16();
+ break;
+
+ case ImageFormat::CODE_DEPTH24_STENCIL8:
+ return ImageFormat::DEPTH24_STENCIL8();
+ break;
+
+ case ImageFormat::CODE_YUV420_PLANAR:
+ return ImageFormat::YUV420_PLANAR();
+ break;
+
+ case ImageFormat::CODE_YUV422:
+ return ImageFormat::YUV422();
+ break;
+
+ case ImageFormat::CODE_YUV444:
+ return ImageFormat::YUV444();
+ break;
+
+ default:
+ return NULL;
+ }
+}
+
+// Helper variables for defining texture formats
+
+// Is floating point format
+static const bool FLOAT_FORMAT = true;
+static const bool INT_FORMAT = false;
+
+// Is opaque format (no alpha)
+static const bool OPAQUE_FORMAT = true;
+static const bool CLEAR_FORMAT = false;
+
+// Is compressed format (not raw component data)
+static const bool COMP_FORMAT = true;
+static const bool UNCOMP_FORMAT = false;
+
+#define DEFINE_TEXTUREFORMAT_METHOD(name, cmpnts, cmprssd, glf, glbf, lb, rb, gb, bb, db, sb, hbpt, pbpt, gldf, opq, fp, code, cs, bp) \
+ const ImageFormat* ImageFormat::name() { \
+ static const ImageFormat format(cmpnts, cmprssd, glf, glbf, lb, rb, gb, bb, db, sb, hbpt, pbpt, gldf, opq, fp, code, cs, bp); \
+ return &format; }
+
+DEFINE_TEXTUREFORMAT_METHOD(L8, 1, UNCOMP_FORMAT, GL_LUMINANCE8, GL_LUMINANCE, 8, 0, 0, 0, 0, 0, 0, 8, 8, GL_UNSIGNED_BYTE, OPAQUE_FORMAT, INT_FORMAT, CODE_L8, COLOR_SPACE_NONE);
+
+DEFINE_TEXTUREFORMAT_METHOD(L16, 1, UNCOMP_FORMAT, GL_LUMINANCE16, GL_LUMINANCE, 16, 0, 0, 0, 0, 0, 0, 16, 16,GL_UNSIGNED_SHORT, OPAQUE_FORMAT, INT_FORMAT, CODE_L16, COLOR_SPACE_NONE);
+
+DEFINE_TEXTUREFORMAT_METHOD(L16F, 1, UNCOMP_FORMAT, GL_LUMINANCE16F_ARB,GL_LUMINANCE, 16, 0, 0, 0, 0, 0, 0, 16, 16, GL_FLOAT, OPAQUE_FORMAT, FLOAT_FORMAT, CODE_L16F, COLOR_SPACE_NONE);
+
+DEFINE_TEXTUREFORMAT_METHOD(L32F, 1, UNCOMP_FORMAT, GL_LUMINANCE32F_ARB,GL_LUMINANCE, 32, 0, 0, 0, 0, 0, 0, 32, 32, GL_FLOAT, OPAQUE_FORMAT, FLOAT_FORMAT, CODE_L32F, COLOR_SPACE_NONE);
+
+DEFINE_TEXTUREFORMAT_METHOD(A8, 1, UNCOMP_FORMAT, GL_ALPHA8, GL_ALPHA, 0, 8, 0, 0, 0, 0, 0, 8, 8, GL_UNSIGNED_BYTE, CLEAR_FORMAT, INT_FORMAT, CODE_A8, COLOR_SPACE_NONE);
+
+DEFINE_TEXTUREFORMAT_METHOD(A16, 1, UNCOMP_FORMAT, GL_ALPHA16, GL_ALPHA, 0, 16, 0, 0, 0, 0, 0, 16, 16, GL_UNSIGNED_SHORT, CLEAR_FORMAT, INT_FORMAT, CODE_A16, COLOR_SPACE_NONE);
+
+DEFINE_TEXTUREFORMAT_METHOD(A16F, 1, UNCOMP_FORMAT, GL_ALPHA16F_ARB, GL_ALPHA, 0, 16, 0, 0, 0, 0, 0, 16, 16, GL_FLOAT, CLEAR_FORMAT, FLOAT_FORMAT, CODE_A16F, COLOR_SPACE_NONE);
+
+DEFINE_TEXTUREFORMAT_METHOD(A32F, 1, UNCOMP_FORMAT, GL_ALPHA32F_ARB, GL_ALPHA, 0, 32, 0, 0, 0, 0, 0, 32, 32, GL_FLOAT, CLEAR_FORMAT, FLOAT_FORMAT, CODE_A32F, COLOR_SPACE_NONE);
+
+DEFINE_TEXTUREFORMAT_METHOD(LA4, 2, UNCOMP_FORMAT, GL_LUMINANCE4_ALPHA4, GL_LUMINANCE_ALPHA, 4, 4, 0, 0, 0, 0, 0, 8, 8, GL_UNSIGNED_BYTE, CLEAR_FORMAT, INT_FORMAT, CODE_LA4, COLOR_SPACE_NONE);
+
+DEFINE_TEXTUREFORMAT_METHOD(LA8, 2, UNCOMP_FORMAT, GL_LUMINANCE8_ALPHA8, GL_LUMINANCE_ALPHA, 8, 8, 0, 0, 0, 0, 0, 16, 16, GL_UNSIGNED_BYTE, CLEAR_FORMAT, INT_FORMAT, CODE_LA8, COLOR_SPACE_NONE);
+
+DEFINE_TEXTUREFORMAT_METHOD(LA16, 2, UNCOMP_FORMAT, GL_LUMINANCE16_ALPHA16, GL_LUMINANCE_ALPHA, 16, 16, 0, 0, 0, 0, 0, 16*2, 16*2, GL_UNSIGNED_SHORT, CLEAR_FORMAT, INT_FORMAT, CODE_LA16, COLOR_SPACE_NONE);
+
+DEFINE_TEXTUREFORMAT_METHOD(LA16F, 2, UNCOMP_FORMAT, GL_LUMINANCE_ALPHA16F_ARB, GL_LUMINANCE_ALPHA, 16, 16, 0, 0, 0, 0, 0, 16*2, 16*2, GL_FLOAT, CLEAR_FORMAT, FLOAT_FORMAT, ImageFormat::CODE_LA16F, ImageFormat::COLOR_SPACE_NONE);
+
+DEFINE_TEXTUREFORMAT_METHOD(LA32F, 2, UNCOMP_FORMAT, GL_LUMINANCE_ALPHA32F_ARB, GL_LUMINANCE_ALPHA, 32, 32, 0, 0, 0, 0, 0, 32*2, 32*2, GL_FLOAT, CLEAR_FORMAT, FLOAT_FORMAT, ImageFormat::CODE_LA32F, ImageFormat::COLOR_SPACE_NONE);
+
+DEFINE_TEXTUREFORMAT_METHOD(BGR8, 3, UNCOMP_FORMAT, GL_RGB8, GL_BGR, 0, 0, 8, 8, 8, 0, 0, 32, 24, GL_UNSIGNED_BYTE, OPAQUE_FORMAT, INT_FORMAT, ImageFormat::CODE_BGR8, ImageFormat::COLOR_SPACE_RGB);
+
+DEFINE_TEXTUREFORMAT_METHOD(RGB5, 3, UNCOMP_FORMAT, GL_RGB5, GL_RGBA, 0, 0, 5, 5, 5, 0, 0, 16, 16, GL_UNSIGNED_BYTE, OPAQUE_FORMAT, INT_FORMAT, ImageFormat::CODE_RGB5, ImageFormat::COLOR_SPACE_RGB);
+
+DEFINE_TEXTUREFORMAT_METHOD(RGB5A1, 4, UNCOMP_FORMAT, GL_RGB5_A1, GL_RGBA, 0, 1, 5, 5, 5, 0, 0, 16, 16, GL_UNSIGNED_BYTE, OPAQUE_FORMAT, INT_FORMAT, ImageFormat::CODE_RGB5A1, ImageFormat::COLOR_SPACE_RGB);
+
+DEFINE_TEXTUREFORMAT_METHOD(RGB8, 3, UNCOMP_FORMAT, GL_RGB8, GL_RGB, 0, 0, 8, 8, 8, 0, 0, 32, 24, GL_UNSIGNED_BYTE, OPAQUE_FORMAT, INT_FORMAT, ImageFormat::CODE_RGB8, ImageFormat::COLOR_SPACE_RGB);
+
+DEFINE_TEXTUREFORMAT_METHOD(RGB10, 3, UNCOMP_FORMAT, GL_RGB10, GL_RGB, 0, 0, 10, 10, 10, 0, 0, 32, 10*3, GL_UNSIGNED_SHORT, OPAQUE_FORMAT, INT_FORMAT, ImageFormat::CODE_RGB10, ImageFormat::COLOR_SPACE_RGB);
+
+DEFINE_TEXTUREFORMAT_METHOD(RGB10A2, 4, UNCOMP_FORMAT, GL_RGB10_A2, GL_RGBA, 0, 2, 10, 10, 10, 0, 0, 32, 32, GL_UNSIGNED_INT_10_10_10_2, OPAQUE_FORMAT, INT_FORMAT, ImageFormat::CODE_RGB10A2, ImageFormat::COLOR_SPACE_RGB);
+
+DEFINE_TEXTUREFORMAT_METHOD(RGB16, 3, UNCOMP_FORMAT, GL_RGB16, GL_RGB, 0, 0, 16, 16, 16, 0, 0, 16*3, 16*3, GL_UNSIGNED_SHORT, OPAQUE_FORMAT, INT_FORMAT, ImageFormat::CODE_RGB16, ImageFormat::COLOR_SPACE_RGB);
+
+DEFINE_TEXTUREFORMAT_METHOD(RGB16F, 3, UNCOMP_FORMAT, GL_RGB16F_ARB, GL_RGB, 0, 0, 16, 16, 16, 0, 0, 16*3, 16*3, GL_FLOAT, OPAQUE_FORMAT, FLOAT_FORMAT, ImageFormat::CODE_RGB16F, ImageFormat::COLOR_SPACE_RGB);
+
+DEFINE_TEXTUREFORMAT_METHOD(RGB32F, 3, UNCOMP_FORMAT, GL_RGB32F_ARB, GL_RGB, 0, 0, 32, 32, 32, 0, 0, 32*3, 32*3, GL_FLOAT, OPAQUE_FORMAT, FLOAT_FORMAT, ImageFormat::CODE_RGB32F, ImageFormat::COLOR_SPACE_RGB);
+
+DEFINE_TEXTUREFORMAT_METHOD(RGBA8, 4, UNCOMP_FORMAT, GL_RGBA8, GL_RGBA, 0, 8, 8, 8, 8, 0, 0, 32, 32, GL_UNSIGNED_BYTE, CLEAR_FORMAT, INT_FORMAT, ImageFormat::CODE_RGBA8, ImageFormat::COLOR_SPACE_RGB);
+
+DEFINE_TEXTUREFORMAT_METHOD(RGBA16, 4, UNCOMP_FORMAT, GL_RGBA16, GL_RGBA, 0, 16, 16, 16, 16, 0, 0, 16*4, 16*4, GL_UNSIGNED_SHORT, CLEAR_FORMAT, INT_FORMAT, ImageFormat::CODE_RGBA16, ImageFormat::COLOR_SPACE_RGB);
+
+DEFINE_TEXTUREFORMAT_METHOD(RGBA16F, 4, UNCOMP_FORMAT, GL_RGBA16F_ARB, GL_RGBA, 0, 16, 16, 16, 16, 0, 0, 16*4, 16*4, GL_FLOAT, CLEAR_FORMAT, FLOAT_FORMAT, ImageFormat::CODE_RGB16F, ImageFormat::COLOR_SPACE_RGB);
+
+DEFINE_TEXTUREFORMAT_METHOD(RGBA32F, 4, UNCOMP_FORMAT, GL_RGBA32F_ARB, GL_RGBA, 0, 32, 32, 32, 32, 0, 0, 32*4, 32*4, GL_FLOAT, CLEAR_FORMAT, FLOAT_FORMAT, ImageFormat::CODE_RGBA32F, ImageFormat::COLOR_SPACE_RGB);
+
+DEFINE_TEXTUREFORMAT_METHOD(RGB_DXT1, 3, COMP_FORMAT, GL_COMPRESSED_RGB_S3TC_DXT1_EXT, GL_RGB, 0, 0, 0, 0, 0, 0, 0, 64, 64, GL_UNSIGNED_BYTE, OPAQUE_FORMAT, INT_FORMAT, ImageFormat::CODE_RGB_DXT1, ImageFormat::COLOR_SPACE_RGB);
+
+DEFINE_TEXTUREFORMAT_METHOD(RGBA_DXT1, 4, COMP_FORMAT, GL_COMPRESSED_RGBA_S3TC_DXT1_EXT, GL_RGBA, 0, 0, 0, 0, 0, 0, 0, 64, 64, GL_UNSIGNED_BYTE, CLEAR_FORMAT, INT_FORMAT, ImageFormat::CODE_RGBA_DXT1, ImageFormat::COLOR_SPACE_RGB);
+
+DEFINE_TEXTUREFORMAT_METHOD(RGBA_DXT3, 4, COMP_FORMAT, GL_COMPRESSED_RGBA_S3TC_DXT3_EXT, GL_RGBA, 0, 0, 0, 0, 0, 0, 0, 128, 128, GL_UNSIGNED_BYTE, CLEAR_FORMAT, INT_FORMAT, ImageFormat::CODE_RGBA_DXT3, ImageFormat::COLOR_SPACE_RGB);
+
+DEFINE_TEXTUREFORMAT_METHOD(RGBA_DXT5, 4, COMP_FORMAT, GL_COMPRESSED_RGBA_S3TC_DXT5_EXT, GL_RGBA, 0, 0, 0, 0, 0, 0, 0, 128, 128, GL_UNSIGNED_BYTE, CLEAR_FORMAT, INT_FORMAT, ImageFormat::CODE_RGBA_DXT5, ImageFormat::COLOR_SPACE_RGB);
+
+DEFINE_TEXTUREFORMAT_METHOD(DEPTH16, 1, UNCOMP_FORMAT, GL_DEPTH_COMPONENT16_ARB, GL_DEPTH_COMPONENT, 0, 0, 0, 0, 0, 0, 16, 16, 16, GL_UNSIGNED_SHORT, CLEAR_FORMAT, INT_FORMAT, ImageFormat::CODE_DEPTH16, ImageFormat::COLOR_SPACE_NONE);
+
+DEFINE_TEXTUREFORMAT_METHOD(DEPTH24, 1, UNCOMP_FORMAT, GL_DEPTH_COMPONENT24_ARB, GL_DEPTH_COMPONENT, 0, 0, 0, 0, 0, 0, 24, 32, 24, GL_UNSIGNED_INT, CLEAR_FORMAT, INT_FORMAT, ImageFormat::CODE_DEPTH24, ImageFormat::COLOR_SPACE_NONE);
+
+DEFINE_TEXTUREFORMAT_METHOD(DEPTH32, 1, UNCOMP_FORMAT, GL_DEPTH_COMPONENT32_ARB, GL_DEPTH_COMPONENT, 0, 0, 0, 0, 0, 0, 32, 32, 32, GL_UNSIGNED_INT, CLEAR_FORMAT, INT_FORMAT, ImageFormat::CODE_DEPTH32, ImageFormat::COLOR_SPACE_NONE);
+
+DEFINE_TEXTUREFORMAT_METHOD(DEPTH32F, 1, UNCOMP_FORMAT, GL_DEPTH_COMPONENT32_ARB, GL_DEPTH_COMPONENT, 0, 0, 0, 0, 0, 0, 32, 32, 32, GL_FLOAT, CLEAR_FORMAT, FLOAT_FORMAT, ImageFormat::CODE_DEPTH32F, ImageFormat::COLOR_SPACE_NONE);
+
+// These formats are for use with Renderbuffers only!
+DEFINE_TEXTUREFORMAT_METHOD(STENCIL1, 1, UNCOMP_FORMAT, GL_STENCIL_INDEX1_EXT, GL_STENCIL_INDEX_EXT, 0, 0, 0, 0, 0, 0, 1, 1, 1, GL_UNSIGNED_BYTE, CLEAR_FORMAT, INT_FORMAT, ImageFormat::CODE_STENCIL1, ImageFormat::COLOR_SPACE_NONE);
+
+DEFINE_TEXTUREFORMAT_METHOD(STENCIL4, 1, UNCOMP_FORMAT, GL_STENCIL_INDEX4_EXT, GL_STENCIL_INDEX_EXT, 0, 0, 0, 0, 0, 0, 4, 4, 4, GL_UNSIGNED_BYTE, CLEAR_FORMAT, INT_FORMAT, ImageFormat::CODE_STENCIL4, ImageFormat::COLOR_SPACE_NONE);
+
+DEFINE_TEXTUREFORMAT_METHOD(STENCIL8, 1, UNCOMP_FORMAT, GL_STENCIL_INDEX8_EXT, GL_STENCIL_INDEX_EXT, 0, 0, 0, 0, 0, 0, 8, 8, 8, GL_UNSIGNED_BYTE, CLEAR_FORMAT, INT_FORMAT, ImageFormat::CODE_STENCIL8, ImageFormat::COLOR_SPACE_NONE);
+
+DEFINE_TEXTUREFORMAT_METHOD(STENCIL16, 1, UNCOMP_FORMAT, GL_STENCIL_INDEX16_EXT, GL_STENCIL_INDEX_EXT, 0, 0, 0, 0, 0, 0, 16, 16, 16, GL_UNSIGNED_SHORT, CLEAR_FORMAT, INT_FORMAT, ImageFormat::CODE_STENCIL16, ImageFormat::COLOR_SPACE_NONE);
+
+DEFINE_TEXTUREFORMAT_METHOD(DEPTH24_STENCIL8, 2, UNCOMP_FORMAT, GL_DEPTH24_STENCIL8_EXT, GL_DEPTH_STENCIL_EXT,0, 0, 0, 0, 0, 24, 8, 32, 32, GL_UNSIGNED_INT, CLEAR_FORMAT, INT_FORMAT, ImageFormat::CODE_DEPTH24_STENCIL8, ImageFormat::COLOR_SPACE_NONE);
+
+DEFINE_TEXTUREFORMAT_METHOD(YUV420_PLANAR, 3, UNCOMP_FORMAT, GL_NONE, GL_NONE, 0, 0, 0, 0, 0, 0, 0, 12, 12, GL_UNSIGNED_BYTE, OPAQUE_FORMAT, INT_FORMAT, ImageFormat::CODE_YUV420_PLANAR, ImageFormat::COLOR_SPACE_YUV);
+DEFINE_TEXTUREFORMAT_METHOD(YUV422, 3, UNCOMP_FORMAT, GL_NONE, GL_NONE, 0, 0, 0, 0, 0, 0, 0, 16, 16, GL_UNSIGNED_BYTE, OPAQUE_FORMAT, INT_FORMAT, ImageFormat::CODE_YUV422, ImageFormat::COLOR_SPACE_YUV);
+DEFINE_TEXTUREFORMAT_METHOD(YUV444, 3, UNCOMP_FORMAT, GL_NONE, GL_NONE, 0, 0, 0, 0, 0, 0, 0, 24, 24, GL_UNSIGNED_BYTE, OPAQUE_FORMAT, INT_FORMAT, ImageFormat::CODE_YUV444, ImageFormat::COLOR_SPACE_YUV);
+
+}
diff --git a/externals/g3dlite/G3D.lib/source/ImageFormat_convert.cpp b/externals/g3dlite/G3D.lib/source/ImageFormat_convert.cpp
new file mode 100644
index 00000000000..9cbc4edcb39
--- /dev/null
+++ b/externals/g3dlite/G3D.lib/source/ImageFormat_convert.cpp
@@ -0,0 +1,1305 @@
+#include "G3D/ImageFormat.h"
+#include "G3D/Color1uint8.h"
+#include "G3D/Color3uint8.h"
+#include "G3D/Color4uint8.h"
+#include "G3D/Color1.h"
+#include "G3D/Color3.h"
+#include "G3D/Color4.h"
+
+
+namespace G3D {
+
+// this is the signature for all conversion routines (same parameters as ImageFormat::convert)
+typedef void (*ConvertFunc)(const Array<const void*>& srcBytes, int srcWidth, int srcHeight, const ImageFormat* srcFormat, int srcRowPadBits, const Array<void*>& dstBytes, const ImageFormat* dstFormat, int dstRowPadBits, bool invertY, ImageFormat::BayerAlgorithm bayerAlg);
+
+// this defines the conversion routines for converting between compatible formats
+static const int NUM_CONVERT_IMAGE_FORMATS = 5;
+struct ConvertAttributes {
+ ConvertFunc m_converter;
+ ImageFormat::Code m_sourceFormats[NUM_CONVERT_IMAGE_FORMATS];
+ ImageFormat::Code m_destFormats[NUM_CONVERT_IMAGE_FORMATS];
+ bool m_handlesSourcePadding;
+ bool m_handlesDestPadding;
+ bool m_handleInvertY;
+};
+
+// forward declare the converters we can use them below
+#define DECLARE_CONVERT_FUNC(name) static void name(const Array<const void*>& srcBytes, int srcWidth, int srcHeight, const ImageFormat* srcFormat, int srcRowPadBits, const Array<void*>& dstBytes, const ImageFormat* dstFormat, int dstRowPadBits, bool invertY, ImageFormat::BayerAlgorithm bayerAlg);
+
+DECLARE_CONVERT_FUNC(l8_to_rgb8);
+DECLARE_CONVERT_FUNC(l32f_to_rgb8);
+DECLARE_CONVERT_FUNC(rgb8_to_rgba8);
+DECLARE_CONVERT_FUNC(rgb8_to_bgr8);
+DECLARE_CONVERT_FUNC(rgb8_to_rgba32f);
+DECLARE_CONVERT_FUNC(bgr8_to_rgb8);
+DECLARE_CONVERT_FUNC(bgr8_to_rgba8);
+DECLARE_CONVERT_FUNC(bgr8_to_rgba32f);
+DECLARE_CONVERT_FUNC(rgba8_to_rgb8);
+DECLARE_CONVERT_FUNC(rgba8_to_bgr8);
+DECLARE_CONVERT_FUNC(rgba8_to_rgba32f);
+DECLARE_CONVERT_FUNC(rgb32f_to_rgba32f);
+DECLARE_CONVERT_FUNC(rgba32f_to_rgb8);
+DECLARE_CONVERT_FUNC(rgba32f_to_rgba8);
+DECLARE_CONVERT_FUNC(rgba32f_to_bgr8);
+DECLARE_CONVERT_FUNC(rgba32f_to_rgb32f);
+DECLARE_CONVERT_FUNC(rgba32f_to_bayer_rggb8);
+DECLARE_CONVERT_FUNC(rgba32f_to_bayer_gbrg8);
+DECLARE_CONVERT_FUNC(rgba32f_to_bayer_grbg8);
+DECLARE_CONVERT_FUNC(rgba32f_to_bayer_bggr8);
+DECLARE_CONVERT_FUNC(bayer_rggb8_to_rgba32f);
+DECLARE_CONVERT_FUNC(bayer_gbrg8_to_rgba32f);
+DECLARE_CONVERT_FUNC(bayer_grbg8_to_rgba32f);
+DECLARE_CONVERT_FUNC(bayer_bggr8_to_rgba32f);
+DECLARE_CONVERT_FUNC(rgb8_to_yuv420p);
+DECLARE_CONVERT_FUNC(rgb8_to_yuv422);
+DECLARE_CONVERT_FUNC(rgb8_to_yuv444);
+DECLARE_CONVERT_FUNC(yuv420p_to_rgb8);
+DECLARE_CONVERT_FUNC(yuv422_to_rgb8);
+DECLARE_CONVERT_FUNC(yuv444_to_rgb8);
+
+// this is the list of mappings between formats and the routines to perform them
+static const ConvertAttributes sConvertMappings[] = {
+
+ // RGB -> RGB color space
+ // L8 ->
+ {l8_to_rgb8, {ImageFormat::CODE_L8, ImageFormat::CODE_NONE}, {ImageFormat::CODE_RGB8, ImageFormat::CODE_NONE}, false, false, true},
+
+ // L32F ->
+ {l32f_to_rgb8, {ImageFormat::CODE_L32F, ImageFormat::CODE_NONE}, {ImageFormat::CODE_RGB8, ImageFormat::CODE_NONE}, false, false, true},
+
+ // RGB8 ->
+ {rgb8_to_rgba8, {ImageFormat::CODE_RGB8, ImageFormat::CODE_NONE}, {ImageFormat::CODE_RGBA8, ImageFormat::CODE_NONE}, false, false, true},
+ {rgb8_to_bgr8, {ImageFormat::CODE_RGB8, ImageFormat::CODE_NONE}, {ImageFormat::CODE_BGR8, ImageFormat::CODE_NONE}, false, false, true},
+ {rgb8_to_rgba32f, {ImageFormat::CODE_RGB8, ImageFormat::CODE_NONE}, {ImageFormat::CODE_RGBA32F, ImageFormat::CODE_NONE}, true, false, true},
+
+ // BGR8 ->
+ {bgr8_to_rgb8, {ImageFormat::CODE_BGR8, ImageFormat::CODE_NONE}, {ImageFormat::CODE_RGB8, ImageFormat::CODE_NONE}, false, false, true},
+ {bgr8_to_rgba8, {ImageFormat::CODE_BGR8, ImageFormat::CODE_NONE}, {ImageFormat::CODE_RGBA8, ImageFormat::CODE_NONE}, false, false, true},
+ {bgr8_to_rgba32f, {ImageFormat::CODE_BGR8, ImageFormat::CODE_NONE}, {ImageFormat::CODE_RGBA32F, ImageFormat::CODE_NONE}, true, false, true},
+
+ // RGBA8 ->
+ {rgba8_to_rgb8, {ImageFormat::CODE_RGBA8, ImageFormat::CODE_NONE}, {ImageFormat::CODE_RGB8, ImageFormat::CODE_NONE}, false, false, true},
+ {rgba8_to_bgr8, {ImageFormat::CODE_RGBA8, ImageFormat::CODE_NONE}, {ImageFormat::CODE_BGR8, ImageFormat::CODE_NONE}, false, false, true},
+ {rgba8_to_rgba32f, {ImageFormat::CODE_RGBA8, ImageFormat::CODE_NONE}, {ImageFormat::CODE_RGBA32F, ImageFormat::CODE_NONE}, true, false, true},
+
+ // RGB32F ->
+ {rgb32f_to_rgba32f, {ImageFormat::CODE_RGB32F, ImageFormat::CODE_NONE}, {ImageFormat::CODE_RGBA32F, ImageFormat::CODE_NONE}, true, false, true},
+
+ // RGBA32F ->
+ {rgba32f_to_rgb8, {ImageFormat::CODE_RGBA32F, ImageFormat::CODE_NONE}, {ImageFormat::CODE_RGB8, ImageFormat::CODE_NONE}, false, true, true},
+ {rgba32f_to_rgba8, {ImageFormat::CODE_RGBA32F, ImageFormat::CODE_NONE}, {ImageFormat::CODE_RGBA8, ImageFormat::CODE_NONE}, false, true, true},
+ {rgba32f_to_bgr8, {ImageFormat::CODE_RGBA32F, ImageFormat::CODE_NONE}, {ImageFormat::CODE_BGR8, ImageFormat::CODE_NONE}, false, true, true},
+ {rgba32f_to_rgb32f, {ImageFormat::CODE_RGBA32F, ImageFormat::CODE_NONE}, {ImageFormat::CODE_RGB32F, ImageFormat::CODE_NONE}, false, true, true},
+
+ // RGB -> BAYER color space
+ {rgba32f_to_bayer_rggb8, {ImageFormat::CODE_RGBA32F, ImageFormat::CODE_NONE}, {ImageFormat::CODE_BAYER_RGGB8, ImageFormat::CODE_NONE}, false, true, true},
+ {rgba32f_to_bayer_gbrg8, {ImageFormat::CODE_RGBA32F, ImageFormat::CODE_NONE}, {ImageFormat::CODE_BAYER_GBRG8, ImageFormat::CODE_NONE}, false, true, true},
+ {rgba32f_to_bayer_grbg8, {ImageFormat::CODE_RGBA32F, ImageFormat::CODE_NONE}, {ImageFormat::CODE_BAYER_GRBG8, ImageFormat::CODE_NONE}, false, true, true},
+ {rgba32f_to_bayer_bggr8, {ImageFormat::CODE_RGBA32F, ImageFormat::CODE_NONE}, {ImageFormat::CODE_BAYER_BGGR8, ImageFormat::CODE_NONE}, false, true, true},
+
+ // BAYER -> RGB color space
+ {bayer_rggb8_to_rgba32f, {ImageFormat::CODE_BAYER_RGGB8, ImageFormat::CODE_NONE}, {ImageFormat::CODE_RGBA32F, ImageFormat::CODE_NONE}, false, false, true},
+ {bayer_gbrg8_to_rgba32f, {ImageFormat::CODE_BAYER_GBRG8, ImageFormat::CODE_NONE}, {ImageFormat::CODE_RGBA32F, ImageFormat::CODE_NONE}, false, false, true},
+ {bayer_grbg8_to_rgba32f, {ImageFormat::CODE_BAYER_GRBG8, ImageFormat::CODE_NONE}, {ImageFormat::CODE_RGBA32F, ImageFormat::CODE_NONE}, false, false, true},
+ {bayer_bggr8_to_rgba32f, {ImageFormat::CODE_BAYER_BGGR8, ImageFormat::CODE_NONE}, {ImageFormat::CODE_RGBA32F, ImageFormat::CODE_NONE}, false, false, true},
+
+ // RGB <-> YUV color space
+ {rgb8_to_yuv420p, {ImageFormat::CODE_RGB8, ImageFormat::CODE_NONE}, {ImageFormat::CODE_YUV420_PLANAR, ImageFormat::CODE_NONE}, false, false, false},
+ {rgb8_to_yuv422, {ImageFormat::CODE_RGB8, ImageFormat::CODE_NONE}, {ImageFormat::CODE_YUV422, ImageFormat::CODE_NONE}, false, false, false},
+ {rgb8_to_yuv444, {ImageFormat::CODE_RGB8, ImageFormat::CODE_NONE}, {ImageFormat::CODE_YUV444, ImageFormat::CODE_NONE}, false, false, false},
+ {yuv420p_to_rgb8, {ImageFormat::CODE_YUV420_PLANAR, ImageFormat::CODE_NONE}, {ImageFormat::CODE_RGB8, ImageFormat::CODE_NONE}, false, false, false},
+ {yuv422_to_rgb8, {ImageFormat::CODE_YUV422, ImageFormat::CODE_NONE}, {ImageFormat::CODE_RGB8, ImageFormat::CODE_NONE}, false, false, false},
+ {yuv444_to_rgb8, {ImageFormat::CODE_YUV444, ImageFormat::CODE_NONE}, {ImageFormat::CODE_RGB8, ImageFormat::CODE_NONE}, false, false, false},
+};
+
+static ConvertFunc findConverter(TextureFormat::Code sourceCode, TextureFormat::Code destCode, bool needsSourcePadding, bool needsDestPadding, bool needsInvertY) {
+ int numRoutines = sizeof(sConvertMappings) / sizeof(ConvertAttributes);
+ for (int routineIndex = 0; routineIndex < numRoutines; ++routineIndex) {
+ int sourceIndex = 0;
+ ConvertAttributes routine = sConvertMappings[routineIndex];
+
+ while (routine.m_sourceFormats[sourceIndex] != ImageFormat::CODE_NONE) {
+ // check for matching source
+ if (routine.m_sourceFormats[sourceIndex] == sourceCode) {
+ int destIndex = 0;
+
+ // now check for matching dest to see if the routine fits
+ while (routine.m_destFormats[destIndex] != ImageFormat::CODE_NONE) {
+
+ // check if dest format matches and padding + invert rules match
+ if ((routine.m_destFormats[destIndex] == destCode) &&
+ (!needsSourcePadding || (routine.m_handlesSourcePadding == needsSourcePadding)) &&
+ (!needsDestPadding || (routine.m_handlesDestPadding == needsDestPadding)) &&
+ (!needsInvertY || (routine.m_handleInvertY == needsInvertY))) {
+
+ // found compatible converter
+ return routine.m_converter;
+ }
+ ++destIndex;
+ }
+ }
+ ++sourceIndex;
+ }
+ }
+
+ return NULL;
+}
+
+bool conversionAvailable(const ImageFormat* srcFormat, int srcRowPadBits, const ImageFormat* dstFormat, int dstRowPadBits, bool invertY = false) {
+ bool conversionAvailable = false;
+
+ // check if a conversion is available
+ if ( (srcFormat->code == dstFormat->code) && (srcRowPadBits == dstRowPadBits) && !invertY) {
+ conversionAvailable = true;
+ } else {
+ ConvertFunc directConverter = findConverter(srcFormat->code, dstFormat->code, srcRowPadBits > 0, dstRowPadBits > 0, invertY);
+
+ conversionAvailable = (directConverter != NULL);
+ }
+
+ return conversionAvailable;
+}
+
+bool ImageFormat::convert(const Array<const void*>& srcBytes, int srcWidth, int srcHeight, const ImageFormat* srcFormat, int srcRowPadBits,
+ const Array<void*>& dstBytes, const ImageFormat* dstFormat, int dstRowPadBits,
+ bool invertY, BayerAlgorithm bayerAlg) {
+
+ bool conversionAvailable = false;
+
+ // Handle direct copy of image to same format
+ if ( (srcFormat->code == dstFormat->code) && (srcRowPadBits == dstRowPadBits) && !invertY) {
+
+ System::memcpy(dstBytes[0], srcBytes[0], iCeil(((srcWidth * srcFormat->cpuBitsPerPixel + srcRowPadBits) * srcHeight) / 8.0f));
+ conversionAvailable = true;
+ } else {
+ // if no direct conversion routine exists,
+ // then look for conversion to intermediate
+ // and then from intermediate to dest.
+ // intermediate format is RGBA32F
+ ConvertFunc directConverter = findConverter(srcFormat->code, dstFormat->code, srcRowPadBits > 0, dstRowPadBits > 0, invertY);
+
+ // if we have a direct converter, use it, otherwise find intermdiate path
+ if (directConverter) {
+ directConverter(srcBytes, srcWidth, srcHeight, srcFormat, srcRowPadBits, dstBytes, dstFormat, dstRowPadBits, invertY, bayerAlg);
+ conversionAvailable = true;
+ } else {
+ ConvertFunc toInterConverter = findConverter(srcFormat->code, ImageFormat::CODE_RGBA32F, srcRowPadBits > 0, false, false);;
+ ConvertFunc fromInterConverter = findConverter(ImageFormat::CODE_RGBA32F, dstFormat->code, false, dstRowPadBits > 0, invertY);;
+
+ if (toInterConverter && fromInterConverter) {
+ Array<void*> tmp;
+ tmp.append(System::malloc(srcWidth * srcHeight * ImageFormat::RGBA32F()->cpuBitsPerPixel * 8));
+
+ toInterConverter(srcBytes, srcWidth, srcHeight, srcFormat, srcRowPadBits, tmp, ImageFormat::RGBA32F(), 0, false, bayerAlg);
+ fromInterConverter(reinterpret_cast<Array<const void*>&>(tmp), srcWidth, srcHeight, ImageFormat::RGBA32F(), 0, dstBytes, dstFormat, dstRowPadBits, invertY, bayerAlg);
+
+ System::free(tmp[0]);
+
+ conversionAvailable = true;
+ }
+ }
+ }
+
+ return conversionAvailable;
+}
+
+
+// *******************
+// RGB -> RGB color space conversions
+// *******************
+
+// L8 ->
+static void l8_to_rgb8(const Array<const void*>& srcBytes, int srcWidth, int srcHeight, const ImageFormat* srcFormat, int srcRowPadBits, const Array<void*>& dstBytes, const ImageFormat* dstFormat, int dstRowPadBits, bool invertY, ImageFormat::BayerAlgorithm bayerAlg) {
+ uint8* dst = static_cast<uint8*>(dstBytes[0]);
+ const uint8* src = static_cast<const uint8*>(srcBytes[0]);
+ for (int y = 0; y < srcHeight; ++y) {
+ for (int x = 0; x < srcWidth; ++x) {
+ int i = (invertY) ? ((srcHeight-1-y) * srcWidth +x) : (y * srcWidth + x);
+ int i3 = i * 3;
+
+ dst[i3 + 0] = src[i];
+ dst[i3 + 1] = src[i];
+ dst[i3 + 2] = src[i];
+ }
+ }
+}
+
+// L32F ->
+static void l32f_to_rgb8(const Array<const void*>& srcBytes, int srcWidth, int srcHeight, const ImageFormat* srcFormat, int srcRowPadBits, const Array<void*>& dstBytes, const ImageFormat* dstFormat, int dstRowPadBits, bool invertY, ImageFormat::BayerAlgorithm bayerAlg) {
+ int srcIndex = 0;
+ int dstByteOffset = 0;
+ uint8* dst = static_cast<uint8*>(dstBytes[0]);
+ const float* src = static_cast<const float*>(srcBytes[0]);
+
+ for (int y = 0; y < srcHeight; ++y) {
+ if (invertY) {
+ srcIndex = srcWidth * (srcHeight - y - 1);
+ }
+
+ for (int x = 0; x < srcWidth; ++x, ++srcIndex, dstByteOffset += 3) {
+ Color3uint8& d = *reinterpret_cast<Color3uint8*>(dst + dstByteOffset);
+ float s = src[srcIndex];
+
+ uint8 c = iMin(255, iFloor(s * 256));
+ d = Color3uint8(c, c, c);
+ }
+ }
+}
+
+// RGB8 ->
+static void rgb8_to_rgba8(const Array<const void*>& srcBytes, int srcWidth, int srcHeight, const ImageFormat* srcFormat, int srcRowPadBits, const Array<void*>& dstBytes, const ImageFormat* dstFormat, int dstRowPadBits, bool invertY, ImageFormat::BayerAlgorithm bayerAlg) {
+ uint8* dst = static_cast<uint8*>(dstBytes[0]);
+ const uint8* src = static_cast<const uint8*>(srcBytes[0]);
+ for (int y = 0; y < srcHeight; ++y) {
+ for (int x = 0; x < srcWidth; ++x) {
+ int i = (invertY) ? ((srcHeight-1-y) * srcWidth +x) : (y * srcWidth + x);
+ int i3 = i * 3;
+ int i4 = i3 + i;
+
+ dst[i4 + 0] = src[i3 + 0];
+ dst[i4 + 1] = src[i3 + 1];
+ dst[i4 + 2] = src[i3 + 2];
+ dst[i4 + 3] = 255;
+ }
+ }
+}
+
+static void rgb8_to_bgr8(const Array<const void*>& srcBytes, int srcWidth, int srcHeight, const ImageFormat* srcFormat, int srcRowPadBits, const Array<void*>& dstBytes, const ImageFormat* dstFormat, int dstRowPadBits, bool invertY, ImageFormat::BayerAlgorithm bayerAlg) {
+ uint8* dst = static_cast<uint8*>(dstBytes[0]);
+ const uint8* src = static_cast<const uint8*>(srcBytes[0]);
+ for (int y = 0; y < srcHeight; ++y) {
+ for (int x = 0; x < srcWidth; ++x) {
+ int i = (invertY) ? ((srcHeight-1-y) * srcWidth +x) : (y * srcWidth + x);
+ int i3 = i * 3;
+ dst[i3 + 0] = src[i3 + 2];
+ dst[i3 + 1] = src[i3 + 1];
+ dst[i3 + 2] = src[i3 + 0];
+ }
+ }
+}
+
+static void rgb8_to_rgba32f(const Array<const void*>& srcBytes, int srcWidth, int srcHeight, const ImageFormat* srcFormat, int srcRowPadBits, const Array<void*>& dstBytes, const ImageFormat* dstFormat, int dstRowPadBits, bool invertY, ImageFormat::BayerAlgorithm bayerAlg) {
+ debugAssertM(srcRowPadBits % 8 == 0, "Source row padding must be a multiple of 8 bits for this format");
+
+ int dstIndex = 0;
+ int srcByteOffset = 0;
+ int srcRowPadBytes = srcRowPadBits / 8;
+ Color4* dst = static_cast<Color4*>(dstBytes[0]);
+ const uint8* src = static_cast<const uint8*>(srcBytes[0]);
+
+ for (int y = 0; y < srcHeight; ++y) {
+ if (invertY) {
+ dstIndex = srcWidth * (srcHeight - 1 - y);
+ }
+ for (int x = 0; x < srcWidth; ++x, ++dstIndex, srcByteOffset += 3) {
+ const Color3uint8& s = *reinterpret_cast<const Color3uint8*>(src + srcByteOffset);
+ dst[dstIndex] = Color4(Color3(s), 1.0f);
+ }
+ srcByteOffset += srcRowPadBytes;
+ }
+}
+
+// BGR8 ->
+static void bgr8_to_rgb8(const Array<const void*>& srcBytes, int srcWidth, int srcHeight, const ImageFormat* srcFormat, int srcRowPadBits, const Array<void*>& dstBytes, const ImageFormat* dstFormat, int dstRowPadBits, bool invertY, ImageFormat::BayerAlgorithm bayerAlg) {
+ uint8* dst = static_cast<uint8*>(dstBytes[0]);
+ const uint8* src = static_cast<const uint8*>(srcBytes[0]);
+ for (int y = 0; y < srcHeight; ++y) {
+ for (int x = 0; x < srcWidth; ++x) {
+ int i = (invertY) ? ((srcHeight-1-y) * srcWidth +x) : (y * srcWidth + x);
+ int i3 = i * 3;
+ dst[i3 + 0] = src[i3 + 2];
+ dst[i3 + 1] = src[i3 + 1];
+ dst[i3 + 2] = src[i3 + 0];
+ }
+ }
+}
+
+static void bgr8_to_rgba8(const Array<const void*>& srcBytes, int srcWidth, int srcHeight, const ImageFormat* srcFormat, int srcRowPadBits, const Array<void*>& dstBytes, const ImageFormat* dstFormat, int dstRowPadBits, bool invertY, ImageFormat::BayerAlgorithm bayerAlg) {
+ uint8* dst = static_cast<uint8*>(dstBytes[0]);
+ const uint8* src = static_cast<const uint8*>(srcBytes[0]);
+ for (int y = 0; y < srcHeight; ++y) {
+ for (int x = 0; x < srcWidth; ++x) {
+ int i = (invertY) ? ((srcHeight-1-y) * srcWidth +x) : (y * srcWidth + x);
+ int i3 = i * 3;
+ int i4 = i3 + i;
+
+ dst[i4 + 0] = src[i3 + 2];
+ dst[i4 + 1] = src[i3 + 1];
+ dst[i4 + 2] = src[i3 + 0];
+ dst[i4 + 3] = 255;
+ }
+ }
+}
+
+static void bgr8_to_rgba32f(const Array<const void*>& srcBytes, int srcWidth, int srcHeight, const ImageFormat* srcFormat, int srcRowPadBits, const Array<void*>& dstBytes, const ImageFormat* dstFormat, int dstRowPadBits, bool invertY, ImageFormat::BayerAlgorithm bayerAlg) {
+ debugAssertM(srcRowPadBits % 8 == 0, "Source row padding must be a multiple of 8 bits for this format");
+
+ int dstIndex = 0;
+ int srcByteOffset = 0;
+ int srcRowPadBytes = srcRowPadBits / 8;
+ Color4* dst = static_cast<Color4*>(dstBytes[0]);
+ const uint8* src = static_cast<const uint8*>(srcBytes[0]);
+
+ for (int y = 0; y < srcHeight; ++y) {
+ if (invertY) {
+ dstIndex = srcWidth * (srcHeight - 1 - y);
+ }
+
+ for (int x = 0; x < srcWidth; ++x, ++dstIndex, srcByteOffset += 3) {
+ const Color3uint8& s = *reinterpret_cast<const Color3uint8*>(src + srcByteOffset);
+ dst[dstIndex] = Color4(Color3(s).bgr(), 1.0f);
+ }
+ srcByteOffset += srcRowPadBytes;
+ }
+}
+
+// RGBA8 ->
+static void rgba8_to_rgb8(const Array<const void*>& srcBytes, int srcWidth, int srcHeight, const ImageFormat* srcFormat, int srcRowPadBits, const Array<void*>& dstBytes, const ImageFormat* dstFormat, int dstRowPadBits, bool invertY, ImageFormat::BayerAlgorithm bayerAlg) {
+ uint8* dst = static_cast<uint8*>(dstBytes[0]);
+ const uint8* src = static_cast<const uint8*>(srcBytes[0]);
+ for (int y = 0; y < srcHeight; ++y) {
+ for (int x = 0; x < srcWidth; ++x) {
+ int i = (invertY) ? ((srcHeight-1-y) * srcWidth +x) : (y * srcWidth + x);
+ int i3 = i * 3;
+ int i4 = i3 + i;
+
+ dst[i3 + 0] = src[i4 + 0];
+ dst[i3 + 1] = src[i4 + 1];
+ dst[i3 + 2] = src[i4 + 2];
+ }
+ }
+}
+
+static void rgba8_to_bgr8(const Array<const void*>& srcBytes, int srcWidth, int srcHeight, const ImageFormat* srcFormat, int srcRowPadBits, const Array<void*>& dstBytes, const ImageFormat* dstFormat, int dstRowPadBits, bool invertY, ImageFormat::BayerAlgorithm bayerAlg) {
+ uint8* dst = static_cast<uint8*>(dstBytes[0]);
+ const uint8* src = static_cast<const uint8*>(srcBytes[0]);
+ for (int y = 0; y < srcHeight; ++y) {
+ for (int x = 0; x < srcWidth; ++x) {
+ int i = (invertY) ? ((srcHeight-1-y) * srcWidth +x) : (y * srcWidth + x);
+ int i3 = i * 3;
+ int i4 = i3 + i;
+
+ dst[i3 + 0] = src[i4 + 2];
+ dst[i3 + 1] = src[i4 + 1];
+ dst[i3 + 2] = src[i4 + 0];
+ }
+ }
+}
+
+static void rgba8_to_rgba32f(const Array<const void*>& srcBytes, int srcWidth, int srcHeight, const ImageFormat* srcFormat, int srcRowPadBits, const Array<void*>& dstBytes, const ImageFormat* dstFormat, int dstRowPadBits, bool invertY, ImageFormat::BayerAlgorithm bayerAlg) {
+ debugAssertM(srcRowPadBits % 8 == 0, "Source row padding must be a multiple of 8 bits for this format");
+
+ int dstIndex = 0;
+ int srcByteOffset = 0;
+ int srcRowPadBytes = srcRowPadBits / 8;
+ Color4* dst = static_cast<Color4*>(dstBytes[0]);
+ const uint8* src = static_cast<const uint8*>(srcBytes[0]);
+
+ for (int y = 0; y < srcHeight; ++y) {
+ if (invertY) {
+ dstIndex = srcWidth * (srcHeight - 1 - y);
+ }
+
+ for (int x = 0; x < srcWidth; ++x, ++dstIndex, srcByteOffset += 4) {
+ const Color4uint8& s = *reinterpret_cast<const Color4uint8*>(src + srcByteOffset);
+ dst[dstIndex] = Color4(s);
+ }
+ srcByteOffset += srcRowPadBytes;
+ }
+}
+
+// RGB32F ->
+static void rgb32f_to_rgba32f(const Array<const void*>& srcBytes, int srcWidth, int srcHeight, const ImageFormat* srcFormat, int srcRowPadBits, const Array<void*>& dstBytes, const ImageFormat* dstFormat, int dstRowPadBits, bool invertY, ImageFormat::BayerAlgorithm bayerAlg) {
+ debugAssertM(srcRowPadBits % 8 == 0, "Source row padding must be a multiple of 8 bits for this format");
+
+ int dstIndex = 0;
+ int srcByteOffset = 0;
+ int srcRowPadBytes = srcRowPadBits / 8;
+ Color4* dst = static_cast<Color4*>(dstBytes[0]);
+ const uint8* src = static_cast<const uint8*>(srcBytes[0]);
+
+ for (int y = 0; y < srcHeight; ++y) {
+ if (invertY) {
+ dstIndex = srcWidth * (srcHeight - 1 - y);
+ }
+
+ for (int x = 0; x < srcWidth; ++x, ++dstIndex, srcByteOffset += 3 * sizeof(float)) {
+ const Color3& s = *reinterpret_cast<const Color3*>(src + srcByteOffset);
+ dst[dstIndex] = Color4(Color3(s), 1.0f);
+ }
+ srcByteOffset += srcRowPadBytes;
+ }
+}
+
+// RGBA32F ->
+static void rgba32f_to_rgb8(const Array<const void*>& srcBytes, int srcWidth, int srcHeight, const ImageFormat* srcFormat, int srcRowPadBits, const Array<void*>& dstBytes, const ImageFormat* dstFormat, int dstRowPadBits, bool invertY, ImageFormat::BayerAlgorithm bayerAlg) {
+ debugAssertM(dstRowPadBits % 8 == 0, "Destination row padding must be a multiple of 8 bits for this format");
+
+ int srcIndex = 0;
+ int dstByteOffset = 0;
+ int dstRowPadBytes = dstRowPadBits / 8;
+ uint8* dst = static_cast<uint8*>(dstBytes[0]);
+ const Color4* src = static_cast<const Color4*>(srcBytes[0]);
+
+ for (int y = 0; y < srcHeight; ++y) {
+ if (invertY) {
+ srcIndex = srcWidth * (srcHeight - y - 1);
+ }
+
+ for (int x = 0; x < srcWidth; ++x, ++srcIndex, dstByteOffset += 3) {
+ Color3uint8& d = *reinterpret_cast<Color3uint8*>(dst + dstByteOffset);
+ const Color4& s = src[srcIndex];
+
+ d = Color3uint8(s.rgb());
+ }
+ dstByteOffset += dstRowPadBytes;
+ }
+}
+
+static void rgba32f_to_rgba8(const Array<const void*>& srcBytes, int srcWidth, int srcHeight, const ImageFormat* srcFormat, int srcRowPadBits, const Array<void*>& dstBytes, const ImageFormat* dstFormat, int dstRowPadBits, bool invertY, ImageFormat::BayerAlgorithm bayerAlg) {
+ debugAssertM(dstRowPadBits % 8 == 0, "Destination row padding must be a multiple of 8 bits for this format");
+
+ int srcIndex = 0;
+ int dstByteOffset = 0;
+ int dstRowPadBytes = dstRowPadBits / 8;
+ uint8* dst = static_cast<uint8*>(dstBytes[0]);
+ const Color4* src = static_cast<const Color4*>(srcBytes[0]);
+
+ for (int y = 0; y < srcHeight; ++y) {
+ if (invertY) {
+ srcIndex = srcWidth * (srcHeight - 1 - y);
+ }
+ for (int x = 0; x < srcWidth; ++x, ++srcIndex, dstByteOffset += 4) {
+ Color4uint8& d = *reinterpret_cast<Color4uint8*>(dst + dstByteOffset);
+ const Color4& s = src[srcIndex];
+
+ d = Color4uint8(s);
+ }
+ dstByteOffset += dstRowPadBytes;
+ }
+}
+
+static void rgba32f_to_bgr8(const Array<const void*>& srcBytes, int srcWidth, int srcHeight, const ImageFormat* srcFormat, int srcRowPadBits, const Array<void*>& dstBytes, const ImageFormat* dstFormat, int dstRowPadBits, bool invertY, ImageFormat::BayerAlgorithm bayerAlg) {
+ debugAssertM(dstRowPadBits % 8 == 0, "Destination row padding must be a multiple of 8 bits for this format");
+
+ int srcIndex = 0;
+ int dstByteOffset = 0;
+ int dstRowPadBytes = dstRowPadBits / 8;
+ uint8* dst = static_cast<uint8*>(dstBytes[0]);
+ const Color4* src = static_cast<const Color4*>(srcBytes[0]);
+
+ for (int y = 0; y < srcHeight; ++y) {
+ if (invertY) {
+ srcIndex = srcWidth * (srcHeight - y - 1);
+ }
+
+ for (int x = 0; x < srcWidth; ++x, ++srcIndex, dstByteOffset += 3) {
+ Color3uint8& d = *reinterpret_cast<Color3uint8*>(dst + dstByteOffset);
+ const Color4& s = src[srcIndex];
+
+ d = Color3uint8(s.rgb()).bgr();
+ }
+ dstByteOffset += dstRowPadBytes;
+ }
+}
+
+static void rgba32f_to_rgb32f(const Array<const void*>& srcBytes, int srcWidth, int srcHeight, const ImageFormat* srcFormat, int srcRowPadBits, const Array<void*>& dstBytes, const ImageFormat* dstFormat, int dstRowPadBits, bool invertY, ImageFormat::BayerAlgorithm bayerAlg) {
+ debugAssertM(dstRowPadBits % 8 == 0, "Destination row padding must be a multiple of 8 bits for this format");
+
+ int srcIndex = 0;
+ int dstByteOffset = 0;
+ int dstRowPadBytes = dstRowPadBits / 8;
+ uint8* dst = static_cast<uint8*>(dstBytes[0]);
+ const Color4* src = static_cast<const Color4*>(srcBytes[0]);
+
+ for (int y = 0; y < srcHeight; ++y) {
+ if (invertY) {
+ srcIndex = srcWidth * (srcHeight - 1 - y);
+ }
+ for (int x = 0; x < srcWidth; ++x, ++srcIndex, dstByteOffset += 3 * sizeof(float)) {
+ Color3& d = *reinterpret_cast<Color3*>(dst + dstByteOffset);
+ const Color4& s = src[srcIndex];
+ d = Color3(s);
+ }
+ dstByteOffset += dstRowPadBytes;
+ }
+}
+
+// *******************
+// RGB <-> YUV color space conversions
+// *******************
+
+static uint32 blendPixels(uint32 pixel1, uint32 pixel2) {
+ static const uint32 rbMask = 0x00FF00FF;
+ static const uint32 agMask = 0xFF00FF00;
+
+ // Compute two color channels at a time. Use >> 1 for fast division by two
+ // Using alternating color channels prevents overflow
+ const uint32 rb = ((pixel1 & rbMask) + (pixel2 & rbMask)) >> 1;
+
+ // Shift first to avoid overflow in alpha channel
+ const uint32 ag = (((pixel1 & agMask) >> 1) + ((pixel2 & agMask) >> 1));
+
+ return ((rb & rbMask) | (ag & agMask));
+}
+
+#define PIXEL_RGB8_TO_YUV_Y(r, g, b) static_cast<uint8>(iClamp(((66 * r + 129 * g + 25 * b + 128) >> 8) + 16, 0, 255))
+#define PIXEL_RGB8_TO_YUV_U(r, g, b) static_cast<uint8>(iClamp(((-38 * r - 74 * g + 112 * b + 128) >> 8) + 128, 0, 255))
+#define PIXEL_RGB8_TO_YUV_V(r, g, b) static_cast<uint8>(iClamp(((112 * r - 94 * g - 18 * b + 128) >> 8) + 128, 0, 255))
+
+static void rgb8_to_yuv420p(const Array<const void*>& srcBytes, int srcWidth, int srcHeight, const ImageFormat* srcFormat, int srcRowPadBits, const Array<void*>& dstBytes, const ImageFormat* dstFormat, int dstRowPadBits, bool invertY, ImageFormat::BayerAlgorithm bayerAlg) {
+ debugAssertM(srcRowPadBits == 0, "Source row padding must be 0 for this format");
+ debugAssertM((srcWidth % 2 == 0) && (srcHeight % 2 == 0), "Source width and height must be a multiple of two");
+
+ const Color3uint8* src = static_cast<const Color3uint8*>(srcBytes[0]);
+
+ uint8* dstY = static_cast<uint8*>(dstBytes[0]);
+ uint8* dstU = static_cast<uint8*>(dstBytes[1]);
+ uint8* dstV = static_cast<uint8*>(dstBytes[2]);
+
+ for (int y = 0; y < srcHeight; y += 2) {
+ for (int x = 0; x < srcWidth; x += 2) {
+
+ // convert 4-pixel block at a time
+ int srcPixelOffset0 = y * srcWidth + x;
+ int srcPixelOffset1 = srcPixelOffset0 + 1;
+ int srcPixelOffset2 = srcPixelOffset0 + srcWidth;
+ int srcPixelOffset3 = srcPixelOffset2 + 1;
+
+ int yIndex = y * srcWidth + x;
+
+ dstY[yIndex] = PIXEL_RGB8_TO_YUV_Y(src[srcPixelOffset0].r, src[srcPixelOffset0].g, src[srcPixelOffset0].b);
+ dstY[yIndex + 1] = PIXEL_RGB8_TO_YUV_Y(src[srcPixelOffset1].r, src[srcPixelOffset1].g, src[srcPixelOffset1].b);
+
+ yIndex += srcWidth;
+ dstY[yIndex] = PIXEL_RGB8_TO_YUV_Y(src[srcPixelOffset2].r, src[srcPixelOffset2].g, src[srcPixelOffset2].b);
+ dstY[yIndex + 1] = PIXEL_RGB8_TO_YUV_Y(src[srcPixelOffset3].r, src[srcPixelOffset3].g, src[srcPixelOffset3].b);
+
+ uint32 blendedPixel = blendPixels(src[srcPixelOffset0].asUInt32(), src[srcPixelOffset2].asUInt32());
+ Color3uint8 uvSrcColor = Color3uint8::fromARGB(blendedPixel);
+
+ int uvIndex = y / 2 * srcWidth / 2 + x / 2;
+ dstU[uvIndex] = PIXEL_RGB8_TO_YUV_U(uvSrcColor.r, uvSrcColor.g, uvSrcColor.b);
+ dstV[uvIndex] = PIXEL_RGB8_TO_YUV_V(uvSrcColor.r, uvSrcColor.g, uvSrcColor.b);
+ }
+ }
+}
+
+static void rgb8_to_yuv422(const Array<const void*>& srcBytes, int srcWidth, int srcHeight, const ImageFormat* srcFormat, int srcRowPadBits, const Array<void*>& dstBytes, const ImageFormat* dstFormat, int dstRowPadBits, bool invertY, ImageFormat::BayerAlgorithm bayerAlg) {
+ debugAssertM(srcRowPadBits == 0, "Source row padding must be 0 for this format");
+ debugAssertM((srcWidth % 2 == 0), "Source width must be a multiple of two");
+
+ const Color3uint8* src = static_cast<const Color3uint8*>(srcBytes[0]);
+
+ uint8* dst = static_cast<uint8*>(dstBytes[0]);
+
+ for (int y = 0; y < srcHeight; ++y) {
+ for (int x = 0; x < srcWidth; x += 2) {
+
+ // convert 2-pixel horizontal block at a time
+ int srcIndex = y * srcWidth + x;
+ int dstIndex = srcIndex * 2;
+
+ uint32 blendedPixel = blendPixels(src[srcIndex].asUInt32(), src[srcIndex + 1].asUInt32());
+ Color3uint8 uvSrcColor = Color3uint8::fromARGB(blendedPixel);
+
+ dst[dstIndex] = PIXEL_RGB8_TO_YUV_Y(src[srcIndex].r, src[srcIndex].g, src[srcIndex].b);
+
+ dst[dstIndex + 1] = PIXEL_RGB8_TO_YUV_U(uvSrcColor.r, uvSrcColor.g, uvSrcColor.b);
+
+ dst[dstIndex + 2] = PIXEL_RGB8_TO_YUV_Y(src[srcIndex + 1].r, src[srcIndex + 1].g, src[srcIndex + 1].b);
+
+ dst[dstIndex + 3] = PIXEL_RGB8_TO_YUV_V(uvSrcColor.r, uvSrcColor.g, uvSrcColor.b);
+
+ }
+ }
+}
+
+static void rgb8_to_yuv444(const Array<const void*>& srcBytes, int srcWidth, int srcHeight, const ImageFormat* srcFormat, int srcRowPadBits, const Array<void*>& dstBytes, const ImageFormat* dstFormat, int dstRowPadBits, bool invertY, ImageFormat::BayerAlgorithm bayerAlg) {
+ debugAssertM(srcRowPadBits == 0, "Source row padding must be 0 for this format");
+
+ const Color3uint8* src = static_cast<const Color3uint8*>(srcBytes[0]);
+
+ Color3uint8* dst = static_cast<Color3uint8*>(dstBytes[0]);
+
+ for (int y = 0; y < srcHeight; ++y) {
+ for (int x = 0; x < srcWidth; ++x) {
+
+ // convert 1-pixels at a time
+ int index = y * srcWidth + x;
+ uint8 y = PIXEL_RGB8_TO_YUV_Y(src[index].r, src[index].g, src[index].b);
+ uint8 u = PIXEL_RGB8_TO_YUV_U(src[index].r, src[index].g, src[index].b);
+ uint8 v = PIXEL_RGB8_TO_YUV_V(src[index].r, src[index].g, src[index].b);
+
+ dst[index].r = y;
+ dst[index].g = u;
+ dst[index].b = v;
+ }
+ }
+}
+
+
+#define PIXEL_YUV_TO_RGB8_R(y, u, v) static_cast<uint8>(iClamp((298 * (y - 16) + 409 * (v - 128) + 128) >> 8, 0, 255))
+#define PIXEL_YUV_TO_RGB8_G(y, u, v) static_cast<uint8>(iClamp((298 * (y - 16) - 100 * (u - 128) - 208 * (v - 128) + 128) >> 8, 0, 255))
+#define PIXEL_YUV_TO_RGB8_B(y, u, v) static_cast<uint8>(iClamp((298 * (y - 16) + 516 * (u - 128) + 128) >> 8, 0, 255))
+
+static void yuv420p_to_rgb8(const Array<const void*>& srcBytes, int srcWidth, int srcHeight, const ImageFormat* srcFormat, int srcRowPadBits, const Array<void*>& dstBytes, const ImageFormat* dstFormat, int dstRowPadBits, bool invertY, ImageFormat::BayerAlgorithm bayerAlg) {
+ debugAssertM(srcRowPadBits == 0, "Source row padding must be 0 for this format");
+ debugAssertM((srcWidth % 2 == 0) && (srcHeight % 2 == 0), "Source width and height must be a multiple of two");
+
+ const uint8* srcY = static_cast<const uint8*>(srcBytes[0]);
+ const uint8* srcU = static_cast<const uint8*>(srcBytes[1]);
+ const uint8* srcV = static_cast<const uint8*>(srcBytes[2]);
+
+ Color3uint8* dst = static_cast<Color3uint8*>(dstBytes[0]);
+
+ for (int y = 0; y < srcHeight; ++y) {
+ for (int x = 0; x < srcWidth; x += 2) {
+
+ // convert to two rgb pixels in a row
+ Color3uint8* rgb = &dst[y * srcWidth + x];
+
+ int yOffset = y * srcWidth + x;
+ int uvOffset = y / 2 * srcWidth / 2 + x / 2;
+
+ rgb->r = PIXEL_YUV_TO_RGB8_R(srcY[yOffset], srcU[uvOffset], srcV[uvOffset]);
+ rgb->g = PIXEL_YUV_TO_RGB8_G(srcY[yOffset], srcU[uvOffset], srcV[uvOffset]);
+ rgb->b = PIXEL_YUV_TO_RGB8_B(srcY[yOffset], srcU[uvOffset], srcV[uvOffset]);
+
+ rgb += 1;
+ rgb->r = PIXEL_YUV_TO_RGB8_R(srcY[yOffset + 1], srcU[uvOffset], srcV[uvOffset]);
+ rgb->g = PIXEL_YUV_TO_RGB8_G(srcY[yOffset + 1], srcU[uvOffset], srcV[uvOffset]);
+ rgb->b = PIXEL_YUV_TO_RGB8_B(srcY[yOffset + 1], srcU[uvOffset], srcV[uvOffset]);
+ }
+ }
+}
+
+static void yuv422_to_rgb8(const Array<const void*>& srcBytes, int srcWidth, int srcHeight, const ImageFormat* srcFormat, int srcRowPadBits, const Array<void*>& dstBytes, const ImageFormat* dstFormat, int dstRowPadBits, bool invertY, ImageFormat::BayerAlgorithm bayerAlg) {
+ debugAssertM(srcRowPadBits == 0, "Source row padding must be 0 for this format");
+ debugAssertM((srcWidth % 2 == 0), "Source width must be a multiple of two");
+
+ const uint8* src = static_cast<const uint8*>(srcBytes[0]);
+
+ Color3uint8* dst = static_cast<Color3uint8*>(dstBytes[0]);
+
+ for (int y = 0; y < srcHeight; ++y) {
+ for (int x = 0; x < srcWidth; x += 2) {
+
+ // convert to two rgb pixels in a row
+ Color3uint8* rgb = &dst[y * srcWidth + x];
+
+ int srcIndex = (y * srcWidth + x) * 2;
+ uint8 y = src[srcIndex];
+ uint8 u = src[srcIndex + 1];
+ uint8 y2 = src[srcIndex + 2];
+ uint8 v = src[srcIndex + 3];
+
+ rgb->r = PIXEL_YUV_TO_RGB8_R(y, u, v);
+ rgb->g = PIXEL_YUV_TO_RGB8_G(y, u, v);
+ rgb->b = PIXEL_YUV_TO_RGB8_B(y, u, v);
+
+ rgb += 1;
+ rgb->r = PIXEL_YUV_TO_RGB8_R(y2, u, v);
+ rgb->g = PIXEL_YUV_TO_RGB8_G(y2, u, v);
+ rgb->b = PIXEL_YUV_TO_RGB8_B(y2, u, v);
+ }
+ }
+}
+
+static void yuv444_to_rgb8(const Array<const void*>& srcBytes, int srcWidth, int srcHeight, const ImageFormat* srcFormat, int srcRowPadBits, const Array<void*>& dstBytes, const ImageFormat* dstFormat, int dstRowPadBits, bool invertY, ImageFormat::BayerAlgorithm bayerAlg) {
+ debugAssertM(srcRowPadBits == 0, "Source row padding must be 0 for this format");
+
+ const Color3uint8* src = static_cast<const Color3uint8*>(srcBytes[0]);
+
+ Color3uint8* dst = static_cast<Color3uint8*>(dstBytes[0]);
+
+ for (int y = 0; y < srcHeight; ++y) {
+ for (int x = 0; x < srcWidth; ++x) {
+
+ // convert to one rgb pixels at a time
+ int index = y * srcWidth + x;
+ Color3uint8* rgb = &dst[index];
+
+ rgb->r = PIXEL_YUV_TO_RGB8_R(src[index].r, src[index].g, src[index].b);
+ rgb->g = PIXEL_YUV_TO_RGB8_G(src[index].r, src[index].g, src[index].b);
+ rgb->b = PIXEL_YUV_TO_RGB8_B(src[index].r, src[index].g, src[index].b);
+ }
+ }
+}
+
+////////////////////////////////////////////////////////////////////////////////////////////////
+//
+// Bayer conversions
+//
+
+// There are two kinds of rows (GR and BG).
+// In each row, there are two kinds of pixels (G/R, B/G).
+// We express the four kinds of INPUT pixels as:
+// GRG, GRG, BGB, BGG
+//
+// There are three kinds of OUTPUT pixels: R, G, B.
+// Thus there are nominally 12 different I/O combinations,
+// but several are impulses because needed output at that
+// location *is* the input (e.g., G_GRG and G_BGG).
+//
+// The following 5x5 row-major filters are named as output_input.
+
+// Green
+static const float G_GRR[5][5] =
+ {{ 0.0f, 0.0f, -1.0f, 0.0f, 0.0f},
+ { 0.0f, 0.0f, 2.0f, 0.0f, 0.0f},
+ { -1.0f, 2.0f, 4.0f, 2.0f, -1.0f},
+ { 0.0f, 0.0f, 2.0f, 0.0f, 0.0f},
+ { 0.0f, 0.0f, -1.0f, 0.0f, 0.0f}};
+
+static const float G_BGB[5][5] =
+ {{ 0.0f, 0.0f, -1.0f, 0.0f, 0.0f},
+ { 0.0f, 0.0f, 2.0f, 0.0f, 0.0f},
+ { -1.0f, 2.0f, 4.0f, 2.0f, -1.0f},
+ { 0.0f, 0.0f, 2.0f, 0.0f, 0.0f},
+ { 0.0f, 0.0f, -1.0f, 0.0f, 0.0f}};
+
+// Red
+//(the caption in the paper is wrong for this case:
+// "R row B column really means R row G column"
+static const float R_GRG[5][5] =
+ {{ 0.0f, 0.0f, 0.5f, 0.0f, 0.0f},
+ { 0.0f, -1.0f, 0.0f, -1.0f, 0.0f},
+ { -1.0f, 4.0f, 5.0f, 4.0f, -1.0f},
+ { 0.0f, -1.0f, 0.0f, -1.0f, 0.0f},
+ { 0.0f, 0.0f, 0.5f, 0.0f, 0.0f}};
+
+static const float R_BGG[5][5] =
+ {{ 0.0f, 0.0f, -1.0f, 0.0f, 0.0f},
+ { 0.0f, -1.0f, 4.0f, -1.0f, 0.0f},
+ { 0.5f, 0.0f, 5.0f, 0.0f, 0.5f},
+ { 0.0f, -1.0f, 4.0f, -1.0f, 0.0f},
+ { 0.0f, 0.0f, -1.0f, 0.0f, 0.0f}};
+
+static const float R_BGB[5][5] =
+ {{ 0.0f, 0.0f, -3.0f/2.0f, 0.0f, 0.0f},
+ { 0.0f, 2.0f, 0.0f, 2.0f, 0.0f},
+ {-3.0f/2.0f, 0.0f, 6.0f, 0.0f, -3.0f/2.0f},
+ { 0.0f, 2.0f, 0.0f, 2.0f, 0.0f},
+ { 0.0f, 0.0f, -3.0f/2.0f, 0.0f, 0.0f}};
+
+
+// Blue
+//(the caption in the paper is wrong for this case:
+// "B row R column really means B row G column")
+#define B_BGG R_GRG
+#define B_GRG R_BGG
+#define B_GRR R_BGB
+
+// =====================================================================
+// Helper methods
+// =====================================================================
+
+
+/** Applies a 5x5 filter to monochrome image I (wrapping at the boundaries) */
+static uint8 applyFilter(const uint8* I,
+ int x,
+ int y,
+ int w,
+ int h,
+ const float filter[5][5]) {
+
+ debugAssert(isEven(w));
+ debugAssert(isEven(h));
+
+ float sum = 0.0f;
+ float denom = 0.0f;
+
+ for (int dy = 0; dy < 5; ++dy) {
+ int offset = ((y + dy + h - 2) % h) * w;
+
+ for (int dx = 0; dx < 5; ++dx) {
+ float f = filter[dy][dx];
+ sum += f * I[((x + dx + w - 2) % w) + offset];
+ denom += f;
+ }
+ }
+
+ return (uint8)iClamp(iRound(sum / denom), 0, 255);
+}
+
+/** Helper method for Bayer grbg and bggr --> rgb8 */
+static void swapRedAndBlue(int N, Color3uint8* out) {
+ for (int i = N - 1; i >= 0; --i) {
+ uint8 tmp = out[i].r;
+ out[i].r = out[i].b;
+ out[i].b = tmp;
+ }
+}
+
+// RGB -> BAYER color space
+
+// =====================================================================
+// rgb8 --> bayer helpers
+// =====================================================================
+static void rgb8_to_bayer_rggb8(const int w, const int h,
+ const uint8* src, uint8* dst) {
+ Color3uint8* srcColor = (Color3uint8*)src;
+ Color1uint8* dstColor = (Color1uint8*)dst;
+
+ // Top row pixels
+ for (int y = 0; y < h - 1; y += 2) {
+ int offset = y * w;
+
+ // Top left pixels
+ for(int x = 0; x < w - 1; x += 2) {
+ dstColor[x + offset] = Color1(srcColor[x + offset].r);
+ }
+
+ // Top right pixels
+ for(int x = 1; x < w - 1; x += 2) {
+ dstColor[x + offset] = Color1(srcColor[x + offset].g);
+ }
+ }
+
+ // Bottom row pixels
+ for (int y = 1; y < h - 1; y += 2) {
+ int offset = y * w;
+
+ // Bottom left pixels
+ for (int x = 0; x < w - 1; x += 2) {
+ dstColor[x + offset] = Color1(srcColor[x + offset].g);
+ }
+
+ // Bottom right pixels
+ for (int x = 1; x < w - 1; x += 2) {
+ dstColor[x + offset] = Color1(srcColor[x + offset].b);
+ }
+ }
+}
+
+
+static void rgb8_to_bayer_grbg8(const int w, const int h,
+ const uint8* src, uint8* dst) {
+ Color3uint8* srcColor = (Color3uint8*)src;
+ Color1uint8* dstColor = (Color1uint8*)dst;
+
+ // Top row pixels
+ for (int y = 0; y < h - 1; y += 2) {
+ int offset = y * w;
+
+ // Top left pixels
+ for (int x = 0; x < w - 1; x += 2) {
+ dstColor[x + offset] = Color1(srcColor[x + offset].g);
+ }
+
+ // Top right pixels
+ for (int x = 1; x < w - 1; x += 2) {
+ dstColor[x + offset] = Color1(srcColor[x + offset].r);
+ }
+ }
+
+ // Bottom row pixels
+ for (int y = 1; y < h - 1; y += 2) {
+ int offset = y * w;
+
+ // Bottom left pixels
+ for (int x = 0; x < w - 1; x += 2) {
+ dstColor[x + offset] = Color1(srcColor[x + offset].b);
+ }
+
+ // Bottom right pixels
+ for (int x = 1; x < w - 1; x += 2) {
+ dstColor[x + offset] = Color1(srcColor[x + offset].g);
+ }
+ }
+}
+
+
+static void rgb8_to_bayer_bggr8(const int w, const int h,
+ const uint8* src, uint8* dst) {
+ Color3uint8* srcColor = (Color3uint8*)src;
+ Color1uint8* dstColor = (Color1uint8*)dst;
+
+ // Top row pixels
+ for (int y = 0; y < h - 1; y += 2) {
+ int offset = y * w;
+
+ // Top left pixels
+ for (int x = 0; x < w - 1; x += 2) {
+ dstColor[x + offset] = Color1(srcColor[x + offset].b);
+ }
+
+ // Top right pixels
+ for (int x = 1; x < w - 1; x += 2) {
+ dstColor[x + offset] = Color1(srcColor[x + offset].g);
+ }
+ }
+
+ // Bottom row pixels
+ for (int y = 1; y < h - 1; y += 2) {
+ int offset = y * w;
+
+ // Bottom left pixels
+ for(int x = 0; x < w - 1; x += 2) {
+ dstColor[x + offset] = Color1(srcColor[x + offset].g);
+ }
+
+ // Bottom right pixels
+ for(int x = 1; x < w - 1; x += 2) {
+ dstColor[x + offset] = Color1(srcColor[x + offset].r);
+ }
+ }
+}
+
+
+static void rgb8_to_bayer_gbrg8(const int w, const int h,
+ const uint8* src, uint8* dst) {
+ Color3uint8* srcColor = (Color3uint8*)src;
+ Color1uint8* dstColor = (Color1uint8*)dst;
+
+ // Top row pixels
+ for(int y = 0; y < h - 1; y += 2) {
+ int offset = y * w;
+
+ // Top left pixels
+ for(int x = 0; x < w - 1; x += 2) {
+ dstColor[x + offset] = Color1(srcColor[x + offset].g);
+ }
+
+ // Top right pixels
+ for(int x = 1; x < w - 1; x += 2) {
+ dstColor[x + offset] = Color1(srcColor[x + offset].b);
+ }
+ }
+
+ // Bottom row pixels
+ for(int y = 1; y < h - 1; y += 2) {
+ int offset = y * w;
+
+ // Bottom left pixels
+ for(int x = 0; x < w - 1; x += 2) {
+ dstColor[x + offset] = Color1(srcColor[x + offset].r);
+ }
+
+ // Bottom right pixels
+ for(int x = 1; x < w - 1; x += 2) {
+ dstColor[x + offset] = Color1(srcColor[x + offset].g);
+ }
+ }
+}
+
+// =====================================================================
+// rgba32f (-->rgb8) --> bayer converter implementations
+// =====================================================================
+static void rgba32f_to_bayer_rggb8(const Array<const void*>& srcBytes, int srcWidth, int srcHeight, const ImageFormat* srcFormat, int srcRowPadBits, const Array<void*>& dstBytes, const ImageFormat* dstFormat, int dstRowPadBits, bool invertY, ImageFormat::BayerAlgorithm bayerAlg) {
+ Array<void*> tmp;
+ tmp.append(System::malloc(srcWidth * srcHeight * sizeof(Color3uint8)));
+
+ rgba32f_to_rgb8(srcBytes, srcWidth, srcHeight, ImageFormat::RGBA32F(), 0, tmp, ImageFormat::RGB8(), 0, invertY, bayerAlg);
+ rgb8_to_bayer_rggb8(srcWidth, srcHeight, static_cast<uint8*>(tmp[0]), static_cast<uint8*>(dstBytes[0]));
+
+ System::free(tmp[0]);
+}
+
+static void rgba32f_to_bayer_gbrg8(const Array<const void*>& srcBytes, int srcWidth, int srcHeight, const ImageFormat* srcFormat, int srcRowPadBits, const Array<void*>& dstBytes, const ImageFormat* dstFormat, int dstRowPadBits, bool invertY, ImageFormat::BayerAlgorithm bayerAlg) {
+ Array<void*> tmp;
+ tmp.append(System::malloc(srcWidth * srcHeight * sizeof(Color3uint8)));
+
+ rgba32f_to_rgb8(srcBytes, srcWidth, srcHeight, ImageFormat::RGBA32F(), 0, tmp, ImageFormat::RGB8(), 0, invertY, bayerAlg);
+ rgb8_to_bayer_grbg8(srcWidth, srcHeight, static_cast<uint8*>(tmp[0]), static_cast<uint8*>(dstBytes[0]));
+
+ System::free(tmp[0]);
+}
+
+static void rgba32f_to_bayer_grbg8(const Array<const void*>& srcBytes, int srcWidth, int srcHeight, const ImageFormat* srcFormat, int srcRowPadBits, const Array<void*>& dstBytes, const ImageFormat* dstFormat, int dstRowPadBits, bool invertY, ImageFormat::BayerAlgorithm bayerAlg) {
+ Array<void*> tmp;
+ tmp.append(System::malloc(srcWidth * srcHeight * sizeof(Color3uint8)));
+
+ rgba32f_to_rgb8(srcBytes, srcWidth, srcHeight, ImageFormat::RGBA32F(), 0, tmp, ImageFormat::RGB8(), 0, invertY, bayerAlg);
+ rgb8_to_bayer_gbrg8(srcWidth, srcHeight, static_cast<uint8*>(tmp[0]), static_cast<uint8*>(dstBytes[0]));
+
+ System::free(tmp[0]);
+}
+
+static void rgba32f_to_bayer_bggr8(const Array<const void*>& srcBytes, int srcWidth, int srcHeight, const ImageFormat* srcFormat, int srcRowPadBits, const Array<void*>& dstBytes, const ImageFormat* dstFormat, int dstRowPadBits, bool invertY, ImageFormat::BayerAlgorithm bayerAlg) {
+ Array<void*> tmp;
+ tmp.append(System::malloc(srcWidth * srcHeight * sizeof(Color3uint8)));
+
+ rgba32f_to_rgb8(srcBytes, srcWidth, srcHeight, ImageFormat::RGBA32F(), 0, tmp, ImageFormat::RGB8(), 0, invertY, bayerAlg);
+ rgb8_to_bayer_bggr8(srcWidth, srcHeight, static_cast<uint8*>(tmp[0]), static_cast<uint8*>(dstBytes[0]));
+
+ System::free(tmp[0]);
+}
+
+// BAYER -> RGB color space
+
+// =====================================================================
+// bayer --> rgb8 helpers
+// =====================================================================
+static void bayer_rggb8_to_rgb8_mhc(int w, int h,
+ const uint8* in, uint8* _out) {
+ debugAssert(in != _out);
+
+ Color3uint8* out = (Color3uint8*)_out;
+
+ for (int y = 0; y < h; ++y) {
+
+ // Row beginning in the input array.
+ int offset = y * w;
+
+ // RG row
+ for (int x = 0; x < w; ++x, ++out) {
+ // R pixel
+ {
+ out->r = in[x + offset];
+ out->g = applyFilter(in, x, y, w, h, G_GRR);
+ out->b = applyFilter(in, x, y, w, h, B_GRR);
+ }
+ ++x; ++out;
+
+ // G pixel
+ {
+ out->r = applyFilter(in, x, y, w, h, R_GRG);
+ out->g = in[x + offset];
+ out->b = applyFilter(in, x, y, w, h, B_GRG);
+ }
+ }
+
+ ++y;
+ offset += w;
+
+ // GB row
+ for (int x = 0; x < w; ++x, ++out) {
+ // G pixel
+ {
+ out->r = applyFilter(in, x, y, w, h, R_BGG);
+ out->g = in[x + offset];
+ out->b = applyFilter(in, x, y, w, h, B_BGG);
+ }
+ ++x; ++out;
+
+ // B pixel
+ {
+ out->r = applyFilter(in, x, y, w, h, R_BGB);
+ out->g = applyFilter(in, x, y, w, h, G_BGB);
+ out->b = in[x + offset];
+ }
+ }
+ }
+}
+
+
+
+static void bayer_gbrg8_to_rgb8_mhc(int w, int h,
+ const uint8* in, uint8* _out) {
+
+ debugAssert(in != _out);
+
+ Color3uint8* out = (Color3uint8*)_out;
+
+ for (int y = 0; y < h; ++y) {
+
+ // Row beginning in the input array.
+ int offset = y * w;
+
+ // GB row
+ for (int x = 0; x < w; ++x, ++out) {
+ // G pixel
+ {
+ out->r = applyFilter(in, x, y, w, h, R_BGG);
+ out->g = in[x + offset];
+ out->b = applyFilter(in, x, y, w, h, B_BGG);
+ }
+ ++x; ++out;
+
+ // B pixel
+ {
+ out->r = applyFilter(in, x, y, w, h, R_BGB);
+ out->g = applyFilter(in, x, y, w, h, G_BGB);
+ out->b = in[x + offset];
+ }
+ }
+ }
+}
+
+
+static void bayer_grbg8_to_rgb8_mhc(int w, int h,
+ const uint8* in, uint8* _out) {
+ // Run the equivalent function for red
+ bayer_gbrg8_to_rgb8_mhc(w, h, in, _out);
+
+ // Now swap red and blue
+ swapRedAndBlue(w * h, (Color3uint8*)_out);
+}
+
+
+static void bayer_bggr8_to_rgb8_mhc(int w, int h,
+ const uint8* in, uint8* _out) {
+ // Run the equivalent function for red
+ bayer_rggb8_to_rgb8_mhc(w, h, in, _out);
+
+ // Now swap red and blue
+ swapRedAndBlue(w * h, (Color3uint8*)_out);
+}
+
+// =====================================================================
+// bayer (--> rgb8) --> rgba32f converter implementations
+// =====================================================================
+static void bayer_rggb8_to_rgba32f(const Array<const void*>& srcBytes, int srcWidth, int srcHeight, const ImageFormat* srcFormat, int srcRowPadBits, const Array<void*>& dstBytes, const ImageFormat* dstFormat, int dstRowPadBits, bool invertY, ImageFormat::BayerAlgorithm bayerAlg) {
+ Array<void*> tmp;
+ tmp.append(System::malloc(srcWidth * srcHeight * sizeof(Color3uint8)));
+
+ bayer_rggb8_to_rgb8_mhc(srcWidth, srcHeight, static_cast<const uint8*>(srcBytes[0]), static_cast<uint8*>(tmp[0]));
+ rgb8_to_rgba32f(reinterpret_cast<Array<const void*>&>(tmp), srcWidth, srcHeight, ImageFormat::RGB8(), 0, dstBytes, ImageFormat::RGBA32F(), 0, invertY, bayerAlg);
+
+ System::free(tmp[0]);
+}
+
+static void bayer_gbrg8_to_rgba32f(const Array<const void*>& srcBytes, int srcWidth, int srcHeight, const ImageFormat* srcFormat, int srcRowPadBits, const Array<void*>& dstBytes, const ImageFormat* dstFormat, int dstRowPadBits, bool invertY, ImageFormat::BayerAlgorithm bayerAlg) {
+ Array<void*> tmp;
+ tmp.append(System::malloc(srcWidth * srcHeight * sizeof(Color3uint8)));
+
+ bayer_grbg8_to_rgb8_mhc(srcWidth, srcHeight, static_cast<const uint8*>(srcBytes[0]), static_cast<uint8*>(tmp[0]));
+ rgb8_to_rgba32f(reinterpret_cast<Array<const void*>&>(tmp), srcWidth, srcHeight, ImageFormat::RGB8(), 0, dstBytes, ImageFormat::RGBA32F(), 0, invertY, bayerAlg);
+
+ System::free(tmp[0]);
+}
+
+static void bayer_grbg8_to_rgba32f(const Array<const void*>& srcBytes, int srcWidth, int srcHeight, const ImageFormat* srcFormat, int srcRowPadBits, const Array<void*>& dstBytes, const ImageFormat* dstFormat, int dstRowPadBits, bool invertY, ImageFormat::BayerAlgorithm bayerAlg) {
+ Array<void*> tmp;
+ tmp.append(System::malloc(srcWidth * srcHeight * sizeof(Color3uint8)));
+
+ bayer_gbrg8_to_rgb8_mhc(srcWidth, srcHeight, static_cast<const uint8*>(srcBytes[0]), static_cast<uint8*>(tmp[0]));
+ rgb8_to_rgba32f(reinterpret_cast<Array<const void*>&>(tmp), srcWidth, srcHeight, ImageFormat::RGB8(), 0, dstBytes, ImageFormat::RGBA32F(), 0, invertY, bayerAlg);
+
+ System::free(tmp[0]);
+}
+
+static void bayer_bggr8_to_rgba32f(const Array<const void*>& srcBytes, int srcWidth, int srcHeight, const ImageFormat* srcFormat, int srcRowPadBits, const Array<void*>& dstBytes, const ImageFormat* dstFormat, int dstRowPadBits, bool invertY, ImageFormat::BayerAlgorithm bayerAlg) {
+ Array<void*> tmp;
+ tmp.append(System::malloc(srcWidth * srcHeight * sizeof(Color3uint8)));
+
+ bayer_bggr8_to_rgb8_mhc(srcWidth, srcHeight, static_cast<const uint8*>(srcBytes[0]), static_cast<uint8*>(tmp[0]));
+ rgb8_to_rgba32f(reinterpret_cast<Array<const void*>&>(tmp), srcWidth, srcHeight, ImageFormat::RGB8(), 0, dstBytes, ImageFormat::RGBA32F(), 0, invertY, bayerAlg);
+
+ System::free(tmp[0]);
+}
+
+
+
+
+
+ // TODO: The following region is commented out because so far
+ // those conversions are not used anywhere else. Until it is
+ // decided that such conversions are not needed, this region
+ // remains commented out.
+
+
+// // =====================================================================
+// // bayer --> bgr8
+// // =====================================================================
+
+// static void bayer_rggb8_to_bgr8_mhc(int w, int h,
+// const uint8* in, uint8* _out) {
+// debugAssert(in != _out);
+
+// Color3uint8* out = (Color3uint8*)_out;
+
+// for (int y = 0; y < h; ++y) {
+
+// // Row beginning in the input array.
+// int offset = y * w;
+
+// // RG row
+// for (int x = 0; x < w; ++x, ++out) {
+// // R pixel
+// {
+// out->b = in[x + offset];
+// out->g = applyFilter(in, x, y, w, h, G_GRR);
+// out->r = applyFilter(in, x, y, w, h, B_GRR);
+// }
+// ++x; ++out;
+
+// // G pixel
+// {
+// out->b = applyFilter(in, x, y, w, h, R_GRG);
+// out->g = in[x + offset];
+// out->r = applyFilter(in, x, y, w, h, B_GRG);
+// }
+// }
+
+// ++y;
+// offset += w;
+
+// // GB row
+// for (int x = 0; x < w; ++x, ++out) {
+// // G pixel
+// {
+// out->b = applyFilter(in, x, y, w, h, R_BGG);
+// out->g = in[x + offset];
+// out->r = applyFilter(in, x, y, w, h, B_BGG);
+// }
+// ++x; ++out;
+
+// // B pixel
+// {
+// out->b = applyFilter(in, x, y, w, h, R_BGB);
+// out->g = applyFilter(in, x, y, w, h, G_BGB);
+// out->r = in[x + offset];
+// }
+// }
+// }
+// }
+
+
+// static void bayer_gbrg8_to_bgr8_mhc(int w, int h,
+// const uint8* in, uint8* _out) {
+
+// debugAssert(in != _out);
+
+// Color3uint8* out = (Color3uint8*)_out;
+
+// for (int y = 0; y < h; ++y) {
+
+// // Row beginning in the input array.
+// int offset = y * w;
+
+// // GB row
+// for (int x = 0; x < srcWidth; ++x, ++out) {
+// // G pixel
+// {
+// out->b = applyFilter(in, x, y, w, h, R_BGG);
+// out->g = in[x + offset];
+// out->r = applyFilter(in, x, y, w, h, B_BGG);
+// }
+// ++x; ++out;
+
+// // B pixel
+// {
+// out->b = applyFilter(in, x, y, w, h, R_BGB);
+// out->g = applyFilter(in, x, y, w, h, G_BGB);
+// out->r = in[x + offset];
+// }
+// }
+// }
+// }
+
+// static void bayer_grbg8_to_bgr8_mhc(int w, int h,
+// const uint8* in, uint8* _out) {
+// // Run the equivalent function for red
+// bayer_gbrg8_to_bgr8_mhc(w, h, in, _out);
+
+// // Now swap red and blue
+// swapRedAndBlue(srcWidth * h, (Color3uint8*)_out);
+// }
+
+// static void bayer_bggr8_to_bgr8_mhc(int w, int h,
+// const uint8* in, uint8* _out) {
+// // Run the equivalent function for red
+// bayer_rggb8_to_bgr8_mhc(w, h, in, _out);
+
+// // Now swap red and blue
+// swapRedAndBlue(srcWidth * h, (Color3uint8*)_out);
+// }
+
+
+
+///////////////////////////////////////////////////
+
+} // namespace G3D
diff --git a/externals/g3dlite/G3D.lib/source/Line.cpp b/externals/g3dlite/G3D.lib/source/Line.cpp
new file mode 100644
index 00000000000..195ae7197f2
--- /dev/null
+++ b/externals/g3dlite/G3D.lib/source/Line.cpp
@@ -0,0 +1,89 @@
+/**
+ @file Line.cpp
+
+ Line class
+
+ @maintainer Morgan McGuire, graphics3d.com
+
+ @created 2001-06-02
+ @edited 2006-01-28
+ */
+
+#include "G3D/Line.h"
+#include "G3D/Plane.h"
+
+namespace G3D {
+
+Vector3 Line::intersection(const Plane& plane) const {
+ float d;
+ Vector3 normal = plane.normal();
+ plane.getEquation(normal, d);
+ float rate = _direction.dot(normal);
+
+ if (rate == 0) {
+
+ return Vector3::inf();
+
+ } else {
+ float t = -(d + _point.dot(normal)) / rate;
+
+ return _point + _direction * t;
+ }
+}
+
+
+Line::Line(class BinaryInput& b) {
+ deserialize(b);
+}
+
+
+void Line::serialize(class BinaryOutput& b) const {
+ _point.serialize(b);
+ _direction.serialize(b);
+}
+
+
+void Line::deserialize(class BinaryInput& b) {
+ _point.deserialize(b);
+ _direction.deserialize(b);
+}
+
+
+Vector3 Line::closestPoint(const Vector3& pt) const {
+ float t = _direction.dot(pt - _point);
+ return _point + _direction * t;
+}
+
+
+Vector3 Line::point() const {
+ return _point;
+}
+
+
+Vector3 Line::direction() const {
+ return _direction;
+}
+
+
+Vector3 Line::closestPoint(const Line& B, float& minDist) const {
+ const Vector3& P1 = _point;
+ const Vector3& U1 = _direction;
+
+ Vector3 P2 = B.point();
+ Vector3 U2 = B.direction();
+
+ const Vector3& P21 = P2 - P1;
+ const Vector3& M = U2.cross(U1);
+ float m2 = M.length();
+
+ Vector3 R = P21.cross(M) / m2;
+
+ float t1 = R.dot(U2);
+
+ minDist = abs(P21.dot(M)) / sqrt(m2);
+
+ return P1 + t1 * U1;
+}
+
+}
+
diff --git a/externals/g3dlite/G3D.lib/source/LineSegment.cpp b/externals/g3dlite/G3D.lib/source/LineSegment.cpp
new file mode 100644
index 00000000000..44dfcb48bc1
--- /dev/null
+++ b/externals/g3dlite/G3D.lib/source/LineSegment.cpp
@@ -0,0 +1,236 @@
+/**
+ @file LineSegment.cpp
+
+ @maintainer Morgan McGuire, matrix@graphics3d.com
+
+ @created 2003-02-08
+ @edited 2008-02-02
+ */
+
+#include "G3D/platform.h"
+#include "G3D/LineSegment.h"
+#include "G3D/Sphere.h"
+#include "G3D/debug.h"
+
+namespace G3D {
+
+
+Vector3 LineSegment::closestPoint(const Vector3& p) const {
+
+ // The vector from the end of the capsule to the point in question.
+ Vector3 v(p - _point);
+
+ // Projection of v onto the line segment scaled by
+ // the length of direction.
+ float t = direction.dot(v);
+
+ // Avoid some square roots. Derivation:
+ // t/direction.length() <= direction.length()
+ // t <= direction.squaredLength()
+
+ if ((t >= 0) && (t <= direction.squaredMagnitude())) {
+
+ // The point falls within the segment. Normalize direction,
+ // divide t by the length of direction.
+ return _point + direction * t / direction.squaredMagnitude();
+
+ } else {
+
+ // The point does not fall within the segment; see which end is closer.
+
+ // Distance from 0, squared
+ float d0Squared = v.squaredMagnitude();
+
+ // Distance from 1, squared
+ float d1Squared = (v - direction).squaredMagnitude();
+
+ if (d0Squared < d1Squared) {
+
+ // Point 0 is closer
+ return _point;
+
+ } else {
+
+ // Point 1 is closer
+ return _point + direction;
+
+ }
+ }
+
+}
+
+Vector3 LineSegment::point(int i) const {
+ switch (i) {
+ case 0:
+ return _point;
+
+ case 1:
+ return _point + direction;
+
+ default:
+ debugAssertM(i == 0 || i == 1, "Argument to point must be 0 or 1");
+ return _point;
+ }
+}
+
+
+bool LineSegment::intersectsSolidSphere(const class Sphere& s) const {
+ return distanceSquared(s.center) <= square(s.radius);
+}
+
+
+LineSegment::LineSegment(class BinaryInput& b) {
+ deserialize(b);
+}
+
+
+void LineSegment::serialize(class BinaryOutput& b) const {
+ _point.serialize(b);
+ direction.serialize(b);
+}
+
+
+void LineSegment::deserialize(class BinaryInput& b) {
+ _point.deserialize(b);
+ direction.deserialize(b);
+}
+
+
+Vector3 LineSegment::randomPoint() const {
+ return _point + uniformRandom(0, 1) * direction;
+}
+
+
+/////////////////////////////////////////////////////////////////////////////////////
+
+LineSegment2D LineSegment2D::fromTwoPoints(const Vector2& p0, const Vector2& p1) {
+ LineSegment2D s;
+ s.m_origin = p0;
+ s.m_direction = p1 - p0;
+ s.m_length = s.m_direction.length();
+ return s;
+}
+
+
+Vector2 LineSegment2D::point(int i) const {
+ debugAssert(i == 0 || i == 1);
+ if (i == 0) {
+ return m_origin;
+ } else {
+ return m_direction + m_origin;
+ }
+}
+
+
+Vector2 LineSegment2D::closestPoint(const Vector2& Q) const {
+ // Two constants that appear in the result
+ const Vector2 k1(m_origin - Q);
+ const Vector2& k2 = m_direction;
+
+ if (fuzzyEq(m_length, 0)) {
+ // This line segment has no length
+ return m_origin;
+ }
+
+ // Time [0, 1] at which we hit the closest point travelling from p0 to p1.
+ // Derivation can be obtained by minimizing the expression
+ // ||P0 + (P1 - P0)t - Q||.
+ const float t = -k1.dot(k2) / (m_length * m_length);
+
+ if (t < 0) {
+ // Clipped to low end point
+ return m_origin;
+ } else if (t > 1) {
+ // Clipped to high end point
+ return m_origin + m_direction;
+ } else {
+ // Subsitute into the line equation to find
+ // the point on the segment.
+ return m_origin + k2 * t;
+ }
+}
+
+
+float LineSegment2D::distance(const Vector2& p) const {
+ Vector2 closest = closestPoint(p);
+ return (closest - p).length();
+}
+
+
+float LineSegment2D::length() const {
+ return m_length;
+}
+
+
+Vector2 LineSegment2D::intersection(const LineSegment2D& other) const {
+
+ if ((m_origin == other.m_origin) ||
+ (m_origin == other.m_origin + other.m_direction)) {
+ return m_origin;
+ }
+
+ if (m_origin + m_direction == other.m_origin) {
+ return other.m_origin;
+ }
+
+ // Note: Now that we've checked the endpoints, all other parallel lines can now be assumed
+ // to not intersect (within numerical precision)
+
+ Vector2 dir1 = m_direction;
+ Vector2 dir2 = other.m_direction;
+ Vector2 origin1 = m_origin;
+ Vector2 origin2 = other.m_origin;
+
+ if (dir1.x == 0) {
+ // Avoid an upcoming divide by zero
+ dir1 = dir1.yx();
+ dir2 = dir2.yx();
+ origin1 = origin1.yx();
+ origin2 = origin2.yx();
+ }
+
+ // t1 = ((other.m_origin.x - m_origin.x) + other.m_direction.x * t2) / m_direction.x
+ //
+ // ((other.m_origin.x - m_origin.x) + other.m_direction.x * t2) * m_direction.y / m_direction.x =
+ // (other.m_origin.y - m_origin.y) + other.m_direction.y * t2
+ //
+ // m = m_direction.y / m_direction.x
+ // d = other.m_origin - m_origin
+ //
+ // (d.x + other.m_direction.x * t2) * m = d.y + other.m_direction.y * t2
+ //
+ // d.x * m + other.m_direction.x * m * t2 = d.y + other.m_direction.y * t2
+ //
+ // d.x * m - d.y = (other.m_direction.y - other.m_direction.x * m) * t2
+ //
+ // (d.x * m - d.y) / (other.m_direction.y - other.m_direction.x * m) = t2
+ //
+
+ Vector2 d = origin2 - origin1;
+ float m = dir1.y / dir1.x;
+
+ float t2 = (d.x * m - d.y) / (dir2.y - dir2.x * m);
+ if (! isFinite(t2)) {
+ // Parallel lines: no intersection
+ return Vector2::inf();
+ }
+
+ if ((t2 < 0.0f) || (t2 > 1.0f)) {
+ // Intersection occurs past the end of the line segments
+ return Vector2::inf();
+ }
+
+ float t1 = (d.x + dir2.x * t2) / dir1.x;
+ if ((t1 < 0.0f) || (t1 > 1.0f)) {
+ // Intersection occurs past the end of the line segments
+ return Vector2::inf();
+ }
+
+ // Return the intersection point (computed from non-transposed
+ // variables even if we flipped above)
+ return m_origin + m_direction * t1;
+
+}
+
+}
+
diff --git a/externals/g3dlite/G3D.lib/source/Log.cpp b/externals/g3dlite/G3D.lib/source/Log.cpp
new file mode 100644
index 00000000000..13cea7a31f0
--- /dev/null
+++ b/externals/g3dlite/G3D.lib/source/Log.cpp
@@ -0,0 +1,157 @@
+/**
+ @file Log.cpp
+
+ @maintainer Morgan McGuire, morgan@graphics3d.com
+ @created 2001-08-04
+ @edited 2005-07-01
+ */
+
+#include "G3D/platform.h"
+#include "G3D/Log.h"
+#include "G3D/format.h"
+#include "G3D/Array.h"
+#include "G3D/fileutils.h"
+#include <time.h>
+
+#ifdef G3D_WIN32
+ #include <imagehlp.h>
+#else
+ #include <stdarg.h>
+#endif
+
+namespace G3D {
+
+void logPrintf(const char* fmt, ...) {
+ va_list arg_list;
+ va_start(arg_list, fmt);
+ Log::common()->vprintf(fmt, arg_list);
+ va_end(arg_list);
+}
+
+
+Log* Log::commonLog = NULL;
+
+Log::Log(const std::string& filename, int stripFromStackBottom) :
+ stripFromStackBottom(stripFromStackBottom) {
+
+ this->filename = filename;
+
+ logFile = fopen(filename.c_str(), "w");
+
+ if (logFile == NULL) {
+ std::string drive, base, ext;
+ Array<std::string> path;
+ parseFilename(filename, drive, path, base, ext);
+ std::string logName = base + ((ext != "") ? ("." + ext) : "");
+
+ // Write time is greater than 1ms. This may be a network drive.... try another file.
+ #ifdef G3D_WIN32
+ logName = std::string(std::getenv("TEMP")) + logName;
+ #else
+ logName = std::string("/tmp/") + logName;
+ #endif
+
+ logFile = fopen(logName.c_str(), "w");
+ }
+
+ // Turn off buffering.
+ setvbuf(logFile, NULL, _IONBF, 0);
+
+ fprintf(logFile, "Application Log\n");
+ time_t t;
+ time(&t);
+ fprintf(logFile, "Start: %s\n", ctime(&t));
+ fflush(logFile);
+
+ if (commonLog == NULL) {
+ commonLog = this;
+ }
+}
+
+
+Log::~Log() {
+ section("Shutdown");
+ println("Closing log file");
+
+ // Make sure we don't leave a dangling pointer
+ if (Log::commonLog == this) {
+ Log::commonLog = NULL;
+ }
+
+ fclose(logFile);
+}
+
+
+FILE* Log::getFile() const {
+ return logFile;
+}
+
+Log* Log::common() {
+ if (commonLog == NULL) {
+ commonLog = new Log();
+ }
+ return commonLog;
+}
+
+
+std::string Log::getCommonLogFilename() {
+ return common()->filename;
+}
+
+
+void Log::section(const std::string& s) {
+ fprintf(logFile, "_____________________________________________________\n");
+ fprintf(logFile, "\n ### %s ###\n\n", s.c_str());
+}
+
+
+void __cdecl Log::printf(const char* fmt, ...) {
+ printHeader();
+
+ va_list arg_list;
+ va_start(arg_list, fmt);
+ print(vformat(fmt, arg_list));
+ va_end(arg_list);
+}
+
+
+void __cdecl Log::vprintf(const char* fmt, va_list argPtr) {
+ vfprintf(logFile, fmt, argPtr);
+}
+
+
+void Log::print(const std::string& s) {
+ printHeader();
+ fprintf(logFile, "%s", s.c_str());
+}
+
+
+void Log::println(const std::string& s) {
+ printHeader();
+ fprintf(logFile, "%s\n", s.c_str());
+}
+
+
+void Log::printHeader() {
+ time_t t;
+ if (time(&t) != ((time_t)-1)) {
+ /*
+ char buf[32];
+ strftime(buf, 32, "[%H:%M:%S]", localtime(&t));
+
+ Removed because this doesn't work on SDL threads.
+
+ #ifdef _DEBUG
+ std::string bt = getBacktrace(15, 2, stripFromStackBottom);
+ fprintf(logFile, "\n %s %s\n\n", buf, bt.c_str());
+ #endif
+
+ fprintf(logFile, "\n %s \n", buf);
+ */
+
+ } else {
+ println("[Error getting time]");
+ }
+}
+
+}
diff --git a/externals/g3dlite/G3D.lib/source/Matrix.cpp b/externals/g3dlite/G3D.lib/source/Matrix.cpp
new file mode 100644
index 00000000000..0c6db214543
--- /dev/null
+++ b/externals/g3dlite/G3D.lib/source/Matrix.cpp
@@ -0,0 +1,1801 @@
+/**
+ @file Matrix.cpp
+ @author Morgan McGuire, matrix@graphics3d.com
+ */
+#include "G3D/Matrix.h"
+#include "G3D/TextOutput.h"
+
+static inline G3D::Matrix::T negate(G3D::Matrix::T x) {
+ return -x;
+}
+
+namespace G3D {
+
+int Matrix::debugNumCopyOps = 0;
+int Matrix::debugNumAllocOps = 0;
+
+void Matrix::serialize(TextOutput& t) const {
+ t.writeSymbol("%");
+ t.writeNumber(rows());
+ t.writeSymbol("x");
+ t.writeNumber(cols());
+ t.pushIndent();
+ t.writeNewline();
+
+ t.writeSymbol("[");
+ for (int r = 0; r < rows(); ++r) {
+ for (int c = 0; c < cols(); ++c) {
+ t.writeNumber(impl->get(r, c));
+ if (c < cols() - 1) {
+ t.writeSymbol(",");
+ } else {
+ if (r < rows() - 1) {
+ t.writeSymbol(";");
+ t.writeNewline();
+ }
+ }
+ }
+ }
+ t.writeSymbol("]");
+ t.popIndent();
+ t.writeNewline();
+}
+
+
+std::string Matrix::toString(const std::string& name) const {
+ std::string s;
+
+ if (name != "") {
+ s += format("%s = \n", name.c_str());
+ }
+
+ s += "[";
+ for (int r = 0; r < rows(); ++r) {
+ for (int c = 0; c < cols(); ++c) {
+ double v = impl->get(r, c);
+
+ if (::fabs(v) < 0.00001) {
+ // Don't print "negative zero"
+ s += format("% 10.04g", 0.0);
+ } else if (v == iRound(v)) {
+ // Print integers nicely
+ s += format("% 10.04g", v);
+ } else {
+ s += format("% 10.04f", v);
+ }
+
+ if (c < cols() - 1) {
+ s += ",";
+ } else if (r < rows() - 1) {
+ s += ";\n ";
+ } else {
+ s += "]\n";
+ }
+ }
+ }
+ return s;
+}
+
+
+#define INPLACE(OP)\
+ ImplRef A = impl;\
+\
+ if (! A.isLastReference()) {\
+ impl = new Impl(A->R, A->C);\
+ }\
+\
+ A->OP(B, *impl);
+
+Matrix& Matrix::operator*=(const T& B) {
+ INPLACE(mul)
+ return *this;
+}
+
+
+Matrix& Matrix::operator-=(const T& B) {
+ INPLACE(sub)
+ return *this;
+}
+
+
+Matrix& Matrix::operator+=(const T& B) {
+ INPLACE(add)
+ return *this;
+}
+
+
+Matrix& Matrix::operator/=(const T& B) {
+ INPLACE(div)
+ return *this;
+}
+
+
+Matrix& Matrix::operator*=(const Matrix& B) {
+ // We can't optimize this one
+ *this = *this * B;
+ return *this;
+}
+
+
+Matrix& Matrix::operator-=(const Matrix& _B) {
+ const Impl& B = *_B.impl;
+ INPLACE(sub)
+ return *this;
+}
+
+
+Matrix& Matrix::operator+=(const Matrix& _B) {
+ const Impl& B = *_B.impl;
+ INPLACE(add)
+ return *this;
+}
+
+
+void Matrix::arrayMulInPlace(const Matrix& _B) {
+ const Impl& B = *_B.impl;
+ INPLACE(arrayMul)
+}
+
+
+void Matrix::arrayDivInPlace(const Matrix& _B) {
+ const Impl& B = *_B.impl;
+ INPLACE(arrayDiv)
+}
+
+#undef INPLACE
+
+Matrix Matrix::fromDiagonal(const Matrix& d) {
+ debugAssert((d.rows() == 1) || (d.cols() == 1));
+
+ int n = d.numElements();
+ Matrix D = zero(n, n);
+ for (int i = 0; i < n; ++i) {
+ D.set(i, i, d.impl->data[i]);
+ }
+
+ return D;
+}
+
+void Matrix::set(int r, int c, T v) {
+ if (! impl.isLastReference()) {
+ // Copy the data before mutating; this object is shared
+ impl = new Impl(*impl);
+ }
+ impl->set(r, c, v);
+}
+
+
+void Matrix::setRow(int r, const Matrix& vec) {
+ debugAssertM(vec.cols() == cols(),
+ "A row must be set to a vector of the same size.");
+ debugAssertM(vec.rows() == 1,
+ "A row must be set to a row vector.");
+
+ debugAssert(r >= 0);
+ debugAssert(r < rows());
+
+ if (! impl.isLastReference()) {
+ // Copy the data before mutating; this object is shared
+ impl = new Impl(*impl);
+ }
+ impl->setRow(r, vec.impl->data);
+}
+
+
+void Matrix::setCol(int c, const Matrix& vec) {
+ debugAssertM(vec.rows() == rows(),
+ "A column must be set to a vector of the same size.");
+ debugAssertM(vec.cols() == 1,
+ "A column must be set to a column vector.");
+
+ debugAssert(c >= 0);
+
+ debugAssert(c < cols());
+
+ if (! impl.isLastReference()) {
+ // Copy the data before mutating; this object is shared
+ impl = new Impl(*impl);
+ }
+ impl->setCol(c, vec.impl->data);
+}
+
+
+Matrix::T Matrix::get(int r, int c) const {
+ return impl->get(r, c);
+}
+
+
+Matrix Matrix::row(int r) const {
+ debugAssert(r >= 0);
+ debugAssert(r < rows());
+ Matrix out(1, cols());
+ out.impl->setRow(1, impl->elt[r]);
+ return out;
+}
+
+
+Matrix Matrix::col(int c) const {
+ debugAssert(c >= 0);
+ debugAssert(c < cols());
+ Matrix out(rows(), 1);
+
+ T* outData = out.impl->data;
+ // Get a pointer to the first element in the column
+ const T* inElt = &(impl->elt[0][c]);
+ int R = rows();
+ int C = cols();
+ for (int r = 0; r < R; ++r) {
+ outData[r] = *inElt;
+ // Skip around to the next row
+ inElt += C;
+ }
+
+ return out;
+}
+
+
+Matrix Matrix::zero(int R, int C) {
+ Impl* A = new Impl(R, C);
+ A->setZero();
+ return Matrix(A);
+}
+
+
+Matrix Matrix::one(int R, int C) {
+ Impl* A = new Impl(R, C);
+ for (int i = R * C - 1; i >= 0; --i) {
+ A->data[i] = 1.0;
+ }
+ return Matrix(A);
+}
+
+
+Matrix Matrix::random(int R, int C) {
+ Impl* A = new Impl(R, C);
+ for (int i = R * C - 1; i >= 0; --i) {
+ A->data[i] = G3D::uniformRandom(0.0, 1.0);
+ }
+ return Matrix(A);
+}
+
+
+Matrix Matrix::identity(int N) {
+ Impl* m = new Impl(N, N);
+ m->setZero();
+ for (int i = 0; i < N; ++i) {
+ m->elt[i][i] = 1.0;
+ }
+ return Matrix(m);
+}
+
+
+// Implement an explicit-output unary method by trampolining to the impl
+#define TRAMPOLINE_EXPLICIT_1(method)\
+void Matrix::method(Matrix& out) const {\
+ if ((out.impl == impl) && impl.isLastReference()) {\
+ impl->method(*out.impl);\
+ } else {\
+ out = this->method();\
+ }\
+}
+
+TRAMPOLINE_EXPLICIT_1(abs)
+TRAMPOLINE_EXPLICIT_1(negate)
+TRAMPOLINE_EXPLICIT_1(arrayLog)
+TRAMPOLINE_EXPLICIT_1(arrayExp)
+TRAMPOLINE_EXPLICIT_1(arrayCos)
+TRAMPOLINE_EXPLICIT_1(arraySin)
+
+void Matrix::mulRow(int r, const T& v) {
+ debugAssert(r >= 0 && r < rows());
+
+ if (! impl.isLastReference()) {
+ impl = new Impl(*impl);
+ }
+
+ impl->mulRow(r, v);
+}
+
+
+void Matrix::transpose(Matrix& out) const {
+ if ((out.impl == impl) && impl.isLastReference() && (impl->R == impl->C)) {
+ // In place
+ impl->transpose(*out.impl);
+ } else {
+ out = this->transpose();
+ }
+}
+
+
+Matrix3 Matrix::toMatrix3() const {
+ debugAssert(impl->R == 3);
+ debugAssert(impl->C == 3);
+ return Matrix3(
+ impl->get(0,0), impl->get(0,1), impl->get(0,2),
+ impl->get(1,0), impl->get(1,1), impl->get(1,2),
+ impl->get(2,0), impl->get(2,1), impl->get(2,2));
+}
+
+
+Matrix4 Matrix::toMatrix4() const {
+ debugAssert(impl->R == 4);
+ debugAssert(impl->C == 4);
+ return Matrix4(
+ impl->get(0,0), impl->get(0,1), impl->get(0,2), impl->get(0,3),
+ impl->get(1,0), impl->get(1,1), impl->get(1,2), impl->get(1,3),
+ impl->get(2,0), impl->get(2,1), impl->get(2,2), impl->get(2,3),
+ impl->get(3,0), impl->get(3,1), impl->get(3,2), impl->get(3,3));
+}
+
+
+Vector2 Matrix::toVector2() const {
+ debugAssert(impl->R * impl->C == 2);
+ if (impl->R > impl->C) {
+ return Vector2(impl->get(0,0), impl->get(1,0));
+ } else {
+ return Vector2(impl->get(0,0), impl->get(0,1));
+ }
+}
+
+
+Vector3 Matrix::toVector3() const {
+ debugAssert(impl->R * impl->C == 3);
+ if (impl->R > impl->C) {
+ return Vector3(impl->get(0,0), impl->get(1,0), impl->get(2, 0));
+ } else {
+ return Vector3(impl->get(0,0), impl->get(0,1), impl->get(0, 2));
+ }
+}
+
+
+Vector4 Matrix::toVector4() const {
+ debugAssert(
+ ((impl->R == 4) && (impl->C == 1)) ||
+ ((impl->R == 1) && (impl->C == 4)));
+
+ if (impl->R > impl->C) {
+ return Vector4(impl->get(0,0), impl->get(1,0), impl->get(2, 0), impl->get(3,0));
+ } else {
+ return Vector4(impl->get(0,0), impl->get(0,1), impl->get(0, 2), impl->get(0,3));
+ }
+}
+
+
+void Matrix::swapRows(int r0, int r1) {
+ debugAssert(r0 >= 0 && r0 < rows());
+ debugAssert(r1 >= 0 && r1 < rows());
+
+ if (r0 == r1) {
+ return;
+ }
+
+ if (! impl.isLastReference()) {
+ impl = new Impl(*impl);
+ }
+
+ impl->swapRows(r0, r1);
+}
+
+
+void Matrix::swapAndNegateCols(int c0, int c1) {
+ debugAssert(c0 >= 0 && c0 < cols());
+ debugAssert(c1 >= 0 && c1 < cols());
+
+ if (c0 == c1) {
+ return;
+ }
+
+ if (! impl.isLastReference()) {
+ impl = new Impl(*impl);
+ }
+
+ impl->swapAndNegateCols(c0, c1);
+}
+
+Matrix Matrix::subMatrix(int r1, int r2, int c1, int c2) const {
+ debugAssert(r2>=r1);
+ debugAssert(c2>=c1);
+ debugAssert(c2<cols());
+ debugAssert(r2<rows());
+ debugAssert(r1>=0);
+ debugAssert(c1>=0);
+
+ Matrix X(r2 - r1 + 1, c2 - c1 + 1);
+
+ for (int r = 0; r < X.rows(); ++r) {
+ for (int c = 0; c < X.cols(); ++c) {
+ X.set(r, c, get(r + r1, c + c1));
+ }
+ }
+
+ return X;
+}
+
+
+bool Matrix::anyNonZero() const {
+ return impl->anyNonZero();
+}
+
+
+bool Matrix::allNonZero() const {
+ return impl->allNonZero();
+}
+
+
+void Matrix::svd(Matrix& U, Array<T>& d, Matrix& V, bool sort) const {
+ debugAssert(rows() >= cols());
+ debugAssertM(&U != &V, "Arguments to SVD must be different matrices");
+ debugAssertM(&U != this, "Arguments to SVD must be different matrices");
+ debugAssertM(&V != this, "Arguments to SVD must be different matrices");
+
+ int R = rows();
+ int C = cols();
+
+ // Make sure we don't overwrite a shared matrix
+ if (! V.impl.isLastReference()) {
+ V = Matrix::zero(C, C);
+ } else {
+ V.impl->setSize(C, C);
+ }
+
+ if (&U != this || ! impl.isLastReference()) {
+ // Make a copy of this for in-place SVD
+ U.impl = new Impl(*impl);
+ }
+
+ d.resize(C);
+ const char* ret = svdCore(U.impl->elt, R, C, d.getCArray(), V.impl->elt);
+
+ debugAssertM(ret == NULL, ret);
+ (void)ret;
+
+ if (sort) {
+ // Sort the singular values from greatest to least
+
+ Array<SortRank> rank(C);
+ for (int c = 0; c < C; ++c) {
+ rank[c].col = c;
+ rank[c].value = d[c];
+ }
+
+ rank.sort(SORT_INCREASING);
+
+ Matrix Uold = U;
+ Matrix Vold = V;
+
+ U = Matrix(U.rows(), U.cols());
+ V = Matrix(V.rows(), V.cols());
+
+ // Now permute U, d, and V appropriately
+ for (int c0 = 0; c0 < C; ++c0) {
+ const int c1 = rank[c0].col;
+
+ d[c0] = rank[c0].value;
+ U.setCol(c0, Uold.col(c1));
+ V.setCol(c0, Vold.col(c1));
+ }
+
+ }
+}
+
+
+#define COMPARE_SCALAR(OP)\
+Matrix Matrix::operator OP (const T& scalar) const {\
+ int R = rows();\
+ int C = cols();\
+ int N = R * C;\
+ Matrix out = Matrix::zero(R, C);\
+\
+ const T* raw = impl->data;\
+ T* outRaw = out.impl->data;\
+ for (int i = 0; i < N; ++i) {\
+ outRaw[i] = raw[i] OP scalar;\
+ }\
+\
+ return out;\
+}
+
+COMPARE_SCALAR(<)
+COMPARE_SCALAR(<=)
+COMPARE_SCALAR(>)
+COMPARE_SCALAR(>=)
+COMPARE_SCALAR(==)
+COMPARE_SCALAR(!=)
+
+#undef COMPARE_SCALAR
+
+double Matrix::normSquared() const {
+ int R = rows();
+ int C = cols();
+ int N = R * C;
+
+ double sum = 0.0;
+
+ const T* raw = impl->data;
+ for (int i = 0; i < N; ++i) {
+ sum += square(raw[i]);
+ }
+
+ return sum;
+}
+
+double Matrix::norm() const {
+ return sqrt(normSquared());
+}
+
+///////////////////////////////////////////////////////////
+
+Matrix::Impl::Impl(const Matrix3& M) : elt(NULL), data(NULL), R(0), C(0), dataSize(0){
+ setSize(3, 3);
+ for (int r = 0; r < 3; ++r) {
+ for (int c = 0; c < 3; ++c) {
+ set(r, c, M[r][c]);
+ }
+ }
+
+}
+
+
+Matrix::Impl::Impl(const Matrix4& M): elt(NULL), data(NULL), R(0), C(0), dataSize(0) {
+ setSize(4, 4);
+ for (int r = 0; r < 4; ++r) {
+ for (int c = 0; c < 4; ++c) {
+ set(r, c, M[r][c]);
+ }
+ }
+}
+
+
+void Matrix::Impl::setSize(int newRows, int newCols) {
+ if ((R == newRows) && (C == newCols)) {
+ // Nothing to do
+ return;
+ }
+
+ int newSize = newRows * newCols;
+
+ R = newRows; C = newCols;
+
+ // Only allocate if we need more space
+ // or the size difference is ridiculous
+ if ((newSize > dataSize) || (newSize < dataSize / 4)) {
+ System::alignedFree(data);
+ data = (float*)System::alignedMalloc(R * C * sizeof(T), 16);
+ ++Matrix::debugNumAllocOps;
+ dataSize = newSize;
+ }
+
+ // Construct the row pointers
+ //delete[] elt;
+ System::free(elt);
+ elt = (T**)System::malloc(R * sizeof(T*));// new T*[R];
+
+ for (int r = 0; r < R; ++ r) {
+ elt[r] = data + r * C;
+ }
+}
+
+
+Matrix::Impl::~Impl() {
+ //delete[] elt;
+ System::free(elt);
+ System::alignedFree(data);
+}
+
+
+Matrix::Impl& Matrix::Impl::operator=(const Impl& m) {
+ setSize(m.R, m.C);
+ System::memcpy(data, m.data, R * C * sizeof(T));
+ ++Matrix::debugNumCopyOps;
+ return *this;
+}
+
+
+void Matrix::Impl::setZero() {
+ System::memset(data, 0, R * C * sizeof(T));
+}
+
+
+void Matrix::Impl::swapRows(int r0, int r1) {
+ T* R0 = elt[r0];
+ T* R1 = elt[r1];
+
+ for (int c = 0; c < C; ++c) {
+ T temp = R0[c];
+ R0[c] = R1[c];
+ R1[c] = temp;
+ }
+}
+
+
+void Matrix::Impl::swapAndNegateCols(int c0, int c1) {
+ for (int r = 0; r < R; ++r) {
+ T* row = elt[r];
+
+ const T temp = -row[c0];
+ row[c0] = -row[c1];
+ row[c1] = temp;
+ }
+}
+
+
+void Matrix::Impl::mulRow(int r, const T& v) {
+ T* row = elt[r];
+
+ for (int c = 0; c < C; ++c) {
+ row[c] *= v;
+ }
+}
+
+
+void Matrix::Impl::mul(const Impl& B, Impl& out) const {
+ const Impl& A = *this;
+
+ debugAssertM(
+ (this != &out) && (&B != &out),
+ "Output argument to mul cannot be the same as an input argument.");
+
+ debugAssert(A.C == B.R);
+ debugAssert(A.R == out.R);
+ debugAssert(B.C == out.C);
+
+ for (int r = 0; r < out.R; ++r) {
+ for (int c = 0; c < out.C; ++c) {
+ T sum = 0.0;
+ for (int i = 0; i < A.C; ++i) {
+ sum += A.get(r, i) * B.get(i, c);
+ }
+ out.set(r, c, sum);
+ }
+ }
+}
+
+
+// We're about to define several similar methods,
+// so use a macro to share implementations. This
+// must be a macro because the difference between
+// the macros is the operation in the inner loop.
+#define IMPLEMENT_ARRAY_2(method, OP)\
+void Matrix::Impl::method(const Impl& B, Impl& out) const {\
+ const Impl& A = *this;\
+ \
+ debugAssert(A.C == B.C);\
+ debugAssert(A.R == B.R);\
+ debugAssert(A.C == out.C);\
+ debugAssert(A.R == out.R);\
+ \
+ for (int i = R * C - 1; i >= 0; --i) {\
+ out.data[i] = A.data[i] OP B.data[i];\
+ }\
+}
+
+
+#define IMPLEMENT_ARRAY_1(method, f)\
+void Matrix::Impl::method(Impl& out) const {\
+ const Impl& A = *this;\
+ \
+ debugAssert(A.C == out.C);\
+ debugAssert(A.R == out.R);\
+ \
+ for (int i = R * C - 1; i >= 0; --i) {\
+ out.data[i] = f(A.data[i]);\
+ }\
+}
+
+
+#define IMPLEMENT_ARRAY_SCALAR(method, OP)\
+void Matrix::Impl::method(Matrix::T B, Impl& out) const {\
+ const Impl& A = *this;\
+ \
+ debugAssert(A.C == out.C);\
+ debugAssert(A.R == out.R);\
+ \
+ for (int i = R * C - 1; i >= 0; --i) {\
+ out.data[i] = A.data[i] OP B;\
+ }\
+}
+
+IMPLEMENT_ARRAY_2(add, +)
+IMPLEMENT_ARRAY_2(sub, -)
+IMPLEMENT_ARRAY_2(arrayMul, *)
+IMPLEMENT_ARRAY_2(arrayDiv, /)
+
+IMPLEMENT_ARRAY_SCALAR(add, +)
+IMPLEMENT_ARRAY_SCALAR(sub, -)
+IMPLEMENT_ARRAY_SCALAR(mul, *)
+IMPLEMENT_ARRAY_SCALAR(div, /)
+
+IMPLEMENT_ARRAY_1(abs, ::fabs)
+IMPLEMENT_ARRAY_1(negate, ::negate)
+IMPLEMENT_ARRAY_1(arrayLog, ::log)
+IMPLEMENT_ARRAY_1(arraySqrt, ::sqrt)
+IMPLEMENT_ARRAY_1(arrayExp, ::exp)
+IMPLEMENT_ARRAY_1(arrayCos, ::cos)
+IMPLEMENT_ARRAY_1(arraySin, ::sin)
+
+#undef IMPLEMENT_ARRAY_SCALAR
+#undef IMPLEMENT_ARRAY_1
+#undef IMPLEMENT_ARRAY_2
+
+// lsub is special because the argument order is reversed
+void Matrix::Impl::lsub(Matrix::T B, Impl& out) const {
+ const Impl& A = *this;
+
+ debugAssert(A.C == out.C);
+ debugAssert(A.R == out.R);
+
+ for (int i = R * C - 1; i >= 0; --i) {
+ out.data[i] = B - A.data[i];
+ }
+}
+
+
+void Matrix::Impl::inverseViaAdjoint(Impl& out) const {
+ debugAssert(&out != this);
+
+ // Inverse = adjoint / determinant
+
+ adjoint(out);
+
+ // Don't call the determinant method when we already have an
+ // adjoint matrix; there's a faster way of computing it: the dot
+ // product of the first row and the adjoint's first col.
+ double det = 0.0;
+ for (int r = R - 1; r >= 0; --r) {
+ det += elt[0][r] * out.elt[r][0];
+ }
+
+ out.div(Matrix::T(det), out);
+}
+
+
+void Matrix::Impl::transpose(Impl& out) const {
+ debugAssert(out.R == C);
+ debugAssert(out.C == R);
+
+ if (&out == this) {
+ // Square matrix in place
+ for (int r = 0; r < R; ++r) {
+ for (int c = r + 1; c < C; ++c) {
+ T temp = get(r, c);
+ out.set(r, c, get(c, r));
+ out.set(c, r, temp);
+ }
+ }
+ } else {
+ for (int r = 0; r < R; ++r) {
+ for (int c = 0; c < C; ++c) {
+ out.set(c, r, get(r, c));
+ }
+ }
+ }
+}
+
+
+void Matrix::Impl::adjoint(Impl& out) const {
+ cofactor(out);
+ // Transpose is safe to perform in place
+ out.transpose(out);
+}
+
+
+void Matrix::Impl::cofactor(Impl& out) const {
+ debugAssert(&out != this);
+ for(int r = 0; r < R; ++r) {
+ for(int c = 0; c < C; ++c) {
+ out.set(r, c, cofactor(r, c));
+ }
+ }
+}
+
+
+Matrix::T Matrix::Impl::cofactor(int r, int c) const {
+ // Strang p. 217
+ float s = isEven(r + c) ? 1.0f : -1.0f;
+
+ return s * determinant(r, c);
+}
+
+
+Matrix::T Matrix::Impl::determinant(int nr, int nc) const {
+ debugAssert(R > 0);
+ debugAssert(C > 0);
+ Impl A(R - 1, C - 1);
+ withoutRowAndCol(nr, nc, A);
+ return A.determinant();
+}
+
+
+void Matrix::Impl::setRow(int r, const T* vals) {
+ debugAssert(r >= 0);
+ System::memcpy(elt[r], vals, sizeof(T) * C);
+}
+
+
+void Matrix::Impl::setCol(int c, const T* vals) {
+ for (int r = 0; r < R; ++r) {
+ elt[r][c] = vals[r];
+ }
+}
+
+
+Matrix::T Matrix::Impl::determinant() const {
+
+ debugAssert(R == C);
+
+ // Compute using cofactors
+ switch(R) {
+ case 0:
+ return 0;
+
+ case 1:
+ // Determinant of a 1x1 is the element
+ return elt[0][0];
+
+ case 2:
+ // Determinant of a 2x2 is ad-bc
+ return elt[0][0] * elt[1][1] - elt[0][1] * elt[1][0];
+
+ case 3:
+ {
+ // Determinant of an nxn matrix is the dot product of the first
+ // row with the first row of cofactors. The base cases of this
+ // method get called a lot, so we spell out the implementation
+ // for the 3x3 case.
+
+ double cofactor00 = elt[1][1] * elt[2][2] - elt[1][2] * elt[2][1];
+ double cofactor10 = elt[1][2] * elt[2][0] - elt[1][0] * elt[2][2];
+ double cofactor20 = elt[1][0] * elt[2][1] - elt[1][1] * elt[2][0];
+
+ return Matrix::T(
+ elt[0][0] * cofactor00 +
+ elt[0][1] * cofactor10 +
+ elt[0][2] * cofactor20);
+ }
+
+ default:
+ {
+ // Determinant of an n x n matrix is the dot product of the first
+ // row with the first row of cofactors
+ T det = 0.0;
+
+ for (int c = 0; c < C; ++c) {
+ det += elt[0][c] * cofactor(0, c);
+ }
+
+ return det;
+ }
+ }
+}
+
+
+void Matrix::Impl::withoutRowAndCol(int excludeRow, int excludeCol, Impl& out) const {
+ debugAssert(out.R == R - 1);
+ debugAssert(out.C == C - 1);
+
+ for (int r = 0; r < out.R; ++r) {
+ for (int c = 0; c < out.C; ++c) {
+ out.elt[r][c] = elt[r + ((r >= excludeRow) ? 1 : 0)][c + ((c >= excludeCol) ? 1 : 0)];
+ }
+ }
+}
+
+
+Matrix Matrix::pseudoInverse(float tolerance) const {
+ if ((cols() == 1) || (rows() == 1)) {
+ return vectorPseudoInverse();
+ } else if ((cols() <= 4) || (rows() <= 4)) {
+ return partitionPseudoInverse();
+ } else {
+ return svdPseudoInverse(tolerance);
+ }
+}
+
+/*
+ Public function for testing purposes only. Use pseudoInverse(), as it contains optimizations for
+ nonsingular matrices with at least one small (<5) dimension.
+*/
+Matrix Matrix::svdPseudoInverse(float tolerance) const {
+ if (cols() > rows()) {
+ return transpose().svdPseudoInverse(tolerance).transpose();
+ }
+
+ // Matrices from SVD
+ Matrix U, V;
+
+ // Diagonal elements
+ Array<T> d;
+
+ svd(U, d, V);
+
+ if (rows() == 1) {
+ d.resize(1, false);
+ }
+
+ if (tolerance < 0) {
+ // TODO: Should be eps(d[0]), which is the largest diagonal
+ tolerance = G3D::max(rows(), cols()) * 0.0001f;
+ }
+
+ Matrix X;
+
+ int r = 0;
+ for (int i = 0; i < d.size(); ++i) {
+ if (d[i] > tolerance) {
+ d[i] = Matrix::T(1) / d[i];
+ ++r;
+ }
+ }
+
+ if (r == 0) {
+ // There were no non-zero elements
+ X = zero(cols(), rows());
+ } else {
+ // Use the first r columns
+
+ // Test code (the rest is below)
+ /*
+ d.resize(r);
+ Matrix testU = U.subMatrix(0, U.rows() - 1, 0, r - 1);
+ Matrix testV = V.subMatrix(0, V.rows() - 1, 0, r - 1);
+ Matrix testX = testV * Matrix::fromDiagonal(d) * testU.transpose();
+ X = testX;
+ */
+
+
+ // We want to do this:
+ //
+ // d.resize(r);
+ // U = U.subMatrix(0, U.rows() - 1, 0, r - 1);
+ // X = V * Matrix::fromDiagonal(d) * U.transpose();
+ //
+ // but creating a large diagonal matrix and then
+ // multiplying by it is wasteful. So we instead
+ // explicitly perform A = (D * U')' = U * D, and
+ // then multiply X = V * A'.
+
+ Matrix A = Matrix(U.rows(), r);
+
+ const T* dPtr = d.getCArray();
+ for (int i = 0; i < A.rows(); ++i) {
+ const T* Urow = U.impl->elt[i];
+ T* Arow = A.impl->elt[i];
+ const int Acols = A.cols();
+ for (int j = 0; j < Acols; ++j) {
+ // A(i,j) = U(i,:) * D(:,j)
+ // This is non-zero only at j = i because D is diagonal
+ // A(i,j) = U(i,j) * D(j,j)
+ Arow[j] = Urow[j] * dPtr[j];
+ }
+ }
+
+ //
+ // Compute X = V.subMatrix(0, V.rows() - 1, 0, r - 1) * A.transpose()
+ //
+ // Avoid the explicit subMatrix call, and by storing A' instead of A, avoid
+ // both the transpose and the memory incoherence of striding across memory
+ // in big steps.
+
+ alwaysAssertM(A.cols() == r,
+ "Internal dimension mismatch during pseudoInverse()");
+ alwaysAssertM(V.cols() >= r,
+ "Internal dimension mismatch during pseudoInverse()");
+
+ X = Matrix(V.rows(), A.rows());
+ T** Xelt = X.impl->elt;
+ for (int i = 0; i < X.rows(); ++i) {
+ const T* Vrow = V.impl->elt[i];
+ for (int j = 0; j < X.cols(); ++j) {
+ const T* Arow = A.impl->elt[j];
+ T sum = 0;
+ for (int k = 0; k < r; ++k) {
+ sum += Vrow[k] * Arow[k];
+ }
+ Xelt[i][j] = sum;
+ }
+ }
+
+ /*
+ // Test that results are the same after optimizations:
+ Matrix diff = X - testX;
+ T n = diff.norm();
+ debugAssert(n < 0.0001);
+ */
+ }
+ return X;
+}
+
+// Computes pseudoinverse for a vector
+Matrix Matrix::vectorPseudoInverse() const {
+ // If vector A has nonzero elements: transpose A, then divide each elt. by the squared norm
+ // If A is zero vector: transpose A
+ double x = 0.0;
+
+ if (anyNonZero()) {
+ x = 1.0 / normSquared();
+ }
+
+ Matrix A(cols(), rows());
+ T** Aelt = A.impl->elt;
+ for (int r = 0; r < rows(); ++r) {
+ const T* MyRow = impl->elt[r];
+ for (int c = 0; c < cols(); ++c) {
+ Aelt[c][r] = T(MyRow[c] * x);
+ }
+ }
+ return Matrix(A);
+}
+
+
+Matrix Matrix::rowPartPseudoInverse() const{
+ int m = rows();
+ int n = cols();
+ alwaysAssertM((m<=n),"Row-partitioned block matrix pseudoinverse requires R<C");
+
+ // B = A * A'
+ Matrix A = *this;
+ Matrix B = Matrix(m,m);
+
+ T** Aelt = A.impl->elt;
+ T** Belt = B.impl->elt;
+ for (int i = 0; i < m; ++i) {
+ const T* Arow = Aelt[i];
+ for (int j = 0; j < m; ++j) {
+ const T* Brow = Aelt[j];
+ T sum = 0;
+ for (int k = 0; k < n; ++k) {
+ sum += Arow[k] * Brow[k];
+ }
+ Belt[i][j] = sum;
+ }
+ }
+
+ // B has size m x m
+ switch (m) {
+ case 2:
+ return row2PseudoInverse(B);
+
+ case 3:
+ return row3PseudoInverse(B);
+
+ case 4:
+ return row4PseudoInverse(B);
+
+ default:
+ alwaysAssertM(false, "G3D internal error: Should have used the vector or general case!");
+ return Matrix();
+ }
+}
+
+Matrix Matrix::colPartPseudoInverse() const{
+ int m = rows();
+ int n = cols();
+ alwaysAssertM((m>=n),"Column-partitioned block matrix pseudoinverse requires R>C");
+ // TODO: Put each of the individual cases in its own helper function
+ // TODO: Push the B computation down into the individual cases
+ // B = A' * A
+ Matrix A = *this;
+ Matrix B = Matrix(n, n);
+ T** Aelt = A.impl->elt;
+ T** Belt = B.impl->elt;
+ for (int i = 0; i < n; ++i) {
+ for (int j = 0; j < n; ++j) {
+ T sum = 0;
+ for (int k = 0; k < m; ++k) {
+ sum += Aelt[k][i] * Aelt[k][j];
+ }
+ Belt[i][j] = sum;
+ }
+ }
+
+ // B has size n x n
+ switch (n) {
+ case 2:
+ return col2PseudoInverse(B);
+
+ case 3:
+ return col3PseudoInverse(B);
+
+ case 4:
+ return col4PseudoInverse(B);
+
+ default:
+ alwaysAssertM(false, "G3D internal error: Should have used the vector or general case!");
+ return Matrix();
+ }
+}
+
+Matrix Matrix::col2PseudoInverse(const Matrix& B) const {
+
+ Matrix A = *this;
+ int m = rows();
+ int n = cols();
+ (void)n;
+
+ // Row-major 2x2 matrix
+ const float B2[2][2] =
+ {{B.get(0,0), B.get(0,1)},
+ {B.get(1,0), B.get(1,1)}};
+
+ float det = (B2[0][0]*B2[1][1]) - (B2[0][1]*B2[1][0]);
+
+ if (fuzzyEq(det, T(0))) {
+
+ // Matrix was singular; the block matrix pseudo-inverse can't
+ // handle that, so fall back to the old case
+ return svdPseudoInverse();
+
+ } else {
+ // invert using formula at http://www.netsoc.tcd.ie/~jgilbert/maths_site/applets/algebra/matrix_inversion.html
+
+ // Multiply by Binv * A'
+ Matrix X(cols(), rows());
+
+ T** Xelt = X.impl->elt;
+ T** Aelt = A.impl->elt;
+ float binv00 = B2[1][1]/det, binv01 = -B2[1][0]/det;
+ float binv10 = -B2[0][1]/det, binv11 = B2[0][0]/det;
+ for (int j = 0; j < m; ++j) {
+ const T* Arow = Aelt[j];
+ float a0 = Arow[0];
+ float a1 = Arow[1];
+ Xelt[0][j] = binv00 * a0 + binv01 * a1;
+ Xelt[1][j] = binv10 * a0 + binv11 * a1;
+ }
+ return X;
+ }
+}
+
+Matrix Matrix::col3PseudoInverse(const Matrix& B) const {
+ Matrix A = *this;
+ int m = rows();
+ int n = cols();
+
+ Matrix3 B3 = B.toMatrix3();
+ if (fuzzyEq(B3.determinant(), (T)0.0)) {
+
+ // Matrix was singular; the block matrix pseudo-inverse can't
+ // handle that, so fall back to the old case
+ return svdPseudoInverse();
+
+ } else {
+ Matrix3 B3inv = B3.inverse();
+
+ // Multiply by Binv * A'
+ Matrix X(cols(), rows());
+
+ T** Xelt = X.impl->elt;
+ T** Aelt = A.impl->elt;
+ for (int i = 0; i < n; ++i) {
+ T* Xrow = Xelt[i];
+ for (int j = 0; j < m; ++j) {
+ const T* Arow = Aelt[j];
+ T sum = 0;
+ const float* Binvrow = B3inv[i];
+ for (int k = 0; k < n; ++k) {
+ sum += Binvrow[k] * Arow[k];
+ }
+ Xrow[j] = sum;
+ }
+ }
+ return X;
+ }
+}
+
+Matrix Matrix::col4PseudoInverse(const Matrix& B) const {
+ Matrix A = *this;
+ int m = rows();
+ int n = cols();
+
+ Matrix4 B4 = B.toMatrix4();
+ if (fuzzyEq(B4.determinant(), (T)0.0)) {
+
+ // Matrix was singular; the block matrix pseudo-inverse can't
+ // handle that, so fall back to the old case
+ return svdPseudoInverse();
+
+ } else {
+ Matrix4 B4inv = B4.inverse();
+
+ // Multiply by Binv * A'
+ Matrix X(cols(), rows());
+
+ T** Xelt = X.impl->elt;
+ T** Aelt = A.impl->elt;
+ for (int i = 0; i < n; ++i) {
+ T* Xrow = Xelt[i];
+ for (int j = 0; j < m; ++j) {
+ const T* Arow = Aelt[j];
+ T sum = 0;
+ const float* Binvrow = B4inv[i];
+ for (int k = 0; k < n; ++k) {
+ sum += Binvrow[k] * Arow[k];
+ }
+ Xrow[j] = sum;
+ }
+ }
+ return X;
+ }
+}
+
+Matrix Matrix::row2PseudoInverse(const Matrix& B) const {
+
+ Matrix A = *this;
+ int m = rows();
+ int n = cols();
+ (void)m;
+
+ // Row-major 2x2 matrix
+ const float B2[2][2] =
+ {{B.get(0,0), B.get(0,1)},
+ {B.get(1,0), B.get(1,1)}};
+
+ float det = (B2[0][0]*B2[1][1]) - (B2[0][1]*B2[1][0]);
+
+ if (fuzzyEq(det, T(0))) {
+
+ // Matrix was singular; the block matrix pseudo-inverse can't
+ // handle that, so fall back to the old case
+ return svdPseudoInverse();
+
+ } else {
+ // invert using formula at http://www.netsoc.tcd.ie/~jgilbert/maths_site/applets/algebra/matrix_inversion.html
+
+ // Multiply by Binv * A'
+ Matrix X(cols(), rows());
+
+ T** Xelt = X.impl->elt;
+ T** Aelt = A.impl->elt;
+ float binv00 = B2[1][1]/det, binv01 = -B2[1][0]/det;
+ float binv10 = -B2[0][1]/det, binv11 = B2[0][0]/det;
+ for (int j = 0; j < n; ++j) {
+ Xelt[j][0] = Aelt[0][j] * binv00 + Aelt[1][j] * binv10;
+ Xelt[j][1] = Aelt[0][j] * binv01 + Aelt[1][j] * binv11;
+ }
+ return X;
+ }
+}
+
+Matrix Matrix::row3PseudoInverse(const Matrix& B) const {
+
+ Matrix A = *this;
+ int m = rows();
+ int n = cols();
+
+ Matrix3 B3 = B.toMatrix3();
+ if (fuzzyEq(B3.determinant(), (T)0.0)) {
+
+ // Matrix was singular; the block matrix pseudo-inverse can't
+ // handle that, so fall back to the old case
+ return svdPseudoInverse();
+
+ } else {
+ Matrix3 B3inv = B3.inverse();
+
+ // Multiply by Binv * A'
+ Matrix X(cols(), rows());
+
+ T** Xelt = X.impl->elt;
+ T** Aelt = A.impl->elt;
+ for (int i = 0; i < n; ++i) {
+ T* Xrow = Xelt[i];
+ for (int j = 0; j < m; ++j) {
+ T sum = 0;
+ for (int k = 0; k < m; ++k) {
+ sum += Aelt[k][i] * B3inv[j][k];
+ }
+ Xrow[j] = sum;
+ }
+ }
+ return X;
+ }
+}
+
+Matrix Matrix::row4PseudoInverse(const Matrix& B) const {
+
+ Matrix A = *this;
+ int m = rows();
+ int n = cols();
+
+ Matrix4 B4 = B.toMatrix4();
+ if (fuzzyEq(B4.determinant(), (T)0.0)) {
+
+ // Matrix was singular; the block matrix pseudo-inverse can't
+ // handle that, so fall back to the old case
+ return svdPseudoInverse();
+
+ } else {
+ Matrix4 B4inv = B4.inverse();
+
+ // Multiply by Binv * A'
+ Matrix X(cols(), rows());
+
+ T** Xelt = X.impl->elt;
+ T** Aelt = A.impl->elt;
+ for (int i = 0; i < n; ++i) {
+ T* Xrow = Xelt[i];
+ for (int j = 0; j < m; ++j) {
+ T sum = 0;
+ for (int k = 0; k < m; ++k) {
+ sum += Aelt[k][i] * B4inv[j][k];
+ }
+ Xrow[j] = sum;
+ }
+ }
+ return X;
+ }
+}
+
+// Uses the block matrix pseudoinverse to compute the pseudoinverse of a full-rank mxn matrix with m >= n
+// http://en.wikipedia.org/wiki/Block_matrix_pseudoinverse
+Matrix Matrix::partitionPseudoInverse() const {
+
+ // Logic:
+ // A^-1 = (A'A)^-1 A'
+ // A has few (n) columns, so A'A is small (n x n) and fast to invert
+
+ int m = rows();
+ int n = cols();
+
+ if (m < n) {
+ // TODO: optimize by pushing through the transpose
+ //return transpose().partitionPseudoInverse().transpose();
+ return rowPartPseudoInverse();
+
+ } else {
+ return colPartPseudoInverse();
+ }
+}
+
+void Matrix::Impl::inverseInPlaceGaussJordan() {
+ debugAssertM(R == C,
+ format(
+ "Cannot perform Gauss-Jordan inverse on a non-square matrix."
+ " (Argument was %dx%d)",
+ R, C));
+
+ // Exchange to float elements
+# define SWAP(x, y) {float temp = x; x = y; y = temp;}
+
+ // The integer arrays pivot, rowIndex, and colIndex are
+ // used for bookkeeping on the pivoting
+ static Array<int> colIndex, rowIndex, pivot;
+
+ int col = 0, row = 0;
+
+ colIndex.resize(R);
+ rowIndex.resize(R);
+ pivot.resize(R);
+
+ static const int NO_PIVOT = -1;
+
+ // Initialize the pivot array to default values.
+ for (int i = 0; i < R; ++i) {
+ pivot[i] = NO_PIVOT;
+ }
+
+ // This is the main loop over the columns to be reduced
+ // Loop over the columns.
+ for (int c = 0; c < R; ++c) {
+
+ // Find the largest element and use that as a pivot
+ float largestMagnitude = 0.0;
+
+ // This is the outer loop of the search for a pivot element
+ for (int r = 0; r < R; ++r) {
+
+ // Unless we've already found the pivot, keep going
+ if (pivot[r] != 0) {
+
+ // Find the largest pivot
+ for (int k = 0; k < R; ++k) {
+ if (pivot[k] == NO_PIVOT) {
+ const float mag = fabs(elt[r][k]);
+
+ if (mag >= largestMagnitude) {
+ largestMagnitude = mag;
+ row = r; col = k;
+ }
+ }
+ }
+ }
+ }
+
+ pivot[col] += 1;
+
+ // Interchange columns so that the pivot element is on the diagonal (we'll have to undo this
+ // at the end)
+ if (row != col) {
+ for (int k = 0; k < R; ++k) {
+ SWAP(elt[row][k], elt[col][k])
+ }
+ }
+
+ // The pivot is now at [row, col]
+ rowIndex[c] = row;
+ colIndex[c] = col;
+
+ double piv = elt[col][col];
+
+ debugAssertM(piv != 0.0, "Matrix is singular");
+
+ // Divide everything by the pivot (avoid computing the division
+ // multiple times).
+ const double pivotInverse = 1.0 / piv;
+ elt[col][col] = 1.0;
+
+ for (int k = 0; k < R; ++k) {
+ elt[col][k] *= Matrix::T(pivotInverse);
+ }
+
+ // Reduce all rows
+ for (int r = 0; r < R; ++r) {
+ // Skip over the pivot row
+ if (r != col) {
+
+ double oldValue = elt[r][col];
+ elt[r][col] = 0.0;
+
+ for (int k = 0; k < R; ++k) {
+ elt[r][k] -= Matrix::T(elt[col][k] * oldValue);
+ }
+ }
+ }
+ }
+
+
+ // Put the columns back in the correct locations
+ for (int i = R - 1; i >= 0; --i) {
+ if (rowIndex[i] != colIndex[i]) {
+ for (int k = 0; k < R; ++k) {
+ SWAP(elt[k][rowIndex[i]], elt[k][colIndex[i]]);
+ }
+ }
+ }
+
+# undef SWAP
+}
+
+
+bool Matrix::Impl::anyNonZero() const {
+ int N = R * C;
+ for (int i = 0; i < N; ++i) {
+ if (data[i] != 0.0) {
+ return true;
+ }
+ }
+ return false;
+}
+
+
+bool Matrix::Impl::allNonZero() const {
+ int N = R * C;
+ for (int i = 0; i < N; ++i) {
+ if (data[i] == 0.0) {
+ return false;
+ }
+ }
+ return true;
+}
+
+
+/** Helper for svdCore */
+static double pythag(double a, double b) {
+
+ double at = fabs(a), bt = fabs(b), ct, result;
+
+ if (at > bt) {
+ ct = bt / at;
+ result = at * sqrt(1.0 + square(ct));
+ } else if (bt > 0.0) {
+ ct = at / bt;
+ result = bt * sqrt(1.0 + square(ct));
+ } else {
+ result = 0.0;
+ }
+
+ return result;
+}
+
+#define SIGN(a, b) ((b) >= 0.0 ? fabs(a) : -fabs(a))
+
+const char* Matrix::svdCore(float** U, int rows, int cols, float* D, float** V) {
+ const int MAX_ITERATIONS = 30;
+
+ int flag, i, its, j, jj, k, l = 0, nm = 0;
+ double c, f, h, s, x, y, z;
+ double anorm = 0.0, g = 0.0, scale = 0.0;
+
+ // Temp row vector
+ double* rv1;
+
+ debugAssertM(rows >= cols, "Must have more rows than columns");
+
+ rv1 = (double*)System::alignedMalloc(cols * sizeof(double), 16);
+ debugAssert(rv1);
+
+ // Householder reduction to bidiagonal form
+ for (i = 0; i < cols; ++i) {
+
+ // Left-hand reduction
+ l = i + 1;
+ rv1[i] = scale * g;
+ g = s = scale = 0.0;
+
+ if (i < rows) {
+
+ for (k = i; k < rows; ++k) {
+ scale += fabs((double)U[k][i]);
+ }
+
+ if (scale) {
+ for (k = i; k < rows; ++k) {
+ U[k][i] = (float)((double)U[k][i]/scale);
+ s += ((double)U[k][i] * (double)U[k][i]);
+ }
+
+ f = (double)U[i][i];
+
+ // TODO: what is this 2-arg sign function?
+ g = -SIGN(sqrt(s), f);
+ h = f * g - s;
+ U[i][i] = (float)(f - g);
+
+ if (i != cols - 1) {
+ for (j = l; j < cols; j++) {
+
+ for (s = 0.0, k = i; k < rows; ++k) {
+ s += ((double)U[k][i] * (double)U[k][j]);
+ }
+
+ f = s / h;
+ for (k = i; k < rows; ++k) {
+ U[k][j] += (float)(f * (double)U[k][i]);
+ }
+ }
+ }
+ for (k = i; k < rows; ++k) {
+ U[k][i] = (float)((double)U[k][i]*scale);
+ }
+ }
+ }
+ D[i] = (float)(scale * g);
+
+ // right-hand reduction
+ g = s = scale = 0.0;
+ if (i < rows && i != cols - 1) {
+ for (k = l; k < cols; ++k) {
+ scale += fabs((double)U[i][k]);
+ }
+
+ if (scale) {
+ for (k = l; k < cols; ++k) {
+ U[i][k] = (float)((double)U[i][k]/scale);
+ s += ((double)U[i][k] * (double)U[i][k]);
+ }
+
+ f = (double)U[i][l];
+ g = -SIGN(sqrt(s), f);
+ h = f * g - s;
+ U[i][l] = (float)(f - g);
+
+ for (k = l; k < cols; ++k) {
+ rv1[k] = (double)U[i][k] / h;
+ }
+
+ if (i != rows - 1) {
+
+ for (j = l; j < rows; ++j) {
+ for (s = 0.0, k = l; k < cols; ++k) {
+ s += ((double)U[j][k] * (double)U[i][k]);
+ }
+
+ for (k = l; k < cols; ++k) {
+ U[j][k] += (float)(s * rv1[k]);
+ }
+ }
+ }
+
+ for (k = l; k < cols; ++k) {
+ U[i][k] = (float)((double)U[i][k]*scale);
+ }
+ }
+ }
+
+ anorm = max(anorm, fabs((double)D[i]) + fabs(rv1[i]));
+ }
+
+ // accumulate the right-hand transformation
+ for (i = cols - 1; i >= 0; --i) {
+ if (i < cols - 1) {
+ if (g) {
+ for (j = l; j < cols; j++) {
+ V[j][i] = (float)(((double)U[i][j] / (double)U[i][l]) / g);
+ }
+
+ // double division to avoid underflow
+ for (j = l; j < cols; ++j) {
+ for (s = 0.0, k = l; k < cols; k++) {
+ s += ((double)U[i][k] * (double)V[k][j]);
+ }
+
+ for (k = l; k < cols; ++k) {
+ V[k][j] += (float)(s * (double)V[k][i]);
+ }
+ }
+ }
+
+ for (j = l; j < cols; ++j) {
+ V[i][j] = V[j][i] = 0.0;
+ }
+ }
+
+ V[i][i] = 1.0;
+ g = rv1[i];
+ l = i;
+ }
+
+ // accumulate the left-hand transformation
+ for (i = cols - 1; i >= 0; --i) {
+ l = i + 1;
+ g = (double)D[i];
+ if (i < cols - 1) {
+ for (j = l; j < cols; ++j) {
+ U[i][j] = 0.0;
+ }
+ }
+
+ if (g) {
+ g = 1.0 / g;
+ if (i != cols - 1) {
+ for (j = l; j < cols; ++j) {
+ for (s = 0.0, k = l; k < rows; ++k) {
+ s += ((double)U[k][i] * (double)U[k][j]);
+ }
+
+ f = (s / (double)U[i][i]) * g;
+
+ for (k = i; k < rows; ++k) {
+ U[k][j] += (float)(f * (double)U[k][i]);
+ }
+ }
+ }
+
+ for (j = i; j < rows; ++j) {
+ U[j][i] = (float)((double)U[j][i]*g);
+ }
+
+ } else {
+ for (j = i; j < rows; ++j) {
+ U[j][i] = 0.0;
+ }
+ }
+ ++U[i][i];
+ }
+
+ // diagonalize the bidiagonal form
+ for (k = cols - 1; k >= 0; --k) {
+ // loop over singular values
+ for (its = 0; its < MAX_ITERATIONS; ++its) {
+ // loop over allowed iterations
+ flag = 1;
+
+ for (l = k; l >= 0; --l) {
+ // test for splitting
+ nm = l - 1;
+ if (fabs(rv1[l]) + anorm == anorm) {
+ flag = 0;
+ break;
+ }
+
+ if (fabs((double)D[nm]) + anorm == anorm) {
+ break;
+ }
+ }
+
+ if (flag) {
+ c = 0.0;
+ s = 1.0;
+ for (i = l; i <= k; ++i) {
+ f = s * rv1[i];
+ if (fabs(f) + anorm != anorm) {
+ g = (double)D[i];
+ h = pythag(f, g);
+ D[i] = (float)h;
+ h = 1.0 / h;
+ c = g * h;
+ s = (- f * h);
+ for (j = 0; j < rows; ++j) {
+ y = (double)U[j][nm];
+ z = (double)U[j][i];
+ U[j][nm] = (float)(y * c + z * s);
+ U[j][i] = (float)(z * c - y * s);
+ }
+ }
+ }
+ }
+
+ z = (double)D[k];
+ if (l == k) {
+ // convergence
+ if (z < 0.0) {
+ // make singular value nonnegative
+ D[k] = (float)(-z);
+
+ for (j = 0; j < cols; ++j) {
+ V[j][k] = (-V[j][k]);
+ }
+ }
+ break;
+ }
+
+ if (its >= MAX_ITERATIONS) {
+ free(rv1);
+ rv1 = NULL;
+ return "Failed to converge.";
+ }
+
+ // shift from bottom 2 x 2 minor
+ x = (double)D[l];
+ nm = k - 1;
+ y = (double)D[nm];
+ g = rv1[nm];
+ h = rv1[k];
+ f = ((y - z) * (y + z) + (g - h) * (g + h)) / (2.0 * h * y);
+ g = pythag(f, 1.0);
+ f = ((x - z) * (x + z) + h * ((y / (f + SIGN(g, f))) - h)) / x;
+
+ // next QR transformation
+ c = s = 1.0;
+ for (j = l; j <= nm; ++j) {
+ i = j + 1;
+ g = rv1[i];
+ y = (double)D[i];
+ h = s * g;
+ g = c * g;
+ z = pythag(f, h);
+ rv1[j] = z;
+ c = f / z;
+ s = h / z;
+ f = x * c + g * s;
+ g = g * c - x * s;
+ h = y * s;
+ y = y * c;
+
+ for (jj = 0; jj < cols; ++jj) {
+ x = (double)V[jj][j];
+ z = (double)V[jj][i];
+ V[jj][j] = (float)(x * c + z * s);
+ V[jj][i] = (float)(z * c - x * s);
+ }
+ z = pythag(f, h);
+ D[j] = (float)z;
+ if (z) {
+ z = 1.0 / z;
+ c = f * z;
+ s = h * z;
+ }
+ f = (c * g) + (s * y);
+ x = (c * y) - (s * g);
+ for (jj = 0; jj < rows; jj++) {
+ y = (double)U[jj][j];
+ z = (double)U[jj][i];
+ U[jj][j] = (float)(y * c + z * s);
+ U[jj][i] = (float)(z * c - y * s);
+ }
+ }
+ rv1[l] = 0.0;
+ rv1[k] = f;
+ D[k] = (float)x;
+ }
+ }
+
+ System::alignedFree(rv1);
+ rv1 = NULL;
+
+ return NULL;
+}
+
+#undef SIGN
+
+}
diff --git a/externals/g3dlite/G3D.lib/source/Matrix3.cpp b/externals/g3dlite/G3D.lib/source/Matrix3.cpp
new file mode 100644
index 00000000000..7fb6648d3f7
--- /dev/null
+++ b/externals/g3dlite/G3D.lib/source/Matrix3.cpp
@@ -0,0 +1,1725 @@
+/**
+ @file Matrix3.cpp
+
+ 3x3 matrix class
+
+ @author Morgan McGuire, graphics3d.com
+
+ @created 2001-06-02
+ @edited 2006-04-06
+*/
+
+#include "G3D/platform.h"
+#include <memory.h>
+#include <assert.h>
+#include "G3D/Matrix3.h"
+#include "G3D/g3dmath.h"
+#include "G3D/BinaryInput.h"
+#include "G3D/BinaryOutput.h"
+#include "G3D/Quat.h"
+
+namespace G3D {
+
+const float Matrix3::EPSILON = 1e-06f;
+
+const Matrix3& Matrix3::zero() {
+ static Matrix3 m(0, 0, 0, 0, 0, 0, 0, 0, 0);
+ return m;
+}
+
+const Matrix3& Matrix3::identity() {
+ static Matrix3 m(1, 0, 0, 0, 1, 0, 0, 0, 1);
+ return m;
+}
+
+
+const float Matrix3::ms_fSvdEpsilon = 1e-04f;
+const int Matrix3::ms_iSvdMaxIterations = 32;
+
+Matrix3::Matrix3(BinaryInput& b) {
+ deserialize(b);
+}
+
+bool Matrix3::fuzzyEq(const Matrix3& b) const {
+ for (int r = 0; r < 3; ++r) {
+ for (int c = 0; c < 3; ++c) {
+ if (! G3D::fuzzyEq(elt[r][c], b[r][c])) {
+ return false;
+ }
+ }
+ }
+ return true;
+}
+
+
+bool Matrix3::isOrthonormal() const {
+ Vector3 X = column(0);
+ Vector3 Y = column(1);
+ Vector3 Z = column(2);
+
+ return
+ (G3D::fuzzyEq(X.dot(Y), 0.0f) &&
+ G3D::fuzzyEq(Y.dot(Z), 0.0f) &&
+ G3D::fuzzyEq(X.dot(Z), 0.0f) &&
+ G3D::fuzzyEq(X.squaredMagnitude(), 1.0f) &&
+ G3D::fuzzyEq(Y.squaredMagnitude(), 1.0f) &&
+ G3D::fuzzyEq(Z.squaredMagnitude(), 1.0f));
+}
+
+//----------------------------------------------------------------------------
+Matrix3::Matrix3(const Quat& _q) {
+ // Implementation from Watt and Watt, pg 362
+ // See also http://www.flipcode.com/documents/matrfaq.html#Q54
+ Quat q = _q;
+ q.unitize();
+ float xx = 2.0f * q.x * q.x;
+ float xy = 2.0f * q.x * q.y;
+ float xz = 2.0f * q.x * q.z;
+ float xw = 2.0f * q.x * q.w;
+
+ float yy = 2.0f * q.y * q.y;
+ float yz = 2.0f * q.y * q.z;
+ float yw = 2.0f * q.y * q.w;
+
+ float zz = 2.0f * q.z * q.z;
+ float zw = 2.0f * q.z * q.w;
+
+ set(1.0f - yy - zz, xy - zw, xz + yw,
+ xy + zw, 1.0f - xx - zz, yz - xw,
+ xz - yw, yz + xw, 1.0f - xx - yy);
+}
+
+//----------------------------------------------------------------------------
+
+Matrix3::Matrix3 (const float aafEntry[3][3]) {
+ memcpy(elt, aafEntry, 9*sizeof(float));
+}
+
+//----------------------------------------------------------------------------
+Matrix3::Matrix3 (const Matrix3& rkMatrix) {
+ memcpy(elt, rkMatrix.elt, 9*sizeof(float));
+}
+
+//----------------------------------------------------------------------------
+Matrix3::Matrix3(
+ float fEntry00, float fEntry01, float fEntry02,
+ float fEntry10, float fEntry11, float fEntry12,
+ float fEntry20, float fEntry21, float fEntry22) {
+ set(fEntry00, fEntry01, fEntry02,
+ fEntry10, fEntry11, fEntry12,
+ fEntry20, fEntry21, fEntry22);
+}
+
+void Matrix3::set(
+ float fEntry00, float fEntry01, float fEntry02,
+ float fEntry10, float fEntry11, float fEntry12,
+ float fEntry20, float fEntry21, float fEntry22) {
+
+ elt[0][0] = fEntry00;
+ elt[0][1] = fEntry01;
+ elt[0][2] = fEntry02;
+ elt[1][0] = fEntry10;
+ elt[1][1] = fEntry11;
+ elt[1][2] = fEntry12;
+ elt[2][0] = fEntry20;
+ elt[2][1] = fEntry21;
+ elt[2][2] = fEntry22;
+}
+
+
+void Matrix3::deserialize(BinaryInput& b) {
+ int r,c;
+ for (c = 0; c < 3; ++c) {
+ for (r = 0; r < 3; ++r) {
+ elt[r][c] = b.readFloat32();
+ }
+ }
+}
+
+
+void Matrix3::serialize(BinaryOutput& b) const {
+ int r,c;
+ for (c = 0; c < 3; ++c) {
+ for (r = 0; r < 3; ++r) {
+ b.writeFloat32(elt[r][c]);
+ }
+ }
+}
+
+
+//----------------------------------------------------------------------------
+Vector3 Matrix3::column (int iCol) const {
+ assert((0 <= iCol) && (iCol < 3));
+ return Vector3(elt[0][iCol], elt[1][iCol],
+ elt[2][iCol]);
+}
+
+const Vector3& Matrix3::row (int iRow) const {
+ assert((0 <= iRow) && (iRow < 3));
+ return *reinterpret_cast<const Vector3*>(elt[iRow]);
+}
+
+
+Vector3 Matrix3::getColumn (int iCol) const {
+ assert((0 <= iCol) && (iCol < 3));
+ return Vector3(elt[0][iCol], elt[1][iCol],
+ elt[2][iCol]);
+}
+
+Vector3 Matrix3::getRow (int iRow) const {
+ return Vector3(elt[iRow][0], elt[iRow][1], elt[iRow][2]);
+}
+
+void Matrix3::setColumn(int iCol, const Vector3 &vector) {
+ debugAssert((iCol >= 0) && (iCol < 3));
+ elt[0][iCol] = vector.x;
+ elt[1][iCol] = vector.y;
+ elt[2][iCol] = vector.z;
+}
+
+
+void Matrix3::setRow(int iRow, const Vector3 &vector) {
+ debugAssert((iRow >= 0) && (iRow < 3));
+ elt[iRow][0] = vector.x;
+ elt[iRow][1] = vector.y;
+ elt[iRow][2] = vector.z;
+}
+
+
+//----------------------------------------------------------------------------
+bool Matrix3::operator== (const Matrix3& rkMatrix) const {
+ for (int iRow = 0; iRow < 3; iRow++) {
+ for (int iCol = 0; iCol < 3; iCol++) {
+ if ( elt[iRow][iCol] != rkMatrix.elt[iRow][iCol] )
+ return false;
+ }
+ }
+
+ return true;
+}
+
+//----------------------------------------------------------------------------
+bool Matrix3::operator!= (const Matrix3& rkMatrix) const {
+ return !operator==(rkMatrix);
+}
+
+//----------------------------------------------------------------------------
+Matrix3 Matrix3::operator+ (const Matrix3& rkMatrix) const {
+ Matrix3 kSum;
+
+ for (int iRow = 0; iRow < 3; iRow++) {
+ for (int iCol = 0; iCol < 3; iCol++) {
+ kSum.elt[iRow][iCol] = elt[iRow][iCol] +
+ rkMatrix.elt[iRow][iCol];
+ }
+ }
+
+ return kSum;
+}
+
+//----------------------------------------------------------------------------
+Matrix3 Matrix3::operator- (const Matrix3& rkMatrix) const {
+ Matrix3 kDiff;
+
+ for (int iRow = 0; iRow < 3; iRow++) {
+ for (int iCol = 0; iCol < 3; iCol++) {
+ kDiff.elt[iRow][iCol] = elt[iRow][iCol] -
+ rkMatrix.elt[iRow][iCol];
+ }
+ }
+
+ return kDiff;
+}
+
+//----------------------------------------------------------------------------
+Matrix3 Matrix3::operator* (const Matrix3& rkMatrix) const {
+ Matrix3 kProd;
+
+ for (int iRow = 0; iRow < 3; iRow++) {
+ for (int iCol = 0; iCol < 3; iCol++) {
+ kProd.elt[iRow][iCol] =
+ elt[iRow][0] * rkMatrix.elt[0][iCol] +
+ elt[iRow][1] * rkMatrix.elt[1][iCol] +
+ elt[iRow][2] * rkMatrix.elt[2][iCol];
+ }
+ }
+
+ return kProd;
+}
+
+Matrix3& Matrix3::operator+= (const Matrix3& rkMatrix) {
+ for (int iRow = 0; iRow < 3; iRow++) {
+ for (int iCol = 0; iCol < 3; iCol++) {
+ elt[iRow][iCol] = elt[iRow][iCol] + rkMatrix.elt[iRow][iCol];
+ }
+ }
+
+ return *this;
+}
+
+Matrix3& Matrix3::operator-= (const Matrix3& rkMatrix) {
+ for (int iRow = 0; iRow < 3; iRow++) {
+ for (int iCol = 0; iCol < 3; iCol++) {
+ elt[iRow][iCol] = elt[iRow][iCol] - rkMatrix.elt[iRow][iCol];
+ }
+ }
+
+ return *this;
+}
+
+Matrix3& Matrix3::operator*= (const Matrix3& rkMatrix) {
+ Matrix3 mulMat;
+ for (int iRow = 0; iRow < 3; iRow++) {
+ for (int iCol = 0; iCol < 3; iCol++) {
+ mulMat.elt[iRow][iCol] =
+ elt[iRow][0] * rkMatrix.elt[0][iCol] +
+ elt[iRow][1] * rkMatrix.elt[1][iCol] +
+ elt[iRow][2] * rkMatrix.elt[2][iCol];
+ }
+ }
+
+ *this = mulMat;
+ return *this;
+}
+
+//----------------------------------------------------------------------------
+Matrix3 Matrix3::operator- () const {
+ Matrix3 kNeg;
+
+ for (int iRow = 0; iRow < 3; iRow++) {
+ for (int iCol = 0; iCol < 3; iCol++) {
+ kNeg[iRow][iCol] = -elt[iRow][iCol];
+ }
+ }
+
+ return kNeg;
+}
+
+//----------------------------------------------------------------------------
+Matrix3 Matrix3::operator* (float fScalar) const {
+ Matrix3 kProd;
+
+ for (int iRow = 0; iRow < 3; iRow++) {
+ for (int iCol = 0; iCol < 3; iCol++) {
+ kProd[iRow][iCol] = fScalar * elt[iRow][iCol];
+ }
+ }
+
+ return kProd;
+}
+
+//----------------------------------------------------------------------------
+Matrix3 operator* (double fScalar, const Matrix3& rkMatrix) {
+ Matrix3 kProd;
+
+ for (int iRow = 0; iRow < 3; iRow++) {
+ for (int iCol = 0; iCol < 3; iCol++) {
+ kProd[iRow][iCol] = fScalar * rkMatrix.elt[iRow][iCol];
+ }
+ }
+
+ return kProd;
+}
+
+Matrix3 operator* (float fScalar, const Matrix3& rkMatrix) {
+ return (double)fScalar * rkMatrix;
+}
+
+
+Matrix3 operator* (int fScalar, const Matrix3& rkMatrix) {
+ return (double)fScalar * rkMatrix;
+}
+//----------------------------------------------------------------------------
+Matrix3 Matrix3::transpose () const {
+ Matrix3 kTranspose;
+
+ for (int iRow = 0; iRow < 3; iRow++) {
+ for (int iCol = 0; iCol < 3; iCol++) {
+ kTranspose[iRow][iCol] = elt[iCol][iRow];
+ }
+ }
+
+ return kTranspose;
+}
+
+//----------------------------------------------------------------------------
+bool Matrix3::inverse (Matrix3& rkInverse, float fTolerance) const {
+ // Invert a 3x3 using cofactors. This is about 8 times faster than
+ // the Numerical Recipes code which uses Gaussian elimination.
+
+ rkInverse[0][0] = elt[1][1] * elt[2][2] -
+ elt[1][2] * elt[2][1];
+ rkInverse[0][1] = elt[0][2] * elt[2][1] -
+ elt[0][1] * elt[2][2];
+ rkInverse[0][2] = elt[0][1] * elt[1][2] -
+ elt[0][2] * elt[1][1];
+ rkInverse[1][0] = elt[1][2] * elt[2][0] -
+ elt[1][0] * elt[2][2];
+ rkInverse[1][1] = elt[0][0] * elt[2][2] -
+ elt[0][2] * elt[2][0];
+ rkInverse[1][2] = elt[0][2] * elt[1][0] -
+ elt[0][0] * elt[1][2];
+ rkInverse[2][0] = elt[1][0] * elt[2][1] -
+ elt[1][1] * elt[2][0];
+ rkInverse[2][1] = elt[0][1] * elt[2][0] -
+ elt[0][0] * elt[2][1];
+ rkInverse[2][2] = elt[0][0] * elt[1][1] -
+ elt[0][1] * elt[1][0];
+
+ float fDet =
+ elt[0][0] * rkInverse[0][0] +
+ elt[0][1] * rkInverse[1][0] +
+ elt[0][2] * rkInverse[2][0];
+
+ if ( G3D::abs(fDet) <= fTolerance )
+ return false;
+
+ float fInvDet = 1.0 / fDet;
+
+ for (int iRow = 0; iRow < 3; iRow++) {
+ for (int iCol = 0; iCol < 3; iCol++)
+ rkInverse[iRow][iCol] *= fInvDet;
+ }
+
+ return true;
+}
+
+//----------------------------------------------------------------------------
+Matrix3 Matrix3::inverse (float fTolerance) const {
+ Matrix3 kInverse = Matrix3::zero();
+ inverse(kInverse, fTolerance);
+ return kInverse;
+}
+
+//----------------------------------------------------------------------------
+float Matrix3::determinant () const {
+ float fCofactor00 = elt[1][1] * elt[2][2] -
+ elt[1][2] * elt[2][1];
+ float fCofactor10 = elt[1][2] * elt[2][0] -
+ elt[1][0] * elt[2][2];
+ float fCofactor20 = elt[1][0] * elt[2][1] -
+ elt[1][1] * elt[2][0];
+
+ float fDet =
+ elt[0][0] * fCofactor00 +
+ elt[0][1] * fCofactor10 +
+ elt[0][2] * fCofactor20;
+
+ return fDet;
+}
+
+//----------------------------------------------------------------------------
+void Matrix3::bidiagonalize (Matrix3& kA, Matrix3& kL,
+ Matrix3& kR) {
+ float afV[3], afW[3];
+ float fLength, fSign, fT1, fInvT1, fT2;
+ bool bIdentity;
+
+ // map first column to (*,0,0)
+ fLength = sqrt(kA[0][0] * kA[0][0] + kA[1][0] * kA[1][0] +
+ kA[2][0] * kA[2][0]);
+
+ if ( fLength > 0.0 ) {
+ fSign = (kA[0][0] > 0.0 ? 1.0 : -1.0);
+ fT1 = kA[0][0] + fSign * fLength;
+ fInvT1 = 1.0 / fT1;
+ afV[1] = kA[1][0] * fInvT1;
+ afV[2] = kA[2][0] * fInvT1;
+
+ fT2 = -2.0 / (1.0 + afV[1] * afV[1] + afV[2] * afV[2]);
+ afW[0] = fT2 * (kA[0][0] + kA[1][0] * afV[1] + kA[2][0] * afV[2]);
+ afW[1] = fT2 * (kA[0][1] + kA[1][1] * afV[1] + kA[2][1] * afV[2]);
+ afW[2] = fT2 * (kA[0][2] + kA[1][2] * afV[1] + kA[2][2] * afV[2]);
+ kA[0][0] += afW[0];
+ kA[0][1] += afW[1];
+ kA[0][2] += afW[2];
+ kA[1][1] += afV[1] * afW[1];
+ kA[1][2] += afV[1] * afW[2];
+ kA[2][1] += afV[2] * afW[1];
+ kA[2][2] += afV[2] * afW[2];
+
+ kL[0][0] = 1.0 + fT2;
+ kL[0][1] = kL[1][0] = fT2 * afV[1];
+ kL[0][2] = kL[2][0] = fT2 * afV[2];
+ kL[1][1] = 1.0 + fT2 * afV[1] * afV[1];
+ kL[1][2] = kL[2][1] = fT2 * afV[1] * afV[2];
+ kL[2][2] = 1.0 + fT2 * afV[2] * afV[2];
+ bIdentity = false;
+ } else {
+ kL = Matrix3::identity();
+ bIdentity = true;
+ }
+
+ // map first row to (*,*,0)
+ fLength = sqrt(kA[0][1] * kA[0][1] + kA[0][2] * kA[0][2]);
+
+ if ( fLength > 0.0 ) {
+ fSign = (kA[0][1] > 0.0 ? 1.0 : -1.0);
+ fT1 = kA[0][1] + fSign * fLength;
+ afV[2] = kA[0][2] / fT1;
+
+ fT2 = -2.0 / (1.0 + afV[2] * afV[2]);
+ afW[0] = fT2 * (kA[0][1] + kA[0][2] * afV[2]);
+ afW[1] = fT2 * (kA[1][1] + kA[1][2] * afV[2]);
+ afW[2] = fT2 * (kA[2][1] + kA[2][2] * afV[2]);
+ kA[0][1] += afW[0];
+ kA[1][1] += afW[1];
+ kA[1][2] += afW[1] * afV[2];
+ kA[2][1] += afW[2];
+ kA[2][2] += afW[2] * afV[2];
+
+ kR[0][0] = 1.0;
+ kR[0][1] = kR[1][0] = 0.0;
+ kR[0][2] = kR[2][0] = 0.0;
+ kR[1][1] = 1.0 + fT2;
+ kR[1][2] = kR[2][1] = fT2 * afV[2];
+ kR[2][2] = 1.0 + fT2 * afV[2] * afV[2];
+ } else {
+ kR = Matrix3::identity();
+ }
+
+ // map second column to (*,*,0)
+ fLength = sqrt(kA[1][1] * kA[1][1] + kA[2][1] * kA[2][1]);
+
+ if ( fLength > 0.0 ) {
+ fSign = (kA[1][1] > 0.0 ? 1.0 : -1.0);
+ fT1 = kA[1][1] + fSign * fLength;
+ afV[2] = kA[2][1] / fT1;
+
+ fT2 = -2.0 / (1.0 + afV[2] * afV[2]);
+ afW[1] = fT2 * (kA[1][1] + kA[2][1] * afV[2]);
+ afW[2] = fT2 * (kA[1][2] + kA[2][2] * afV[2]);
+ kA[1][1] += afW[1];
+ kA[1][2] += afW[2];
+ kA[2][2] += afV[2] * afW[2];
+
+ float fA = 1.0 + fT2;
+ float fB = fT2 * afV[2];
+ float fC = 1.0 + fB * afV[2];
+
+ if ( bIdentity ) {
+ kL[0][0] = 1.0;
+ kL[0][1] = kL[1][0] = 0.0;
+ kL[0][2] = kL[2][0] = 0.0;
+ kL[1][1] = fA;
+ kL[1][2] = kL[2][1] = fB;
+ kL[2][2] = fC;
+ } else {
+ for (int iRow = 0; iRow < 3; iRow++) {
+ float fTmp0 = kL[iRow][1];
+ float fTmp1 = kL[iRow][2];
+ kL[iRow][1] = fA * fTmp0 + fB * fTmp1;
+ kL[iRow][2] = fB * fTmp0 + fC * fTmp1;
+ }
+ }
+ }
+}
+
+//----------------------------------------------------------------------------
+void Matrix3::golubKahanStep (Matrix3& kA, Matrix3& kL,
+ Matrix3& kR) {
+ float fT11 = kA[0][1] * kA[0][1] + kA[1][1] * kA[1][1];
+ float fT22 = kA[1][2] * kA[1][2] + kA[2][2] * kA[2][2];
+ float fT12 = kA[1][1] * kA[1][2];
+ float fTrace = fT11 + fT22;
+ float fDiff = fT11 - fT22;
+ float fDiscr = sqrt(fDiff * fDiff + 4.0 * fT12 * fT12);
+ float fRoot1 = 0.5 * (fTrace + fDiscr);
+ float fRoot2 = 0.5 * (fTrace - fDiscr);
+
+ // adjust right
+ float fY = kA[0][0] - (G3D::abs(fRoot1 - fT22) <=
+ G3D::abs(fRoot2 - fT22) ? fRoot1 : fRoot2);
+ float fZ = kA[0][1];
+ float fInvLength = 1.0 / sqrt(fY * fY + fZ * fZ);
+ float fSin = fZ * fInvLength;
+ float fCos = -fY * fInvLength;
+
+ float fTmp0 = kA[0][0];
+ float fTmp1 = kA[0][1];
+ kA[0][0] = fCos * fTmp0 - fSin * fTmp1;
+ kA[0][1] = fSin * fTmp0 + fCos * fTmp1;
+ kA[1][0] = -fSin * kA[1][1];
+ kA[1][1] *= fCos;
+
+ int iRow;
+
+ for (iRow = 0; iRow < 3; iRow++) {
+ fTmp0 = kR[0][iRow];
+ fTmp1 = kR[1][iRow];
+ kR[0][iRow] = fCos * fTmp0 - fSin * fTmp1;
+ kR[1][iRow] = fSin * fTmp0 + fCos * fTmp1;
+ }
+
+ // adjust left
+ fY = kA[0][0];
+
+ fZ = kA[1][0];
+
+ fInvLength = 1.0 / sqrt(fY * fY + fZ * fZ);
+
+ fSin = fZ * fInvLength;
+
+ fCos = -fY * fInvLength;
+
+ kA[0][0] = fCos * kA[0][0] - fSin * kA[1][0];
+
+ fTmp0 = kA[0][1];
+
+ fTmp1 = kA[1][1];
+
+ kA[0][1] = fCos * fTmp0 - fSin * fTmp1;
+
+ kA[1][1] = fSin * fTmp0 + fCos * fTmp1;
+
+ kA[0][2] = -fSin * kA[1][2];
+
+ kA[1][2] *= fCos;
+
+ int iCol;
+
+ for (iCol = 0; iCol < 3; iCol++) {
+ fTmp0 = kL[iCol][0];
+ fTmp1 = kL[iCol][1];
+ kL[iCol][0] = fCos * fTmp0 - fSin * fTmp1;
+ kL[iCol][1] = fSin * fTmp0 + fCos * fTmp1;
+ }
+
+ // adjust right
+ fY = kA[0][1];
+
+ fZ = kA[0][2];
+
+ fInvLength = 1.0 / sqrt(fY * fY + fZ * fZ);
+
+ fSin = fZ * fInvLength;
+
+ fCos = -fY * fInvLength;
+
+ kA[0][1] = fCos * kA[0][1] - fSin * kA[0][2];
+
+ fTmp0 = kA[1][1];
+
+ fTmp1 = kA[1][2];
+
+ kA[1][1] = fCos * fTmp0 - fSin * fTmp1;
+
+ kA[1][2] = fSin * fTmp0 + fCos * fTmp1;
+
+ kA[2][1] = -fSin * kA[2][2];
+
+ kA[2][2] *= fCos;
+
+ for (iRow = 0; iRow < 3; iRow++) {
+ fTmp0 = kR[1][iRow];
+ fTmp1 = kR[2][iRow];
+ kR[1][iRow] = fCos * fTmp0 - fSin * fTmp1;
+ kR[2][iRow] = fSin * fTmp0 + fCos * fTmp1;
+ }
+
+ // adjust left
+ fY = kA[1][1];
+
+ fZ = kA[2][1];
+
+ fInvLength = 1.0 / sqrt(fY * fY + fZ * fZ);
+
+ fSin = fZ * fInvLength;
+
+ fCos = -fY * fInvLength;
+
+ kA[1][1] = fCos * kA[1][1] - fSin * kA[2][1];
+
+ fTmp0 = kA[1][2];
+
+ fTmp1 = kA[2][2];
+
+ kA[1][2] = fCos * fTmp0 - fSin * fTmp1;
+
+ kA[2][2] = fSin * fTmp0 + fCos * fTmp1;
+
+ for (iCol = 0; iCol < 3; iCol++) {
+ fTmp0 = kL[iCol][1];
+ fTmp1 = kL[iCol][2];
+ kL[iCol][1] = fCos * fTmp0 - fSin * fTmp1;
+ kL[iCol][2] = fSin * fTmp0 + fCos * fTmp1;
+ }
+}
+
+//----------------------------------------------------------------------------
+void Matrix3::singularValueDecomposition (Matrix3& kL, Vector3& kS,
+ Matrix3& kR) const {
+ int iRow, iCol;
+
+ Matrix3 kA = *this;
+ bidiagonalize(kA, kL, kR);
+
+ for (int i = 0; i < ms_iSvdMaxIterations; i++) {
+ float fTmp, fTmp0, fTmp1;
+ float fSin0, fCos0, fTan0;
+ float fSin1, fCos1, fTan1;
+
+ bool bTest1 = (G3D::abs(kA[0][1]) <=
+ ms_fSvdEpsilon * (G3D::abs(kA[0][0]) + G3D::abs(kA[1][1])));
+ bool bTest2 = (G3D::abs(kA[1][2]) <=
+ ms_fSvdEpsilon * (G3D::abs(kA[1][1]) + G3D::abs(kA[2][2])));
+
+ if ( bTest1 ) {
+ if ( bTest2 ) {
+ kS[0] = kA[0][0];
+ kS[1] = kA[1][1];
+ kS[2] = kA[2][2];
+ break;
+ } else {
+ // 2x2 closed form factorization
+ fTmp = (kA[1][1] * kA[1][1] - kA[2][2] * kA[2][2] +
+ kA[1][2] * kA[1][2]) / (kA[1][2] * kA[2][2]);
+ fTan0 = 0.5 * (fTmp + sqrt(fTmp * fTmp + 4.0));
+ fCos0 = 1.0 / sqrt(1.0 + fTan0 * fTan0);
+ fSin0 = fTan0 * fCos0;
+
+ for (iCol = 0; iCol < 3; iCol++) {
+ fTmp0 = kL[iCol][1];
+ fTmp1 = kL[iCol][2];
+ kL[iCol][1] = fCos0 * fTmp0 - fSin0 * fTmp1;
+ kL[iCol][2] = fSin0 * fTmp0 + fCos0 * fTmp1;
+ }
+
+ fTan1 = (kA[1][2] - kA[2][2] * fTan0) / kA[1][1];
+ fCos1 = 1.0 / sqrt(1.0 + fTan1 * fTan1);
+ fSin1 = -fTan1 * fCos1;
+
+ for (iRow = 0; iRow < 3; iRow++) {
+ fTmp0 = kR[1][iRow];
+ fTmp1 = kR[2][iRow];
+ kR[1][iRow] = fCos1 * fTmp0 - fSin1 * fTmp1;
+ kR[2][iRow] = fSin1 * fTmp0 + fCos1 * fTmp1;
+ }
+
+ kS[0] = kA[0][0];
+ kS[1] = fCos0 * fCos1 * kA[1][1] -
+ fSin1 * (fCos0 * kA[1][2] - fSin0 * kA[2][2]);
+ kS[2] = fSin0 * fSin1 * kA[1][1] +
+ fCos1 * (fSin0 * kA[1][2] + fCos0 * kA[2][2]);
+ break;
+ }
+ } else {
+ if ( bTest2 ) {
+ // 2x2 closed form factorization
+ fTmp = (kA[0][0] * kA[0][0] + kA[1][1] * kA[1][1] -
+ kA[0][1] * kA[0][1]) / (kA[0][1] * kA[1][1]);
+ fTan0 = 0.5 * ( -fTmp + sqrt(fTmp * fTmp + 4.0));
+ fCos0 = 1.0 / sqrt(1.0 + fTan0 * fTan0);
+ fSin0 = fTan0 * fCos0;
+
+ for (iCol = 0; iCol < 3; iCol++) {
+ fTmp0 = kL[iCol][0];
+ fTmp1 = kL[iCol][1];
+ kL[iCol][0] = fCos0 * fTmp0 - fSin0 * fTmp1;
+ kL[iCol][1] = fSin0 * fTmp0 + fCos0 * fTmp1;
+ }
+
+ fTan1 = (kA[0][1] - kA[1][1] * fTan0) / kA[0][0];
+ fCos1 = 1.0 / sqrt(1.0 + fTan1 * fTan1);
+ fSin1 = -fTan1 * fCos1;
+
+ for (iRow = 0; iRow < 3; iRow++) {
+ fTmp0 = kR[0][iRow];
+ fTmp1 = kR[1][iRow];
+ kR[0][iRow] = fCos1 * fTmp0 - fSin1 * fTmp1;
+ kR[1][iRow] = fSin1 * fTmp0 + fCos1 * fTmp1;
+ }
+
+ kS[0] = fCos0 * fCos1 * kA[0][0] -
+ fSin1 * (fCos0 * kA[0][1] - fSin0 * kA[1][1]);
+ kS[1] = fSin0 * fSin1 * kA[0][0] +
+ fCos1 * (fSin0 * kA[0][1] + fCos0 * kA[1][1]);
+ kS[2] = kA[2][2];
+ break;
+ } else {
+ golubKahanStep(kA, kL, kR);
+ }
+ }
+ }
+
+ // positize diagonal
+ for (iRow = 0; iRow < 3; iRow++) {
+ if ( kS[iRow] < 0.0 ) {
+ kS[iRow] = -kS[iRow];
+
+ for (iCol = 0; iCol < 3; iCol++)
+ kR[iRow][iCol] = -kR[iRow][iCol];
+ }
+ }
+}
+
+//----------------------------------------------------------------------------
+void Matrix3::singularValueComposition (const Matrix3& kL,
+ const Vector3& kS, const Matrix3& kR) {
+ int iRow, iCol;
+ Matrix3 kTmp;
+
+ // product S*R
+ for (iRow = 0; iRow < 3; iRow++) {
+ for (iCol = 0; iCol < 3; iCol++)
+ kTmp[iRow][iCol] = kS[iRow] * kR[iRow][iCol];
+ }
+
+ // product L*S*R
+ for (iRow = 0; iRow < 3; iRow++) {
+ for (iCol = 0; iCol < 3; iCol++) {
+ elt[iRow][iCol] = 0.0;
+
+ for (int iMid = 0; iMid < 3; iMid++)
+ elt[iRow][iCol] += kL[iRow][iMid] * kTmp[iMid][iCol];
+ }
+ }
+}
+
+//----------------------------------------------------------------------------
+void Matrix3::orthonormalize () {
+ // Algorithm uses Gram-Schmidt orthogonalization. If 'this' matrix is
+ // M = [m0|m1|m2], then orthonormal output matrix is Q = [q0|q1|q2],
+ //
+ // q0 = m0/|m0|
+ // q1 = (m1-(q0*m1)q0)/|m1-(q0*m1)q0|
+ // q2 = (m2-(q0*m2)q0-(q1*m2)q1)/|m2-(q0*m2)q0-(q1*m2)q1|
+ //
+ // where |V| indicates length of vector V and A*B indicates dot
+ // product of vectors A and B.
+
+ // compute q0
+ float fInvLength = 1.0 / sqrt(elt[0][0] * elt[0][0]
+ + elt[1][0] * elt[1][0] +
+ elt[2][0] * elt[2][0]);
+
+ elt[0][0] *= fInvLength;
+ elt[1][0] *= fInvLength;
+ elt[2][0] *= fInvLength;
+
+ // compute q1
+ float fDot0 =
+ elt[0][0] * elt[0][1] +
+ elt[1][0] * elt[1][1] +
+ elt[2][0] * elt[2][1];
+
+ elt[0][1] -= fDot0 * elt[0][0];
+ elt[1][1] -= fDot0 * elt[1][0];
+ elt[2][1] -= fDot0 * elt[2][0];
+
+ fInvLength = 1.0 / sqrt(elt[0][1] * elt[0][1] +
+ elt[1][1] * elt[1][1] +
+ elt[2][1] * elt[2][1]);
+
+ elt[0][1] *= fInvLength;
+ elt[1][1] *= fInvLength;
+ elt[2][1] *= fInvLength;
+
+ // compute q2
+ float fDot1 =
+ elt[0][1] * elt[0][2] +
+ elt[1][1] * elt[1][2] +
+ elt[2][1] * elt[2][2];
+
+ fDot0 =
+ elt[0][0] * elt[0][2] +
+ elt[1][0] * elt[1][2] +
+ elt[2][0] * elt[2][2];
+
+ elt[0][2] -= fDot0 * elt[0][0] + fDot1 * elt[0][1];
+ elt[1][2] -= fDot0 * elt[1][0] + fDot1 * elt[1][1];
+ elt[2][2] -= fDot0 * elt[2][0] + fDot1 * elt[2][1];
+
+ fInvLength = 1.0 / sqrt(elt[0][2] * elt[0][2] +
+ elt[1][2] * elt[1][2] +
+ elt[2][2] * elt[2][2]);
+
+ elt[0][2] *= fInvLength;
+ elt[1][2] *= fInvLength;
+ elt[2][2] *= fInvLength;
+}
+
+//----------------------------------------------------------------------------
+void Matrix3::qDUDecomposition (Matrix3& kQ,
+ Vector3& kD, Vector3& kU) const {
+ // Factor M = QR = QDU where Q is orthogonal, D is diagonal,
+ // and U is upper triangular with ones on its diagonal. Algorithm uses
+ // Gram-Schmidt orthogonalization (the QR algorithm).
+ //
+ // If M = [ m0 | m1 | m2 ] and Q = [ q0 | q1 | q2 ], then
+ //
+ // q0 = m0/|m0|
+ // q1 = (m1-(q0*m1)q0)/|m1-(q0*m1)q0|
+ // q2 = (m2-(q0*m2)q0-(q1*m2)q1)/|m2-(q0*m2)q0-(q1*m2)q1|
+ //
+ // where |V| indicates length of vector V and A*B indicates dot
+ // product of vectors A and B. The matrix R has entries
+ //
+ // r00 = q0*m0 r01 = q0*m1 r02 = q0*m2
+ // r10 = 0 r11 = q1*m1 r12 = q1*m2
+ // r20 = 0 r21 = 0 r22 = q2*m2
+ //
+ // so D = diag(r00,r11,r22) and U has entries u01 = r01/r00,
+ // u02 = r02/r00, and u12 = r12/r11.
+
+ // Q = rotation
+ // D = scaling
+ // U = shear
+
+ // D stores the three diagonal entries r00, r11, r22
+ // U stores the entries U[0] = u01, U[1] = u02, U[2] = u12
+
+ // build orthogonal matrix Q
+ float fInvLength = 1.0 / sqrt(elt[0][0] * elt[0][0]
+ + elt[1][0] * elt[1][0] +
+ elt[2][0] * elt[2][0]);
+ kQ[0][0] = elt[0][0] * fInvLength;
+ kQ[1][0] = elt[1][0] * fInvLength;
+ kQ[2][0] = elt[2][0] * fInvLength;
+
+ float fDot = kQ[0][0] * elt[0][1] + kQ[1][0] * elt[1][1] +
+ kQ[2][0] * elt[2][1];
+ kQ[0][1] = elt[0][1] - fDot * kQ[0][0];
+ kQ[1][1] = elt[1][1] - fDot * kQ[1][0];
+ kQ[2][1] = elt[2][1] - fDot * kQ[2][0];
+ fInvLength = 1.0 / sqrt(kQ[0][1] * kQ[0][1] + kQ[1][1] * kQ[1][1] +
+ kQ[2][1] * kQ[2][1]);
+ kQ[0][1] *= fInvLength;
+ kQ[1][1] *= fInvLength;
+ kQ[2][1] *= fInvLength;
+
+ fDot = kQ[0][0] * elt[0][2] + kQ[1][0] * elt[1][2] +
+ kQ[2][0] * elt[2][2];
+ kQ[0][2] = elt[0][2] - fDot * kQ[0][0];
+ kQ[1][2] = elt[1][2] - fDot * kQ[1][0];
+ kQ[2][2] = elt[2][2] - fDot * kQ[2][0];
+ fDot = kQ[0][1] * elt[0][2] + kQ[1][1] * elt[1][2] +
+ kQ[2][1] * elt[2][2];
+ kQ[0][2] -= fDot * kQ[0][1];
+ kQ[1][2] -= fDot * kQ[1][1];
+ kQ[2][2] -= fDot * kQ[2][1];
+ fInvLength = 1.0 / sqrt(kQ[0][2] * kQ[0][2] + kQ[1][2] * kQ[1][2] +
+ kQ[2][2] * kQ[2][2]);
+ kQ[0][2] *= fInvLength;
+ kQ[1][2] *= fInvLength;
+ kQ[2][2] *= fInvLength;
+
+ // guarantee that orthogonal matrix has determinant 1 (no reflections)
+ float fDet = kQ[0][0] * kQ[1][1] * kQ[2][2] + kQ[0][1] * kQ[1][2] * kQ[2][0] +
+ kQ[0][2] * kQ[1][0] * kQ[2][1] - kQ[0][2] * kQ[1][1] * kQ[2][0] -
+ kQ[0][1] * kQ[1][0] * kQ[2][2] - kQ[0][0] * kQ[1][2] * kQ[2][1];
+
+ if ( fDet < 0.0 ) {
+ for (int iRow = 0; iRow < 3; iRow++)
+ for (int iCol = 0; iCol < 3; iCol++)
+ kQ[iRow][iCol] = -kQ[iRow][iCol];
+ }
+
+ // build "right" matrix R
+ Matrix3 kR;
+
+ kR[0][0] = kQ[0][0] * elt[0][0] + kQ[1][0] * elt[1][0] +
+ kQ[2][0] * elt[2][0];
+
+ kR[0][1] = kQ[0][0] * elt[0][1] + kQ[1][0] * elt[1][1] +
+ kQ[2][0] * elt[2][1];
+
+ kR[1][1] = kQ[0][1] * elt[0][1] + kQ[1][1] * elt[1][1] +
+ kQ[2][1] * elt[2][1];
+
+ kR[0][2] = kQ[0][0] * elt[0][2] + kQ[1][0] * elt[1][2] +
+ kQ[2][0] * elt[2][2];
+
+ kR[1][2] = kQ[0][1] * elt[0][2] + kQ[1][1] * elt[1][2] +
+ kQ[2][1] * elt[2][2];
+
+ kR[2][2] = kQ[0][2] * elt[0][2] + kQ[1][2] * elt[1][2] +
+ kQ[2][2] * elt[2][2];
+
+ // the scaling component
+ kD[0] = kR[0][0];
+
+ kD[1] = kR[1][1];
+
+ kD[2] = kR[2][2];
+
+ // the shear component
+ float fInvD0 = 1.0 / kD[0];
+
+ kU[0] = kR[0][1] * fInvD0;
+
+ kU[1] = kR[0][2] * fInvD0;
+
+ kU[2] = kR[1][2] / kD[1];
+}
+
+//----------------------------------------------------------------------------
+float Matrix3::maxCubicRoot (float afCoeff[3]) {
+ // Spectral norm is for A^T*A, so characteristic polynomial
+ // P(x) = c[0]+c[1]*x+c[2]*x^2+x^3 has three positive float roots.
+ // This yields the assertions c[0] < 0 and c[2]*c[2] >= 3*c[1].
+
+ // quick out for uniform scale (triple root)
+ const float fOneThird = 1.0f / 3.0f;
+ const float fEpsilon = 1e-06f;
+ float fDiscr = afCoeff[2] * afCoeff[2] - 3.0f * afCoeff[1];
+
+ if ( fDiscr <= fEpsilon )
+ return -fOneThird*afCoeff[2];
+
+ // Compute an upper bound on roots of P(x). This assumes that A^T*A
+ // has been scaled by its largest entry.
+ float fX = 1.0f;
+
+ float fPoly = afCoeff[0] + fX * (afCoeff[1] + fX * (afCoeff[2] + fX));
+
+ if ( fPoly < 0.0f ) {
+ // uses a matrix norm to find an upper bound on maximum root
+ fX = G3D::abs(afCoeff[0]);
+ float fTmp = 1.0 + G3D::abs(afCoeff[1]);
+
+ if ( fTmp > fX )
+ fX = fTmp;
+
+ fTmp = 1.0 + G3D::abs(afCoeff[2]);
+
+ if ( fTmp > fX )
+ fX = fTmp;
+ }
+
+ // Newton's method to find root
+ float fTwoC2 = 2.0f * afCoeff[2];
+
+ for (int i = 0; i < 16; i++) {
+ fPoly = afCoeff[0] + fX * (afCoeff[1] + fX * (afCoeff[2] + fX));
+
+ if ( G3D::abs(fPoly) <= fEpsilon )
+ return fX;
+
+ float fDeriv = afCoeff[1] + fX * (fTwoC2 + 3.0f * fX);
+
+ fX -= fPoly / fDeriv;
+ }
+
+ return fX;
+}
+
+//----------------------------------------------------------------------------
+float Matrix3::spectralNorm () const {
+ Matrix3 kP;
+ int iRow, iCol;
+ float fPmax = 0.0;
+
+ for (iRow = 0; iRow < 3; iRow++) {
+ for (iCol = 0; iCol < 3; iCol++) {
+ kP[iRow][iCol] = 0.0;
+
+ for (int iMid = 0; iMid < 3; iMid++) {
+ kP[iRow][iCol] +=
+ elt[iMid][iRow] * elt[iMid][iCol];
+ }
+
+ if ( kP[iRow][iCol] > fPmax )
+ fPmax = kP[iRow][iCol];
+ }
+ }
+
+ float fInvPmax = 1.0 / fPmax;
+
+ for (iRow = 0; iRow < 3; iRow++) {
+ for (iCol = 0; iCol < 3; iCol++)
+ kP[iRow][iCol] *= fInvPmax;
+ }
+
+ float afCoeff[3];
+ afCoeff[0] = -(kP[0][0] * (kP[1][1] * kP[2][2] - kP[1][2] * kP[2][1]) +
+ kP[0][1] * (kP[2][0] * kP[1][2] - kP[1][0] * kP[2][2]) +
+ kP[0][2] * (kP[1][0] * kP[2][1] - kP[2][0] * kP[1][1]));
+ afCoeff[1] = kP[0][0] * kP[1][1] - kP[0][1] * kP[1][0] +
+ kP[0][0] * kP[2][2] - kP[0][2] * kP[2][0] +
+ kP[1][1] * kP[2][2] - kP[1][2] * kP[2][1];
+ afCoeff[2] = -(kP[0][0] + kP[1][1] + kP[2][2]);
+
+ float fRoot = maxCubicRoot(afCoeff);
+ float fNorm = sqrt(fPmax * fRoot);
+ return fNorm;
+}
+
+//----------------------------------------------------------------------------
+void Matrix3::toAxisAngle (Vector3& rkAxis, float& rfRadians) const {
+ //
+ // Let (x,y,z) be the unit-length axis and let A be an angle of rotation.
+ // The rotation matrix is R = I + sin(A)*P + (1-cos(A))*P^2 (Rodrigues' formula) where
+ // I is the identity and
+ //
+ // +- -+
+ // P = | 0 -z +y |
+ // | +z 0 -x |
+ // | -y +x 0 |
+ // +- -+
+ //
+ // If A > 0, R represents a counterclockwise rotation about the axis in
+ // the sense of looking from the tip of the axis vector towards the
+ // origin. Some algebra will show that
+ //
+ // cos(A) = (trace(R)-1)/2 and R - R^t = 2*sin(A)*P
+ //
+ // In the event that A = pi, R-R^t = 0 which prevents us from extracting
+ // the axis through P. Instead note that R = I+2*P^2 when A = pi, so
+ // P^2 = (R-I)/2. The diagonal entries of P^2 are x^2-1, y^2-1, and
+ // z^2-1. We can solve these for axis (x,y,z). Because the angle is pi,
+ // it does not matter which sign you choose on the square roots.
+
+ float fTrace = elt[0][0] + elt[1][1] + elt[2][2];
+ float fCos = 0.5f * (fTrace - 1.0f);
+ rfRadians = G3D::aCos(fCos); // in [0,PI]
+
+ if ( rfRadians > 0.0 ) {
+ if ( rfRadians < pi() ) {
+ rkAxis.x = elt[2][1] - elt[1][2];
+ rkAxis.y = elt[0][2] - elt[2][0];
+ rkAxis.z = elt[1][0] - elt[0][1];
+ rkAxis.unitize();
+ } else {
+ // angle is PI
+ float fHalfInverse;
+
+ if ( elt[0][0] >= elt[1][1] ) {
+ // r00 >= r11
+ if ( elt[0][0] >= elt[2][2] ) {
+ // r00 is maximum diagonal term
+ rkAxis.x = 0.5 * sqrt(elt[0][0] -
+ elt[1][1] - elt[2][2] + 1.0);
+ fHalfInverse = 0.5 / rkAxis.x;
+ rkAxis.y = fHalfInverse * elt[0][1];
+ rkAxis.z = fHalfInverse * elt[0][2];
+ } else {
+ // r22 is maximum diagonal term
+ rkAxis.z = 0.5 * sqrt(elt[2][2] -
+ elt[0][0] - elt[1][1] + 1.0);
+ fHalfInverse = 0.5 / rkAxis.z;
+ rkAxis.x = fHalfInverse * elt[0][2];
+ rkAxis.y = fHalfInverse * elt[1][2];
+ }
+ } else {
+ // r11 > r00
+ if ( elt[1][1] >= elt[2][2] ) {
+ // r11 is maximum diagonal term
+ rkAxis.y = 0.5 * sqrt(elt[1][1] -
+ elt[0][0] - elt[2][2] + 1.0);
+ fHalfInverse = 0.5 / rkAxis.y;
+ rkAxis.x = fHalfInverse * elt[0][1];
+ rkAxis.z = fHalfInverse * elt[1][2];
+ } else {
+ // r22 is maximum diagonal term
+ rkAxis.z = 0.5 * sqrt(elt[2][2] -
+ elt[0][0] - elt[1][1] + 1.0);
+ fHalfInverse = 0.5 / rkAxis.z;
+ rkAxis.x = fHalfInverse * elt[0][2];
+ rkAxis.y = fHalfInverse * elt[1][2];
+ }
+ }
+ }
+ } else {
+ // The angle is 0 and the matrix is the identity. Any axis will
+ // work, so just use the x-axis.
+ rkAxis.x = 1.0;
+ rkAxis.y = 0.0;
+ rkAxis.z = 0.0;
+ }
+}
+
+//----------------------------------------------------------------------------
+Matrix3 Matrix3::fromAxisAngle (const Vector3& _axis, float fRadians) {
+ Vector3 axis = _axis.direction();
+
+ Matrix3 m;
+ float fCos = cos(fRadians);
+ float fSin = sin(fRadians);
+ float fOneMinusCos = 1.0 - fCos;
+ float fX2 = square(axis.x);
+ float fY2 = square(axis.y);
+ float fZ2 = square(axis.z);
+ float fXYM = axis.x * axis.y * fOneMinusCos;
+ float fXZM = axis.x * axis.z * fOneMinusCos;
+ float fYZM = axis.y * axis.z * fOneMinusCos;
+ float fXSin = axis.x * fSin;
+ float fYSin = axis.y * fSin;
+ float fZSin = axis.z * fSin;
+
+ m.elt[0][0] = fX2 * fOneMinusCos + fCos;
+ m.elt[0][1] = fXYM - fZSin;
+ m.elt[0][2] = fXZM + fYSin;
+
+ m.elt[1][0] = fXYM + fZSin;
+ m.elt[1][1] = fY2 * fOneMinusCos + fCos;
+ m.elt[1][2] = fYZM - fXSin;
+
+ m.elt[2][0] = fXZM - fYSin;
+ m.elt[2][1] = fYZM + fXSin;
+ m.elt[2][2] = fZ2 * fOneMinusCos + fCos;
+
+ return m;
+}
+
+//----------------------------------------------------------------------------
+bool Matrix3::toEulerAnglesXYZ (float& rfXAngle, float& rfYAngle,
+ float& rfZAngle) const {
+ // rot = cy*cz -cy*sz sy
+ // cz*sx*sy+cx*sz cx*cz-sx*sy*sz -cy*sx
+ // -cx*cz*sy+sx*sz cz*sx+cx*sy*sz cx*cy
+
+ if ( elt[0][2] < 1.0f ) {
+ if ( elt[0][2] > -1.0f ) {
+ rfXAngle = G3D::aTan2( -elt[1][2], elt[2][2]);
+ rfYAngle = (float) G3D::aSin(elt[0][2]);
+ rfZAngle = G3D::aTan2( -elt[0][1], elt[0][0]);
+ return true;
+ } else {
+ // WARNING. Not unique. XA - ZA = -atan2(r10,r11)
+ rfXAngle = -G3D::aTan2(elt[1][0], elt[1][1]);
+ rfYAngle = -(float)halfPi();
+ rfZAngle = 0.0f;
+ return false;
+ }
+ } else {
+ // WARNING. Not unique. XAngle + ZAngle = atan2(r10,r11)
+ rfXAngle = G3D::aTan2(elt[1][0], elt[1][1]);
+ rfYAngle = (float)halfPi();
+ rfZAngle = 0.0f;
+ return false;
+ }
+}
+
+//----------------------------------------------------------------------------
+bool Matrix3::toEulerAnglesXZY (float& rfXAngle, float& rfZAngle,
+ float& rfYAngle) const {
+ // rot = cy*cz -sz cz*sy
+ // sx*sy+cx*cy*sz cx*cz -cy*sx+cx*sy*sz
+ // -cx*sy+cy*sx*sz cz*sx cx*cy+sx*sy*sz
+
+ if ( elt[0][1] < 1.0f ) {
+ if ( elt[0][1] > -1.0f ) {
+ rfXAngle = G3D::aTan2(elt[2][1], elt[1][1]);
+ rfZAngle = (float) asin( -elt[0][1]);
+ rfYAngle = G3D::aTan2(elt[0][2], elt[0][0]);
+ return true;
+ } else {
+ // WARNING. Not unique. XA - YA = atan2(r20,r22)
+ rfXAngle = G3D::aTan2(elt[2][0], elt[2][2]);
+ rfZAngle = (float)halfPi();
+ rfYAngle = 0.0;
+ return false;
+ }
+ } else {
+ // WARNING. Not unique. XA + YA = atan2(-r20,r22)
+ rfXAngle = G3D::aTan2( -elt[2][0], elt[2][2]);
+ rfZAngle = -(float)halfPi();
+ rfYAngle = 0.0f;
+ return false;
+ }
+}
+
+//----------------------------------------------------------------------------
+bool Matrix3::toEulerAnglesYXZ (float& rfYAngle, float& rfXAngle,
+ float& rfZAngle) const {
+ // rot = cy*cz+sx*sy*sz cz*sx*sy-cy*sz cx*sy
+ // cx*sz cx*cz -sx
+ // -cz*sy+cy*sx*sz cy*cz*sx+sy*sz cx*cy
+
+ if ( elt[1][2] < 1.0 ) {
+ if ( elt[1][2] > -1.0 ) {
+ rfYAngle = G3D::aTan2(elt[0][2], elt[2][2]);
+ rfXAngle = (float) asin( -elt[1][2]);
+ rfZAngle = G3D::aTan2(elt[1][0], elt[1][1]);
+ return true;
+ } else {
+ // WARNING. Not unique. YA - ZA = atan2(r01,r00)
+ rfYAngle = G3D::aTan2(elt[0][1], elt[0][0]);
+ rfXAngle = (float)halfPi();
+ rfZAngle = 0.0;
+ return false;
+ }
+ } else {
+ // WARNING. Not unique. YA + ZA = atan2(-r01,r00)
+ rfYAngle = G3D::aTan2( -elt[0][1], elt[0][0]);
+ rfXAngle = -(float)halfPi();
+ rfZAngle = 0.0f;
+ return false;
+ }
+}
+
+//----------------------------------------------------------------------------
+bool Matrix3::toEulerAnglesYZX (float& rfYAngle, float& rfZAngle,
+ float& rfXAngle) const {
+ // rot = cy*cz sx*sy-cx*cy*sz cx*sy+cy*sx*sz
+ // sz cx*cz -cz*sx
+ // -cz*sy cy*sx+cx*sy*sz cx*cy-sx*sy*sz
+
+ if ( elt[1][0] < 1.0 ) {
+ if ( elt[1][0] > -1.0 ) {
+ rfYAngle = G3D::aTan2( -elt[2][0], elt[0][0]);
+ rfZAngle = (float) asin(elt[1][0]);
+ rfXAngle = G3D::aTan2( -elt[1][2], elt[1][1]);
+ return true;
+ } else {
+ // WARNING. Not unique. YA - XA = -atan2(r21,r22);
+ rfYAngle = -G3D::aTan2(elt[2][1], elt[2][2]);
+ rfZAngle = -(float)halfPi();
+ rfXAngle = 0.0;
+ return false;
+ }
+ } else {
+ // WARNING. Not unique. YA + XA = atan2(r21,r22)
+ rfYAngle = G3D::aTan2(elt[2][1], elt[2][2]);
+ rfZAngle = (float)halfPi();
+ rfXAngle = 0.0f;
+ return false;
+ }
+}
+
+//----------------------------------------------------------------------------
+bool Matrix3::toEulerAnglesZXY (float& rfZAngle, float& rfXAngle,
+ float& rfYAngle) const {
+ // rot = cy*cz-sx*sy*sz -cx*sz cz*sy+cy*sx*sz
+ // cz*sx*sy+cy*sz cx*cz -cy*cz*sx+sy*sz
+ // -cx*sy sx cx*cy
+
+ if ( elt[2][1] < 1.0 ) {
+ if ( elt[2][1] > -1.0 ) {
+ rfZAngle = G3D::aTan2( -elt[0][1], elt[1][1]);
+ rfXAngle = (float) asin(elt[2][1]);
+ rfYAngle = G3D::aTan2( -elt[2][0], elt[2][2]);
+ return true;
+ } else {
+ // WARNING. Not unique. ZA - YA = -atan(r02,r00)
+ rfZAngle = -G3D::aTan2(elt[0][2], elt[0][0]);
+ rfXAngle = -(float)halfPi();
+ rfYAngle = 0.0f;
+ return false;
+ }
+ } else {
+ // WARNING. Not unique. ZA + YA = atan2(r02,r00)
+ rfZAngle = G3D::aTan2(elt[0][2], elt[0][0]);
+ rfXAngle = (float)halfPi();
+ rfYAngle = 0.0f;
+ return false;
+ }
+}
+
+//----------------------------------------------------------------------------
+bool Matrix3::toEulerAnglesZYX (float& rfZAngle, float& rfYAngle,
+ float& rfXAngle) const {
+ // rot = cy*cz cz*sx*sy-cx*sz cx*cz*sy+sx*sz
+ // cy*sz cx*cz+sx*sy*sz -cz*sx+cx*sy*sz
+ // -sy cy*sx cx*cy
+
+ if ( elt[2][0] < 1.0 ) {
+ if ( elt[2][0] > -1.0 ) {
+ rfZAngle = atan2f(elt[1][0], elt[0][0]);
+ rfYAngle = asinf(-(double)elt[2][1]);
+ rfXAngle = atan2f(elt[2][1], elt[2][2]);
+ return true;
+ } else {
+ // WARNING. Not unique. ZA - XA = -atan2(r01,r02)
+ rfZAngle = -G3D::aTan2(elt[0][1], elt[0][2]);
+ rfYAngle = (float)halfPi();
+ rfXAngle = 0.0f;
+ return false;
+ }
+ } else {
+ // WARNING. Not unique. ZA + XA = atan2(-r01,-r02)
+ rfZAngle = G3D::aTan2( -elt[0][1], -elt[0][2]);
+ rfYAngle = -(float)halfPi();
+ rfXAngle = 0.0f;
+ return false;
+ }
+}
+
+//----------------------------------------------------------------------------
+Matrix3 Matrix3::fromEulerAnglesXYZ (float fYAngle, float fPAngle,
+ float fRAngle) {
+ float fCos, fSin;
+
+ fCos = cosf(fYAngle);
+ fSin = sinf(fYAngle);
+ Matrix3 kXMat(1.0f, 0.0f, 0.0f, 0.0f, fCos, -fSin, 0.0, fSin, fCos);
+
+ fCos = cosf(fPAngle);
+ fSin = sinf(fPAngle);
+ Matrix3 kYMat(fCos, 0.0f, fSin, 0.0f, 1.0f, 0.0f, -fSin, 0.0f, fCos);
+
+ fCos = cosf(fRAngle);
+ fSin = sinf(fRAngle);
+ Matrix3 kZMat(fCos, -fSin, 0.0f, fSin, fCos, 0.0f, 0.0f, 0.0f, 1.0f);
+
+ return kXMat * (kYMat * kZMat);
+}
+
+//----------------------------------------------------------------------------
+Matrix3 Matrix3::fromEulerAnglesXZY (float fYAngle, float fPAngle,
+ float fRAngle) {
+
+ float fCos, fSin;
+
+ fCos = cosf(fYAngle);
+ fSin = sinf(fYAngle);
+ Matrix3 kXMat(1.0, 0.0, 0.0, 0.0, fCos, -fSin, 0.0, fSin, fCos);
+
+ fCos = cosf(fPAngle);
+ fSin = sinf(fPAngle);
+ Matrix3 kZMat(fCos, -fSin, 0.0, fSin, fCos, 0.0, 0.0, 0.0, 1.0);
+
+ fCos = cosf(fRAngle);
+ fSin = sinf(fRAngle);
+ Matrix3 kYMat(fCos, 0.0, fSin, 0.0, 1.0, 0.0, -fSin, 0.0, fCos);
+
+ return kXMat * (kZMat * kYMat);
+}
+
+//----------------------------------------------------------------------------
+Matrix3 Matrix3::fromEulerAnglesYXZ(
+ float fYAngle,
+ float fPAngle,
+ float fRAngle) {
+
+ float fCos, fSin;
+
+ fCos = cos(fYAngle);
+ fSin = sin(fYAngle);
+ Matrix3 kYMat(fCos, 0.0f, fSin, 0.0f, 1.0f, 0.0f, -fSin, 0.0f, fCos);
+
+ fCos = cos(fPAngle);
+ fSin = sin(fPAngle);
+ Matrix3 kXMat(1.0f, 0.0f, 0.0f, 0.0f, fCos, -fSin, 0.0f, fSin, fCos);
+
+ fCos = cos(fRAngle);
+ fSin = sin(fRAngle);
+ Matrix3 kZMat(fCos, -fSin, 0.0f, fSin, fCos, 0.0f, 0.0f, 0.0f, 1.0f);
+
+ return kYMat * (kXMat * kZMat);
+}
+
+//----------------------------------------------------------------------------
+Matrix3 Matrix3::fromEulerAnglesYZX(
+ float fYAngle,
+ float fPAngle,
+ float fRAngle) {
+
+ float fCos, fSin;
+
+ fCos = cos(fYAngle);
+ fSin = sin(fYAngle);
+ Matrix3 kYMat(fCos, 0.0f, fSin, 0.0f, 1.0f, 0.0f, -fSin, 0.0f, fCos);
+
+ fCos = cos(fPAngle);
+ fSin = sin(fPAngle);
+ Matrix3 kZMat(fCos, -fSin, 0.0f, fSin, fCos, 0.0f, 0.0f, 0.0f, 1.0f);
+
+ fCos = cos(fRAngle);
+ fSin = sin(fRAngle);
+ Matrix3 kXMat(1.0f, 0.0f, 0.0f, 0.0f, fCos, -fSin, 0.0f, fSin, fCos);
+
+ return kYMat * (kZMat * kXMat);
+}
+
+//----------------------------------------------------------------------------
+Matrix3 Matrix3::fromEulerAnglesZXY (float fYAngle, float fPAngle,
+ float fRAngle) {
+ float fCos, fSin;
+
+ fCos = cos(fYAngle);
+ fSin = sin(fYAngle);
+ Matrix3 kZMat(fCos, -fSin, 0.0, fSin, fCos, 0.0, 0.0, 0.0, 1.0);
+
+ fCos = cos(fPAngle);
+ fSin = sin(fPAngle);
+ Matrix3 kXMat(1.0, 0.0, 0.0, 0.0, fCos, -fSin, 0.0, fSin, fCos);
+
+ fCos = cos(fRAngle);
+ fSin = sin(fRAngle);
+ Matrix3 kYMat(fCos, 0.0, fSin, 0.0, 1.0, 0.0, -fSin, 0.0, fCos);
+
+ return kZMat * (kXMat * kYMat);
+}
+
+//----------------------------------------------------------------------------
+Matrix3 Matrix3::fromEulerAnglesZYX (float fYAngle, float fPAngle,
+ float fRAngle) {
+ float fCos, fSin;
+
+ fCos = cos(fYAngle);
+ fSin = sin(fYAngle);
+ Matrix3 kZMat(fCos, -fSin, 0.0, fSin, fCos, 0.0, 0.0, 0.0, 1.0);
+
+ fCos = cos(fPAngle);
+ fSin = sin(fPAngle);
+ Matrix3 kYMat(fCos, 0.0, fSin, 0.0, 1.0, 0.0, -fSin, 0.0, fCos);
+
+ fCos = cos(fRAngle);
+ fSin = sin(fRAngle);
+ Matrix3 kXMat(1.0, 0.0, 0.0, 0.0, fCos, -fSin, 0.0, fSin, fCos);
+
+ return kZMat * (kYMat * kXMat);
+}
+
+//----------------------------------------------------------------------------
+void Matrix3::tridiagonal (float afDiag[3], float afSubDiag[3]) {
+ // Householder reduction T = Q^t M Q
+ // Input:
+ // mat, symmetric 3x3 matrix M
+ // Output:
+ // mat, orthogonal matrix Q
+ // diag, diagonal entries of T
+ // subd, subdiagonal entries of T (T is symmetric)
+
+ float fA = elt[0][0];
+ float fB = elt[0][1];
+ float fC = elt[0][2];
+ float fD = elt[1][1];
+ float fE = elt[1][2];
+ float fF = elt[2][2];
+
+ afDiag[0] = fA;
+ afSubDiag[2] = 0.0;
+
+ if ( G3D::abs(fC) >= EPSILON ) {
+ float fLength = sqrt(fB * fB + fC * fC);
+ float fInvLength = 1.0 / fLength;
+ fB *= fInvLength;
+ fC *= fInvLength;
+ float fQ = 2.0 * fB * fE + fC * (fF - fD);
+ afDiag[1] = fD + fC * fQ;
+ afDiag[2] = fF - fC * fQ;
+ afSubDiag[0] = fLength;
+ afSubDiag[1] = fE - fB * fQ;
+ elt[0][0] = 1.0;
+ elt[0][1] = 0.0;
+ elt[0][2] = 0.0;
+ elt[1][0] = 0.0;
+ elt[1][1] = fB;
+ elt[1][2] = fC;
+ elt[2][0] = 0.0;
+ elt[2][1] = fC;
+ elt[2][2] = -fB;
+ } else {
+ afDiag[1] = fD;
+ afDiag[2] = fF;
+ afSubDiag[0] = fB;
+ afSubDiag[1] = fE;
+ elt[0][0] = 1.0;
+ elt[0][1] = 0.0;
+ elt[0][2] = 0.0;
+ elt[1][0] = 0.0;
+ elt[1][1] = 1.0;
+ elt[1][2] = 0.0;
+ elt[2][0] = 0.0;
+ elt[2][1] = 0.0;
+ elt[2][2] = 1.0;
+ }
+}
+
+//----------------------------------------------------------------------------
+bool Matrix3::qLAlgorithm (float afDiag[3], float afSubDiag[3]) {
+ // QL iteration with implicit shifting to reduce matrix from tridiagonal
+ // to diagonal
+
+ for (int i0 = 0; i0 < 3; i0++) {
+ const int iMaxIter = 32;
+ int iIter;
+
+ for (iIter = 0; iIter < iMaxIter; iIter++) {
+ int i1;
+
+ for (i1 = i0; i1 <= 1; i1++) {
+ float fSum = G3D::abs(afDiag[i1]) +
+ G3D::abs(afDiag[i1 + 1]);
+
+ if ( G3D::abs(afSubDiag[i1]) + fSum == fSum )
+ break;
+ }
+
+ if ( i1 == i0 )
+ break;
+
+ float fTmp0 = (afDiag[i0 + 1] - afDiag[i0]) / (2.0 * afSubDiag[i0]);
+
+ float fTmp1 = sqrt(fTmp0 * fTmp0 + 1.0);
+
+ if ( fTmp0 < 0.0 )
+ fTmp0 = afDiag[i1] - afDiag[i0] + afSubDiag[i0] / (fTmp0 - fTmp1);
+ else
+ fTmp0 = afDiag[i1] - afDiag[i0] + afSubDiag[i0] / (fTmp0 + fTmp1);
+
+ float fSin = 1.0;
+
+ float fCos = 1.0;
+
+ float fTmp2 = 0.0;
+
+ for (int i2 = i1 - 1; i2 >= i0; i2--) {
+ float fTmp3 = fSin * afSubDiag[i2];
+ float fTmp4 = fCos * afSubDiag[i2];
+
+ if (G3D::abs(fTmp3) >= G3D::abs(fTmp0)) {
+ fCos = fTmp0 / fTmp3;
+ fTmp1 = sqrt(fCos * fCos + 1.0);
+ afSubDiag[i2 + 1] = fTmp3 * fTmp1;
+ fSin = 1.0 / fTmp1;
+ fCos *= fSin;
+ } else {
+ fSin = fTmp3 / fTmp0;
+ fTmp1 = sqrt(fSin * fSin + 1.0);
+ afSubDiag[i2 + 1] = fTmp0 * fTmp1;
+ fCos = 1.0 / fTmp1;
+ fSin *= fCos;
+ }
+
+ fTmp0 = afDiag[i2 + 1] - fTmp2;
+ fTmp1 = (afDiag[i2] - fTmp0) * fSin + 2.0 * fTmp4 * fCos;
+ fTmp2 = fSin * fTmp1;
+ afDiag[i2 + 1] = fTmp0 + fTmp2;
+ fTmp0 = fCos * fTmp1 - fTmp4;
+
+ for (int iRow = 0; iRow < 3; iRow++) {
+ fTmp3 = elt[iRow][i2 + 1];
+ elt[iRow][i2 + 1] = fSin * elt[iRow][i2] +
+ fCos * fTmp3;
+ elt[iRow][i2] = fCos * elt[iRow][i2] -
+ fSin * fTmp3;
+ }
+ }
+
+ afDiag[i0] -= fTmp2;
+ afSubDiag[i0] = fTmp0;
+ afSubDiag[i1] = 0.0;
+ }
+
+ if ( iIter == iMaxIter ) {
+ // should not get here under normal circumstances
+ return false;
+ }
+ }
+
+ return true;
+}
+
+//----------------------------------------------------------------------------
+void Matrix3::eigenSolveSymmetric (float afEigenvalue[3],
+ Vector3 akEigenvector[3]) const {
+ Matrix3 kMatrix = *this;
+ float afSubDiag[3];
+ kMatrix.tridiagonal(afEigenvalue, afSubDiag);
+ kMatrix.qLAlgorithm(afEigenvalue, afSubDiag);
+
+ for (int i = 0; i < 3; i++) {
+ akEigenvector[i][0] = kMatrix[0][i];
+ akEigenvector[i][1] = kMatrix[1][i];
+ akEigenvector[i][2] = kMatrix[2][i];
+ }
+
+ // make eigenvectors form a right--handed system
+ Vector3 kCross = akEigenvector[1].cross(akEigenvector[2]);
+
+ float fDet = akEigenvector[0].dot(kCross);
+
+ if ( fDet < 0.0 ) {
+ akEigenvector[2][0] = - akEigenvector[2][0];
+ akEigenvector[2][1] = - akEigenvector[2][1];
+ akEigenvector[2][2] = - akEigenvector[2][2];
+ }
+}
+
+//----------------------------------------------------------------------------
+void Matrix3::tensorProduct (const Vector3& rkU, const Vector3& rkV,
+ Matrix3& rkProduct) {
+ for (int iRow = 0; iRow < 3; iRow++) {
+ for (int iCol = 0; iCol < 3; iCol++) {
+ rkProduct[iRow][iCol] = rkU[iRow] * rkV[iCol];
+ }
+ }
+}
+
+//----------------------------------------------------------------------------
+
+// Runs in 52 cycles on AMD, 76 cycles on Intel Centrino
+//
+// The loop unrolling is necessary for performance.
+// I was unable to improve performance further by flattening the matrices
+// into float*'s instead of 2D arrays.
+//
+// -morgan
+void Matrix3::_mul(const Matrix3& A, const Matrix3& B, Matrix3& out) {
+ const float* ARowPtr = A.elt[0];
+ float* outRowPtr = out.elt[0];
+ outRowPtr[0] =
+ ARowPtr[0] * B.elt[0][0] +
+ ARowPtr[1] * B.elt[1][0] +
+ ARowPtr[2] * B.elt[2][0];
+ outRowPtr[1] =
+ ARowPtr[0] * B.elt[0][1] +
+ ARowPtr[1] * B.elt[1][1] +
+ ARowPtr[2] * B.elt[2][1];
+ outRowPtr[2] =
+ ARowPtr[0] * B.elt[0][2] +
+ ARowPtr[1] * B.elt[1][2] +
+ ARowPtr[2] * B.elt[2][2];
+
+ ARowPtr = A.elt[1];
+ outRowPtr = out.elt[1];
+
+ outRowPtr[0] =
+ ARowPtr[0] * B.elt[0][0] +
+ ARowPtr[1] * B.elt[1][0] +
+ ARowPtr[2] * B.elt[2][0];
+ outRowPtr[1] =
+ ARowPtr[0] * B.elt[0][1] +
+ ARowPtr[1] * B.elt[1][1] +
+ ARowPtr[2] * B.elt[2][1];
+ outRowPtr[2] =
+ ARowPtr[0] * B.elt[0][2] +
+ ARowPtr[1] * B.elt[1][2] +
+ ARowPtr[2] * B.elt[2][2];
+
+ ARowPtr = A.elt[2];
+ outRowPtr = out.elt[2];
+
+ outRowPtr[0] =
+ ARowPtr[0] * B.elt[0][0] +
+ ARowPtr[1] * B.elt[1][0] +
+ ARowPtr[2] * B.elt[2][0];
+ outRowPtr[1] =
+ ARowPtr[0] * B.elt[0][1] +
+ ARowPtr[1] * B.elt[1][1] +
+ ARowPtr[2] * B.elt[2][1];
+ outRowPtr[2] =
+ ARowPtr[0] * B.elt[0][2] +
+ ARowPtr[1] * B.elt[1][2] +
+ ARowPtr[2] * B.elt[2][2];
+}
+
+//----------------------------------------------------------------------------
+void Matrix3::_transpose(const Matrix3& A, Matrix3& out) {
+ out[0][0] = A.elt[0][0];
+ out[0][1] = A.elt[1][0];
+ out[0][2] = A.elt[2][0];
+ out[1][0] = A.elt[0][1];
+ out[1][1] = A.elt[1][1];
+ out[1][2] = A.elt[2][1];
+ out[2][0] = A.elt[0][2];
+ out[2][1] = A.elt[1][2];
+ out[2][2] = A.elt[2][2];
+}
+
+//-----------------------------------------------------------------------------
+std::string Matrix3::toString() const {
+ return G3D::format("[%g, %g, %g; %g, %g, %g; %g, %g, %g]",
+ elt[0][0], elt[0][1], elt[0][2],
+ elt[1][0], elt[1][1], elt[1][2],
+ elt[2][0], elt[2][1], elt[2][2]);
+}
+
+
+
+} // namespace
+
diff --git a/externals/g3dlite/G3D.lib/source/Matrix4.cpp b/externals/g3dlite/G3D.lib/source/Matrix4.cpp
new file mode 100644
index 00000000000..091af4a9bd5
--- /dev/null
+++ b/externals/g3dlite/G3D.lib/source/Matrix4.cpp
@@ -0,0 +1,433 @@
+/**
+ @file Matrix4.cpp
+
+
+ @maintainer Morgan McGuire, matrix@graphics3d.com
+
+ @created 2003-10-02
+ @edited 2008-07-17
+ */
+
+#include "G3D/platform.h"
+#include "G3D/Matrix4.h"
+#include "G3D/Matrix3.h"
+#include "G3D/Vector4.h"
+#include "G3D/Vector3.h"
+#include "G3D/BinaryInput.h"
+#include "G3D/BinaryOutput.h"
+#include "G3D/CoordinateFrame.h"
+#include "G3D/Rect2D.h"
+
+namespace G3D {
+
+const Matrix4& Matrix4::identity() {
+ static Matrix4 m(
+ 1, 0, 0, 0,
+ 0, 1, 0, 0,
+ 0, 0, 1, 0,
+ 0, 0, 0, 1);
+ return m;
+}
+
+
+const Matrix4& Matrix4::zero() {
+ static Matrix4 m(
+ 0, 0, 0, 0,
+ 0, 0, 0, 0,
+ 0, 0, 0, 0,
+ 0, 0, 0, 0);
+ return m;
+}
+
+
+Matrix4::Matrix4(const class CoordinateFrame& cframe) {
+ for (int r = 0; r < 3; ++r) {
+ for (int c = 0; c < 3; ++c) {
+ elt[r][c] = cframe.rotation[r][c];
+ }
+ elt[r][3] = cframe.translation[r];
+ }
+ elt[3][0] = 0.0f;
+ elt[3][1] = 0.0f;
+ elt[3][2] = 0.0f;
+ elt[3][3] = 1.0f;
+}
+
+Matrix4::Matrix4(const Matrix3& upper3x3, const Vector3& lastCol) {
+ for (int r = 0; r < 3; ++r) {
+ for (int c = 0; c < 3; ++c) {
+ elt[r][c] = upper3x3[r][c];
+ }
+ elt[r][3] = lastCol[r];
+ }
+ elt[3][0] = 0.0f;
+ elt[3][1] = 0.0f;
+ elt[3][2] = 0.0f;
+ elt[3][3] = 1.0f;
+}
+
+
+Matrix3 Matrix4::upper3x3() const {
+ return Matrix3(elt[0][0], elt[0][1], elt[0][2],
+ elt[1][0], elt[1][1], elt[1][2],
+ elt[2][0], elt[2][1], elt[2][2]);
+}
+
+
+Matrix4 Matrix4::orthogonalProjection(
+ const class Rect2D& rect,
+ float nearval,
+ float farval) {
+ return Matrix4::orthogonalProjection(rect.x0(), rect.x1(), rect.y1(), rect.y0(), nearval, farval);
+}
+
+
+Matrix4 Matrix4::orthogonalProjection(
+ float left,
+ float right,
+ float bottom,
+ float top,
+ float nearval,
+ float farval) {
+
+ // Adapted from Mesa. Note that Microsoft (http://msdn.microsoft.com/library/default.asp?url=/library/en-us/opengl/glfunc03_8qnj.asp)
+ // and Linux (http://www.xfree86.org/current/glOrtho.3.html) have different matrices shown in their documentation.
+
+ float x, y, z;
+ float tx, ty, tz;
+
+ x = 2.0f / (right-left);
+ y = 2.0f / (top-bottom);
+ z = -2.0f / (farval-nearval);
+ tx = -(right+left) / (right-left);
+ ty = -(top+bottom) / (top-bottom);
+ tz = -(farval+nearval) / (farval-nearval);
+
+ return
+ Matrix4( x , 0.0f, 0.0f, tx,
+ 0.0f, y , 0.0f, ty,
+ 0.0f, 0.0f, z , tz,
+ 0.0f, 0.0f, 0.0f, 1.0f);
+}
+
+
+Matrix4 Matrix4::perspectiveProjection(
+ float left,
+ float right,
+ float bottom,
+ float top,
+ float nearval,
+ float farval) {
+
+ float x, y, a, b, c, d;
+
+ x = (2.0f*nearval) / (right-left);
+ y = (2.0f*nearval) / (top-bottom);
+ a = (right+left) / (right-left);
+ b = (top+bottom) / (top-bottom);
+
+ if ((float)farval >= (float)inf()) {
+ // Infinite view frustum
+ c = -1.0f;
+ d = -2.0f * nearval;
+ } else {
+ c = -(farval+nearval) / (farval-nearval);
+ d = -(2.0f*farval*nearval) / (farval-nearval);
+ }
+
+ return Matrix4(
+ x, 0, a, 0,
+ 0, y, b, 0,
+ 0, 0, c, d,
+ 0, 0, -1, 0);
+}
+
+
+Matrix4::Matrix4(
+ float r1c1, float r1c2, float r1c3, float r1c4,
+ float r2c1, float r2c2, float r2c3, float r2c4,
+ float r3c1, float r3c2, float r3c3, float r3c4,
+ float r4c1, float r4c2, float r4c3, float r4c4) {
+ elt[0][0] = r1c1; elt[0][1] = r1c2; elt[0][2] = r1c3; elt[0][3] = r1c4;
+ elt[1][0] = r2c1; elt[1][1] = r2c2; elt[1][2] = r2c3; elt[1][3] = r2c4;
+ elt[2][0] = r3c1; elt[2][1] = r3c2; elt[2][2] = r3c3; elt[2][3] = r3c4;
+ elt[3][0] = r4c1; elt[3][1] = r4c2; elt[3][2] = r4c3; elt[3][3] = r4c4;
+}
+
+/**
+ init should be <B>row major</B>.
+ */
+Matrix4::Matrix4(const float* init) {
+ for (int r = 0; r < 4; ++r) {
+ for (int c = 0; c < 4; ++c) {
+ elt[r][c] = init[r * 4 + c];
+ }
+ }
+}
+
+
+Matrix4::Matrix4(const double* init) {
+ for (int r = 0; r < 4; ++r) {
+ for (int c = 0; c < 4; ++c) {
+ elt[r][c] = (float)init[r * 4 + c];
+ }
+ }
+}
+
+
+Matrix4::Matrix4() {
+ for (int r = 0; r < 4; ++r) {
+ for (int c = 0; c < 4; ++c) {
+ elt[r][c] = 0;
+ }
+ }
+}
+
+
+void Matrix4::setRow(int r, const Vector4& v) {
+ for (int c = 0; c < 4; ++c) {
+ elt[r][c] = v[c];
+ }
+}
+
+
+void Matrix4::setColumn(int c, const Vector4& v) {
+ for (int r = 0; r < 4; ++r) {
+ elt[r][c] = v[r];
+ }
+}
+
+
+Vector4 Matrix4::getRow(int r) const {
+ return row(r);
+}
+
+
+const Vector4& Matrix4::row(int r) const {
+ return reinterpret_cast<const Vector4*>(elt[r])[0];
+}
+
+
+Vector4 Matrix4::getColumn(int c) const {
+ return column(c);
+}
+
+Vector4 Matrix4::column(int c) const {
+ Vector4 v;
+ for (int r = 0; r < 4; ++r) {
+ v[r] = elt[r][c];
+ }
+ return v;
+}
+
+
+Matrix4 Matrix4::operator*(const Matrix4& other) const {
+ Matrix4 result;
+ for (int r = 0; r < 4; ++r) {
+ for (int c = 0; c < 4; ++c) {
+ for (int i = 0; i < 4; ++i) {
+ result.elt[r][c] += elt[r][i] * other.elt[i][c];
+ }
+ }
+ }
+
+ return result;
+}
+
+
+Matrix4 Matrix4::operator*(const float s) const {
+ Matrix4 result;
+ for (int r = 0; r < 4; ++r) {
+ for (int c = 0; c < 4; ++c) {
+ result.elt[r][c] = elt[r][c] * s;
+ }
+ }
+
+ return result;
+}
+
+
+Vector3 Matrix4::homoMul(const class Vector3& v, float w) const {
+ Vector4 r = (*this) * Vector4(v, w);
+ return r.xyz() * (1.0f / r.w);
+}
+
+
+Vector4 Matrix4::operator*(const Vector4& vector) const {
+ Vector4 result(0,0,0,0);
+ for (int r = 0; r < 4; ++r) {
+ for (int c = 0; c < 4; ++c) {
+ result[r] += elt[r][c] * vector[c];
+ }
+ }
+
+ return result;
+}
+
+
+Matrix4 Matrix4::transpose() const {
+ Matrix4 result;
+ for (int r = 0; r < 4; ++r) {
+ for (int c = 0; c < 4; ++c) {
+ result.elt[c][r] = elt[r][c];
+ }
+ }
+
+ return result;
+}
+
+
+bool Matrix4::operator!=(const Matrix4& other) const {
+ return ! (*this == other);
+}
+
+
+bool Matrix4::operator==(const Matrix4& other) const {
+
+ // If the bit patterns are identical, they must be
+ // the same matrix. If not, they *might* still have
+ // equal elements due to floating point weirdness.
+ if (memcmp(this, &other, sizeof(Matrix4) == 0)) {
+ return true;
+ }
+
+ for (int r = 0; r < 4; ++r) {
+ for (int c = 0; c < 4; ++c) {
+ if (elt[r][c] != other.elt[r][c]) {
+ return false;
+ }
+ }
+ }
+
+ return true;
+}
+
+
+float Matrix4::determinant() const {
+ // Determinant is the dot product of the first row and the first row
+ // of cofactors (i.e. the first col of the adjoint matrix)
+ return cofactor().getRow(0).dot(getRow(0));
+}
+
+
+Matrix4 Matrix4::adjoint() const {
+ return cofactor().transpose();
+}
+
+
+Matrix4 Matrix4::inverse() const {
+ // Inverse = adjoint / determinant
+
+ Matrix4 A = adjoint();
+
+ // Determinant is the dot product of the first row and the first row
+ // of cofactors (i.e. the first col of the adjoint matrix)
+ float det = A.getColumn(0).dot(getRow(0));
+
+ return A * (1.0f / det);
+}
+
+
+Matrix4 Matrix4::cofactor() const {
+ Matrix4 out;
+
+ // We'll use i to incrementally compute -1 ^ (r+c)
+ int i = 1;
+
+ for (int r = 0; r < 4; ++r) {
+ for (int c = 0; c < 4; ++c) {
+ // Compute the determinant of the 3x3 submatrix
+ float det = subDeterminant(r, c);
+ out.elt[r][c] = i * det;
+ i = -i;
+ }
+ i = -i;
+ }
+
+ return out;
+}
+
+
+float Matrix4::subDeterminant(int excludeRow, int excludeCol) const {
+ // Compute non-excluded row and column indices
+ int row[3];
+ int col[3];
+
+ for (int i = 0; i < 3; ++i) {
+ row[i] = i;
+ col[i] = i;
+
+ if (i >= excludeRow) {
+ ++row[i];
+ }
+ if (i >= excludeCol) {
+ ++col[i];
+ }
+ }
+
+ // Compute the first row of cofactors
+ float cofactor00 =
+ elt[row[1]][col[1]] * elt[row[2]][col[2]] -
+ elt[row[1]][col[2]] * elt[row[2]][col[1]];
+
+ float cofactor10 =
+ elt[row[1]][col[2]] * elt[row[2]][col[0]] -
+ elt[row[1]][col[0]] * elt[row[2]][col[2]];
+
+ float cofactor20 =
+ elt[row[1]][col[0]] * elt[row[2]][col[1]] -
+ elt[row[1]][col[1]] * elt[row[2]][col[0]];
+
+ // Product of the first row and the cofactors along the first row
+ return
+ elt[row[0]][col[0]] * cofactor00 +
+ elt[row[0]][col[1]] * cofactor10 +
+ elt[row[0]][col[2]] * cofactor20;
+}
+
+
+CoordinateFrame Matrix4::approxCoordinateFrame() const {
+ CoordinateFrame cframe;
+
+ for (int r = 0; r < 3; ++r) {
+ for (int c = 0; c < 3; ++c) {
+ cframe.rotation[r][c] = elt[r][c];
+ }
+ cframe.translation[r] = elt[r][3];
+ }
+
+ // Ensure that the rotation matrix is orthonormal
+ cframe.rotation.orthonormalize();
+
+ return cframe;
+}
+
+
+void Matrix4::serialize(class BinaryOutput& b) const {
+ for (int r = 0; r < 4; ++r) {
+ for (int c = 0; c < 4; ++c) {
+ b.writeFloat32(elt[r][c]);
+ }
+ }
+}
+
+
+void Matrix4::deserialize(class BinaryInput& b) {
+ for (int r = 0; r < 4; ++r) {
+ for (int c = 0; c < 4; ++c) {
+ elt[r][c] = b.readFloat32();
+ }
+ }
+}
+
+std::string Matrix4::toString() const {
+ return G3D::format("[%g, %g, %g, %g; %g, %g, %g, %g; %g, %g, %g, %g; %g, %g, %g, %g]",
+ elt[0][0], elt[0][1], elt[0][2], elt[0][3],
+ elt[1][0], elt[1][1], elt[1][2], elt[1][3],
+ elt[2][0], elt[2][1], elt[2][2], elt[2][3],
+ elt[3][0], elt[3][1], elt[3][2], elt[3][3]);
+}
+
+} // namespace
+
+
diff --git a/externals/g3dlite/G3D.lib/source/MeshAlg.cpp b/externals/g3dlite/G3D.lib/source/MeshAlg.cpp
new file mode 100644
index 00000000000..24b90ab5a92
--- /dev/null
+++ b/externals/g3dlite/G3D.lib/source/MeshAlg.cpp
@@ -0,0 +1,733 @@
+/**
+ @file MeshAlg.cpp
+
+ @maintainer Morgan McGuire, matrix@graphics3d.com
+ @created 2003-09-14
+ @edited 2008-09-03
+
+ Copyright 2000-2008, Morgan McGuire.
+ All rights reserved.
+
+ */
+
+#include "G3D/MeshAlg.h"
+#include "G3D/Table.h"
+#include "G3D/Set.h"
+#include "G3D/Box.h"
+#include "G3D/Sphere.h"
+#include "G3D/vectorMath.h"
+#include "G3D/AABox.h"
+
+#include <climits>
+
+namespace G3D {
+
+const int MeshAlg::Face::NONE = INT_MIN;
+
+void MeshAlg::generateGrid(
+ Array<Vector3>& vertex,
+ Array<Vector2>& texCoord,
+ Array<int>& index,
+ int wCells,
+ int hCells,
+ const Vector2& textureScale,
+ bool spaceCentered,
+ bool twoSided,
+ const CoordinateFrame& xform) {
+
+ vertex.fastClear();
+ texCoord.fastClear();
+ index.fastClear();
+
+ // Generate vertices
+ for (int z = 0; z <= hCells; ++z) {
+ for (int x = 0; x <= wCells; ++x) {
+ Vector3 v(x / (float)wCells, 0, z / (float)hCells);
+
+ Vector2 t = v.xz() * textureScale;
+
+ texCoord.append(t);
+
+ if (spaceCentered) {
+ v -= Vector3(0.5f, 0, 0.5f);
+ }
+ v = xform.pointToWorldSpace(v);
+ vertex.append(v);
+ }
+ }
+
+ // Generate indices
+ for (int z = 0; z < hCells; ++z) {
+ for (int x = 0; x < wCells; ++x) {
+ int A = x + z * (wCells + 1);
+ int B = A + 1;
+ int C = A + (wCells + 1);
+ int D = C + 1;
+
+ // A B
+ // *-----*
+ // | \ |
+ // | \ |
+ // *-----*
+ // C D
+
+ index.append(A, D, B);
+ index.append(A, C, D);
+ }
+ }
+
+ if (twoSided) {
+ // The index array needs to have reversed winding for the bottom
+ // and offset by the original number of vertices
+ Array<int> ti = index;
+ ti.reverse();
+ for (int i = 0; i < ti.size(); ++i) {
+ ti[i] += vertex.size();
+ }
+ index.append(ti);
+
+ // Duplicate the arrays
+ vertex.append(Array<Vector3>(vertex));
+ texCoord.append(Array<Vector2>(texCoord));
+ }
+}
+
+MeshAlg::Face::Face() {
+ for (int i = 0; i < 3; ++i) {
+ edgeIndex[i] = 0;
+ vertexIndex[i] = 0;
+ }
+}
+
+
+MeshAlg::Edge::Edge() {
+ for (int i = 0; i < 2; ++i) {
+ vertexIndex[i] = 0;
+ // Negative face indices are faces that don't exist
+ faceIndex[i] = -1;
+ }
+}
+
+
+MeshAlg::Geometry& MeshAlg::Geometry::operator=(const MeshAlg::Geometry& src) {
+ vertexArray.resize(src.vertexArray.size());
+ normalArray.resize(src.vertexArray.size());
+
+ System::memcpy(vertexArray.getCArray(), src.vertexArray.getCArray(), sizeof(Vector3)*vertexArray.size());
+ System::memcpy(normalArray.getCArray(), src.normalArray.getCArray(), sizeof(Vector3)*normalArray.size());
+
+ return *this;
+}
+
+
+void MeshAlg::computeNormals(
+ Geometry& geometry,
+ const Array<int>& indexArray) {
+
+ Array<Face> faceArray;
+ Array<Vertex> vertexArray;
+ Array<Edge> edgeArray;
+ Array<Vector3> faceNormalArray;
+
+ computeAdjacency(geometry.vertexArray, indexArray, faceArray, edgeArray, vertexArray);
+
+ computeNormals(geometry.vertexArray, faceArray, vertexArray,
+ geometry.normalArray, faceNormalArray);
+}
+
+
+void MeshAlg::computeNormals(
+ const Array<Vector3>& vertexGeometry,
+ const Array<Face>& faceArray,
+ const Array< Array<int> >& adjacentFaceArray,
+ Array<Vector3>& vertexNormalArray,
+ Array<Vector3>& faceNormalArray) {
+
+ // Construct a fake vertex array for backwards compatibility
+ Array<Vertex> fakeVertexArray(adjacentFaceArray.size());
+
+ for (int v = 0; v < adjacentFaceArray.size(); ++v) {
+ fakeVertexArray[v].faceIndex = adjacentFaceArray[v];
+ // We leave out the edges because they aren't used to compute normals
+ }
+
+ computeNormals(vertexGeometry, faceArray, fakeVertexArray,
+ vertexNormalArray, faceNormalArray);
+}
+
+
+void MeshAlg::computeNormals(
+ const Array<Vector3>& vertexGeometry,
+ const Array<Face>& faceArray,
+ const Array<Vertex>& vertexArray,
+ Array<Vector3>& vertexNormalArray,
+ Array<Vector3>& faceNormalArray) {
+
+ // Face normals (not unit length)
+ faceNormalArray.resize(faceArray.size());
+ for (int f = 0; f < faceArray.size(); ++f) {
+ const Face& face = faceArray[f];
+
+ Vector3 vertex[3];
+ for (int j = 0; j < 3; ++j) {
+ vertex[j] = vertexGeometry[face.vertexIndex[j]];
+ debugAssert(vertex[j].isFinite());
+ }
+
+ faceNormalArray[f] = (vertex[1] - vertex[0]).cross(vertex[2] - vertex[0]);
+# ifdef G3D_DEBUG
+ const Vector3& N = faceNormalArray[f];
+ debugAssert(N.isFinite());
+# endif
+ }
+
+ // Per-vertex normals, computed by averaging
+ vertexNormalArray.resize(vertexGeometry.size());
+ for (int v = 0; v < vertexNormalArray.size(); ++v) {
+ Vector3 sum = Vector3::zero();
+ for (int k = 0; k < vertexArray[v].faceIndex.size(); ++k) {
+ const int f = vertexArray[v].faceIndex[k];
+ sum += faceNormalArray[f];
+ }
+ vertexNormalArray[v] = sum.directionOrZero();
+# ifdef G3D_DEBUG
+ const Vector3& N = vertexNormalArray[v];
+ debugAssert(N.isUnit() || N.isZero());
+# endif
+ }
+
+
+ for (int f = 0; f < faceArray.size(); ++f) {
+ faceNormalArray[f] = faceNormalArray[f].directionOrZero();
+# ifdef G3D_DEBUG
+ const Vector3& N = faceNormalArray[f];
+ debugAssert(N.isUnit() || N.isZero());
+# endif
+ }
+
+}
+
+
+void MeshAlg::computeFaceNormals(
+ const Array<Vector3>& vertexArray,
+ const Array<MeshAlg::Face>& faceArray,
+ Array<Vector3>& faceNormals,
+ bool normalize) {
+
+ faceNormals.resize(faceArray.size());
+
+ for (int f = 0; f < faceArray.size(); ++f) {
+ const MeshAlg::Face& face = faceArray[f];
+
+ const Vector3& v0 = vertexArray[face.vertexIndex[0]];
+ const Vector3& v1 = vertexArray[face.vertexIndex[1]];
+ const Vector3& v2 = vertexArray[face.vertexIndex[2]];
+
+ faceNormals[f] = (v1 - v0).cross(v2 - v0);
+ }
+
+ if (normalize) {
+ for (int f = 0; f < faceArray.size(); ++f) {
+ faceNormals[f] = faceNormals[f].direction();
+ }
+ }
+}
+
+
+void MeshAlg::identifyBackfaces(
+ const Array<Vector3>& vertexArray,
+ const Array<MeshAlg::Face>& faceArray,
+ const Vector4& HP,
+ Array<bool>& backface) {
+
+ Vector3 P = HP.xyz();
+
+ backface.resize(faceArray.size());
+
+ if (fuzzyEq(HP.w, 0.0)) {
+ // Infinite case
+ for (int f = faceArray.size() - 1; f >= 0; --f) {
+ const MeshAlg::Face& face = faceArray[f];
+
+ const Vector3& v0 = vertexArray[face.vertexIndex[0]];
+ const Vector3& v1 = vertexArray[face.vertexIndex[1]];
+ const Vector3& v2 = vertexArray[face.vertexIndex[2]];
+
+ const Vector3 N = (v1 - v0).cross(v2 - v0);
+
+ backface[f] = N.dot(P) < 0;
+ }
+ } else {
+ // Finite case
+ for (int f = faceArray.size() - 1; f >= 0; --f) {
+ const MeshAlg::Face& face = faceArray[f];
+
+ const Vector3& v0 = vertexArray[face.vertexIndex[0]];
+ const Vector3& v1 = vertexArray[face.vertexIndex[1]];
+ const Vector3& v2 = vertexArray[face.vertexIndex[2]];
+
+ const Vector3 N = (v1 - v0).cross(v2 - v0);
+
+ backface[f] = N.dot(P - v0) < 0;
+ }
+ }
+}
+
+
+void MeshAlg::identifyBackfaces(
+ const Array<Vector3>& vertexArray,
+ const Array<MeshAlg::Face>& faceArray,
+ const Vector4& HP,
+ Array<bool>& backface,
+ const Array<Vector3>& faceNormals) {
+
+ Vector3 P = HP.xyz();
+
+ backface.resize(faceArray.size());
+
+ if (fuzzyEq(HP.w, 0.0)) {
+ // Infinite case
+ for (int f = faceArray.size() - 1; f >= 0; --f) {
+ const Vector3& N = faceNormals[f];
+ backface[f] = N.dot(P) < 0;
+ }
+ } else {
+ // Finite case
+ for (int f = faceArray.size() - 1; f >= 0; --f) {
+ const MeshAlg::Face& face = faceArray[f];
+ const Vector3& v0 = vertexArray[face.vertexIndex[0]];
+ const Vector3& N = faceNormals[f];
+
+ backface[f] = N.dot(P - v0) < 0;
+ }
+ }
+}
+
+
+void MeshAlg::createIndexArray(int n, Array<int>& array, int start, int run, int skip) {
+ debugAssert(skip >= 0);
+ debugAssert(run >= 0);
+
+ array.resize(n);
+ if (skip == 0) {
+ for (int i = 0; i < n; ++i) {
+ array[i] = start + i;
+ }
+ } else {
+ int rcount = 0;
+ int j = start;
+ for (int i = 0; i < n; ++i) {
+ array[i] = j;
+
+ ++j;
+ ++rcount;
+
+ if (rcount == run) {
+ rcount = 0;
+ j += skip;
+ }
+ }
+ }
+}
+
+
+void MeshAlg::computeAreaStatistics(
+ const Array<Vector3>& vertexArray,
+ const Array<int>& indexArray,
+ double& minEdgeLength,
+ double& meanEdgeLength,
+ double& medianEdgeLength,
+ double& maxEdgeLength,
+ double& minFaceArea,
+ double& meanFaceArea,
+ double& medianFaceArea,
+ double& maxFaceArea) {
+
+ debugAssert(indexArray.size() % 3 == 0);
+
+ Array<double> area(indexArray.size() / 3);
+ Array<double> magnitude(indexArray.size());
+
+ for (int i = 0; i < indexArray.size(); i += 3) {
+ const Vector3& v0 = vertexArray[indexArray[i]];
+ const Vector3& v1 = vertexArray[indexArray[i + 1]];
+ const Vector3& v2 = vertexArray[indexArray[i + 2]];
+
+ area[i / 3] = (v1 - v0).cross(v2 - v0).magnitude() / 2.0;
+ magnitude[i] = (v1 - v0).magnitude();
+ magnitude[i + 1] = (v2 - v1).magnitude();
+ magnitude[i + 2] = (v0 - v2).magnitude();
+ }
+
+ area.sort();
+ magnitude.sort();
+
+ minEdgeLength = max(0.0, magnitude[0]);
+ maxEdgeLength = max(0.0, magnitude.last());
+ medianEdgeLength = max(0.0, magnitude[magnitude.size() / 2]);
+ meanEdgeLength = 0;
+ for (int i = 0; i < magnitude.size(); ++i) {
+ meanEdgeLength += magnitude[i];
+ }
+ meanEdgeLength /= magnitude.size();
+
+ minFaceArea = max(0.0, area[0]);
+ maxFaceArea = max(0.0, area.last());
+ medianFaceArea = max(0.0, area[area.size() / 2]);
+ meanFaceArea = 0;
+ for (int i = 0; i < area.size(); ++i) {
+ meanFaceArea += area[i];
+ }
+ meanFaceArea /= area.size();
+
+
+ // Make sure round-off hasn't pushed values less than zero
+ meanFaceArea = max(0.0, meanFaceArea);
+ meanEdgeLength = max(0.0, meanEdgeLength);
+}
+
+
+int MeshAlg::countBoundaryEdges(const Array<MeshAlg::Edge>& edgeArray) {
+ int b = 0;
+
+ for (int i = 0; i < edgeArray.size(); ++i) {
+ if ((edgeArray[i].faceIndex[0] == MeshAlg::Face::NONE) !=
+ (edgeArray[i].faceIndex[1] == MeshAlg::Face::NONE)) {
+ ++b;
+ }
+ }
+
+ return b;
+}
+
+void MeshAlg::computeBounds(
+ const Array<Vector3>& vertexArray,
+ const Array<int>& indexArray,
+ Box& box,
+ Sphere& sphere) {
+
+ Array<Vector3> newArray(indexArray.size());
+ for (int i = 0; i < indexArray.size(); ++i) {
+ newArray[i] = vertexArray[indexArray[i]];
+ }
+ computeBounds(newArray, box, sphere);
+}
+
+
+void MeshAlg::computeBounds(
+ const Array<Vector3>& vertexArray,
+ Box& box,
+ Sphere& sphere) {
+
+ Vector3 xmin, xmax, ymin, ymax, zmin, zmax;
+
+ // FIRST PASS: find 6 minima/maxima points
+ xmin.x = ymin.y = zmin.z = inf();
+ xmax.x = ymax.y = zmax.z = -inf();
+
+ for (int v = 0; v < vertexArray.size(); ++v) {
+ const Vector3& vertex = vertexArray[v];
+
+ if (vertex.x < xmin.x) {
+ xmin = vertex;
+ }
+
+ if (vertex.x > xmax.x) {
+ xmax = vertex;
+ }
+
+ if (vertex.y < ymin.y) {
+ ymin = vertex;
+ }
+
+ if (vertex.y > ymax.y) {
+ ymax = vertex;
+ }
+
+ if (vertex.z < zmin.z) {
+ zmin = vertex;
+ }
+
+ if (vertex.z > zmax.z) {
+ zmax = vertex;
+ }
+ }
+
+ // Set points dia1 & dia2 to the maximally separated pair
+ Vector3 dia1 = xmin;
+ Vector3 dia2 = xmax;
+ {
+ // Set xspan = distance between the 2 points xmin & xmax (squared)
+ double xspan = (xmax - xmin).squaredMagnitude();
+
+ // Same for y & z spans
+ double yspan = (ymax - ymin).squaredMagnitude();
+ double zspan = (zmax - zmin).squaredMagnitude();
+
+ double maxspan = xspan;
+
+ if (yspan > maxspan) {
+ maxspan = yspan;
+ dia1 = ymin;
+ dia2 = ymax;
+ }
+
+ if (zspan > maxspan) {
+ maxspan = zspan;
+ dia1 = zmin;
+ dia2 = zmax;
+ }
+ }
+
+
+ // dia1, dia2 is a diameter of initial sphere
+
+ // calc initial center
+ Vector3 center = (dia1 + dia2) / 2.0;
+
+ // calculate initial radius^2 and radius
+ Vector3 d = dia2 - sphere.center;
+
+ double radSq = d.squaredMagnitude();
+ double rad = sqrt(radSq);
+
+ // SECOND PASS: increment current sphere
+ double old_to_p, old_to_new;
+
+ for (int v = 0; v < vertexArray.size(); ++v) {
+ const Vector3& vertex = vertexArray[v];
+
+ d = vertex - center;
+
+ double old_to_p_sq = d.squaredMagnitude();
+
+ // do r^2 test first
+ if (old_to_p_sq > radSq) {
+ // this point is outside of current sphere
+ old_to_p = sqrt(old_to_p_sq);
+
+ // calc radius of new sphere
+ rad = (rad + old_to_p) / 2.0;
+
+ // for next r^2 compare
+ radSq = rad * rad;
+ old_to_new = old_to_p - rad;
+
+ // calc center of new sphere
+ center = (rad * center + old_to_new * vertex) / old_to_p;
+ }
+ }
+
+ const Vector3 min(xmin.x, ymin.y, zmin.z);
+ const Vector3 max(xmax.x, ymax.y, zmax.z);
+
+ box = Box(min, max);
+
+ const double boxRadSq = (max - min).squaredMagnitude() * 0.25;
+
+ if (boxRadSq >= radSq){
+ if (isNaN(center.x) || ! isFinite(rad)) {
+ sphere = Sphere(Vector3::zero(), inf());
+ } else {
+ sphere = Sphere(center, rad);
+ }
+ }else{
+ sphere = Sphere((max + min) * 0.5, sqrt(boxRadSq));
+ }
+}
+
+
+void MeshAlg::computeTangentVectors(
+ const Vector3& normal,
+ const Vector3 position[3],
+ const Vector2 texCoord[3],
+ Vector3& tangent,
+ Vector3& binormal) {
+
+ Vector3 v[3];
+ Vector2 t[3];
+
+ // TODO: don't need the copy
+ // Make a copy so that we can sort
+ for (int i = 0; i < 3; ++i) {
+ v[i] = position[i];
+ t[i] = texCoord[i];
+ }
+
+ /////////////////////////////////////////////////
+ // Begin by computing the tangent
+
+ // Bubble sort the vertices by decreasing texture coordinate y.
+ if (t[0].y < t[1].y) {
+ std::swap(v[0], v[1]);
+ std::swap(t[0], t[1]);
+ }
+
+ // t0 >= t1
+
+ if (t[0].y < t[2].y) {
+ std::swap(v[0], v[2]);
+ std::swap(t[0], t[2]);
+ }
+
+ // t0 >= t2, t0 >= t1
+
+ if (t[1].y < t[2].y) {
+ std::swap(v[1], v[2]);
+ std::swap(t[1], t[2]);
+ }
+
+ // t0 >= t1 >= t2
+
+ float amount;
+
+ // Compute the direction of constant y.
+ if (fuzzyEq(t[2].y, t[0].y)) {
+ // Degenerate case-- the texture coordinates do not vary across this
+ // triangle.
+ amount = 1.0;
+ } else {
+ // Solve lerp(t[0].y, t[2].y, amount) = t[1].y for amount:
+ //
+ // t0 + (t2 - t0) * a = t1
+ // a = (t1 - t0) / (t2 - t0)
+
+ amount = (t[1].y - t[0].y) / (t[2].y - t[0].y);
+ }
+
+ tangent = lerp(v[0], v[2], amount) - v[1];
+
+ // Make sure the tangent points in the right direction and is
+ // perpendicular to the normal.
+ if (lerp(t[0].x, t[2].x, amount) < t[1].x) {
+ tangent = -tangent;
+ }
+
+ // TODO: do we need this? We take this component off
+ // at the end anyway
+ tangent -= tangent.dot(normal) * normal;
+
+ // Normalize the tangent so it contributes
+ // equally at the vertex (TODO: do we need this?)
+ if (fuzzyEq(tangent.magnitude(), 0.0)) {
+ tangent = Vector3::unitX();
+ } else {
+ tangent = tangent.direction();
+ }
+
+ //////////////////////////////////////////////////
+ // Now compute the binormal (same code, but in x)
+
+ // Sort the vertices by texture coordinate x.
+ if (t[0].x < t[1].x) {
+ std::swap(v[0], v[1]);
+ std::swap(t[0], t[1]);
+ }
+
+ if (t[0].x < t[2].x) {
+ std::swap(v[0], v[2]);
+ std::swap(t[0], t[2]);
+ }
+
+ if (t[1].x < t[2].x) {
+ std::swap(v[1], v[2]);
+ std::swap(t[1], t[2]);
+ }
+
+ // Compute the direction of constant x.
+ if (fuzzyEq(t[2].x, t[0].x)) {
+ amount = 1.0;
+ } else {
+ amount = (t[1].x - t[0].x) / (t[2].x - t[0].x);
+ }
+
+ binormal = lerp(v[0], v[2], amount) - v[1];
+
+ // Make sure the binormal points in the right direction and is
+ // perpendicular to the normal.
+ if (lerp(t[0].y, t[2].y, amount) < t[1].y) {
+ binormal = -binormal;
+ }
+
+ binormal -= binormal.dot(normal) * normal;
+
+ // Normalize the binormal so that it contributes
+ // an equal amount to the per-vertex value (TODO: do we need this?
+ // Nelson Max showed that we don't for computing per-vertex normals)
+ if (fuzzyEq(binormal.magnitude(), 0.0)) {
+ binormal = Vector3::unitZ();
+ } else {
+ binormal = binormal.direction();
+ }
+
+ // This computation gives the opposite convention of what we want.
+ binormal = -binormal;
+
+}
+
+
+void MeshAlg::computeTangentSpaceBasis(
+ const Array<Vector3>& vertexArray,
+ const Array<Vector2>& texCoordArray,
+ const Array<Vector3>& vertexNormalArray,
+ const Array<Face>& faceArray,
+ Array<Vector3>& tangent,
+ Array<Vector3>& binormal) {
+
+ debugAssertM(faceArray.size() != 0, "Unable to calculate valid tangent space without faces.");
+
+ // The three vertices and texCoords of each face
+ Vector3 position[3];
+ Vector2 texCoord[3];
+ Vector3 t, b;
+
+ tangent.resize(vertexArray.size());
+ binormal.resize(vertexArray.size());
+
+ // Zero the output arrays.
+ System::memset(tangent.getCArray(), 0, sizeof(Vector3) * tangent.size());
+ System::memset(binormal.getCArray(), 0, sizeof(Vector3) * binormal.size());
+
+ // Iterate over faces, computing the tangent vectors for each
+ // vertex. Accumulate those into the tangent and binormal arrays
+ // and then orthonormalize at the end.
+
+ for (int f = 0; f < faceArray.size(); ++f) {
+ const Face& face = faceArray[f];
+
+ for (int v = 0; v < 3; ++v) {
+ int i = face.vertexIndex[v];
+ position[v] = vertexArray[i];
+ texCoord[v] = texCoordArray[i];
+ }
+
+ const Vector3 faceNormal((position[1] - position[0]).cross(position[2] - position[0]).direction());
+ computeTangentVectors(faceNormal, position, texCoord, t, b);
+
+ for (int v = 0; v < 3; ++v) {
+ int i = face.vertexIndex[v];
+ tangent[i] += t;
+ binormal[i] += b;
+ }
+ }
+
+ // Normalize the basis vectors
+ for (int v = 0; v < vertexArray.size(); ++v) {
+ // Remove the component parallel to the normal
+ const Vector3& N = vertexNormalArray[v];
+ debugAssertM(N.isUnit() || N.isZero(), "Input normals must have unit length");
+
+ tangent[v] -= tangent[v].dot(N) * N;
+ binormal[v] -= binormal[v].dot(N) * N;
+
+ // Normalize
+ tangent[v] = tangent[v].directionOrZero();
+ binormal[v] = binormal[v].directionOrZero();
+
+ // Note that the tangent and binormal might not be perpendicular anymore
+ }
+}
+
+
+
+} // G3D namespace
diff --git a/externals/g3dlite/G3D.lib/source/MeshAlgAdjacency.cpp b/externals/g3dlite/G3D.lib/source/MeshAlgAdjacency.cpp
new file mode 100644
index 00000000000..a8b35f32c86
--- /dev/null
+++ b/externals/g3dlite/G3D.lib/source/MeshAlgAdjacency.cpp
@@ -0,0 +1,729 @@
+/**
+ @file MeshAlgAdjacency.cpp
+
+ @maintainer Morgan McGuire, matrix@graphics3d.com
+ @created 2003-09-14
+ @edited 2005-02-24
+
+ Copyright 2000-2003, Morgan McGuire.
+ All rights reserved.
+
+ */
+
+#include "G3D/Table.h"
+#include "G3D/MeshAlg.h"
+#include "G3D/Set.h"
+
+
+namespace G3D {
+
+/**
+ A half [i.e. directed] edge.
+ */
+class MeshDirectedEdgeKey {
+public:
+
+ /** vertexIndex[0] <= vertexIndex[1] */
+ int vertexIndex[2];
+
+ MeshDirectedEdgeKey() {}
+
+ MeshDirectedEdgeKey(
+ const int i0,
+ const int i1) {
+
+ if (i0 <= i1) {
+ vertexIndex[0] = i0;
+ vertexIndex[1] = i1;
+ } else {
+ vertexIndex[0] = i1;
+ vertexIndex[1] = i0;
+ }
+ }
+
+
+ bool operator==(const MeshDirectedEdgeKey& e2) const {
+ for (int i = 0; i < 2; ++i) {
+ if (vertexIndex[i] != e2.vertexIndex[i]) {
+ return false;
+ }
+ }
+ return true;
+ }
+};
+
+}
+
+template<> struct HashTrait<G3D::MeshDirectedEdgeKey> {
+ static size_t hashCode(const G3D::MeshDirectedEdgeKey& key) {
+ return key.vertexIndex[0] + (key.vertexIndex[1] << 16);
+ }
+};
+
+namespace G3D {
+
+/**
+ A hashtable mapping edges to lists of indices for
+ faces. This is used instead of Table because of the
+ special logic in insert.
+
+ Used only for MeshAlg::computeAdjacency.
+
+ In the face lists, index <I>f</I> >= 0 indicates that
+ <I>f</I> contains the edge as a forward edge. Index <I>f</I> < 0
+ indicates that ~<I>f</I> contains the edge as a backward edge.
+ */
+class MeshEdgeTable {
+public:
+ typedef Table<MeshDirectedEdgeKey, Array<int> > ET;
+
+private:
+
+ ET table;
+
+public:
+
+ /**
+ Clears the table.
+ */
+ void clear() {
+ table.clear();
+ }
+
+ /**
+ Inserts the faceIndex into the edge's face list.
+ The index may be a negative number indicating a backface.
+ */
+ void insert(const MeshDirectedEdgeKey& edge, int faceIndex) {
+
+ // debugAssertM((table.size() > 20) && (table.debugGetLoad() < 0.5 || table.debugGetNumBuckets() < 20),
+ // "MeshEdgeTable is using a poor hash function.");
+
+ if (! table.containsKey(edge)) {
+ // First time
+ Array<int> x(1);
+ x[0] = faceIndex;
+ table.set(edge, x);
+ } else {
+ table[edge].append(faceIndex);
+ }
+ }
+
+ /**
+ Returns the face list for a given edge
+ */
+ const Array<int>& get(const MeshDirectedEdgeKey& edge) {
+ return table[edge];
+ }
+
+ ET::Iterator begin() {
+ return table.begin();
+ }
+
+ const ET::Iterator end() const {
+ return table.end();
+ }
+
+};
+
+
+/**
+ edgeTable[edgeKey] is a list of faces containing
+
+ Used and cleared by MeshModel::computeAdjacency()
+ */
+static MeshEdgeTable edgeTable;
+
+/**
+ Assigns the edge index into the next unassigned edge
+ index. The edge index may be negative, indicating
+ a reverse edge.
+ */
+static void assignEdgeIndex(MeshAlg::Face& face, int e) {
+ for (int i = 0; i < 3; ++i) {
+ if (face.edgeIndex[i] == MeshAlg::Face::NONE) {
+ face.edgeIndex[i] = e;
+ return;
+ }
+ }
+
+ debugAssertM(false, "Face has already been assigned 3 edges");
+}
+
+
+void MeshAlg::computeAdjacency(
+ const Array<Vector3>& vertexGeometry,
+ const Array<int>& indexArray,
+ Array<Face>& faceArray,
+ Array<Edge>& edgeArray,
+ Array< Array<int> >& adjacentFaceArray) {
+
+ Array<Vertex> vertexArray;
+
+ computeAdjacency(vertexGeometry, indexArray, faceArray, edgeArray, vertexArray);
+
+ // Convert the vertexArray into adjacentFaceArray
+ adjacentFaceArray.clear();
+ adjacentFaceArray.resize(vertexArray.size());
+ for (int v = 0; v < adjacentFaceArray.size(); ++v) {
+ adjacentFaceArray[v] = vertexArray[v].faceIndex;
+ }
+}
+
+
+void MeshAlg::computeAdjacency(
+ const Array<Vector3>& vertexGeometry,
+ const Array<int>& indexArray,
+ Array<Face>& faceArray,
+ Array<Edge>& edgeArray,
+ Array<Vertex>& vertexArray) {
+
+ edgeArray.clear();
+ vertexArray.clear();
+ faceArray.clear();
+ edgeTable.clear();
+
+ // Face normals
+ Array<Vector3> faceNormal;
+
+ // This array has the same size as the vertex array
+ vertexArray.resize(vertexGeometry.size());
+
+ // Iterate through the triangle list
+ for (int q = 0; q < indexArray.size(); q += 3) {
+
+ Vector3 vertex[3];
+ int f = faceArray.size();
+ MeshAlg::Face& face = faceArray.next();
+
+ // Construct the face
+ for (int j = 0; j < 3; ++j) {
+ int v = indexArray[q + j];
+ face.vertexIndex[j] = v;
+ face.edgeIndex[j] = Face::NONE;
+
+ // Store back pointers in the vertices
+ vertexArray[v].faceIndex.append(f);
+
+ // We'll need these vertices to find the face normal
+ vertex[j] = vertexGeometry[v];
+ }
+
+ // Compute the face normal
+ Vector3 N = (vertex[1] - vertex[0]).cross(vertex[2] - vertex[0]);
+ faceNormal.append(N.directionOrZero());
+
+ static const int nextIndex[] = {1, 2, 0};
+
+ // Add each edge to the edge table.
+ for (int j = 0; j < 3; ++j) {
+ const int i0 = indexArray[q + j];
+ const int i1 = indexArray[q + nextIndex[j]];
+
+ const MeshDirectedEdgeKey edge(i0, i1);
+
+ if (i0 == edge.vertexIndex[0]) {
+ // The edge was directed in the same manner as in the face
+ edgeTable.insert(edge, f);
+ } else {
+ // The edge was directed in the opposite manner as in the face
+ edgeTable.insert(edge, ~f);
+ }
+ }
+ }
+
+ // For each edge in the edge table, create an edge in the edge array.
+ // Collapse every 2 edges from adjacent faces.
+
+ MeshEdgeTable::ET::Iterator cur = edgeTable.begin();
+ MeshEdgeTable::ET::Iterator end = edgeTable.end();
+
+ Array<Edge> tempEdgeArray;
+ while (cur != end) {
+ MeshDirectedEdgeKey& edgeKey = cur->key;
+ Array<int>& faceIndexArray = cur->value;
+
+ // Process this edge
+ while (faceIndexArray.size() > 0) {
+
+ // Remove the last index
+ int f0 = faceIndexArray.pop();
+
+ // Find the normal to that face
+ const Vector3& n0 = faceNormal[(f0 >= 0) ? f0 : ~f0];
+
+ bool found = false;
+
+ // We try to find the matching face with the closest
+ // normal. This ensures that we don't introduce a lot
+ // of artificial ridges into flat parts of a mesh.
+ double ndotn = -2;
+ int f1 = -1, i1 = -1;
+
+ // Try to Find the face with the matching edge
+ for (int i = faceIndexArray.size() - 1; i >= 0; --i) {
+ int f = faceIndexArray[i];
+
+ if ((f >= 0) != (f0 >= 0)) {
+ // This face contains the oppositely oriented edge
+ // and has not been assigned too many edges
+
+ const Vector3& n1 = faceNormal[(f >= 0) ? f : ~f];
+ double d = n1.dot(n0);
+
+ if (found) {
+ // We previously found a good face; see if this
+ // one is better.
+ if (d > ndotn) {
+ // This face is better.
+ ndotn = d;
+ f1 = f;
+ i1 = i;
+ }
+ } else {
+ // This is the first face we've found
+ found = true;
+ ndotn = d;
+ f1 = f;
+ i1 = i;
+ }
+ }
+ }
+
+ // Create the new edge
+ int e = tempEdgeArray.size();
+ Edge& edge = tempEdgeArray.next();
+
+ edge.vertexIndex[0] = edgeKey.vertexIndex[0];
+ edge.vertexIndex[1] = edgeKey.vertexIndex[1];
+
+ if (f0 >= 0) {
+ edge.faceIndex[0] = f0;
+ edge.faceIndex[1] = Face::NONE;
+ assignEdgeIndex(faceArray[f0], e);
+ } else {
+ // The face indices above are two's complemented.
+ // this code restores them to regular indices.
+ debugAssert((~f0) >= 0);
+ edge.faceIndex[1] = ~f0;
+ edge.faceIndex[0] = Face::NONE;
+
+ // The edge index *does* need to be inverted, however.
+ assignEdgeIndex(faceArray[~f0], ~e);
+ }
+
+ if (found) {
+ // We found a matching face; remove both
+ // faces from the active list.
+ faceIndexArray.fastRemove(i1);
+
+ if (f1 >= 0) {
+ edge.faceIndex[0] = f1;
+ assignEdgeIndex(faceArray[f1], e);
+ } else {
+ edge.faceIndex[1] = ~f1;
+ assignEdgeIndex(faceArray[~f1], ~e);
+ }
+ }
+ }
+
+ ++cur;
+ }
+
+ edgeTable.clear();
+
+ // Move boundary edges to the end of the list and then
+ // clean up the face references into them
+ {
+ // Map old edge indices to new edge indices
+ Array<int> newIndex(tempEdgeArray.size());
+
+
+ // Index of the start and end of the edge array
+ int i = 0;
+ int j = tempEdgeArray.size() - 1;
+
+ edgeArray.resize(tempEdgeArray.size());
+ for (int e = 0; e < tempEdgeArray.size(); ++e) {
+ if (tempEdgeArray[e].boundary()) {
+ newIndex[e] = j;
+ --j;
+ } else {
+ newIndex[e] = i;
+ ++i;
+ }
+ edgeArray[newIndex[e]] = tempEdgeArray[e];
+ }
+
+ debugAssertM(i == j + 1, "Counting from front and back of array did not match");
+
+ // Fix the faces
+ for (int f = 0; f < faceArray.size(); ++f) {
+ Face& face = faceArray[f];
+ for (int q = 0; q < 3; ++q) {
+ int e = face.edgeIndex[q];
+ if (e < 0) {
+ // Backwards edge; twiddle before and after conversion
+ face.edgeIndex[q] = ~newIndex[~e];
+ } else {
+ // Regular edge; remap the index
+ face.edgeIndex[q] = newIndex[e];
+ }
+ }
+ }
+ }
+
+ // Now order the edge indices inside the faces correctly.
+ for (int f = 0; f < faceArray.size(); ++f) {
+ Face& face = faceArray[f];
+ int e0 = face.edgeIndex[0];
+ int e1 = face.edgeIndex[1];
+ int e2 = face.edgeIndex[2];
+
+ // e0 will always remain first. The only
+ // question is whether e1 and e2 should be swapped.
+
+ // See if e1 begins at the vertex where e1 ends.
+ const int e0End = (e0 < 0) ?
+ edgeArray[~e0].vertexIndex[0] :
+ edgeArray[e0].vertexIndex[1];
+
+ const int e1Begin = (e1 < 0) ?
+ edgeArray[~e1].vertexIndex[1] :
+ edgeArray[e1].vertexIndex[0];
+
+ if (e0End != e1Begin) {
+ // We must swap e1 and e2
+ face.edgeIndex[1] = e2;
+ face.edgeIndex[2] = e1;
+ }
+ }
+
+ // Fill out the edge adjacency information in the vertex array
+ for (int e = 0; e < edgeArray.size(); ++e) {
+ const Edge& edge = edgeArray[e];
+ vertexArray[edge.vertexIndex[0]].edgeIndex.append(e);
+ vertexArray[edge.vertexIndex[1]].edgeIndex.append(~e);
+ }
+}
+
+
+void MeshAlg::weldBoundaryEdges(
+ Array<Face>& faceArray,
+ Array<Edge>& edgeArray,
+ Array<Vertex>& vertexArray) {
+
+ // Copy over the original edge array
+ Array<Edge> oldEdgeArray = edgeArray;
+
+ // newEdgeIndex[e] is the new index of the old edge with index e
+ // Note that newEdgeIndex[e] might be negative, indicating that
+ // the edge switched direction between the arrays.
+ Array<int> newEdgeIndex(edgeArray.size());
+ edgeArray.resize(0);
+
+ // boundaryEdgeIndices[v_low] is an array of the indices of
+ // all boundary edges whose lower vertex is v_low.
+ Table<int, Array<int> > boundaryEdgeIndices;
+
+ // Copy over non-boundary edges to the new array
+ for (int e = 0; e < oldEdgeArray.size(); ++e) {
+ if (oldEdgeArray[e].boundary()) {
+
+ // Add to the boundary table
+ const int v_low = iMin(oldEdgeArray[e].vertexIndex[0], oldEdgeArray[e].vertexIndex[1]);
+ if (! boundaryEdgeIndices.containsKey(v_low)) {
+ boundaryEdgeIndices.set(v_low, Array<int>());
+ }
+ boundaryEdgeIndices[v_low].append(e);
+
+ // We'll fill out newEdgeIndex[e] later, when we find pairs
+
+ } else {
+
+ // Copy the edge to the new array
+ newEdgeIndex[e] = edgeArray.size();
+ edgeArray.append(oldEdgeArray[e]);
+
+ }
+ }
+
+
+ // Remove all edges from the table that have pairs.
+ Table<int, Array<int> >::Iterator cur = boundaryEdgeIndices.begin();
+ Table<int, Array<int> >::Iterator end = boundaryEdgeIndices.end();
+ while (cur != end) {
+ Array<int>& boundaryEdge = cur->value;
+
+ for (int i = 0; i < boundaryEdge.size(); ++i) {
+ int ei = boundaryEdge[i];
+ const Edge& edgei = oldEdgeArray[ei];
+
+ for (int j = i + 1; j < boundaryEdge.size(); ++j) {
+ int ej = boundaryEdge[j];
+ const Edge& edgej = oldEdgeArray[ej];
+
+ // See if edge ei is the reverse (match) of edge ej.
+
+ // True if the edges match
+ bool match = false;
+
+ // True if edgej's vertex indices are reversed from
+ // edgei's (usually true).
+ bool reversej = false;
+
+ int u = edgei.vertexIndex[0];
+ int v = edgei.vertexIndex[1];
+
+ if (edgei.faceIndex[0] != Face::NONE) {
+ // verts|faces
+ // edgei = [u v A /]
+
+ if (edgej.faceIndex[0] != Face::NONE) {
+ if ((edgej.vertexIndex[0] == v) && (edgej.vertexIndex[1] == u)) {
+ // This is the most common of the four cases
+
+ // edgej = [v u B /]
+ match = true;
+ reversej = true;
+ }
+ } else {
+ if ((edgej.vertexIndex[0] == u) && (edgej.vertexIndex[1] == v)) {
+ // edgej = [u v / B]
+ match = true;
+ }
+ }
+ } else {
+ // edgei = [u v / A]
+ if (edgej.faceIndex[0] != Face::NONE) {
+ if ((edgej.vertexIndex[0] == u) && (edgej.vertexIndex[1] == v)) {
+ // edgej = [u v B /]
+ match = true;
+ }
+ } else {
+ if ((edgej.vertexIndex[0] == v) && (edgej.vertexIndex[1] == u)) {
+ // edgej = [v u / B]
+ match = true;
+ reversej = true;
+ }
+ }
+ }
+
+ if (match) {
+ // ei and ej can be paired as a single edge
+ int e = edgeArray.size();
+ Edge& edge = edgeArray.next();
+
+ // Follow the direction of edgei.
+ edge = edgei;
+ newEdgeIndex[ei] = e;
+
+ // Insert the face index for edgej.
+ int fj = edgej.faceIndex[0];
+ if (fj == Face::NONE) {
+ fj = edgej.faceIndex[1];
+ }
+
+ if (edge.faceIndex[0] == Face::NONE) {
+ edge.faceIndex[0] = fj;
+ } else {
+ edge.faceIndex[1] = fj;
+ }
+
+ if (reversej) {
+ // The new edge is backwards of the old edge for ej
+ newEdgeIndex[ej] = ~e;
+ } else {
+ newEdgeIndex[ej] = e;
+ }
+
+ // Remove both ei and ej from being candidates for future pairing.
+ // Remove ej first since it comes later in the list (removing
+ // ei would decrease the index of ej to j - 1).
+ boundaryEdge.fastRemove(j);
+ boundaryEdge.fastRemove(i);
+
+ // Re-process element i, which is now a new edge index
+ --i;
+
+ // Jump out of the j for-loop
+ break;
+ }
+ }
+ }
+ ++cur;
+ }
+
+ // Anything remaining in the table is a real boundary edge; just copy it to
+ // the end of the array.
+ cur = boundaryEdgeIndices.begin();
+ end = boundaryEdgeIndices.end();
+ while (cur != end) {
+ Array<int>& boundaryEdge = cur->value;
+
+ for (int b = 0; b < boundaryEdge.size(); ++b) {
+ const int e = boundaryEdge[b];
+
+ newEdgeIndex[e] = edgeArray.size();
+ edgeArray.append(oldEdgeArray[e]);
+ }
+
+ ++cur;
+ }
+
+ // Finally, fix up edge indices in the face and vertex arrays
+ for (int f = 0; f < faceArray.size(); ++f) {
+ Face& face = faceArray[f];
+ for (int i = 0; i < 3; ++i) {
+ int e = face.edgeIndex[i];
+
+ if (e < 0) {
+ face.edgeIndex[i] = ~newEdgeIndex[~e];
+ } else {
+ face.edgeIndex[i] = newEdgeIndex[e];
+ }
+ }
+ }
+
+ for (int v = 0; v < vertexArray.size(); ++v) {
+ Vertex& vertex = vertexArray[v];
+ for (int i = 0; i < vertex.edgeIndex.size(); ++i) {
+ int e = vertex.edgeIndex[i];
+
+ if (e < 0) {
+ vertex.edgeIndex[i] = ~newEdgeIndex[~e];
+ } else {
+ vertex.edgeIndex[i] = newEdgeIndex[e];
+ }
+ }
+ }
+}
+
+
+void MeshAlg::weldAdjacency(
+ const Array<Vector3>& originalGeometry,
+ Array<Face>& faceArray,
+ Array<Edge>& edgeArray,
+ Array<Vertex>& vertexArray,
+ double radius) {
+
+ // Num vertices
+ const int n = originalGeometry.size();
+
+ // canonical[v] = first occurance of any vertex near oldVertexArray[v]
+ Array<int> canonical(n);
+
+ Array<int> toNew, toOld;
+ // Throw away the new vertex array
+ Array<Vector3> dummy;
+ computeWeld(originalGeometry, dummy, toNew, toOld, radius);
+
+ for (int v = 0; v < canonical.size(); ++v) {
+ // Round-trip through the toNew/toOld process. This will give
+ // us the original vertex.
+ canonical[v] = toOld[toNew[v]];
+ }
+
+ // Destroy vertexArray (we reconstruct it below)
+ vertexArray.clear();
+ vertexArray.resize(n);
+
+ bool hasBoundaryEdges = false;
+
+ // Fix edge vertex indices
+ for (int e = 0; e < edgeArray.size(); ++e) {
+ Edge& edge = edgeArray[e];
+
+ const int v0 = canonical[edge.vertexIndex[0]];
+ const int v1 = canonical[edge.vertexIndex[1]];
+
+ edge.vertexIndex[0] = v0;
+ edge.vertexIndex[1] = v1;
+
+ vertexArray[v0].edgeIndex.append(e);
+ vertexArray[v1].edgeIndex.append(~e);
+
+ hasBoundaryEdges = hasBoundaryEdges || edge.boundary();
+ }
+
+ // Fix face vertex indices
+ for (int f = 0; f < faceArray.size(); ++f) {
+ Face& face = faceArray[f];
+ for (int i = 0; i < 3; ++i) {
+ const int v = canonical[face.vertexIndex[i]];
+
+ face.vertexIndex[i] = v;
+
+ // Add the back pointer
+ vertexArray[v].faceIndex.append(f);
+ }
+ }
+
+ if (hasBoundaryEdges) {
+ // As a result of the welding, some of the boundary edges at
+ // the end of the array may now have mates and no longer be
+ // boundaries. Try to pair these up.
+
+ weldBoundaryEdges(faceArray, edgeArray, vertexArray);
+ }
+}
+
+
+void MeshAlg::debugCheckConsistency(
+ const Array<Face>& faceArray,
+ const Array<Edge>& edgeArray,
+ const Array<Vertex>& vertexArray) {
+
+#ifdef _DEBUG
+ for (int v = 0; v < vertexArray.size(); ++v) {
+ const MeshAlg::Vertex& vertex = vertexArray[v];
+
+ for (int i = 0; i < vertex.edgeIndex.size(); ++i) {
+ const int e = vertex.edgeIndex[i];
+ debugAssert(edgeArray[(e >= 0) ? e : ~e].containsVertex(v));
+ }
+
+ for (int i = 0; i < vertex.faceIndex.size(); ++i) {
+ const int f = vertex.faceIndex[i];
+ debugAssert(faceArray[f].containsVertex(v));
+ }
+
+ }
+
+ for (int e = 0; e < edgeArray.size(); ++e) {
+ const MeshAlg::Edge& edge = edgeArray[e];
+
+ for (int i = 0; i < 2; ++i) {
+ debugAssert((edge.faceIndex[i] == MeshAlg::Face::NONE) ||
+ faceArray[edge.faceIndex[i]].containsEdge(e));
+
+ debugAssert(vertexArray[edge.vertexIndex[i]].inEdge(e));
+ }
+ }
+
+ // Every face's edge must be on that face
+ for (int f = 0; f < faceArray.size(); ++f) {
+ const MeshAlg::Face& face = faceArray[f];
+ for (int i = 0; i < 3; ++i) {
+ int e = face.edgeIndex[i];
+ int ei = (e >= 0) ? e : ~e;
+ debugAssert(edgeArray[ei].inFace(f));
+
+ // Make sure the edge is oriented appropriately
+ if (e >= 0) {
+ debugAssert(edgeArray[ei].faceIndex[0] == (int)f);
+ } else {
+ debugAssert(edgeArray[ei].faceIndex[1] == (int)f);
+ }
+
+ debugAssert(vertexArray[face.vertexIndex[i]].inFace(f));
+ }
+ }
+#else
+ (void)faceArray;
+ (void)edgeArray;
+ (void)vertexArray;
+#endif // _DEBUG
+}
+
+} // G3D namespace
diff --git a/externals/g3dlite/G3D.lib/source/MeshAlgWeld.cpp b/externals/g3dlite/G3D.lib/source/MeshAlgWeld.cpp
new file mode 100644
index 00000000000..cd4d1f9c7d5
--- /dev/null
+++ b/externals/g3dlite/G3D.lib/source/MeshAlgWeld.cpp
@@ -0,0 +1,213 @@
+/**
+ @file MeshAlgWeld.cpp
+
+ The MeshAlg::computeWeld method.
+
+ @maintainer Morgan McGuire, matrix@graphics3d.com
+ @created 2003-10-22
+ @edited 2005-02-24
+
+ Copyright 2000-2003, Morgan McGuire.
+ All rights reserved.
+
+ */
+
+#include "G3D/MeshAlg.h"
+#include "G3D/Table.h"
+#include "G3D/Set.h"
+
+namespace G3D {
+
+namespace _internal {
+
+class Welder {
+private:
+
+ // Intentionally illegal
+ Welder& operator=(const Welder& w);
+
+public:
+ /** Indices of newVertexArray elements in <B>or near</B> a grid cell. */
+ typedef Array<int> List;
+
+ enum {GRID_RES = 32};
+
+ List grid[GRID_RES][GRID_RES][GRID_RES];
+
+ const Array<Vector3>& oldVertexArray;
+ Array<Vector3>& newVertexArray;
+ Array<int>& toNew;
+ Array<int>& toOld;
+
+ /** Must be less than one grid cell, not checked */
+ const double radius;
+
+ /** (oldVertexArray[i] - offset) * scale is on the range [0, 1] */
+ Vector3 offset;
+ Vector3 scale;
+
+ Welder(
+ const Array<Vector3>& _oldVertexArray,
+ Array<Vector3>& _newVertexArray,
+ Array<int>& _toNew,
+ Array<int>& _toOld,
+ double _radius);
+
+ /**
+ Computes the grid index from an ordinate.
+ */
+ void toGridCoords(Vector3 v, int& x, int& y, int& z) const;
+
+ /** Gets the index of a vertex, adding it to
+ newVertexArray if necessary. */
+ int getIndex(const Vector3& vertex);
+
+ void weld();
+};
+
+} // namespace _internal
+
+} // namespace G3D
+
+template<> struct HashTrait<G3D::_internal::Welder::List*> {
+ static size_t hashCode(const G3D::_internal::Welder::List* key) { return reinterpret_cast<size_t>(key); }
+};
+
+namespace G3D {
+namespace _internal {
+
+Welder::Welder(
+ const Array<Vector3>& _oldVertexArray,
+ Array<Vector3>& _newVertexArray,
+ Array<int>& _toNew,
+ Array<int>& _toOld,
+ double _radius) :
+ oldVertexArray(_oldVertexArray),
+ newVertexArray(_newVertexArray),
+ toNew(_toNew),
+ toOld(_toOld),
+ radius(_radius) {
+
+ // Compute a scale factor that moves the range
+ // of all ordinates to [0, 1]
+ Vector3 minBound = Vector3::inf();
+ Vector3 maxBound = -minBound;
+
+ for (int i = 0; i < oldVertexArray.size(); ++i) {
+ minBound = minBound.min(oldVertexArray[i]);
+ maxBound = maxBound.max(oldVertexArray[i]);
+ }
+
+ offset = minBound;
+ scale = maxBound - minBound;
+ for (int i = 0; i < 3; ++i) {
+ // The model might have zero extent along some axis
+ if (fuzzyEq(scale[i], 0.0)) {
+ scale[i] = 1.0;
+ } else {
+ scale[i] = 1.0 / scale[i];
+ }
+ }
+}
+
+
+void Welder::toGridCoords(Vector3 v, int& x, int& y, int& z) const {
+ v = (v - offset) * scale;
+ x = iClamp(iFloor(v.x * GRID_RES), 0, GRID_RES - 1);
+ y = iClamp(iFloor(v.y * GRID_RES), 0, GRID_RES - 1);
+ z = iClamp(iFloor(v.z * GRID_RES), 0, GRID_RES - 1);
+}
+
+
+int Welder::getIndex(const Vector3& vertex) {
+
+ int closestIndex = -1;
+ double distanceSquared = inf();
+
+ int ix, iy, iz;
+ toGridCoords(vertex, ix, iy, iz);
+
+ // Check against all vertices within radius of this grid cube
+ const List& list = grid[ix][iy][iz];
+
+ for (int i = 0; i < list.size(); ++i) {
+ double d = (newVertexArray[list[i]] - vertex).squaredMagnitude();
+
+ if (d < distanceSquared) {
+ distanceSquared = d;
+ closestIndex = list[i];
+ }
+ }
+
+ if (distanceSquared <= radius * radius) {
+
+ return closestIndex;
+
+ } else {
+
+ // This is a new vertex
+ int newIndex = newVertexArray.size();
+ newVertexArray.append(vertex);
+
+ // Create a new vertex and store its index in the
+ // neighboring grid cells (usually, only 1 neighbor)
+
+ Set<List*> neighbors;
+
+ for (float dx = -1; dx <= +1; ++dx) {
+ for (float dy = -1; dy <= +1; ++dy) {
+ for (float dz = -1; dz <= +1; ++dz) {
+ int ix, iy, iz;
+ toGridCoords(vertex + Vector3(dx, dy, dz) * radius, ix, iy, iz);
+ neighbors.insert(&(grid[ix][iy][iz]));
+ }
+ }
+ }
+
+ Set<List*>::Iterator neighbor(neighbors.begin());
+ Set<List*>::Iterator none(neighbors.end());
+
+ while (neighbor != none) {
+ (*neighbor)->append(newIndex);
+ ++neighbor;
+ }
+
+ return newIndex;
+ }
+}
+
+
+void Welder::weld() {
+ newVertexArray.resize(0);
+
+ // Prime the vertex positions
+ for (int i = 0; i < oldVertexArray.size(); ++i) {
+ getIndex(oldVertexArray[i]);
+ }
+
+ // Now create the official remapping by snapping to
+ // nearby vertices.
+ toNew.resize(oldVertexArray.size());
+ toOld.resize(newVertexArray.size());
+
+ for (int oi = 0; oi < oldVertexArray.size(); ++oi) {
+ toNew[oi] = getIndex(oldVertexArray[oi]);
+ toOld[toNew[oi]] = oi;
+ }
+}
+
+} // internal namespace
+
+
+void MeshAlg::computeWeld(
+ const Array<Vector3>& oldVertexArray,
+ Array<Vector3>& newVertexArray,
+ Array<int>& toNew,
+ Array<int>& toOld,
+ double radius) {
+
+ _internal::Welder welder(oldVertexArray, newVertexArray, toNew, toOld, radius);
+ welder.weld();
+}
+
+} // G3D namespace
diff --git a/externals/g3dlite/G3D.lib/source/MeshAlgWeld2.cpp b/externals/g3dlite/G3D.lib/source/MeshAlgWeld2.cpp
new file mode 100644
index 00000000000..13f731353a6
--- /dev/null
+++ b/externals/g3dlite/G3D.lib/source/MeshAlgWeld2.cpp
@@ -0,0 +1,377 @@
+/**
+ @file MeshAlgWeld2.cpp
+
+ @author Morgan McGuire, Kyle Whitson, Corey Taylor
+
+ @created 2008-07-30
+ @edited 2008-11-10
+ */
+
+#include "G3D/platform.h"
+#include "G3D/Vector2.h"
+#include "G3D/Vector3.h"
+#include "G3D/Sphere.h"
+#include "G3D/PointHashGrid.h"
+#include "G3D/MeshAlg.h"
+
+namespace G3D { namespace _internal{
+
+/** Used by WeldHelper2::smoothNormals. */
+class VN {
+public:
+ Vector3 vertex;
+ Vector3 normal;
+
+ VN() {}
+ VN(const Vector3& v, const Vector3& n) : vertex(v), normal(n) {}
+};
+
+/** Used by WeldHelper::getIndex to maintain a list of vertices by location. */
+class VNTi {
+public:
+ Vector3 vertex;
+ Vector3 normal;
+ Vector2 texCoord;
+ int index;
+
+ VNTi() : index(0) {}
+
+ VNTi(const Vector3& v, const Vector3& n, const Vector2& t, int i) :
+ vertex(v), normal(n), texCoord(t), index(i) {}
+};
+
+
+}} // G3D
+
+template <> struct HashTrait <G3D::_internal::VN> {
+ static size_t hashCode(const G3D::_internal::VN& k) { return static_cast<size_t>(k.vertex.hashCode()); }
+};
+template <> struct HashTrait <G3D::_internal::VNTi> {
+ static size_t hashCode(const G3D::_internal::VNTi& k) { return static_cast<size_t>(k.vertex.hashCode()); }
+};
+
+
+template<> struct EqualsTrait <G3D::_internal::VN> {
+ static bool equals(const G3D::_internal::VN& a, const G3D::_internal::VN& b) { return a.vertex == b.vertex; }
+};
+template<> struct EqualsTrait <G3D::_internal::VNTi> {
+ static bool equals(const G3D::_internal::VNTi& a, const G3D::_internal::VNTi& b) { return a.vertex == b.vertex; }
+};
+
+template<> struct PositionTrait<G3D::_internal::VN> {
+ static void getPosition(const G3D::_internal::VN& v, G3D::Vector3& p) { p = v.vertex; }
+};
+template<> struct PositionTrait<G3D::_internal::VNTi> {
+ static void getPosition(const G3D::_internal::VNTi& v, G3D::Vector3& p) { p = v.vertex; }
+};
+
+namespace G3D { namespace _internal {
+
+class WeldHelper {
+private:
+ /** Used by getIndex and updateTriLists */
+ PointHashGrid<VNTi> weldGrid;
+
+ Array<Vector3>* outputVertexArray;
+ Array<Vector3>* outputNormalArray;
+ Array<Vector2>* outputTexCoordArray;
+
+ float vertexWeldRadius;
+ /** Squared radius allowed for welding similar normals. */
+ float normalWeldRadius2;
+ float texCoordWeldRadius2;
+
+ float normalSmoothingAngle;
+
+ /**
+ Returns the index of the vertex in
+ outputVertexArray/outputNormalArray/outputTexCoordArray
+ that is within the global tolerances of v,n,t. If there
+ is no such vertex, adds it to the arrays and returns that index.
+
+ Called from updateTriLists().
+ */
+ int getIndex(const Vector3& v, const Vector3& n, const Vector2& t) {
+ PointHashGrid<VNTi>::SphereIterator it =
+ weldGrid.beginSphereIntersection(Sphere(v, vertexWeldRadius));
+
+ if (n.isZero()) {
+ // Don't bother trying to match the surface normal, since this vertex has no surface normal.
+ while (it.hasMore()) {
+ if ((t - it->texCoord).squaredLength() <= texCoordWeldRadius2) {
+ // This is the vertex
+ return it->index;
+ }
+ ++it;
+ }
+ } else {
+ while (it.hasMore()) {
+ if (((n - it->normal).squaredLength() <= normalWeldRadius2) &&
+ ((t - it->texCoord).squaredLength() <= texCoordWeldRadius2)) {
+ // This is the vertex
+ return it->index;
+ }
+ ++it;
+ }
+ }
+
+ // Note that a sliver triangle processed before its neighbors may reach here
+ // with a zero length normal.
+
+ // The vertex does not exist. Create it.
+ const int i = outputVertexArray->size();
+ outputVertexArray->append(v);
+ outputNormalArray->append(n);
+ outputTexCoordArray->append(t);
+
+ // Store in the grid so that it will be remembered.
+ weldGrid.insert(VNTi(v, n, t, i));
+
+ return i;
+ }
+
+
+ /**
+ Updates each indexArray to refer to vertices in the
+ outputVertexArray.
+
+ Called from process()
+ */
+ void updateTriLists(
+ Array<Array<int>*>& indexArrayArray,
+ const Array<Vector3>& vertexArray,
+ const Array<Vector3>& normalArray,
+ const Array<Vector2>& texCoordArray) {
+
+ // Compute a hash grid so that we can find neighbors quickly.
+ // It begins empty and is extended as the tri lists are iterated
+ // through.
+ weldGrid.clear();
+
+ // Process all triLists
+ int numTriLists = indexArrayArray.size();
+ int u = 0;
+ for (int t = 0; t < numTriLists; ++t) {
+ Array<int>& triList = *(indexArrayArray[t]);
+
+ // For all vertices in this list
+ for (int v = 0; v < triList.size(); ++v) {
+ // This vertex mapped to u in the flatVertexArray
+ triList[v] = getIndex(vertexArray[u], normalArray[u], texCoordArray[u]);
+
+ /*
+# ifdef G3D_DEBUG
+ {
+ int i = triList[v];
+ Vector3 N = normalArray[i];
+ debugAssertM(N.length() > 0.9f, "Produced non-unit normal");
+ }
+# endif
+ */
+ ++u;
+ }
+ }
+ }
+
+ /** Expands the indexed triangle lists into a triangle list.
+
+ Called from process() */
+ void unroll(
+ const Array<Array<int>*>& indexArrayArray,
+ const Array<Vector3>& vertexArray,
+ const Array<Vector2>& texCoordArray,
+ Array<Vector3>& unrolledVertexArray,
+ Array<Vector2>& unrolledTexCoordArray) {
+
+ int numTriLists = indexArrayArray.size();
+ for (int t = 0; t < numTriLists; ++t) {
+ const Array<int>& triList = *(indexArrayArray[t]);
+ for (int v = 0; v < triList.size(); ++v) {
+ int i = triList[v];
+ unrolledVertexArray.append(vertexArray[i]);
+ unrolledTexCoordArray.append(texCoordArray[i]);
+ }
+ }
+ }
+
+ /** For every three vertices, compute the face normal and store it three times.
+ Sliver triangles have a zero surface normal, which we will later take to
+ match *any* surface normal. */
+ void computeFaceNormals(
+ const Array<Vector3>& vertexArray,
+ Array<Vector3>& faceNormalArray) {
+
+ debugAssertM(vertexArray.size() % 3 == 0, "Input is not a triangle soup");
+ debugAssertM(faceNormalArray.size() == 0, "Output must start empty.");
+
+ for (int v = 0; v < vertexArray.size(); v += 3) {
+ const Vector3& e0 = vertexArray[v + 1] - vertexArray[v];
+ const Vector3& e1 = vertexArray[v + 2] - vertexArray[v];
+
+ // Note that the length may be zero in the case of sliver polygons, e.g.,
+ // those correcting a T-junction.
+ const Vector3& n = e0.cross(e1).directionOrZero();
+
+ // Append the normal once per vertex.
+ faceNormalArray.append(n, n, n);
+ }
+ }
+
+
+ /**
+ Computes @a smoothNormalArray, whose elements are those of normalArray averaged
+ with neighbors within the angular cutoff.
+ */
+ void smoothNormals(
+ const Array<Vector3>& vertexArray,
+ const Array<Vector3>& normalArray,
+ Array<Vector3>& smoothNormalArray) {
+
+ const float cosThresholdAngle = (float)cos(normalSmoothingAngle);
+
+ debugAssert(vertexArray.size() == normalArray.size());
+ smoothNormalArray.resize(normalArray.size());
+
+ // Compute a hash grid so that we can find neighbors quickly.
+ PointHashGrid<VN> grid(vertexWeldRadius);
+ for (int v = 0; v < normalArray.size(); ++v) {
+ grid.insert(VN(vertexArray[v], normalArray[v]));
+ }
+
+ for (int v = 0; v < normalArray.size(); ++v) {
+ // Compute the sum of all nearby normals within the cutoff angle.
+ // Search within the vertexWeldRadius, since those are the vertices
+ // that will collapse to the same point.
+ PointHashGrid<VN>::SphereIterator it =
+ grid.beginSphereIntersection(Sphere(vertexArray[v], vertexWeldRadius));
+
+ Vector3 sum;
+
+ const Vector3& original = normalArray[v];
+ while (it.hasMore()) {
+ const Vector3& N = it->normal;
+ const float cosAngle = N.dot(original);
+
+ if (cosAngle > cosThresholdAngle) {
+ // This normal is close enough to consider
+ sum += N;
+ }
+ ++it;
+ }
+
+ const Vector3& average = sum.directionOrZero();
+
+ const bool indeterminate = average.isZero();
+ // Never "smooth" a normal so far that it points backwards
+ const bool backFacing = original.dot(average) < 0;
+
+ if (indeterminate || backFacing) {
+ // Revert to the face normal
+ smoothNormalArray[v] = original;
+ } else {
+ // Average available normals
+ smoothNormalArray[v] = average;
+ }
+ }
+ }
+
+public:
+
+
+ /**
+ Algorithm:
+
+ 1. Unroll the indexed triangle list into a triangle list, where
+ there are duplicated vertices.
+
+ 2. Compute face normals for all triangles, and expand those into
+ the triangle vertices.
+
+ 3. At each vertex, average all normals that are within normalSmoothingAngle.
+
+ 4. Generate output indexArrayArray. While doing so, merge all vertices where
+ the distance between position, texCoord, and normal is within the thresholds.
+ */
+ void process(
+ Array<Vector3>& vertexArray,
+ Array<Vector2>& texCoordArray,
+ Array<Vector3>& normalArray,
+ Array<Array<int>*>& indexArrayArray,
+ float normAngle,
+ float texRadius,
+ float normRadius) {
+
+ normalSmoothingAngle = normAngle;
+ normalWeldRadius2 = square(normRadius);
+ texCoordWeldRadius2 = square(texRadius);
+
+ const bool hasTexCoords = (texCoordArray.size() > 0);
+
+ if (hasTexCoords) {
+ debugAssertM(vertexArray.size() == texCoordArray.size(),
+ "Input arrays are not parallel.");
+ }
+
+ Array<Vector3> unrolledVertexArray;
+ Array<Vector3> unrolledFaceNormalArray;
+ Array<Vector3> unrolledSmoothNormalArray;
+ Array<Vector2> unrolledTexCoordArray;
+
+ if (! hasTexCoords) {
+ // Generate all zero texture coordinates
+ texCoordArray.resize(vertexArray.size());
+ }
+
+ // Generate a flat (unrolled) triangle list with texture coordinates.
+ unroll(indexArrayArray, vertexArray, texCoordArray,
+ unrolledVertexArray, unrolledTexCoordArray);
+
+ // Put the output back into the input slots. Clear immediately to reduce peak
+ // memory.
+ outputVertexArray = &vertexArray;
+ outputNormalArray = &normalArray;
+ outputTexCoordArray = &texCoordArray;
+ outputVertexArray->fastClear();
+ outputNormalArray->fastClear();
+ outputTexCoordArray->fastClear();
+
+ // For every three vertices, generate their face normal and store it at
+ // each vertex. The output array has the same length as the input.
+ computeFaceNormals(unrolledVertexArray, unrolledFaceNormalArray);
+
+ // Compute smooth normals at vertices.
+ smoothNormals(unrolledVertexArray, unrolledFaceNormalArray, unrolledSmoothNormalArray);
+ unrolledFaceNormalArray.clear();
+
+ // Regenerate the triangle lists
+ updateTriLists(indexArrayArray, unrolledVertexArray, unrolledSmoothNormalArray, unrolledTexCoordArray);
+
+ if (! hasTexCoords) {
+ // Throw away the generated texCoords
+ texCoordArray.resize(0);
+ }
+ }
+
+ WeldHelper(float vertRadius) :
+ weldGrid(vertRadius),
+ vertexWeldRadius(vertRadius) {}
+
+};
+} // Internal
+
+void MeshAlg::weld(
+ Array<Vector3>& vertexArray,
+ Array<Vector2>& texCoordArray,
+ Array<Vector3>& normalArray,
+ Array<Array<int>*>& indexArrayArray,
+ float normalSmoothingAngle,
+ float vertexWeldRadius,
+ float textureWeldRadius,
+ float normalWeldRadius) {
+
+ _internal::WeldHelper(vertexWeldRadius).process(
+ vertexArray, texCoordArray, normalArray, indexArrayArray,
+ normalSmoothingAngle, textureWeldRadius, normalWeldRadius);
+}
+
+} // G3D
diff --git a/externals/g3dlite/G3D.lib/source/MeshBuilder.cpp b/externals/g3dlite/G3D.lib/source/MeshBuilder.cpp
new file mode 100644
index 00000000000..43ee6e50ac8
--- /dev/null
+++ b/externals/g3dlite/G3D.lib/source/MeshBuilder.cpp
@@ -0,0 +1,113 @@
+/**
+ @file MeshBuilder.cpp
+
+ @maintainer Morgan McGuire, matrix@graphics3d.com
+
+ @created 2002-02-27
+ @edited 2005-02-24
+ */
+
+#include "G3D/MeshBuilder.h"
+#include "G3D/MeshAlg.h"
+
+namespace G3D {
+
+void MeshBuilder::setName(const std::string& n) {
+ name = n;
+}
+
+
+void MeshBuilder::commit(std::string& n, Array<int>& indexArray, Array<Vector3>& outvertexArray) {
+ n = name;
+
+ // Make the data fit in a unit cube
+ centerTriList();
+
+ Array<int> toNew, toOld;
+
+ if (close == MeshBuilder::AUTO_WELD) {
+ Array<int> index;
+ MeshAlg::createIndexArray(triList.size(), index);
+ double minEdgeLen, maxEdgeLen, meanEdgeLen, medianEdgeLen;
+ double minFaceArea, maxFaceArea, meanFaceArea, medianFaceArea;
+ MeshAlg::computeAreaStatistics(triList, index,
+ minEdgeLen, meanEdgeLen, medianEdgeLen, maxEdgeLen,
+ minFaceArea, meanFaceArea, medianFaceArea, maxFaceArea);
+ close = minEdgeLen * 0.1;
+ }
+
+ MeshAlg::computeWeld(triList, outvertexArray, toNew, toOld, close);
+
+ // Construct triangles
+ for (int t = 0; t < triList.size(); t += 3) {
+ int index[3];
+
+ for (int i = 0; i < 3; ++i) {
+ index[i] = toNew[t + i];
+ }
+
+ // Throw out zero size triangles
+ if ((index[0] != index[1]) &&
+ (index[1] != index[2]) &&
+ (index[2] != index[0])) {
+ indexArray.append(index[0], index[1], index[2]);
+ }
+ }
+}
+
+
+void MeshBuilder::centerTriList() {
+ // Compute the range of the vertices
+ Vector3 vmin, vmax;
+
+ computeBounds(vmin, vmax);
+
+ Vector3 diagonal = vmax - vmin;
+ double scale = max(max(diagonal.x, diagonal.y), diagonal.z) / 2;
+ debugAssert(scale > 0);
+
+ Vector3 translation = vmin + diagonal / 2;
+
+ // Center and scale all vertices in the input list
+ int v;
+
+ //Matrix3 rot90 = Matrix3::fromAxisAngle(Vector3::UNIT_Y, toRadians(180)) * Matrix3::fromAxisAngle(Vector3::UNIT_X, toRadians(90));
+ for (v = 0; v < triList.size(); ++v) {
+ triList[v] = (triList[v] - translation) / scale;
+ //triList[v] = rot90 * triList[v];
+ }
+}
+
+
+void MeshBuilder::computeBounds(Vector3& min, Vector3& max) {
+ min = Vector3::inf();
+ max = -min;
+
+ int v;
+ for (v = 0; v < triList.size(); ++v) {
+ min = min.min(triList[v]);
+ max = max.max(triList[v]);
+ }
+}
+
+
+void MeshBuilder::addTriangle(const Vector3& a, const Vector3& b, const Vector3& c) {
+ triList.append(a, b, c);
+
+ if (_twoSided) {
+ triList.append(c, b, a);
+ }
+}
+
+
+void MeshBuilder::addQuad(const Vector3& a, const Vector3& b, const Vector3& c, const Vector3& d) {
+ addTriangle(a, b, c);
+ addTriangle(a, c, d);
+}
+
+
+void MeshBuilder::addTriangle(const Triangle& t) {
+ addTriangle(t.vertex(0), t.vertex(1), t.vertex(2));
+}
+
+} // namespace
diff --git a/externals/g3dlite/G3D.lib/source/NetAddress.cpp b/externals/g3dlite/G3D.lib/source/NetAddress.cpp
new file mode 100644
index 00000000000..64d692d4763
--- /dev/null
+++ b/externals/g3dlite/G3D.lib/source/NetAddress.cpp
@@ -0,0 +1,164 @@
+/**
+ @file NetMessage.cpp
+
+ @maintainer Morgan McGuire, morgan@cs.brown.edu
+ @created 2005-02-06
+ @edited 2005-02-06
+ */
+#include "G3D/platform.h"
+#include "G3D/NetAddress.h"
+#include "G3D/BinaryInput.h"
+#include "G3D/BinaryOutput.h"
+#include "G3D/Array.h"
+#include "G3D/stringutils.h"
+#include "G3D/System.h"
+#include "G3D/NetworkDevice.h"
+
+#if defined(G3D_LINUX) || defined(G3D_OSX)
+ #include <unistd.h>
+ #include <errno.h>
+ #include <sys/socket.h>
+ #include <netinet/in.h>
+ #include <arpa/inet.h>
+ #include <netdb.h>
+ #include <netinet/tcp.h>
+ #define _alloca alloca
+
+# ifndef SOCKADDR_IN
+# define SOCKADDR_IN struct sockaddr_in
+# endif
+# ifndef SOCKET
+# define SOCKET int
+# endif
+
+// SOCKADDR_IN is supposed to be defined in NetAddress.h
+#ifndef SOCKADDR_IN
+# error Network headers included in wrong order
+#endif
+#endif
+
+
+namespace G3D {
+
+NetAddress::NetAddress() {
+ System::memset(&addr, 0, sizeof(addr));
+}
+
+void NetAddress::init(uint32 host, uint16 port) {
+ if ((host != 0) || (port != 0)) {
+ addr.sin_family = AF_INET;
+ addr.sin_port = htons(port);
+ if (host == 0) {
+ host = INADDR_ANY;
+ }
+ addr.sin_addr.s_addr = htonl(host);
+ } else {
+ System::memset(&addr, 0, sizeof(addr));
+ }
+}
+
+
+NetAddress::NetAddress(
+ const std::string& hostname,
+ uint16 port) {
+ init(hostname, port);
+}
+
+
+void NetAddress::init(
+ const std::string& hostname,
+ uint16 port) {
+
+ uint32 addr;
+
+ if (hostname == "") {
+ addr = INADDR_NONE;
+ } else {
+ addr = inet_addr(hostname.c_str());
+ }
+
+ // The address wasn't in numeric form, resolve it
+ if (addr == INADDR_NONE) {
+ // Get the IP address of the server and store it in host
+ struct hostent* host = gethostbyname(hostname.c_str());
+
+ if (host == NULL) {
+ init(0, 0);
+ return;
+ }
+
+ System::memcpy(&addr, host->h_addr_list[0], host->h_length);
+ }
+
+ if (addr != INADDR_NONE) {
+ addr = ntohl(addr);
+ }
+ init(addr, port);
+}
+
+
+NetAddress::NetAddress(uint32 hostip, uint16 port) {
+ init(hostip, port);
+}
+
+
+NetAddress NetAddress::broadcastAddress(uint16 port) {
+ return NetAddress(NetworkDevice::instance()->broadcastAddressArray()[0], port);
+}
+
+
+NetAddress::NetAddress(const std::string& hostnameAndPort) {
+
+ Array<std::string> part = stringSplit(hostnameAndPort, ':');
+
+ debugAssert(part.length() == 2);
+ init(part[0], atoi(part[1].c_str()));
+}
+
+
+NetAddress::NetAddress(const SOCKADDR_IN& a) {
+ addr = a;
+}
+
+
+NetAddress::NetAddress(const struct in_addr& addr, uint16 port) {
+ #ifdef G3D_WIN32
+ init(ntohl(addr.S_un.S_addr), port);
+ #else
+ init(htonl(addr.s_addr), port);
+ #endif
+}
+
+
+void NetAddress::serialize(class BinaryOutput& b) const {
+ b.writeUInt32(ip());
+ b.writeUInt16(port());
+}
+
+
+void NetAddress::deserialize(class BinaryInput& b) {
+ uint32 i;
+ uint16 p;
+
+ i = b.readUInt32();
+ p = b.readUInt16();
+
+ init(i, p);
+}
+
+
+bool NetAddress::ok() const {
+ return addr.sin_family != 0;
+}
+
+
+std::string NetAddress::ipString() const {
+ return format("%s", inet_ntoa(*(in_addr*)&(addr.sin_addr)));
+}
+
+
+std::string NetAddress::toString() const {
+ return ipString() + format(":%d", ntohs(addr.sin_port));
+}
+
+}
diff --git a/externals/g3dlite/G3D.lib/source/NetworkDevice.cpp b/externals/g3dlite/G3D.lib/source/NetworkDevice.cpp
new file mode 100644
index 00000000000..246c97d4dbf
--- /dev/null
+++ b/externals/g3dlite/G3D.lib/source/NetworkDevice.cpp
@@ -0,0 +1,1362 @@
+/**
+ @file NetworkDevice.cpp
+
+ @maintainer Morgan McGuire, morgan@cs.brown.edu
+ @created 2002-11-22
+ @edited 2006-02-24
+ */
+
+#include <stdlib.h>
+#include <time.h>
+#include "G3D/platform.h"
+#include "G3D/TextOutput.h"
+#include "G3D/NetworkDevice.h"
+#include "G3D/NetAddress.h"
+#include "G3D/BinaryInput.h"
+#include "G3D/BinaryOutput.h"
+#include "G3D/Log.h"
+#include "G3D/G3DGameUnits.h"
+#include "G3D/stringutils.h"
+#include "G3D/debug.h"
+
+#include <cstring>
+
+#if defined(G3D_LINUX) || defined(G3D_OSX) || defined(G3D_FREEBSD)
+# include <sys/types.h>
+# include <sys/socket.h>
+# include <ifaddrs.h>
+# include <netinet/in.h>
+# include <net/if.h>
+# ifdef __linux__
+# include <sys/ioctl.h>
+# include <netinet/in.h>
+# include <unistd.h>
+# include <string.h>
+// Match Linux to FreeBSD
+# define AF_LINK AF_PACKET
+# else
+# include <net/if_dl.h>
+# include <sys/sockio.h>
+# endif
+
+ #include <unistd.h>
+ #include <errno.h>
+ #include <sys/socket.h>
+ #include <netinet/in.h>
+ #include <arpa/inet.h>
+ #include <netdb.h>
+ #include <netinet/tcp.h>
+ #include <sys/ioctl.h>
+ #include <netinet/if_ether.h>
+ #include <net/ethernet.h>
+ #include <net/if.h>
+
+ #include <sys/types.h>
+
+ #define _alloca alloca
+
+ /** Define an error code for non-windows platforms. */
+ int WSAGetLastError() {
+ return -1;
+ }
+
+ #define SOCKET_ERROR -1
+
+ static std::string socketErrorCode(int code) {
+ return G3D::format("CODE %d: %s\n", code, strerror(code));
+ }
+
+ static std::string socketErrorCode() {
+ return socketErrorCode(errno);
+ }
+
+ static const int WSAEWOULDBLOCK = -100;
+
+ typedef int SOCKET;
+ typedef struct sockaddr_in SOCKADDR_IN;
+
+#else
+
+ // Windows
+ static std::string socketErrorCode(int code) {
+ LPTSTR formatMsg = NULL;
+
+ FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER |
+ FORMAT_MESSAGE_IGNORE_INSERTS |
+ FORMAT_MESSAGE_FROM_SYSTEM,
+ NULL,
+ code,
+ 0,
+ (LPTSTR)&formatMsg,
+ 0,
+ NULL);
+
+ return G3D::format("CODE %d: %s\n", code, formatMsg);
+ }
+
+ static std::string socketErrorCode() {
+ return socketErrorCode(GetLastError());
+ }
+
+#endif
+
+
+#ifndef _SOCKLEN_T
+# if defined(G3D_WIN32) || defined(G3D_OSX)
+ typedef int socklen_t;
+# endif
+#endif
+
+namespace G3D {
+
+NetworkDevice* NetworkDevice::s_instance = NULL;
+
+std::ostream& operator<<(std::ostream& os, const NetAddress& a) {
+ return os << a.toString();
+}
+
+
+static void logSocketInfo(const SOCKET& sock) {
+ uint32 val;
+ socklen_t sz = 4;
+ int ret;
+
+ ret = getsockopt(sock, SOL_SOCKET, SO_RCVBUF, (char*)&val, (socklen_t*)&sz);
+ logPrintf("SOL_SOCKET/SO_RCVBUF = %d\n", val);
+
+ ret = getsockopt(sock, SOL_SOCKET, SO_SNDBUF, (char*)&val, (socklen_t*)&sz);
+ logPrintf("SOL_SOCKET/SO_SNDBUF = %d\n", val);
+
+ // Note: timeout = 0 means no timeout
+ ret = getsockopt(sock, SOL_SOCKET, SO_RCVTIMEO, (char*)&val, (socklen_t*)&sz);
+ logPrintf("SOL_SOCKET/SO_RCVTIMEO = %d\n", val);
+
+ ret = getsockopt(sock, SOL_SOCKET, SO_SNDTIMEO, (char*)&val, (socklen_t*)&sz);
+ logPrintf("SOL_SOCKET/SO_SNDTIMEO = %d\n", val);
+}
+
+
+/////////////////////////////////////////////////////////////////////////////
+
+/** Invokes select on one socket. Returns SOCKET_ERROR on error, 0 if
+ there is no read pending, sock if there a read pending. */
+static int selectOneReadSocket(const SOCKET& sock) {
+ // 0 time timeout is specified to poll and return immediately
+ struct timeval timeout;
+ timeout.tv_sec = 0;
+ timeout.tv_usec = 0;
+
+ // Create a set that contains just this one socket
+ fd_set socketSet;
+ FD_ZERO(&socketSet);
+ FD_SET(sock, &socketSet);
+
+ int ret = select(sock + 1, &socketSet, NULL, NULL, &timeout);
+
+ return ret;
+}
+
+
+/** Returns true if the socket has a read pending */
+static bool readWaiting(const SOCKET& sock) {
+ int ret = selectOneReadSocket(sock);
+
+ switch (ret) {
+ case SOCKET_ERROR:
+ logPrintf("ERROR: selectOneReadSocket returned "
+ "SOCKET_ERROR in readWaiting(). %s", socketErrorCode().c_str());
+ // Return true so that we'll force an error on read and close
+ // the socket.
+ return true;
+
+ case 0:
+ return false;
+
+ default:
+ return true;
+ }
+}
+
+
+/** Invokes select on one socket. */
+static int selectOneWriteSocket(const SOCKET& sock) {
+ // 0 time timeout is specified to poll and return immediately
+ struct timeval timeout;
+ timeout.tv_sec = 0;
+ timeout.tv_usec = 0;
+
+ // Create a set that contains just this one socket
+ fd_set socketSet;
+ FD_ZERO(&socketSet);
+ FD_SET(sock, &socketSet);
+
+ return select(sock + 1, NULL, &socketSet, NULL, &timeout);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+NetworkDevice* NetworkDevice::instance() {
+ if (s_instance == NULL) {
+ s_instance = new NetworkDevice();
+ if (! s_instance->init()) {
+ delete s_instance;
+ s_instance = NULL;
+ }
+ }
+ return s_instance;
+}
+
+
+void NetworkDevice::cleanup() {
+ if (s_instance) {
+ s_instance->_cleanup();
+ delete s_instance;
+ s_instance = NULL;
+ }
+}
+
+
+NetworkDevice::NetworkDevice() {
+ initialized = false;
+}
+
+
+NetworkDevice::~NetworkDevice() {
+}
+
+
+std::string NetworkDevice::localHostName() const {
+ char ac[128];
+ if (gethostname(ac, sizeof(ac)) == -1) {
+ Log::common()->printf("Error while getting local host name\n");
+ return "localhost";
+ }
+ return gethostbyname(ac)->h_name;
+}
+
+#ifndef G3D_WIN32
+const char* errnoToString() {
+ switch (errno) {
+ case EBADF:
+ return "file descriptor is invalid.";
+
+ case EINVAL:
+ return "Request or argp is not valid.";
+
+ case ENOTTY:
+ return
+ "file descriptor is not associated with a character special device OR "
+ "The specified request does not apply to the "
+ "kind of object that the descriptor fildes references.";
+
+ case EADDRNOTAVAIL:
+ return "Address not available.";
+
+ default:
+ {
+ static char buffer[20];
+ sprintf(buffer, "Error %d", errno);
+ return buffer;
+ }
+ }
+}
+#endif
+
+
+NetworkDevice::EthernetAdapter::EthernetAdapter() {
+ name = "";
+ ip = 0;
+ hostname = "";
+ subnet = 0;
+ broadcast = 0;
+ for (int i = 0; i < 6; ++i) {
+ mac[i] = 0;
+ }
+}
+
+void NetworkDevice::EthernetAdapter::describe(TextOutput& t) const {
+ t.writeSymbol("{");
+ t.pushIndent();
+ t.writeNewline();
+
+ t.writeSymbols("hostname", "=");
+ t.writeString(hostname);
+ t.writeNewline();
+
+ t.writeSymbols("name", "=");
+ t.writeString(name);
+ t.writeNewline();
+
+ t.writeSymbols("ip", "=");
+ t.writeSymbol(formatIP(ip));
+ t.writeNewline();
+
+ t.writeSymbols("subnet", "=");
+ t.writeSymbol(formatIP(subnet));
+ t.writeNewline();
+
+ t.writeSymbols("broadcast", "=");
+ t.writeSymbol(formatIP(broadcast));
+ t.writeNewline();
+
+ t.writeSymbols("mac", "=");
+ t.writeSymbol(formatMAC(mac));
+ t.writeNewline();
+
+ t.popIndent();
+ t.writeSymbol("}");
+ t.writeNewline();
+}
+
+
+void NetworkDevice::addAdapter(const EthernetAdapter& a) {
+ m_adapterArray.append(a);
+ if (a.broadcast != 0) {
+ int i = m_broadcastAddresses.findIndex(a.broadcast);
+ if (i == -1) {
+ m_broadcastAddresses.append(a.broadcast);
+ }
+ }
+}
+
+
+std::string NetworkDevice::formatIP(uint32 addr) {
+ return format("%3d.%3d.%3d.%3d", (addr >> 24) & 0xFF, (addr >> 16) & 0xFF,
+ (addr >> 8) & 0xFF, addr & 0xFF);
+}
+
+
+std::string NetworkDevice::formatMAC(const uint8 MAC[6]) {
+ return format("%02x:%02x:%02x:%02x:%02x:%02x", MAC[0], MAC[1], MAC[2], MAC[3], MAC[4], MAC[5]);
+}
+
+
+#ifdef G3D_WIN32
+
+bool NetworkDevice::init() {
+ debugAssert(! initialized);
+
+ logPrintf("Network Startup");
+ logPrintf("Starting WinSock networking.\n");
+ WSADATA wsda;
+ WSAStartup(MAKEWORD(G3D_WINSOCK_MAJOR_VERSION, G3D_WINSOCK_MINOR_VERSION), &wsda);
+
+ std::string hostname = "localhost";
+ {
+ char ac[128];
+ if (gethostname(ac, sizeof(ac)) == -1) {
+ logPrintf("Warning: Error while getting local host name\n");
+ } else {
+ hostname = gethostbyname(ac)->h_name;
+ }
+ }
+
+ EthernetAdapter a;
+ a.hostname = hostname;
+ a.name = "";
+ a.ip = NetAddress(hostname, 0).ip();
+
+ // TODO: Find subnet on Win32
+ a.subnet = 0x0000FFFF;
+
+ // TODO: Find broadcast on Win32
+ a.broadcast = 0xFFFFFFFF;
+
+ // TODO: find MAC on Win32
+
+ addAdapter(a);
+
+ std::string machine = localHostName();
+ std::string addr = NetAddress(machine, 0).ipString();
+ logPrintf(
+ "Network:\n"
+ " Status: %s\n"
+ " Loaded winsock specification version %d (%d is "
+ "the highest available)\n"
+ " %d sockets available\n"
+ " Largest UDP datagram packet size is %d bytes\n\n",
+ wsda.szDescription,
+ wsda.szSystemStatus,
+ wsda.wVersion,
+ wsda.wHighVersion,
+ wsda.iMaxSockets,
+ wsda.iMaxUdpDg);
+
+ // TODO: WSAIoctl for subnet and broadcast addresses
+ // http://msdn.microsoft.com/en-us/library/ms741621(VS.85).aspx
+ //
+ // TODO: SIO_GET_INTERFACE_LIST
+
+ initialized = true;
+
+ return true;
+}
+#endif
+
+
+#if defined(G3D_LINUX) || defined(G3D_OSX) || defined(G3D_FREEBSD)
+
+const sockaddr_in* castToIP4(const sockaddr* addr) {
+ if (addr == NULL) {
+ return NULL;
+ } else if (addr->sa_family == AF_INET) {
+ // An IPv4 address
+ return reinterpret_cast<const sockaddr_in*>(addr);
+ } else {
+ // Not an IPv4 address
+ return NULL;
+ }
+}
+
+uint32 getIP(const sockaddr_in* addr) {
+ if (addr != NULL) {
+ return ntohl(addr->sin_addr.s_addr);
+ } else {
+ return 0;
+ }
+}
+
+
+bool NetworkDevice::init() {
+ debugAssert(! initialized);
+
+ // Used for combining the MAC and ip information
+ typedef Table<std::string, EthernetAdapter> AdapterTable;
+
+ AdapterTable table;
+
+ // Head of a linked list of network interfaces on this machine
+ ifaddrs* ifap = NULL;
+
+ int r = getifaddrs(&ifap);
+
+ if (r != 0) {
+ logPrintf("ERROR: getifaddrs returned %d\n", r);
+ return false;
+ }
+
+ ifaddrs* current = ifap;
+
+ if (current == NULL) {
+ logPrintf("WARNING: No network interfaces found\n");
+ EthernetAdapter a;
+ a.name = "fallback";
+ a.hostname = "localhost";
+ a.ip = (127 << 24) | 1;
+ a.broadcast = 0xFFFFFFFF;
+ a.subnet = 0x000000FF;
+ addAdapter(a);
+
+ } else {
+
+ while (current != NULL) {
+
+ bool up = (current->ifa_flags & IFF_UP);
+ bool loopback = (current->ifa_flags & IFF_LOOPBACK);
+
+ if (! up || loopback) {
+ // Skip this adapter; it is offline or is a loopback
+ current = current->ifa_next;
+ continue;
+ }
+
+ if (! table.containsKey(current->ifa_name)) {
+ EthernetAdapter a;
+ a.name = current->ifa_name;
+ table.set(a.name, a);
+ }
+
+ // This adapter must exist because it was created above
+ EthernetAdapter& adapter = table[current->ifa_name];
+
+ const sockaddr_in* interfaceAddress = castToIP4(current->ifa_addr);
+ const sockaddr_in* broadcastAddress = castToIP4(current->ifa_dstaddr);
+ const sockaddr_in* subnetMask = castToIP4(current->ifa_netmask);
+
+ uint32 ip = getIP(interfaceAddress);
+ uint32 ba = getIP(broadcastAddress);
+ uint32 sn = getIP(subnetMask);
+
+ if (ip != 0) {
+ adapter.ip = ip;
+ }
+
+ if (ba != 0) {
+ adapter.broadcast = ba;
+ }
+
+ if (sn != 0) {
+ adapter.subnet = sn;
+ }
+
+ uint8_t* MAC = NULL;
+ // Extract MAC address
+ if ((current->ifa_addr != NULL) && (current->ifa_addr->sa_family == AF_LINK)) {
+# ifdef __linux__
+ {
+ // Linux
+ struct ifreq ifr;
+
+ int fd = socket(AF_INET, SOCK_DGRAM, 0);
+
+ ifr.ifr_addr.sa_family = AF_INET;
+ strcpy(ifr.ifr_name, current->ifa_name);
+ ioctl(fd, SIOCGIFHWADDR, &ifr);
+ close(fd);
+
+ MAC = reinterpret_cast<uint8_t*>(ifr.ifr_hwaddr.sa_data);
+ }
+# else
+ {
+ // The MAC address and the interfaceAddress come in as
+ // different interfaces with the same name.
+
+ // Posix/FreeBSD/Mac OS
+ sockaddr_dl* sdl = (struct sockaddr_dl *)current->ifa_addr;
+ MAC = reinterpret_cast<uint8_t*>(LLADDR(sdl));
+ }
+# endif
+
+ // See if there was a MAC address
+ if (MAC != NULL) {
+ bool anyNonZero = false;
+ for (int i = 0; i < 6; ++i) {
+ anyNonZero = anyNonZero || (MAC[i] != 0);
+ }
+ if (anyNonZero) {
+ System::memcpy(adapter.mac, MAC, 6);
+ }
+ }
+ }
+
+ current = current->ifa_next;
+ }
+
+ freeifaddrs(ifap);
+ ifap = NULL;
+ }
+
+ // Extract all interesting adapters from the table
+ for (AdapterTable::Iterator it = table.begin(); it.hasMore(); ++it) {
+ const EthernetAdapter& adapter = it->value;
+
+ // Only add adapters that have IP addresses
+ if (adapter.ip != 0) {
+ addAdapter(adapter);
+ } else {
+ logPrintf("NetworkDevice: Ignored adapter %s because ip = 0\n", adapter.name.c_str());
+ }
+ }
+
+ initialized = true;
+
+ return true;
+}
+
+#endif
+
+
+void NetworkDevice::_cleanup() {
+ debugAssert(initialized);
+
+ logPrintf("Network Cleanup");
+# ifdef G3D_WIN32
+ WSACleanup();
+# endif
+ logPrintf("Network cleaned up.");
+}
+
+bool NetworkDevice::bind(SOCKET sock, const NetAddress& addr) const {
+ Log::common()->printf("Binding socket %d on port %d ",
+ sock, htons(addr.addr.sin_port));
+ if (::bind(sock, (struct sockaddr*)&(addr.addr), sizeof(addr.addr)) ==
+ SOCKET_ERROR) {
+
+ Log::common()->println("FAIL");
+ Log::common()->println(socketErrorCode());
+ closesocket(sock);
+ return false;
+ }
+
+ Log::common()->println("Ok");
+ return true;
+}
+
+
+void NetworkDevice::closesocket(SOCKET& sock) const {
+ if (sock != 0) {
+ #ifdef G3D_WIN32
+ ::closesocket(sock);
+ #else
+ close(sock);
+ #endif
+
+ Log::common()->printf("Closed socket %d\n", sock);
+ sock = 0;
+ }
+}
+
+
+void NetworkDevice::localHostAddresses(Array<NetAddress>& array) const {
+ array.resize(0);
+
+ char ac[128];
+
+ if (gethostname(ac, sizeof(ac)) == SOCKET_ERROR) {
+ Log::common()->printf("Error while getting local host name\n");
+ return;
+ }
+
+ struct hostent* phe = gethostbyname(ac);
+ if (phe == 0) {
+ Log::common()->printf("Error while getting local host address\n");
+ return;
+ }
+
+ for (int i = 0; (phe->h_addr_list[i] != 0); ++i) {
+ struct in_addr addr;
+ memcpy(&addr, phe->h_addr_list[i], sizeof(struct in_addr));
+ array.append(NetAddress(addr));
+ }
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+Conduit::Conduit() : binaryOutput("<memory>", G3D_LITTLE_ENDIAN) {
+ sock = 0;
+ mSent = 0;
+ mReceived = 0;
+ bSent = 0;
+ bReceived = 0;
+}
+
+
+Conduit::~Conduit() {
+ NetworkDevice::instance()->closesocket(sock);
+}
+
+
+uint64 Conduit::bytesSent() const {
+ return bSent;
+}
+
+
+uint64 Conduit::bytesReceived() const {
+ return bReceived;
+}
+
+
+uint64 Conduit::messagesSent() const {
+ return mSent;
+}
+
+
+uint64 Conduit::messagesReceived() const {
+ return mReceived;
+}
+
+
+bool Conduit::ok() const {
+ return (sock != 0) && (sock != SOCKET_ERROR);
+}
+
+
+bool Conduit::messageWaiting() {
+ return readWaiting(sock);
+}
+
+
+/**
+ Increases the send and receive sizes of a socket to 2 MB from 8k
+ */
+static void increaseBufferSize(SOCKET sock) {
+
+ // Increase the buffer size; the default (8192) is too easy to
+ // overflow when the network latency is high.
+ {
+ uint32 val = 1024 * 1024 * 2;
+ if (setsockopt(sock, SOL_SOCKET, SO_RCVBUF,
+ (char*)&val, sizeof(val)) == SOCKET_ERROR) {
+ Log::common()->printf("WARNING: Increasing socket "
+ "receive buffer to %d failed.\n", val);
+ Log::common()->println(socketErrorCode());
+ }
+
+ if (setsockopt(sock, SOL_SOCKET, SO_SNDBUF,
+ (char*)&val, sizeof(val)) == SOCKET_ERROR) {
+ Log::common()->printf("WARNING: Increasing socket "
+ "send buffer to %d failed.\n", val);
+ Log::common()->println(socketErrorCode());
+ }
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////////
+
+ReliableConduitRef ReliableConduit::create(const NetAddress& address) {
+ return new ReliableConduit(address);
+}
+
+
+ReliableConduit::ReliableConduit(
+ const NetAddress& _addr) : state(NO_MESSAGE), receiveBuffer(NULL),
+ receiveBufferTotalSize(0), receiveBufferUsedSize(0) {
+
+ NetworkDevice* nd = NetworkDevice::instance();
+
+ messageType = 0;
+
+ addr = _addr;
+ Log::common()->print("Creating a TCP socket ");
+ sock = socket(AF_INET, SOCK_STREAM, IPPROTO_IP);
+
+ if (sock == SOCKET_ERROR) {
+ Log::common()->println("FAIL");
+ Log::common()->println(socketErrorCode());
+ nd->closesocket(sock);
+ return;
+ }
+
+ Log::common()->println("Ok");
+
+ // Setup socket options (both constructors should set the same options)
+
+ // Disable Nagle's algorithm (we send lots of small packets)
+ const int T = true;
+ if (setsockopt(sock, IPPROTO_TCP, TCP_NODELAY,
+ (const char*)&T, sizeof(T)) == SOCKET_ERROR) {
+
+ Log::common()->println("WARNING: Disabling Nagel's "
+ "algorithm failed.");
+ Log::common()->println(socketErrorCode());
+ } else {
+ Log::common()->println("Disabled Nagel's algorithm.");
+ }
+
+ // Set the NO LINGER option so the socket doesn't hang around if
+ // there is unsent data in the queue when it closes.
+ struct linger ling;
+ ling.l_onoff = 0;
+ ling.l_linger = 0;
+ if (setsockopt(sock, SOL_SOCKET, SO_LINGER,
+ (const char*)&ling, sizeof(ling)) == SOCKET_ERROR) {
+
+ Log::common()->println("WARNING: Setting socket no linger failed.");
+ Log::common()->println(socketErrorCode());
+ } else {
+ Log::common()->println("Set socket option no_linger.");
+ }
+
+ // Set reuse address so that a new server can start up soon after
+ // an old one has closed.
+ if (setsockopt(sock, SOL_SOCKET, SO_REUSEADDR,
+ (const char*)&T, sizeof(T)) == SOCKET_ERROR) {
+
+ Log::common()->println("WARNING: Setting socket reuseaddr failed.");
+ Log::common()->println(socketErrorCode());
+ } else {
+ Log::common()->println("Set socket option reuseaddr.");
+ }
+
+ // Ideally, we'd like to specify IPTOS_LOWDELAY as well.
+
+ logSocketInfo(sock);
+
+ increaseBufferSize(sock);
+
+ Log::common()->printf("Created TCP socket %d\n", sock);
+
+ std::string x = addr.toString();
+ Log::common()->printf("Connecting to %s on TCP socket %d ", x.c_str(), sock);
+
+ int ret = connect(sock, (struct sockaddr *) &(addr.addr), sizeof(addr.addr));
+
+ if (ret == WSAEWOULDBLOCK) {
+ RealTime t = System::time() + 5.0;
+ // Non-blocking; we must wait until select returns non-zero
+ while ((selectOneWriteSocket(sock) == 0) && (System::time() < t)) {
+ System::sleep(0.02);
+ }
+
+ // TODO: check for failure on the select call
+
+ } else if (ret != 0) {
+ sock = (SOCKET)SOCKET_ERROR;
+ Log::common()->println("FAIL");
+ Log::common()->println(socketErrorCode());
+ return;
+ }
+
+ Log::common()->println("Ok");
+}
+
+
+ReliableConduit::ReliableConduit(
+ const SOCKET& _sock,
+ const NetAddress& _addr) :
+ state(NO_MESSAGE),
+ receiveBuffer(NULL),
+ receiveBufferTotalSize(0),
+ receiveBufferUsedSize(0) {
+ sock = _sock;
+ addr = _addr;
+
+ messageType = 0;
+
+ // Setup socket options (both constructors should set the same options)
+
+ // Disable Nagle's algorithm (we send lots of small packets)
+ const int T = true;
+ if (setsockopt(sock, IPPROTO_TCP, TCP_NODELAY,
+ (const char*)&T, sizeof(T)) == SOCKET_ERROR) {
+
+ Log::common()->println("WARNING: Disabling Nagel's algorithm failed.");
+ Log::common()->println(socketErrorCode());
+ } else {
+ Log::common()->println("Disabled Nagel's algorithm.");
+ }
+
+ // Set the NO LINGER option so the socket doesn't hang around if
+ // there is unsent data in the queue when it closes.
+ struct linger ling;
+ ling.l_onoff = 0;
+ ling.l_linger = 0;
+ if (setsockopt(sock, SOL_SOCKET, SO_LINGER,
+ (const char*)&ling, sizeof(ling)) == SOCKET_ERROR) {
+
+ Log::common()->println("WARNING: Setting socket no linger failed.");
+ Log::common()->println(socketErrorCode());
+ } else {
+ Log::common()->println("Set socket option no_linger.");
+ }
+
+ // Set reuse address so that a new server can start up soon after
+ // an old one has closed.
+ if (setsockopt(sock, SOL_SOCKET, SO_REUSEADDR,
+ (const char*)&T, sizeof(T)) == SOCKET_ERROR) {
+
+ Log::common()->println("WARNING: Setting socket reuseaddr failed.");
+ Log::common()->println(socketErrorCode());
+ } else {
+ Log::common()->println("Set socket option reuseaddr.");
+ }
+
+ // Ideally, we'd like to specify IPTOS_LOWDELAY as well.
+
+ logSocketInfo(sock);
+}
+
+
+ReliableConduit::~ReliableConduit() {
+ free(receiveBuffer);
+ receiveBuffer = NULL;
+ receiveBufferTotalSize = 0;
+ receiveBufferUsedSize = 0;
+}
+
+
+bool ReliableConduit::messageWaiting() {
+ switch (state) {
+ case HOLDING:
+ // We've already read the message and are waiting
+ // for a receive call.
+ return true;
+
+ case RECEIVING:
+
+ if (! ok()) {
+ return false;
+ }
+ // We're currently receiving the message. Read a little more.
+ receiveIntoBuffer();
+
+ if (messageSize == receiveBufferUsedSize) {
+ // We've read the whole mesage. Switch to holding state
+ // and return true.
+ state = HOLDING;
+ return true;
+ } else {
+ // There are more bytes left to read. We'll read them on
+ // the next call. Because the *entire* message is not ready,
+ // return false.
+ return false;
+ }
+ break;
+
+ case NO_MESSAGE:
+ if (Conduit::messageWaiting()) {
+ // Message incoming. Read the header.
+
+ state = RECEIVING;
+ receiveHeader();
+
+ // Loop back around now that we're in the receive state; we
+ // may be able to read the whole message before returning
+ // to the caller.
+ return messageWaiting();
+ } else {
+ // No message incoming.
+ return false;
+ }
+ }
+
+ debugAssertM(false, "Should not reach this point");
+ return false;
+}
+
+
+uint32 ReliableConduit::waitingMessageType() {
+ // The messageWaiting call is what actually receives the message.
+ if (messageWaiting()) {
+ return messageType;
+ } else {
+ return 0;
+ }
+}
+
+
+void ReliableConduit::sendBuffer(const BinaryOutput& b) {
+ NetworkDevice* nd = NetworkDevice::instance();
+ int ret = ::send(sock, (const char*)b.getCArray(), b.size(), 0);
+
+ if (ret == SOCKET_ERROR) {
+ Log::common()->println("Error occured while sending message.");
+ Log::common()->println(socketErrorCode());
+ nd->closesocket(sock);
+ return;
+ }
+
+ ++mSent;
+ bSent += b.size();
+
+ // Verify the packet was actually sent
+ // Conversion to unsigned is safe because -1 is caught earlier
+ debugAssert(ret == b.size());
+}
+
+
+/** Null serializer. Used by reliable conduit::send(type) */
+class Dummy {
+public:
+ void serialize(BinaryOutput& b) const { (void)b; }
+};
+
+
+void ReliableConduit::send(uint32 type) {
+ static Dummy dummy;
+ send(type, dummy);
+}
+
+
+
+NetAddress ReliableConduit::address() const {
+ return addr;
+}
+
+
+void ReliableConduit::receiveHeader() {
+ NetworkDevice* nd = NetworkDevice::instance();
+ debugAssert(state == RECEIVING);
+
+ // Read the type
+ uint32 tmp;
+ int ret = recv(sock, (char*)&tmp, sizeof(tmp), 0);
+
+ // The type is the first four bytes. It is little endian.
+ if (System::machineEndian() == G3D_LITTLE_ENDIAN) {
+ messageType = tmp;
+ } else {
+ // Swap the byte order
+ for (int i = 0; i < 4; ++i) {
+ ((char*)&messageType)[i] = ((char*)&tmp)[3 - i];
+ }
+ }
+
+ if ((ret == SOCKET_ERROR) || (ret != sizeof(messageType))) {
+ Log::common()->printf("Call to recv failed. ret = %d,"
+ " sizeof(messageType) = %d\n",
+ (int)ret, (int)sizeof(messageType));
+ Log::common()->println(socketErrorCode());
+ nd->closesocket(sock);
+ messageType = 0;
+ return;
+ }
+
+ // Read the size
+ ret = recv(sock, (char*)&messageSize, sizeof(messageSize), 0);
+
+ if ((ret == SOCKET_ERROR) || (ret != sizeof(messageSize))) {
+ Log::common()->printf("Call to recv failed. ret = %d,"
+ " sizeof(len) = %d\n", (int)ret,
+ (int)sizeof(messageSize));
+ Log::common()->println(socketErrorCode());
+ nd->closesocket(sock);
+ messageType = 0;
+ return;
+ }
+
+ messageSize = ntohl(messageSize);
+ debugAssert(messageSize < 6e7);
+
+ debugAssert(receiveBufferUsedSize == 0);
+
+ // Extend the size of the buffer.
+ if (messageSize > receiveBufferTotalSize) {
+ receiveBuffer = realloc(receiveBuffer, messageSize);
+ receiveBufferTotalSize = messageSize;
+ }
+
+ if (receiveBuffer == NULL) {
+ Log::common()->println("Could not allocate a memory buffer "
+ "during receivePacket.");
+ nd->closesocket(sock);
+ }
+
+ bReceived += 4;
+}
+
+
+void ReliableConduit::receiveIntoBuffer() {
+ NetworkDevice* nd = NetworkDevice::instance();
+
+ debugAssert(state == RECEIVING);
+ debugAssert(messageType != 0);
+ debugAssertM(receiveBufferUsedSize < messageSize, "Message already received.");
+ debugAssertM(messageSize >= receiveBufferUsedSize, "Message size overflow.");
+
+ // Read the data itself
+ int ret = 0;
+ uint32 left = messageSize - receiveBufferUsedSize;
+ int count = 0;
+ while ((ret != SOCKET_ERROR) && (left > 0) && (count < 100)) {
+
+ ret = recv(sock, ((char*)receiveBuffer) + receiveBufferUsedSize, left, 0);
+
+ if (ret > 0) {
+ left -= ret;
+ receiveBufferUsedSize += ret;
+ bReceived += ret;
+
+ if (left > 0) {
+ // There's still more. Give the machine a chance to read
+ // more data, but don't wait forever.
+
+ ++count;
+ System::sleep(0.001);
+ }
+ } else {
+ // Something went wrong; our blocking read returned nothing.
+ break;
+ }
+ }
+
+ if ((ret == 0) || (ret == SOCKET_ERROR)) {
+
+ if (ret == SOCKET_ERROR) {
+ Log::common()->printf("Call to recv failed. ret = %d,"
+ " sizeof(messageSize) = %d\n", ret, messageSize);
+ Log::common()->println(socketErrorCode());
+ } else {
+ Log::common()->printf("recv returned 0\n");
+ }
+ nd->closesocket(sock);
+ return;
+ }
+
+ ++mReceived;
+}
+
+
+///////////////////////////////////////////////////////////////////////////////
+LightweightConduitRef LightweightConduit::create(
+ uint16 receivePort,
+ bool enableReceive,
+ bool enableBroadcast) {
+
+ return new LightweightConduit(receivePort, enableReceive, enableBroadcast);
+}
+
+LightweightConduit::LightweightConduit(
+ uint16 port,
+ bool enableReceive,
+ bool enableBroadcast) {
+ NetworkDevice* nd = NetworkDevice::instance();
+
+ Log::common()->print("Creating a UDP socket ");
+ sock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
+
+ if (sock == SOCKET_ERROR) {
+ sock = 0;
+ Log::common()->println("FAIL");
+ Log::common()->println(socketErrorCode());
+ return;
+ }
+ Log::common()->println("Ok");
+
+ if (enableReceive) {
+ debugAssert(port != 0);
+ if (! nd->bind(sock, NetAddress(0, port))) {
+ nd->closesocket(sock);
+ sock = (SOCKET)SOCKET_ERROR;
+ }
+ }
+
+ // Figuring out the MTU seems very complicated, so we just set it to 1000,
+ // which is likely to be safe. See IP_MTU for more information.
+ MTU = 1000;
+
+ increaseBufferSize(sock);
+
+ if (enableBroadcast) {
+ int TR = true;
+ if (setsockopt(sock, SOL_SOCKET, SO_BROADCAST,
+ (const char*)&TR, sizeof(TR)) != 0) {
+ Log::common()->println("Call to setsockopt failed");
+ Log::common()->println(socketErrorCode());
+ nd->closesocket(sock);
+ sock = 0;
+ return;
+ }
+ }
+
+ Log::common()->printf("Done creating UDP socket %d\n", sock);
+
+ alreadyReadMessage = false;
+}
+
+
+LightweightConduit::~LightweightConduit() {
+}
+
+
+bool LightweightConduit::receive(NetAddress& sender) {
+ // This both checks to ensure that a message was waiting and
+ // actively consumes the message from the network stream if
+ // it has not been read yet.
+ uint32 t = waitingMessageType();
+ if (t == 0) {
+ return false;
+ }
+
+ sender = messageSender;
+ alreadyReadMessage = false;
+
+ if (messageBuffer.size() < 4) {
+ // Something went wrong
+ return false;
+ }
+
+ return true;
+}
+
+
+void LightweightConduit::sendBuffer(const NetAddress& a, BinaryOutput& b) {
+ NetworkDevice* nd = NetworkDevice::instance();
+ if (sendto(sock, (const char*)b.getCArray(), b.size(), 0,
+ (struct sockaddr *) &(a.addr), sizeof(a.addr)) == SOCKET_ERROR) {
+ Log::common()->printf("Error occured while sending packet "
+ "to %s\n", inet_ntoa(a.addr.sin_addr));
+ Log::common()->println(socketErrorCode());
+ nd->closesocket(sock);
+ } else {
+ ++mSent;
+ bSent += b.size();
+ }
+}
+
+
+bool LightweightConduit::messageWaiting() {
+ // We may have already pulled the message off the network stream
+ return alreadyReadMessage || Conduit::messageWaiting();
+}
+
+
+uint32 LightweightConduit::waitingMessageType() {
+ NetworkDevice* nd = NetworkDevice::instance();
+ if (! messageWaiting()) {
+ return 0;
+ }
+
+ if (! alreadyReadMessage) {
+ messageBuffer.resize(8192);
+
+ SOCKADDR_IN remote_addr;
+ int iRemoteAddrLen = sizeof(sockaddr);
+
+ int ret = recvfrom(sock, (char*)messageBuffer.getCArray(),
+ messageBuffer.size(), 0, (struct sockaddr *) &remote_addr,
+ (socklen_t*)&iRemoteAddrLen);
+
+ if (ret == SOCKET_ERROR) {
+ Log::common()->println("Error: recvfrom failed in "
+ "LightweightConduit::waitingMessageType().");
+ Log::common()->println(socketErrorCode());
+ nd->closesocket(sock);
+ messageBuffer.resize(0);
+ messageSender = NetAddress();
+ messageType = 0;
+ return 0;
+ }
+
+ messageSender = NetAddress(remote_addr);
+
+ ++mReceived;
+ bReceived += ret;
+
+ messageBuffer.resize(ret, DONT_SHRINK_UNDERLYING_ARRAY);
+
+ // The type is the first four bytes. It is little endian.
+ if (System::machineEndian() == G3D_LITTLE_ENDIAN) {
+ messageType = *((uint32*)messageBuffer.getCArray());
+ } else {
+ // Swap the byte order
+ for (int i = 0; i < 4; ++i) {
+ ((char*)&messageType)[i] = messageBuffer[3 - i];
+ }
+ }
+
+ alreadyReadMessage = true;
+ }
+
+ return messageType;
+}
+
+
+///////////////////////////////////////////////////////////////////////////////
+
+NetListenerRef NetListener::create(const uint16 port) {
+ return new NetListener(port);
+}
+
+
+NetListener::NetListener(uint16 port) {
+ NetworkDevice* nd = NetworkDevice::instance();
+
+ // Start the listener socket
+ Log::common()->print("Creating a listener ");
+ sock = socket(AF_INET, SOCK_STREAM, IPPROTO_IP);
+
+ if (sock == SOCKET_ERROR) {
+ Log::common()->printf("FAIL");
+ Log::common()->println(socketErrorCode());
+ return;
+ }
+ Log::common()->println("Ok");
+
+ const int T = true;
+
+ // Set reuse address so that a new server can start up soon after
+ // an old one has closed.
+ if (setsockopt(sock, SOL_SOCKET, SO_REUSEADDR,
+ (const char*)&T, sizeof(T)) == SOCKET_ERROR) {
+
+ Log::common()->println("WARNING: Setting socket reuseaddr failed.");
+ Log::common()->println(socketErrorCode());
+ } else {
+ Log::common()->println("Set socket option reuseaddr.");
+ }
+
+
+ if (! nd->bind(sock, NetAddress(0, port))) {
+ Log::common()->printf("Unable to bind!\n");
+ nd->closesocket(sock);
+ sock = (SOCKET)SOCKET_ERROR;
+ return;
+ }
+
+ Log::common()->printf("Listening on port %5d ", port);
+
+ // listen is supposed to return 0 when there is no error.
+ // The 2nd argument is the number of connections to allow pending
+ // at any time.
+ int L = listen(sock, 100);
+ if (L == SOCKET_ERROR) {
+ Log::common()->println("FAIL");
+ Log::common()->println(socketErrorCode());
+ nd->closesocket(sock);
+ sock = (SOCKET)SOCKET_ERROR;
+ return;
+ }
+ Log::common()->println("Ok");
+ Log::common()->printf("Now listening on socket %d.\n\n", sock);
+}
+
+
+NetListener::~NetListener() {
+ NetworkDevice* nd = NetworkDevice::instance();
+ nd->closesocket(sock);
+}
+
+
+ReliableConduitRef NetListener::waitForConnection() {
+ NetworkDevice* nd = NetworkDevice::instance();
+ // The address of the connecting host
+ SOCKADDR_IN remote_addr;
+ int iAddrLen = sizeof(remote_addr);
+
+ Log::common()->println("Blocking in NetListener::waitForConnection().");
+
+ SOCKET sClient = accept(sock, (struct sockaddr*) &remote_addr,
+ (socklen_t*)&iAddrLen);
+
+ if (sClient == SOCKET_ERROR) {
+ Log::common()->println("Error in NetListener::acceptConnection.");
+ Log::common()->println(socketErrorCode());
+ nd->closesocket(sock);
+ return NULL;
+ }
+
+ Log::common()->printf("%s connected, transferred to socket %d.\n",
+ inet_ntoa(remote_addr.sin_addr), sClient);
+
+ #ifndef G3D_WIN32
+ return new ReliableConduit(sClient,
+ NetAddress(htonl(remote_addr.sin_addr.s_addr),
+ ntohs(remote_addr.sin_port)));
+ #else
+ return new ReliableConduit(sClient,
+ NetAddress(ntohl(remote_addr.sin_addr.S_un.S_addr),
+ ntohs(remote_addr.sin_port)));
+ #endif
+}
+
+
+bool NetListener::ok() const {
+ return (sock != 0) && (sock != SOCKET_ERROR);
+}
+
+
+bool NetListener::clientWaiting() const {
+ return readWaiting(sock);
+}
+
+////////////////////////////////////////////////////////////////////////////////////////////////
+
+void NetworkDevice::describeSystem(
+ TextOutput& t) {
+
+ t.writeSymbols("Network", "{");
+ t.writeNewline();
+ t.pushIndent();
+
+ for (int i = 0; i < m_adapterArray.size(); ++i) {
+ m_adapterArray[i].describe(t);
+ }
+
+
+ t.popIndent();
+ t.writeSymbols("}");
+ t.writeNewline();
+ t.writeNewline();
+}
+
+
+void NetworkDevice::describeSystem(
+ std::string& s) {
+
+ TextOutput t;
+ describeSystem(t);
+ t.commitString(s);
+}
+
+} // namespace
diff --git a/externals/g3dlite/G3D.lib/source/PhysicsFrame.cpp b/externals/g3dlite/G3D.lib/source/PhysicsFrame.cpp
new file mode 100644
index 00000000000..034e585d01f
--- /dev/null
+++ b/externals/g3dlite/G3D.lib/source/PhysicsFrame.cpp
@@ -0,0 +1,77 @@
+/**
+ @file PhysicsFrame.cpp
+
+ @maintainer Morgan McGuire, matrix@graphics3d.com
+
+ @created 2002-07-09
+ @edited 2006-01-25
+*/
+
+#include "G3D/platform.h"
+#include "G3D/PhysicsFrame.h"
+#include "G3D/BinaryInput.h"
+#include "G3D/BinaryOutput.h"
+
+namespace G3D {
+
+PhysicsFrame::PhysicsFrame() {
+ translation = Vector3::zero();
+ rotation = Quat();
+}
+
+
+PhysicsFrame::PhysicsFrame(
+ const CoordinateFrame& coordinateFrame) {
+
+ translation = coordinateFrame.translation;
+ rotation = Quat(coordinateFrame.rotation);
+}
+
+
+PhysicsFrame PhysicsFrame::operator*(const PhysicsFrame& other) const {
+ PhysicsFrame result;
+
+ result.rotation = rotation * other.rotation;
+ result.translation = translation + rotation.toRotationMatrix() * other.translation;
+
+ return result;
+}
+
+
+CoordinateFrame PhysicsFrame::toCoordinateFrame() const {
+ CoordinateFrame f;
+
+ f.translation = translation;
+ f.rotation = rotation.toRotationMatrix();
+
+ return f;
+}
+
+
+PhysicsFrame PhysicsFrame::lerp(
+ const PhysicsFrame& other,
+ float alpha) const {
+
+ PhysicsFrame result;
+
+ result.translation = translation.lerp(other.translation, alpha);
+ result.rotation = rotation.slerp(other.rotation, alpha);
+
+ return result;
+}
+
+
+void PhysicsFrame::deserialize(class BinaryInput& b) {
+ translation.deserialize(b);
+ rotation.deserialize(b);
+}
+
+
+void PhysicsFrame::serialize(class BinaryOutput& b) const {
+ translation.serialize(b);
+ rotation.serialize(b);
+}
+
+
+}; // namespace
+
diff --git a/externals/g3dlite/G3D.lib/source/Plane.cpp b/externals/g3dlite/G3D.lib/source/Plane.cpp
new file mode 100644
index 00000000000..bb8ed5c6033
--- /dev/null
+++ b/externals/g3dlite/G3D.lib/source/Plane.cpp
@@ -0,0 +1,149 @@
+/**
+ @file Plane.cpp
+
+ @maintainer Morgan McGuire, matrix@graphics3d.com
+
+ @created 2003-02-06
+ @edited 2006-01-29
+ */
+
+#include "G3D/platform.h"
+#include "G3D/Plane.h"
+#include "G3D/BinaryOutput.h"
+#include "G3D/BinaryInput.h"
+#include "G3D/stringutils.h"
+
+namespace G3D {
+
+Plane::Plane(class BinaryInput& b) {
+ deserialize(b);
+}
+
+
+void Plane::serialize(class BinaryOutput& b) const {
+ _normal.serialize(b);
+ b.writeFloat64(_distance);
+}
+
+
+void Plane::deserialize(class BinaryInput& b) {
+ _normal.deserialize(b);
+ _distance = (float)b.readFloat64();
+}
+
+
+Plane::Plane(
+ Vector4 point0,
+ Vector4 point1,
+ Vector4 point2) {
+
+ debugAssertM(
+ point0.w != 0 ||
+ point1.w != 0 ||
+ point2.w != 0,
+ "At least one point must be finite.");
+
+ // Rotate the points around so that the finite points come first.
+
+ while ((point0.w == 0) &&
+ ((point1.w == 0) || (point2.w != 0))) {
+ Vector4 temp = point0;
+ point0 = point1;
+ point1 = point2;
+ point2 = temp;
+ }
+
+ Vector3 dir1;
+ Vector3 dir2;
+
+ if (point1.w == 0) {
+ // 1 finite, 2 infinite points; the plane must contain
+ // the direction of the two direcitons
+ dir1 = point1.xyz();
+ dir2 = point2.xyz();
+ } else if (point2.w != 0) {
+ // 3 finite points, the plane must contain the directions
+ // betwseen the points.
+ dir1 = point1.xyz() - point0.xyz();
+ dir2 = point2.xyz() - point0.xyz();
+ } else {
+ // 2 finite, 1 infinite point; the plane must contain
+ // the direction between the first two points and the
+ // direction of the third point.
+ dir1 = point1.xyz() - point0.xyz();
+ dir2 = point2.xyz();
+ }
+
+ _normal = dir1.cross(dir2).direction();
+ _distance = _normal.dot(point0.xyz());
+}
+
+
+Plane::Plane(
+ const Vector3& point0,
+ const Vector3& point1,
+ const Vector3& point2) {
+
+ _normal = (point1 - point0).cross(point2 - point0).direction();
+ _distance = _normal.dot(point0);
+}
+
+
+Plane::Plane(
+ const Vector3& __normal,
+ const Vector3& point) {
+
+ _normal = __normal.direction();
+ _distance = _normal.dot(point);
+}
+
+
+Plane Plane::fromEquation(float a, float b, float c, float d) {
+ Vector3 n(a, b, c);
+ float magnitude = n.magnitude();
+ d /= magnitude;
+ n /= magnitude;
+ return Plane(n, -d);
+}
+
+
+void Plane::flip() {
+ _normal = -_normal;
+ _distance = -_distance;
+}
+
+
+void Plane::getEquation(Vector3& n, float& d) const {
+ double _d;
+ getEquation(n, _d);
+ d = (float)_d;
+}
+
+void Plane::getEquation(Vector3& n, double& d) const {
+ n = _normal;
+ d = -_distance;
+}
+
+
+void Plane::getEquation(float& a, float& b, float& c, float& d) const {
+ double _a, _b, _c, _d;
+ getEquation(_a, _b, _c, _d);
+ a = (float)_a;
+ b = (float)_b;
+ c = (float)_c;
+ d = (float)_d;
+}
+
+void Plane::getEquation(double& a, double& b, double& c, double& d) const {
+ a = _normal.x;
+ b = _normal.y;
+ c = _normal.z;
+ d = -_distance;
+}
+
+
+std::string Plane::toString() const {
+ return format("Plane(%g, %g, %g, %g)", _normal.x, _normal.y, _normal.z, _distance);
+}
+
+}
diff --git a/externals/g3dlite/G3D.lib/source/Quat.cpp b/externals/g3dlite/G3D.lib/source/Quat.cpp
new file mode 100644
index 00000000000..225c5b51acc
--- /dev/null
+++ b/externals/g3dlite/G3D.lib/source/Quat.cpp
@@ -0,0 +1,583 @@
+/**
+ @file Quat.cpp
+
+ Quaternion implementation based on Watt & Watt page 363
+
+ @author Morgan McGuire, graphics3d.com
+
+ @created 2002-01-23
+ @edited 2006-01-31
+ */
+
+#include "G3D/Quat.h"
+#include "G3D/BinaryInput.h"
+#include "G3D/BinaryOutput.h"
+
+namespace G3D {
+
+Quat Quat::fromAxisAngleRotation(
+ const Vector3& axis,
+ float angle) {
+
+ Quat q;
+
+ q.w = cos(angle / 2.0f);
+ q.imag() = axis.direction() * sin(angle / 2.0f);
+
+ return q;
+}
+
+
+Quat::Quat(
+ const Matrix3& rot) {
+
+ static const int plus1mod3[] = {1, 2, 0};
+
+ // Find the index of the largest diagonal component
+ // These ? operations hopefully compile to conditional
+ // move instructions instead of branches.
+ int i = (rot[1][1] > rot[0][0]) ? 1 : 0;
+ i = (rot[2][2] > rot[i][i]) ? 2 : i;
+
+ // Find the indices of the other elements
+ int j = plus1mod3[i];
+ int k = plus1mod3[j];
+
+ // Index the elements of the vector part of the quaternion as a float*
+ float* v = (float*)(this);
+
+ // If we attempted to pre-normalize and trusted the matrix to be
+ // perfectly orthonormal, the result would be:
+ //
+ // double c = sqrt((rot[i][i] - (rot[j][j] + rot[k][k])) + 1.0)
+ // v[i] = -c * 0.5
+ // v[j] = -(rot[i][j] + rot[j][i]) * 0.5 / c
+ // v[k] = -(rot[i][k] + rot[k][i]) * 0.5 / c
+ // w = (rot[j][k] - rot[k][j]) * 0.5 / c
+ //
+ // Since we're going to pay the sqrt anyway, we perform a post normalization, which also
+ // fixes any poorly normalized input. Multiply all elements by 2*c in the above, giving:
+
+ // nc2 = -c^2
+ double nc2 = ((rot[j][j] + rot[k][k]) - rot[i][i]) - 1.0;
+ v[i] = nc2;
+ w = (rot[j][k] - rot[k][j]);
+ v[j] = -(rot[i][j] + rot[j][i]);
+ v[k] = -(rot[i][k] + rot[k][i]);
+
+ // We now have the correct result with the wrong magnitude, so normalize it:
+ float s = sqrt(x*x + y*y + z*z + w*w);
+ if (s > 0.00001f) {
+ s = 1.0f / s;
+ x *= s;
+ y *= s;
+ z *= s;
+ w *= s;
+ } else {
+ // The quaternion is nearly zero. Make it 0 0 0 1
+ x = 0.0f;
+ y = 0.0f;
+ z = 0.0f;
+ w = 1.0f;
+ }
+}
+
+
+void Quat::toAxisAngleRotation(
+ Vector3& axis,
+ double& angle) const {
+
+ // Decompose the quaternion into an angle and an axis.
+
+ axis = Vector3(x, y, z);
+ angle = 2 * acos(w);
+
+ float len = sqrt(1.0f - w * w);
+
+ if (fuzzyGt(abs(len), 0.0f)) {
+ axis /= len;
+ }
+
+ // Reduce the range of the angle.
+
+ if (angle < 0) {
+ angle = -angle;
+ axis = -axis;
+ }
+
+ while (angle > twoPi()) {
+ angle -= twoPi();
+ }
+
+ if (abs(angle) > pi()) {
+ angle -= twoPi();
+ }
+
+ // Make the angle positive.
+
+ if (angle < 0.0f) {
+ angle = -angle;
+ axis = -axis;
+ }
+}
+
+
+Matrix3 Quat::toRotationMatrix() const {
+ Matrix3 out = Matrix3::zero();
+
+ toRotationMatrix(out);
+
+ return out;
+}
+
+
+void Quat::toRotationMatrix(
+ Matrix3& rot) const {
+
+ rot = Matrix3(*this);
+}
+
+
+Quat Quat::slerp(
+ const Quat& _quat1,
+ float alpha,
+ float threshold) const {
+
+ // From: Game Physics -- David Eberly pg 538-540
+ // Modified to include lerp for small angles, which
+ // is a common practice.
+
+ // See also:
+ // http://number-none.com/product/Understanding%20Slerp,%20Then%20Not%20Using%20It/index.html
+
+ const Quat& quat0 = *this;
+ Quat quat1 = _quat1;
+
+ // angle between quaternion rotations
+ float phi;
+ float cosphi = quat0.dot(quat1);
+
+
+ if (cosphi < 0) {
+ // Change the sign and fix the dot product; we need to
+ // loop the other way to get the shortest path
+ quat1 = -quat1;
+ cosphi = -cosphi;
+ }
+
+ // Using G3D::aCos will clamp the angle to 0 and pi
+ phi = static_cast<float>(G3D::aCos(cosphi));
+
+ if (phi >= threshold) {
+ // For large angles, slerp
+ float scale0, scale1;
+
+ scale0 = sin((1.0f - alpha) * phi);
+ scale1 = sin(alpha * phi);
+
+ return ( (quat0 * scale0) + (quat1 * scale1) ) / sin(phi);
+ } else {
+ // For small angles, linear interpolate
+ return quat0.nlerp(quat1, alpha);
+ }
+}
+
+
+Quat Quat::nlerp(
+ const Quat& quat1,
+ float alpha) const {
+
+ Quat result = (*this) * (1.0f - alpha) + quat1 * alpha;
+ return result / result.magnitude();
+}
+
+
+Quat Quat::operator*(const Quat& other) const {
+
+ // Following Watt & Watt, page 360
+ const Vector3& v1 = imag();
+ const Vector3& v2 = other.imag();
+ float s1 = w;
+ float s2 = other.w;
+
+ return Quat(s1*v2 + s2*v1 + v1.cross(v2), s1*s2 - v1.dot(v2));
+}
+
+
+// From "Uniform Random Rotations", Ken Shoemake, Graphics Gems III.
+Quat Quat::unitRandom() {
+ float x0 = uniformRandom();
+ float r1 = sqrtf(1 - x0),
+ r2 = sqrtf(x0);
+ float t1 = (float)G3D::twoPi() * uniformRandom();
+ float t2 = (float)G3D::twoPi() * uniformRandom();
+ float c1 = cosf(t1),
+ s1 = sinf(t1);
+ float c2 = cosf(t2),
+ s2 = sinf(t2);
+ return Quat(s1 * r1, c1 * r1, s2 * r2, c2 * r2);
+}
+
+
+void Quat::deserialize(class BinaryInput& b) {
+ x = b.readFloat32();
+ y = b.readFloat32();
+ z = b.readFloat32();
+ w = b.readFloat32();
+}
+
+
+void Quat::serialize(class BinaryOutput& b) const {
+ b.writeFloat32(x);
+ b.writeFloat32(y);
+ b.writeFloat32(z);
+ b.writeFloat32(w);
+}
+
+
+// 2-char swizzles
+
+Vector2 Quat::xx() const { return Vector2 (x, x); }
+Vector2 Quat::yx() const { return Vector2 (y, x); }
+Vector2 Quat::zx() const { return Vector2 (z, x); }
+Vector2 Quat::wx() const { return Vector2 (w, x); }
+Vector2 Quat::xy() const { return Vector2 (x, y); }
+Vector2 Quat::yy() const { return Vector2 (y, y); }
+Vector2 Quat::zy() const { return Vector2 (z, y); }
+Vector2 Quat::wy() const { return Vector2 (w, y); }
+Vector2 Quat::xz() const { return Vector2 (x, z); }
+Vector2 Quat::yz() const { return Vector2 (y, z); }
+Vector2 Quat::zz() const { return Vector2 (z, z); }
+Vector2 Quat::wz() const { return Vector2 (w, z); }
+Vector2 Quat::xw() const { return Vector2 (x, w); }
+Vector2 Quat::yw() const { return Vector2 (y, w); }
+Vector2 Quat::zw() const { return Vector2 (z, w); }
+Vector2 Quat::ww() const { return Vector2 (w, w); }
+
+// 3-char swizzles
+
+Vector3 Quat::xxx() const { return Vector3 (x, x, x); }
+Vector3 Quat::yxx() const { return Vector3 (y, x, x); }
+Vector3 Quat::zxx() const { return Vector3 (z, x, x); }
+Vector3 Quat::wxx() const { return Vector3 (w, x, x); }
+Vector3 Quat::xyx() const { return Vector3 (x, y, x); }
+Vector3 Quat::yyx() const { return Vector3 (y, y, x); }
+Vector3 Quat::zyx() const { return Vector3 (z, y, x); }
+Vector3 Quat::wyx() const { return Vector3 (w, y, x); }
+Vector3 Quat::xzx() const { return Vector3 (x, z, x); }
+Vector3 Quat::yzx() const { return Vector3 (y, z, x); }
+Vector3 Quat::zzx() const { return Vector3 (z, z, x); }
+Vector3 Quat::wzx() const { return Vector3 (w, z, x); }
+Vector3 Quat::xwx() const { return Vector3 (x, w, x); }
+Vector3 Quat::ywx() const { return Vector3 (y, w, x); }
+Vector3 Quat::zwx() const { return Vector3 (z, w, x); }
+Vector3 Quat::wwx() const { return Vector3 (w, w, x); }
+Vector3 Quat::xxy() const { return Vector3 (x, x, y); }
+Vector3 Quat::yxy() const { return Vector3 (y, x, y); }
+Vector3 Quat::zxy() const { return Vector3 (z, x, y); }
+Vector3 Quat::wxy() const { return Vector3 (w, x, y); }
+Vector3 Quat::xyy() const { return Vector3 (x, y, y); }
+Vector3 Quat::yyy() const { return Vector3 (y, y, y); }
+Vector3 Quat::zyy() const { return Vector3 (z, y, y); }
+Vector3 Quat::wyy() const { return Vector3 (w, y, y); }
+Vector3 Quat::xzy() const { return Vector3 (x, z, y); }
+Vector3 Quat::yzy() const { return Vector3 (y, z, y); }
+Vector3 Quat::zzy() const { return Vector3 (z, z, y); }
+Vector3 Quat::wzy() const { return Vector3 (w, z, y); }
+Vector3 Quat::xwy() const { return Vector3 (x, w, y); }
+Vector3 Quat::ywy() const { return Vector3 (y, w, y); }
+Vector3 Quat::zwy() const { return Vector3 (z, w, y); }
+Vector3 Quat::wwy() const { return Vector3 (w, w, y); }
+Vector3 Quat::xxz() const { return Vector3 (x, x, z); }
+Vector3 Quat::yxz() const { return Vector3 (y, x, z); }
+Vector3 Quat::zxz() const { return Vector3 (z, x, z); }
+Vector3 Quat::wxz() const { return Vector3 (w, x, z); }
+Vector3 Quat::xyz() const { return Vector3 (x, y, z); }
+Vector3 Quat::yyz() const { return Vector3 (y, y, z); }
+Vector3 Quat::zyz() const { return Vector3 (z, y, z); }
+Vector3 Quat::wyz() const { return Vector3 (w, y, z); }
+Vector3 Quat::xzz() const { return Vector3 (x, z, z); }
+Vector3 Quat::yzz() const { return Vector3 (y, z, z); }
+Vector3 Quat::zzz() const { return Vector3 (z, z, z); }
+Vector3 Quat::wzz() const { return Vector3 (w, z, z); }
+Vector3 Quat::xwz() const { return Vector3 (x, w, z); }
+Vector3 Quat::ywz() const { return Vector3 (y, w, z); }
+Vector3 Quat::zwz() const { return Vector3 (z, w, z); }
+Vector3 Quat::wwz() const { return Vector3 (w, w, z); }
+Vector3 Quat::xxw() const { return Vector3 (x, x, w); }
+Vector3 Quat::yxw() const { return Vector3 (y, x, w); }
+Vector3 Quat::zxw() const { return Vector3 (z, x, w); }
+Vector3 Quat::wxw() const { return Vector3 (w, x, w); }
+Vector3 Quat::xyw() const { return Vector3 (x, y, w); }
+Vector3 Quat::yyw() const { return Vector3 (y, y, w); }
+Vector3 Quat::zyw() const { return Vector3 (z, y, w); }
+Vector3 Quat::wyw() const { return Vector3 (w, y, w); }
+Vector3 Quat::xzw() const { return Vector3 (x, z, w); }
+Vector3 Quat::yzw() const { return Vector3 (y, z, w); }
+Vector3 Quat::zzw() const { return Vector3 (z, z, w); }
+Vector3 Quat::wzw() const { return Vector3 (w, z, w); }
+Vector3 Quat::xww() const { return Vector3 (x, w, w); }
+Vector3 Quat::yww() const { return Vector3 (y, w, w); }
+Vector3 Quat::zww() const { return Vector3 (z, w, w); }
+Vector3 Quat::www() const { return Vector3 (w, w, w); }
+
+// 4-char swizzles
+
+Vector4 Quat::xxxx() const { return Vector4 (x, x, x, x); }
+Vector4 Quat::yxxx() const { return Vector4 (y, x, x, x); }
+Vector4 Quat::zxxx() const { return Vector4 (z, x, x, x); }
+Vector4 Quat::wxxx() const { return Vector4 (w, x, x, x); }
+Vector4 Quat::xyxx() const { return Vector4 (x, y, x, x); }
+Vector4 Quat::yyxx() const { return Vector4 (y, y, x, x); }
+Vector4 Quat::zyxx() const { return Vector4 (z, y, x, x); }
+Vector4 Quat::wyxx() const { return Vector4 (w, y, x, x); }
+Vector4 Quat::xzxx() const { return Vector4 (x, z, x, x); }
+Vector4 Quat::yzxx() const { return Vector4 (y, z, x, x); }
+Vector4 Quat::zzxx() const { return Vector4 (z, z, x, x); }
+Vector4 Quat::wzxx() const { return Vector4 (w, z, x, x); }
+Vector4 Quat::xwxx() const { return Vector4 (x, w, x, x); }
+Vector4 Quat::ywxx() const { return Vector4 (y, w, x, x); }
+Vector4 Quat::zwxx() const { return Vector4 (z, w, x, x); }
+Vector4 Quat::wwxx() const { return Vector4 (w, w, x, x); }
+Vector4 Quat::xxyx() const { return Vector4 (x, x, y, x); }
+Vector4 Quat::yxyx() const { return Vector4 (y, x, y, x); }
+Vector4 Quat::zxyx() const { return Vector4 (z, x, y, x); }
+Vector4 Quat::wxyx() const { return Vector4 (w, x, y, x); }
+Vector4 Quat::xyyx() const { return Vector4 (x, y, y, x); }
+Vector4 Quat::yyyx() const { return Vector4 (y, y, y, x); }
+Vector4 Quat::zyyx() const { return Vector4 (z, y, y, x); }
+Vector4 Quat::wyyx() const { return Vector4 (w, y, y, x); }
+Vector4 Quat::xzyx() const { return Vector4 (x, z, y, x); }
+Vector4 Quat::yzyx() const { return Vector4 (y, z, y, x); }
+Vector4 Quat::zzyx() const { return Vector4 (z, z, y, x); }
+Vector4 Quat::wzyx() const { return Vector4 (w, z, y, x); }
+Vector4 Quat::xwyx() const { return Vector4 (x, w, y, x); }
+Vector4 Quat::ywyx() const { return Vector4 (y, w, y, x); }
+Vector4 Quat::zwyx() const { return Vector4 (z, w, y, x); }
+Vector4 Quat::wwyx() const { return Vector4 (w, w, y, x); }
+Vector4 Quat::xxzx() const { return Vector4 (x, x, z, x); }
+Vector4 Quat::yxzx() const { return Vector4 (y, x, z, x); }
+Vector4 Quat::zxzx() const { return Vector4 (z, x, z, x); }
+Vector4 Quat::wxzx() const { return Vector4 (w, x, z, x); }
+Vector4 Quat::xyzx() const { return Vector4 (x, y, z, x); }
+Vector4 Quat::yyzx() const { return Vector4 (y, y, z, x); }
+Vector4 Quat::zyzx() const { return Vector4 (z, y, z, x); }
+Vector4 Quat::wyzx() const { return Vector4 (w, y, z, x); }
+Vector4 Quat::xzzx() const { return Vector4 (x, z, z, x); }
+Vector4 Quat::yzzx() const { return Vector4 (y, z, z, x); }
+Vector4 Quat::zzzx() const { return Vector4 (z, z, z, x); }
+Vector4 Quat::wzzx() const { return Vector4 (w, z, z, x); }
+Vector4 Quat::xwzx() const { return Vector4 (x, w, z, x); }
+Vector4 Quat::ywzx() const { return Vector4 (y, w, z, x); }
+Vector4 Quat::zwzx() const { return Vector4 (z, w, z, x); }
+Vector4 Quat::wwzx() const { return Vector4 (w, w, z, x); }
+Vector4 Quat::xxwx() const { return Vector4 (x, x, w, x); }
+Vector4 Quat::yxwx() const { return Vector4 (y, x, w, x); }
+Vector4 Quat::zxwx() const { return Vector4 (z, x, w, x); }
+Vector4 Quat::wxwx() const { return Vector4 (w, x, w, x); }
+Vector4 Quat::xywx() const { return Vector4 (x, y, w, x); }
+Vector4 Quat::yywx() const { return Vector4 (y, y, w, x); }
+Vector4 Quat::zywx() const { return Vector4 (z, y, w, x); }
+Vector4 Quat::wywx() const { return Vector4 (w, y, w, x); }
+Vector4 Quat::xzwx() const { return Vector4 (x, z, w, x); }
+Vector4 Quat::yzwx() const { return Vector4 (y, z, w, x); }
+Vector4 Quat::zzwx() const { return Vector4 (z, z, w, x); }
+Vector4 Quat::wzwx() const { return Vector4 (w, z, w, x); }
+Vector4 Quat::xwwx() const { return Vector4 (x, w, w, x); }
+Vector4 Quat::ywwx() const { return Vector4 (y, w, w, x); }
+Vector4 Quat::zwwx() const { return Vector4 (z, w, w, x); }
+Vector4 Quat::wwwx() const { return Vector4 (w, w, w, x); }
+Vector4 Quat::xxxy() const { return Vector4 (x, x, x, y); }
+Vector4 Quat::yxxy() const { return Vector4 (y, x, x, y); }
+Vector4 Quat::zxxy() const { return Vector4 (z, x, x, y); }
+Vector4 Quat::wxxy() const { return Vector4 (w, x, x, y); }
+Vector4 Quat::xyxy() const { return Vector4 (x, y, x, y); }
+Vector4 Quat::yyxy() const { return Vector4 (y, y, x, y); }
+Vector4 Quat::zyxy() const { return Vector4 (z, y, x, y); }
+Vector4 Quat::wyxy() const { return Vector4 (w, y, x, y); }
+Vector4 Quat::xzxy() const { return Vector4 (x, z, x, y); }
+Vector4 Quat::yzxy() const { return Vector4 (y, z, x, y); }
+Vector4 Quat::zzxy() const { return Vector4 (z, z, x, y); }
+Vector4 Quat::wzxy() const { return Vector4 (w, z, x, y); }
+Vector4 Quat::xwxy() const { return Vector4 (x, w, x, y); }
+Vector4 Quat::ywxy() const { return Vector4 (y, w, x, y); }
+Vector4 Quat::zwxy() const { return Vector4 (z, w, x, y); }
+Vector4 Quat::wwxy() const { return Vector4 (w, w, x, y); }
+Vector4 Quat::xxyy() const { return Vector4 (x, x, y, y); }
+Vector4 Quat::yxyy() const { return Vector4 (y, x, y, y); }
+Vector4 Quat::zxyy() const { return Vector4 (z, x, y, y); }
+Vector4 Quat::wxyy() const { return Vector4 (w, x, y, y); }
+Vector4 Quat::xyyy() const { return Vector4 (x, y, y, y); }
+Vector4 Quat::yyyy() const { return Vector4 (y, y, y, y); }
+Vector4 Quat::zyyy() const { return Vector4 (z, y, y, y); }
+Vector4 Quat::wyyy() const { return Vector4 (w, y, y, y); }
+Vector4 Quat::xzyy() const { return Vector4 (x, z, y, y); }
+Vector4 Quat::yzyy() const { return Vector4 (y, z, y, y); }
+Vector4 Quat::zzyy() const { return Vector4 (z, z, y, y); }
+Vector4 Quat::wzyy() const { return Vector4 (w, z, y, y); }
+Vector4 Quat::xwyy() const { return Vector4 (x, w, y, y); }
+Vector4 Quat::ywyy() const { return Vector4 (y, w, y, y); }
+Vector4 Quat::zwyy() const { return Vector4 (z, w, y, y); }
+Vector4 Quat::wwyy() const { return Vector4 (w, w, y, y); }
+Vector4 Quat::xxzy() const { return Vector4 (x, x, z, y); }
+Vector4 Quat::yxzy() const { return Vector4 (y, x, z, y); }
+Vector4 Quat::zxzy() const { return Vector4 (z, x, z, y); }
+Vector4 Quat::wxzy() const { return Vector4 (w, x, z, y); }
+Vector4 Quat::xyzy() const { return Vector4 (x, y, z, y); }
+Vector4 Quat::yyzy() const { return Vector4 (y, y, z, y); }
+Vector4 Quat::zyzy() const { return Vector4 (z, y, z, y); }
+Vector4 Quat::wyzy() const { return Vector4 (w, y, z, y); }
+Vector4 Quat::xzzy() const { return Vector4 (x, z, z, y); }
+Vector4 Quat::yzzy() const { return Vector4 (y, z, z, y); }
+Vector4 Quat::zzzy() const { return Vector4 (z, z, z, y); }
+Vector4 Quat::wzzy() const { return Vector4 (w, z, z, y); }
+Vector4 Quat::xwzy() const { return Vector4 (x, w, z, y); }
+Vector4 Quat::ywzy() const { return Vector4 (y, w, z, y); }
+Vector4 Quat::zwzy() const { return Vector4 (z, w, z, y); }
+Vector4 Quat::wwzy() const { return Vector4 (w, w, z, y); }
+Vector4 Quat::xxwy() const { return Vector4 (x, x, w, y); }
+Vector4 Quat::yxwy() const { return Vector4 (y, x, w, y); }
+Vector4 Quat::zxwy() const { return Vector4 (z, x, w, y); }
+Vector4 Quat::wxwy() const { return Vector4 (w, x, w, y); }
+Vector4 Quat::xywy() const { return Vector4 (x, y, w, y); }
+Vector4 Quat::yywy() const { return Vector4 (y, y, w, y); }
+Vector4 Quat::zywy() const { return Vector4 (z, y, w, y); }
+Vector4 Quat::wywy() const { return Vector4 (w, y, w, y); }
+Vector4 Quat::xzwy() const { return Vector4 (x, z, w, y); }
+Vector4 Quat::yzwy() const { return Vector4 (y, z, w, y); }
+Vector4 Quat::zzwy() const { return Vector4 (z, z, w, y); }
+Vector4 Quat::wzwy() const { return Vector4 (w, z, w, y); }
+Vector4 Quat::xwwy() const { return Vector4 (x, w, w, y); }
+Vector4 Quat::ywwy() const { return Vector4 (y, w, w, y); }
+Vector4 Quat::zwwy() const { return Vector4 (z, w, w, y); }
+Vector4 Quat::wwwy() const { return Vector4 (w, w, w, y); }
+Vector4 Quat::xxxz() const { return Vector4 (x, x, x, z); }
+Vector4 Quat::yxxz() const { return Vector4 (y, x, x, z); }
+Vector4 Quat::zxxz() const { return Vector4 (z, x, x, z); }
+Vector4 Quat::wxxz() const { return Vector4 (w, x, x, z); }
+Vector4 Quat::xyxz() const { return Vector4 (x, y, x, z); }
+Vector4 Quat::yyxz() const { return Vector4 (y, y, x, z); }
+Vector4 Quat::zyxz() const { return Vector4 (z, y, x, z); }
+Vector4 Quat::wyxz() const { return Vector4 (w, y, x, z); }
+Vector4 Quat::xzxz() const { return Vector4 (x, z, x, z); }
+Vector4 Quat::yzxz() const { return Vector4 (y, z, x, z); }
+Vector4 Quat::zzxz() const { return Vector4 (z, z, x, z); }
+Vector4 Quat::wzxz() const { return Vector4 (w, z, x, z); }
+Vector4 Quat::xwxz() const { return Vector4 (x, w, x, z); }
+Vector4 Quat::ywxz() const { return Vector4 (y, w, x, z); }
+Vector4 Quat::zwxz() const { return Vector4 (z, w, x, z); }
+Vector4 Quat::wwxz() const { return Vector4 (w, w, x, z); }
+Vector4 Quat::xxyz() const { return Vector4 (x, x, y, z); }
+Vector4 Quat::yxyz() const { return Vector4 (y, x, y, z); }
+Vector4 Quat::zxyz() const { return Vector4 (z, x, y, z); }
+Vector4 Quat::wxyz() const { return Vector4 (w, x, y, z); }
+Vector4 Quat::xyyz() const { return Vector4 (x, y, y, z); }
+Vector4 Quat::yyyz() const { return Vector4 (y, y, y, z); }
+Vector4 Quat::zyyz() const { return Vector4 (z, y, y, z); }
+Vector4 Quat::wyyz() const { return Vector4 (w, y, y, z); }
+Vector4 Quat::xzyz() const { return Vector4 (x, z, y, z); }
+Vector4 Quat::yzyz() const { return Vector4 (y, z, y, z); }
+Vector4 Quat::zzyz() const { return Vector4 (z, z, y, z); }
+Vector4 Quat::wzyz() const { return Vector4 (w, z, y, z); }
+Vector4 Quat::xwyz() const { return Vector4 (x, w, y, z); }
+Vector4 Quat::ywyz() const { return Vector4 (y, w, y, z); }
+Vector4 Quat::zwyz() const { return Vector4 (z, w, y, z); }
+Vector4 Quat::wwyz() const { return Vector4 (w, w, y, z); }
+Vector4 Quat::xxzz() const { return Vector4 (x, x, z, z); }
+Vector4 Quat::yxzz() const { return Vector4 (y, x, z, z); }
+Vector4 Quat::zxzz() const { return Vector4 (z, x, z, z); }
+Vector4 Quat::wxzz() const { return Vector4 (w, x, z, z); }
+Vector4 Quat::xyzz() const { return Vector4 (x, y, z, z); }
+Vector4 Quat::yyzz() const { return Vector4 (y, y, z, z); }
+Vector4 Quat::zyzz() const { return Vector4 (z, y, z, z); }
+Vector4 Quat::wyzz() const { return Vector4 (w, y, z, z); }
+Vector4 Quat::xzzz() const { return Vector4 (x, z, z, z); }
+Vector4 Quat::yzzz() const { return Vector4 (y, z, z, z); }
+Vector4 Quat::zzzz() const { return Vector4 (z, z, z, z); }
+Vector4 Quat::wzzz() const { return Vector4 (w, z, z, z); }
+Vector4 Quat::xwzz() const { return Vector4 (x, w, z, z); }
+Vector4 Quat::ywzz() const { return Vector4 (y, w, z, z); }
+Vector4 Quat::zwzz() const { return Vector4 (z, w, z, z); }
+Vector4 Quat::wwzz() const { return Vector4 (w, w, z, z); }
+Vector4 Quat::xxwz() const { return Vector4 (x, x, w, z); }
+Vector4 Quat::yxwz() const { return Vector4 (y, x, w, z); }
+Vector4 Quat::zxwz() const { return Vector4 (z, x, w, z); }
+Vector4 Quat::wxwz() const { return Vector4 (w, x, w, z); }
+Vector4 Quat::xywz() const { return Vector4 (x, y, w, z); }
+Vector4 Quat::yywz() const { return Vector4 (y, y, w, z); }
+Vector4 Quat::zywz() const { return Vector4 (z, y, w, z); }
+Vector4 Quat::wywz() const { return Vector4 (w, y, w, z); }
+Vector4 Quat::xzwz() const { return Vector4 (x, z, w, z); }
+Vector4 Quat::yzwz() const { return Vector4 (y, z, w, z); }
+Vector4 Quat::zzwz() const { return Vector4 (z, z, w, z); }
+Vector4 Quat::wzwz() const { return Vector4 (w, z, w, z); }
+Vector4 Quat::xwwz() const { return Vector4 (x, w, w, z); }
+Vector4 Quat::ywwz() const { return Vector4 (y, w, w, z); }
+Vector4 Quat::zwwz() const { return Vector4 (z, w, w, z); }
+Vector4 Quat::wwwz() const { return Vector4 (w, w, w, z); }
+Vector4 Quat::xxxw() const { return Vector4 (x, x, x, w); }
+Vector4 Quat::yxxw() const { return Vector4 (y, x, x, w); }
+Vector4 Quat::zxxw() const { return Vector4 (z, x, x, w); }
+Vector4 Quat::wxxw() const { return Vector4 (w, x, x, w); }
+Vector4 Quat::xyxw() const { return Vector4 (x, y, x, w); }
+Vector4 Quat::yyxw() const { return Vector4 (y, y, x, w); }
+Vector4 Quat::zyxw() const { return Vector4 (z, y, x, w); }
+Vector4 Quat::wyxw() const { return Vector4 (w, y, x, w); }
+Vector4 Quat::xzxw() const { return Vector4 (x, z, x, w); }
+Vector4 Quat::yzxw() const { return Vector4 (y, z, x, w); }
+Vector4 Quat::zzxw() const { return Vector4 (z, z, x, w); }
+Vector4 Quat::wzxw() const { return Vector4 (w, z, x, w); }
+Vector4 Quat::xwxw() const { return Vector4 (x, w, x, w); }
+Vector4 Quat::ywxw() const { return Vector4 (y, w, x, w); }
+Vector4 Quat::zwxw() const { return Vector4 (z, w, x, w); }
+Vector4 Quat::wwxw() const { return Vector4 (w, w, x, w); }
+Vector4 Quat::xxyw() const { return Vector4 (x, x, y, w); }
+Vector4 Quat::yxyw() const { return Vector4 (y, x, y, w); }
+Vector4 Quat::zxyw() const { return Vector4 (z, x, y, w); }
+Vector4 Quat::wxyw() const { return Vector4 (w, x, y, w); }
+Vector4 Quat::xyyw() const { return Vector4 (x, y, y, w); }
+Vector4 Quat::yyyw() const { return Vector4 (y, y, y, w); }
+Vector4 Quat::zyyw() const { return Vector4 (z, y, y, w); }
+Vector4 Quat::wyyw() const { return Vector4 (w, y, y, w); }
+Vector4 Quat::xzyw() const { return Vector4 (x, z, y, w); }
+Vector4 Quat::yzyw() const { return Vector4 (y, z, y, w); }
+Vector4 Quat::zzyw() const { return Vector4 (z, z, y, w); }
+Vector4 Quat::wzyw() const { return Vector4 (w, z, y, w); }
+Vector4 Quat::xwyw() const { return Vector4 (x, w, y, w); }
+Vector4 Quat::ywyw() const { return Vector4 (y, w, y, w); }
+Vector4 Quat::zwyw() const { return Vector4 (z, w, y, w); }
+Vector4 Quat::wwyw() const { return Vector4 (w, w, y, w); }
+Vector4 Quat::xxzw() const { return Vector4 (x, x, z, w); }
+Vector4 Quat::yxzw() const { return Vector4 (y, x, z, w); }
+Vector4 Quat::zxzw() const { return Vector4 (z, x, z, w); }
+Vector4 Quat::wxzw() const { return Vector4 (w, x, z, w); }
+Vector4 Quat::xyzw() const { return Vector4 (x, y, z, w); }
+Vector4 Quat::yyzw() const { return Vector4 (y, y, z, w); }
+Vector4 Quat::zyzw() const { return Vector4 (z, y, z, w); }
+Vector4 Quat::wyzw() const { return Vector4 (w, y, z, w); }
+Vector4 Quat::xzzw() const { return Vector4 (x, z, z, w); }
+Vector4 Quat::yzzw() const { return Vector4 (y, z, z, w); }
+Vector4 Quat::zzzw() const { return Vector4 (z, z, z, w); }
+Vector4 Quat::wzzw() const { return Vector4 (w, z, z, w); }
+Vector4 Quat::xwzw() const { return Vector4 (x, w, z, w); }
+Vector4 Quat::ywzw() const { return Vector4 (y, w, z, w); }
+Vector4 Quat::zwzw() const { return Vector4 (z, w, z, w); }
+Vector4 Quat::wwzw() const { return Vector4 (w, w, z, w); }
+Vector4 Quat::xxww() const { return Vector4 (x, x, w, w); }
+Vector4 Quat::yxww() const { return Vector4 (y, x, w, w); }
+Vector4 Quat::zxww() const { return Vector4 (z, x, w, w); }
+Vector4 Quat::wxww() const { return Vector4 (w, x, w, w); }
+Vector4 Quat::xyww() const { return Vector4 (x, y, w, w); }
+Vector4 Quat::yyww() const { return Vector4 (y, y, w, w); }
+Vector4 Quat::zyww() const { return Vector4 (z, y, w, w); }
+Vector4 Quat::wyww() const { return Vector4 (w, y, w, w); }
+Vector4 Quat::xzww() const { return Vector4 (x, z, w, w); }
+Vector4 Quat::yzww() const { return Vector4 (y, z, w, w); }
+Vector4 Quat::zzww() const { return Vector4 (z, z, w, w); }
+Vector4 Quat::wzww() const { return Vector4 (w, z, w, w); }
+Vector4 Quat::xwww() const { return Vector4 (x, w, w, w); }
+Vector4 Quat::ywww() const { return Vector4 (y, w, w, w); }
+Vector4 Quat::zwww() const { return Vector4 (z, w, w, w); }
+Vector4 Quat::wwww() const { return Vector4 (w, w, w, w); }
+}
+
diff --git a/externals/g3dlite/G3D.lib/source/Ray.cpp b/externals/g3dlite/G3D.lib/source/Ray.cpp
new file mode 100644
index 00000000000..bf6aadc08d1
--- /dev/null
+++ b/externals/g3dlite/G3D.lib/source/Ray.cpp
@@ -0,0 +1,112 @@
+/**
+ @file Ray.cpp
+
+ @maintainer Morgan McGuire, matrix@graphics3d.com
+
+ @created 2002-07-12
+ @edited 2004-03-19
+ */
+
+#include "G3D/platform.h"
+#include "G3D/Ray.h"
+#include "G3D/Plane.h"
+#include "G3D/Sphere.h"
+#include "G3D/CollisionDetection.h"
+
+namespace G3D {
+
+Ray::Ray(class BinaryInput& b) {
+ deserialize(b);
+}
+
+
+void Ray::serialize(class BinaryOutput& b) const {
+ origin.serialize(b);
+ direction.serialize(b);
+}
+
+
+void Ray::deserialize(class BinaryInput& b) {
+ origin.deserialize(b);
+ direction.deserialize(b);
+}
+
+
+Ray Ray::refract(
+ const Vector3& newOrigin,
+ const Vector3& normal,
+ float iInside,
+ float iOutside) const {
+
+ Vector3 D = direction.refractionDirection(normal, iInside, iOutside);
+ return Ray::fromOriginAndDirection(
+ newOrigin + (direction + normal * (float)sign(direction.dot(normal))) * 0.001f, D);
+}
+
+
+Ray Ray::reflect(
+ const Vector3& newOrigin,
+ const Vector3& normal) const {
+
+ Vector3 D = direction.reflectionDirection(normal);
+ return Ray::fromOriginAndDirection(newOrigin + (D + normal) * 0.001f, D);
+}
+
+
+Vector3 Ray::intersection(const Plane& plane) const {
+ float d;
+ Vector3 normal = plane.normal();
+ plane.getEquation(normal, d);
+ float rate = direction.dot(normal);
+
+ if (rate >= 0.0f) {
+ return Vector3::inf();
+ } else {
+ float t = -(d + origin.dot(normal)) / rate;
+
+ return origin + direction * t;
+ }
+}
+
+
+float Ray::intersectionTime(const class Sphere& sphere) const {
+ Vector3 dummy;
+ return CollisionDetection::collisionTimeForMovingPointFixedSphere(
+ origin, direction, sphere, dummy);
+}
+
+
+float Ray::intersectionTime(const class Plane& plane) const {
+ Vector3 dummy;
+ return CollisionDetection::collisionTimeForMovingPointFixedPlane(
+ origin, direction, plane, dummy);
+}
+
+
+float Ray::intersectionTime(const class Box& box) const {
+ Vector3 dummy;
+ float time = CollisionDetection::collisionTimeForMovingPointFixedBox(
+ origin, direction, box, dummy);
+
+ if ((time == inf()) && (box.contains(origin))) {
+ return 0.0f;
+ } else {
+ return time;
+ }
+}
+
+
+float Ray::intersectionTime(const class AABox& box) const {
+ Vector3 dummy;
+ bool inside;
+ float time = CollisionDetection::collisionTimeForMovingPointFixedAABox(
+ origin, direction, box, dummy, inside);
+
+ if ((time == inf()) && inside) {
+ return 0.0f;
+ } else {
+ return time;
+ }
+}
+
+}
diff --git a/externals/g3dlite/G3D.lib/source/RegistryUtil.cpp b/externals/g3dlite/G3D.lib/source/RegistryUtil.cpp
new file mode 100644
index 00000000000..28ff6955d9b
--- /dev/null
+++ b/externals/g3dlite/G3D.lib/source/RegistryUtil.cpp
@@ -0,0 +1,290 @@
+/**
+ @file RegistryUtil.cpp
+
+ @created 2006-04-06
+ @edited 2006-04-24
+
+ Copyright 2000-2006, Morgan McGuire.
+ All rights reserved.
+*/
+
+#include "G3D/platform.h"
+
+// This file is only used on Windows
+#ifdef G3D_WIN32
+
+#include "G3D/RegistryUtil.h"
+#include "G3D/System.h"
+
+namespace G3D {
+
+// static helpers
+static HKEY getRootKeyFromString(const char* str, size_t length);
+
+
+bool RegistryUtil::keyExists(const std::string& key) {
+ size_t pos = key.find('\\', 0);
+ if (pos == std::string::npos) {
+ return false;
+ }
+
+ HKEY hkey = getRootKeyFromString(key.c_str(), pos);
+
+ if (hkey == NULL) {
+ return false;
+ }
+
+ HKEY openKey;
+ int32 result = RegOpenKeyExA(hkey, (key.c_str() + pos + 1), 0, KEY_READ, &openKey);
+
+ debugAssert(result == ERROR_SUCCESS || result == ERROR_FILE_NOT_FOUND);
+
+ if (result == ERROR_SUCCESS) {
+ RegCloseKey(openKey);
+ return true;
+ } else {
+ return false;
+ }
+}
+
+bool RegistryUtil::valueExists(const std::string& key, const std::string& value) {
+ size_t pos = key.find('\\', 0);
+ if (pos == std::string::npos) {
+ return false;
+ }
+
+ HKEY hkey = getRootKeyFromString(key.c_str(), pos);
+
+ if ( hkey == NULL ) {
+ return false;
+ }
+
+ HKEY openKey;
+ int32 result = RegOpenKeyExA(hkey, (key.c_str() + pos + 1), 0, KEY_READ, &openKey);
+ debugAssert(result == ERROR_SUCCESS || result == ERROR_FILE_NOT_FOUND);
+
+ if (result == ERROR_SUCCESS) {
+ uint32 dataSize = 0;
+ result = RegQueryValueExA(openKey, value.c_str(), NULL, NULL, NULL, reinterpret_cast<LPDWORD>(&dataSize));
+
+ debugAssert(result == ERROR_SUCCESS || result == ERROR_FILE_NOT_FOUND);
+ RegCloseKey(openKey);
+ }
+ return (result == ERROR_SUCCESS);
+}
+
+
+bool RegistryUtil::readInt32(const std::string& key, const std::string& value, int32& data) {
+ size_t pos = key.find('\\', 0);
+ if (pos == std::string::npos) {
+ return false;
+ }
+
+ HKEY hkey = getRootKeyFromString(key.c_str(), pos);
+
+ if ( hkey == NULL ) {
+ return false;
+ }
+
+ HKEY openKey;
+ int32 result = RegOpenKeyExA(hkey, (key.c_str() + pos + 1), 0, KEY_READ, &openKey);
+ debugAssert(result == ERROR_SUCCESS || result == ERROR_FILE_NOT_FOUND);
+
+ if (result == ERROR_SUCCESS) {
+ uint32 dataSize = sizeof(int32);
+ result = RegQueryValueExA(openKey, value.c_str(), NULL, NULL, reinterpret_cast<LPBYTE>(&data), reinterpret_cast<LPDWORD>(&dataSize));
+
+ debugAssertM(result == ERROR_SUCCESS, "Could not read registry key value.");
+
+ RegCloseKey(openKey);
+ }
+ return (result == ERROR_SUCCESS);
+}
+
+bool RegistryUtil::readBytes(const std::string& key, const std::string& value, uint8* data, uint32& dataSize) {
+ size_t pos = key.find('\\', 0);
+ if (pos == std::string::npos) {
+ return false;
+ }
+
+ HKEY hkey = getRootKeyFromString(key.c_str(), pos);
+
+ if (hkey == NULL) {
+ return false;
+ }
+
+ HKEY openKey;
+ int32 result = RegOpenKeyExA(hkey, (key.c_str() + pos + 1), 0, KEY_READ, &openKey);
+ debugAssert(result == ERROR_SUCCESS || result == ERROR_FILE_NOT_FOUND);
+
+ if (result == ERROR_SUCCESS) {
+ if (data == NULL) {
+ result = RegQueryValueExA(openKey, value.c_str(), NULL, NULL, NULL, reinterpret_cast<LPDWORD>(&dataSize));
+ } else {
+ result = RegQueryValueExA(openKey, value.c_str(), NULL, NULL, reinterpret_cast<LPBYTE>(&data), reinterpret_cast<LPDWORD>(&dataSize));
+ }
+
+ debugAssertM(result == ERROR_SUCCESS, "Could not read registry key value.");
+
+ RegCloseKey(openKey);
+ }
+ return (result == ERROR_SUCCESS);
+}
+
+bool RegistryUtil::readString(const std::string& key, const std::string& value, std::string& data) {
+ size_t pos = key.find('\\', 0);
+ if (pos == std::string::npos) {
+ return false;
+ }
+
+ HKEY hkey = getRootKeyFromString(key.c_str(), pos);
+
+ if (hkey == NULL) {
+ return false;
+ }
+
+ HKEY openKey;
+ int32 result = RegOpenKeyExA(hkey, (key.c_str() + pos + 1), 0, KEY_READ, &openKey);
+ debugAssert(result == ERROR_SUCCESS || result == ERROR_FILE_NOT_FOUND);
+
+ if (result == ERROR_SUCCESS) {
+ uint32 dataSize = 0;
+
+ result = RegQueryValueExA(openKey, value.c_str(), NULL, NULL, NULL, reinterpret_cast<LPDWORD>(&dataSize));
+ debugAssertM(result == ERROR_SUCCESS, "Could not read registry key value.");
+
+ // increment datasize to allow for non null-terminated strings in registry
+ dataSize += 1;
+
+ if (result == ERROR_SUCCESS) {
+ char* tmpStr = static_cast<char*>(System::malloc(dataSize));
+ System::memset(tmpStr, 0, dataSize);
+
+ result = RegQueryValueExA(openKey, value.c_str(), NULL, NULL, reinterpret_cast<LPBYTE>(tmpStr), reinterpret_cast<LPDWORD>(&dataSize));
+ debugAssertM(result == ERROR_SUCCESS, "Could not read registry key value.");
+
+ if (result == ERROR_SUCCESS) {
+ data = tmpStr;
+ }
+
+ RegCloseKey(openKey);
+ System::free(tmpStr);
+ }
+ }
+ return (result == ERROR_SUCCESS);
+}
+
+bool RegistryUtil::writeInt32(const std::string& key, const std::string& value, int32 data) {
+ size_t pos = key.find('\\', 0);
+ if (pos == std::string::npos) {
+ return false;
+ }
+
+ HKEY hkey = getRootKeyFromString(key.c_str(), pos);
+
+ if (hkey == NULL) {
+ return false;
+ }
+
+ HKEY openKey;
+ int32 result = RegOpenKeyExA(hkey, (key.c_str() + pos + 1), 0, KEY_WRITE, &openKey);
+ debugAssert(result == ERROR_SUCCESS || result == ERROR_FILE_NOT_FOUND);
+
+ if (result == ERROR_SUCCESS) {
+ result = RegSetValueExA(openKey, value.c_str(), 0, REG_DWORD, reinterpret_cast<const BYTE*>(&data), sizeof(int32));
+
+ debugAssertM(result == ERROR_SUCCESS, "Could not write registry key value.");
+
+ RegCloseKey(openKey);
+ }
+ return (result == ERROR_SUCCESS);
+}
+
+bool RegistryUtil::writeBytes(const std::string& key, const std::string& value, const uint8* data, uint32 dataSize) {
+ debugAssert(data);
+
+ size_t pos = key.find('\\', 0);
+ if (pos == std::string::npos) {
+ return false;
+ }
+
+ HKEY hkey = getRootKeyFromString(key.c_str(), pos);
+
+ if (hkey == NULL) {
+ return false;
+ }
+
+ HKEY openKey;
+ int32 result = RegOpenKeyExA(hkey, (key.c_str() + pos + 1), 0, KEY_WRITE, &openKey);
+ debugAssert(result == ERROR_SUCCESS || result == ERROR_FILE_NOT_FOUND);
+
+ if (result == ERROR_SUCCESS) {
+ if (data) {
+ result = RegSetValueExA(openKey, value.c_str(), 0, REG_BINARY, reinterpret_cast<const BYTE*>(data), dataSize);
+ }
+
+ debugAssertM(result == ERROR_SUCCESS, "Could not write registry key value.");
+
+ RegCloseKey(openKey);
+ }
+ return (result == ERROR_SUCCESS);
+}
+
+bool RegistryUtil::writeString(const std::string& key, const std::string& value, const std::string& data) {
+ size_t pos = key.find('\\', 0);
+ if (pos == std::string::npos) {
+ return false;
+ }
+
+ HKEY hkey = getRootKeyFromString(key.c_str(), pos);
+
+ if (hkey == NULL) {
+ return false;
+ }
+
+ HKEY openKey;
+ int32 result = RegOpenKeyExA(hkey, (key.c_str() + pos + 1), 0, KEY_WRITE, &openKey);
+ debugAssert(result == ERROR_SUCCESS || result == ERROR_FILE_NOT_FOUND);
+
+ if (result == ERROR_SUCCESS) {
+ result = RegSetValueExA(openKey, value.c_str(), 0, REG_SZ, reinterpret_cast<const BYTE*>(data.c_str()), (data.size() + 1));
+ debugAssertM(result == ERROR_SUCCESS, "Could not write registry key value.");
+
+ RegCloseKey(openKey);
+ }
+ return (result == ERROR_SUCCESS);
+}
+
+
+// static helpers
+static HKEY getRootKeyFromString(const char* str, uint32 length) {
+ debugAssert(str);
+
+ if (str) {
+ if ( strncmp(str, "HKEY_CLASSES_ROOT", length) == 0 ) {
+ return HKEY_CLASSES_ROOT;
+ } else if ( strncmp(str, "HKEY_CURRENT_CONFIG", length) == 0 ) {
+ return HKEY_CURRENT_CONFIG;
+ } else if ( strncmp(str, "HKEY_CURRENT_USER", length) == 0 ) {
+ return HKEY_CURRENT_USER;
+ } else if ( strncmp(str, "HKEY_LOCAL_MACHINE", length) == 0 ) {
+ return HKEY_LOCAL_MACHINE;
+ } else if ( strncmp(str, "HKEY_PERFORMANCE_DATA", length) == 0 ) {
+ return HKEY_PERFORMANCE_DATA;
+ } else if ( strncmp(str, "HKEY_PERFORMANCE_NLSTEXT", length) == 0 ) {
+ return HKEY_PERFORMANCE_NLSTEXT;
+ } else if ( strncmp(str, "HKEY_PERFORMANCE_TEXT", length) == 0 ) {
+ return HKEY_PERFORMANCE_TEXT;
+ } else if ( strncmp(str, "HKEY_CLASSES_ROOT", length) == 0 ) {
+ return HKEY_CLASSES_ROOT;
+ } else {
+ return NULL;
+ }
+ } else {
+ return NULL;
+ }
+}
+
+} // namespace G3D
+
+#endif // G3D_WIN32
diff --git a/externals/g3dlite/G3D.lib/source/Sphere.cpp b/externals/g3dlite/G3D.lib/source/Sphere.cpp
new file mode 100644
index 00000000000..935598d52dd
--- /dev/null
+++ b/externals/g3dlite/G3D.lib/source/Sphere.cpp
@@ -0,0 +1,196 @@
+/**
+ @file Sphere.cpp
+
+ Sphere class
+
+ @maintainer Morgan McGuire, matrix@graphics3d.com
+
+ @created 2001-04-17
+ @edited 2006-02-05
+ */
+
+#include "G3D/platform.h"
+#include "G3D/Sphere.h"
+#include "G3D/stringutils.h"
+#include "G3D/BinaryOutput.h"
+#include "G3D/BinaryInput.h"
+#include "G3D/AABox.h"
+#include "G3D/Plane.h"
+
+namespace G3D {
+
+int32 Sphere::dummy;
+
+Sphere::Sphere(class BinaryInput& b) {
+ deserialize(b);
+}
+
+
+void Sphere::serialize(class BinaryOutput& b) const {
+ center.serialize(b);
+ b.writeFloat64(radius);
+}
+
+
+void Sphere::deserialize(class BinaryInput& b) {
+ center.deserialize(b);
+ radius = (float)b.readFloat64();
+}
+
+
+std::string Sphere::toString() const {
+ return format("Sphere(<%g, %g, %g>, %g)",
+ center.x, center.y, center.z, radius);
+}
+
+
+bool Sphere::contains(const Vector3& point) const {
+ double distance = (center - point).squaredMagnitude();
+ return distance <= (radius * radius);
+}
+
+
+bool Sphere::intersects(const Sphere& other) const {
+ return (other.center - center).length() <= (radius + other.radius);
+}
+
+bool Sphere::culledBy(
+ const Array<Plane>& plane,
+ int& cullingPlaneIndex,
+ const uint32 inMask,
+ uint32& outMask) const {
+
+ return culledBy(plane.getCArray(), plane.size(), cullingPlaneIndex, inMask, outMask);
+}
+
+
+bool Sphere::culledBy(
+ const Array<Plane>& plane,
+ int& cullingPlaneIndex,
+ const uint32 inMask) const {
+
+ return culledBy(plane.getCArray(), plane.size(), cullingPlaneIndex, inMask);
+}
+
+
+bool Sphere::culledBy(
+ const class Plane* plane,
+ int numPlanes,
+ int& cullingPlane,
+ const uint32 _inMask,
+ uint32& childMask) const {
+
+ if (radius == inf()) {
+ // No plane can cull the infinite box
+ return false;
+ }
+
+ uint32 inMask = _inMask;
+ assert(numPlanes < 31);
+
+ childMask = 0;
+
+ // See if there is one plane for which all of the
+ // vertices are in the negative half space.
+ for (int p = 0; p < numPlanes; p++) {
+
+ // Only test planes that are not masked
+ if ((inMask & 1) != 0) {
+
+ bool culledLow = ! plane[p].halfSpaceContainsFinite(center + plane[p].normal() * radius);
+ bool culledHigh = ! plane[p].halfSpaceContainsFinite(center - plane[p].normal() * radius);
+
+ if (culledLow) {
+ // Plane p culled the sphere
+ cullingPlane = p;
+
+ // The caller should not recurse into the children,
+ // since the parent is culled. If they do recurse,
+ // make them only test against this one plane, which
+ // will immediately cull the volume.
+ childMask = 1 << p;
+ return true;
+
+ } else if (culledHigh) {
+ // The bounding volume straddled the plane; we have
+ // to keep testing against this plane
+ childMask |= (1 << p);
+ }
+ }
+
+ // Move on to the next bit.
+ inMask = inMask >> 1;
+ }
+
+ // None of the planes could cull this box
+ cullingPlane = -1;
+ return false;
+}
+
+
+bool Sphere::culledBy(
+ const class Plane* plane,
+ int numPlanes,
+ int& cullingPlane,
+ const uint32 _inMask) const {
+
+ uint32 inMask = _inMask;
+ assert(numPlanes < 31);
+
+ // See if there is one plane for which all of the
+ // vertices are in the negative half space.
+ for (int p = 0; p < numPlanes; p++) {
+
+ // Only test planes that are not masked
+ if ((inMask & 1) != 0) {
+ bool culled = ! plane[p].halfSpaceContains(center + plane[p].normal() * radius);
+ if (culled) {
+ // Plane p culled the sphere
+ cullingPlane = p;
+ return true;
+ }
+ }
+
+ // Move on to the next bit.
+ inMask = inMask >> 1;
+ }
+
+ // None of the planes could cull this box
+ cullingPlane = -1;
+ return false;
+}
+
+
+Vector3 Sphere::randomSurfacePoint() const {
+ return Vector3::random() * radius + center;
+}
+
+
+Vector3 Sphere::randomInteriorPoint() const {
+ Vector3 result;
+ do {
+ result = Vector3(uniformRandom(-1, 1),
+ uniformRandom(-1, 1),
+ uniformRandom(-1, 1));
+ } while (result.squaredMagnitude() >= 1.0f);
+
+ return result * radius + center;
+}
+
+
+float Sphere::volume() const {
+ return (float)pi() * (4.0f / 3.0f) * powf((float)radius, 3.0f);
+}
+
+
+float Sphere::area() const {
+ return (float)pi() * 4.0f * powf((float)radius, 2.0f);
+}
+
+
+void Sphere::getBounds(AABox& out) const {
+ Vector3 extent(radius, radius, radius);
+ out = AABox(center - extent, center + extent);
+}
+
+} // namespace
diff --git a/externals/g3dlite/G3D.lib/source/SplineBase.cpp b/externals/g3dlite/G3D.lib/source/SplineBase.cpp
new file mode 100644
index 00000000000..41221624b06
--- /dev/null
+++ b/externals/g3dlite/G3D.lib/source/SplineBase.cpp
@@ -0,0 +1,162 @@
+#include "G3D/platform.h"
+#include "G3D/Spline.h"
+
+namespace G3D {
+
+float SplineBase::getFinalInterval() const {
+ if (! cyclic) {
+ return 0;
+ } else if (finalInterval <= 0) {
+ int N = time.size();
+ if (N >= 2) {
+ return (time[1] - time[0] + time[N - 1] - time[N - 2]) * 0.5f;
+ } else {
+ return 1.0f;
+ }
+ } else {
+ return finalInterval;
+ }
+}
+
+
+Matrix4 SplineBase::computeBasis() {
+ // The standard Catmull-Rom spline basis (e.g., Watt & Watt p108)
+ // is for [u^3 u^2 u^1 u^0] * B * [p[0] p[1] p[2] p[3]]^T.
+ // We need a basis formed for:
+ //
+ // U * C * [2*p'[1] p[1] p[2] 2*p'[2]]^T
+ //
+ // U * C * [p2-p0 p1 p2 p3-p1]^T
+ //
+ // To make this transformation, compute the differences of columns in C:
+ // For [p0 p1 p2 p3]
+ Matrix4 basis =
+ Matrix4( -1, 3, -3, 1,
+ 2, -5, 4, -1,
+ -1, 0, 1, 0,
+ 0, 2, 0, 0) * 0.5f;
+
+ // For [-p0 p1 p2 p3]^T
+ basis.setColumn(0, -basis.column(0));
+
+ // For [-p0 p1 p2 p3-p1]^T
+ basis.setColumn(1, basis.column(1) + basis.column(3));
+
+ // For [p2-p0 p1 p2 p3-p1]^T
+ basis.setColumn(2, basis.column(2) - basis.column(0));
+
+ return basis;
+}
+
+
+float SplineBase::duration() const {
+ if (time.size() == 0) {
+ return 0;
+ } else {
+ return time.last() - time[0] + getFinalInterval();
+ }
+}
+
+
+void SplineBase::computeIndexInBounds(float s, int& i, float& u) const {
+ int N = time.size();
+ float t0 = time[0];
+ float tn = time[N - 1];
+
+ i = iFloor((N - 1) * (s - t0) / (tn - t0));
+
+ // Inclusive bounds for binary search
+ int hi = N - 1;
+ int lo = 0;
+
+ while ((time[i] > s) || (time[i + 1] <= s)) {
+
+ if (time[i] > s) {
+ // too big
+ hi = i - 1;
+ } else if (time[i + 1] <= s) {
+ // too small
+ lo = i + 1;
+ }
+
+ i = (hi + lo) / 2;
+ }
+
+ // Having exited the above loop, i must be correct, so compute u.
+ u = (s - time[i]) / (time[i + 1] - time[i]);
+}
+
+
+void SplineBase::computeIndex(float s, int& i, float& u) const {
+ int N = time.size();
+ debugAssertM(N > 0, "No control points");
+ float t0 = time[0];
+ float tn = time[N - 1];
+
+ if (N < 2) {
+ // No control points to work with
+ i = 0;
+ u = 0.0;
+ } else if (cyclic) {
+ float fi = getFinalInterval();
+
+ // Cyclic spline
+ if ((s < t0) || (s >= tn + fi)) {
+ // Cyclic, off the bottom or top
+
+ // Compute offset and reduce to the in-bounds case
+
+ float d = duration();
+ // Number of times we wrapped around the cyclic array
+ int wraps = iFloor((s - t0) / d);
+
+ debugAssert(s - d * wraps >= t0);
+ debugAssert(s - d * wraps < tn + getFinalInterval());
+
+ computeIndex(s - d * wraps, i, u);
+ i += wraps * N;
+
+ } else if (s >= tn) {
+ debugAssert(s < tn + fi);
+ // Cyclic, off the top but before the end of the last interval
+ i = N - 1;
+ u = (s - tn) / fi;
+
+ } else {
+ // Cyclic, in bounds
+ computeIndexInBounds(s, i, u);
+ }
+
+ } else {
+ // Non-cyclic
+
+ if (s < t0) {
+ // Non-cyclic, off the bottom. Assume points are spaced
+ // following the first time interval.
+
+ float dt = time[1] - t0;
+ float x = (s - t0) / dt;
+ i = iFloor(x);
+ u = x - i;
+
+ } else if (s >= tn) {
+ // Non-cyclic, off the top. Assume points are spaced following
+ // the last time interval.
+
+ float dt = tn - time[N - 2];
+ float x = N - 1 + (s - tn) / dt;
+ i = iFloor(x);
+ u = x - i;
+
+ } else {
+ // In bounds, non-cyclic. Assume a regular
+ // distribution (which gives O(1) for uniform spacing)
+ // and then binary search to handle the general case
+ // efficiently.
+ computeIndexInBounds(s, i, u);
+
+ } // if in bounds
+ } // if cyclic
+}
+
+}
diff --git a/externals/g3dlite/G3D.lib/source/Stopwatch.cpp b/externals/g3dlite/G3D.lib/source/Stopwatch.cpp
new file mode 100644
index 00000000000..e55f689a9e2
--- /dev/null
+++ b/externals/g3dlite/G3D.lib/source/Stopwatch.cpp
@@ -0,0 +1,96 @@
+/**
+ @file Stopwatch.cpp
+
+ @maintainer Morgan McGuire, matrix@graphics3d.com
+
+ @created 2005-10-05
+ @edited 2005-10-05
+
+ Copyright 2000-2003, Morgan McGuire.
+ All rights reserved.
+ */
+
+#include "G3D/Stopwatch.h"
+#include "G3D/System.h"
+
+namespace G3D {
+
+Stopwatch::Stopwatch() : inBetween(false), lastTockTime(-1),
+ lastDuration(0), lastCycleCount(0), m_fps(0), emwaFPS(0),
+ m_smoothFPS(0), emwaDuration(0) {
+ computeOverhead();
+}
+
+
+void Stopwatch::computeOverhead() {
+ cycleOverhead = 0;
+ tick();
+ tock();
+ cycleOverhead = elapsedCycles();
+}
+
+
+void Stopwatch::tick() {
+ // This is 'alwaysAssert' instead of 'debugAssert'
+ // since people rarely profile in debug mode.
+ alwaysAssertM(! inBetween, "Stopwatch::tick() called twice in a row.");
+ inBetween = true;
+
+ // We read RDTSC twice here, but it is more abstract to implement this
+ // way and at least we're reading the cycle count last.
+ timeStart = System::time();
+ System::beginCycleCount(cycleStart);
+}
+
+
+void Stopwatch::tock() {
+ System::endCycleCount(cycleStart);
+ RealTime now = System::time();
+ lastDuration = now - timeStart;
+ if (abs(emwaDuration - lastDuration) > max(emwaDuration, lastDuration) * 0.50) {
+ // Off by more than 50%
+ emwaDuration = lastDuration;
+ } else {
+ emwaDuration = lastDuration * 0.05 + emwaDuration * 0.95;
+ }
+
+ lastCycleCount = cycleStart - cycleOverhead;
+ if (lastCycleCount < 0) {
+ lastCycleCount = 0;
+ }
+
+ if (lastTockTime != -1.0) {
+ m_fps = 1.0 / (now - lastTockTime);
+
+ // Time smooth the average
+ emwaFPS = m_fps * 0.01 + emwaFPS * 0.99;
+
+ if (abs(emwaFPS - m_fps) > max(emwaFPS, m_fps) * 0.08) {
+ // The difference between emwa and m_fps is way off
+ // update emwa directly.
+ emwaFPS = m_fps;
+ }
+
+ // Update m_smoothFPS only when the value varies significantly
+ // We round so as to not mislead the user as to the accuracy of
+ // the number.
+ if (m_smoothFPS == 0) {
+ m_smoothFPS = m_fps;
+ } else if (emwaFPS <= 10) {
+ if (::fabs(m_smoothFPS - emwaFPS) > .75) {
+ m_smoothFPS = floor(emwaFPS * 10.0 + 0.5) / 10.0;
+ }
+ } else {
+ if (::fabs(m_smoothFPS - emwaFPS) > 1.25) {
+ m_smoothFPS = floor(emwaFPS + 0.5);
+ }
+ }
+ }
+ lastTockTime = now;
+
+ alwaysAssertM(inBetween, "Stopwatch::tock() called without matching tick.");
+ inBetween = false;
+}
+
+}
+
diff --git a/externals/g3dlite/G3D.lib/source/System.cpp b/externals/g3dlite/G3D.lib/source/System.cpp
new file mode 100644
index 00000000000..748e13257f1
--- /dev/null
+++ b/externals/g3dlite/G3D.lib/source/System.cpp
@@ -0,0 +1,1864 @@
+/**
+ @file System.cpp
+
+ @maintainer Morgan McGuire, matrix@graphics3d.com
+
+ Note: every routine must call init() first.
+
+ There are two kinds of detection used in this file. At compile
+ time, the _MSC_VER #define is used to determine whether x86 assembly
+ can be used at all. At runtime, processor detection is used to
+ determine if we can safely call the routines that use that assembly.
+
+ @cite Rob Wyatt http://www.gamasutra.com/features/wyatts_world/19990709/processor_detection_01.htm
+ @cite Benjamin Jurke http://www.flipcode.com/cgi-bin/msg.cgi?showThread=COTD-ProcessorDetectionClass&forum=cotd&id=-1
+ @cite Michael Herf http://www.stereopsis.com/memcpy.html
+
+ @created 2003-01-25
+ @edited 2008-09-02
+ */
+
+#include "G3D/platform.h"
+#include "G3D/System.h"
+#include "G3D/debug.h"
+#include "G3D/fileutils.h"
+#include "G3D/TextOutput.h"
+#include "G3D/G3DGameUnits.h"
+#include "G3D/Crypto.h"
+#include "G3D/prompt.h"
+#include "G3D/Log.h"
+#include <time.h>
+
+#include <cstring>
+#include <cstdio>
+
+// Uncomment the following line to turn off G3D::System memory
+// allocation and use the operating system's malloc.
+//#define NO_BUFFERPOOL
+
+#if defined(__i386__) || defined(__x86_64__) || defined(G3D_WIN32)
+# define G3D_NOT_OSX_PPC
+#endif
+
+#ifdef G3D_WIN32
+
+# include <conio.h>
+# include <sys/timeb.h>
+# include "G3D/RegistryUtil.h"
+
+#elif defined(G3D_LINUX)
+
+ #include <stdlib.h>
+ #include <stdio.h>
+ #include <errno.h>
+ #include <sys/types.h>
+ #include <sys/select.h>
+ #include <termios.h>
+ #include <unistd.h>
+ #include <sys/ioctl.h>
+ #include <sys/time.h>
+ #include <pthread.h>
+
+#elif defined(G3D_OSX)
+
+ #include <stdlib.h>
+ #include <stdio.h>
+ #include <errno.h>
+ #include <sys/types.h>
+ #include <sys/sysctl.h>
+ #include <sys/select.h>
+ #include <sys/time.h>
+ #include <termios.h>
+ #include <unistd.h>
+ #include <pthread.h>
+ #include <mach-o/arch.h>
+
+ #include <sstream>
+ #include <CoreServices/CoreServices.h>
+#endif
+
+#if defined(SSE)
+ #include <xmmintrin.h>
+#endif
+
+namespace G3D {
+
+struct CpuInfo
+{
+public:
+ int m_cpuSpeed;
+ bool m_hasCPUID;
+ bool m_hasRDTSC;
+ bool m_hasMMX;
+ bool m_hasSSE;
+ bool m_hasSSE2;
+ bool m_hasSSE3;
+ bool m_has3DNOW;
+ char m_cpuVendorStr[1024];
+};
+
+// helper macro to call cpuid functions and return all values
+#ifdef _MSC_VER
+
+ // VC on Intel
+# define CALL_CPUID(func, areg, breg, creg, dreg) \
+ __asm mov eax, func \
+ __asm cpuid \
+ __asm mov areg, eax \
+ __asm mov breg, ebx \
+ __asm mov creg, ecx \
+ __asm mov dreg, edx
+
+#elif defined(__GNUC__) && defined(G3D_OSX_INTEL)
+ // GCC on OS X intel
+# define CALL_CPUID(func, areg, breg, creg, dreg) \
+ areg = 0; \
+ breg = 0; \
+ creg = 0; \
+ dreg = 0;
+#else
+ // Any other compiler/platform, likely GCC
+# define CALL_CPUID(func, areg, breg, creg, dreg) \
+ __asm__ ( \
+ "cpuid \n": \
+ "=a" (areg), \
+ "=b" (breg), \
+ "=c" (creg), \
+ "=d" (dreg): \
+ "a" (func) \
+ );
+#endif
+
+// this holds the data directory set by the application (currently GApp) for use by findDataFile
+static char g_appDataDir[FILENAME_MAX] = "";
+
+static CpuInfo g_cpuInfo = {
+ 0, false, false, false, false, false, false, false, {'U', 'n', 'k', 'n', 'o', 'w', 'n', '\0'}};
+
+static G3DEndian _machineEndian = G3D_LITTLE_ENDIAN;
+static char _cpuArchCstr[1024];
+static char _operatingSystemCstr[1024];
+
+#ifdef G3D_WIN32
+/** Used by getTick() for timing */
+static LARGE_INTEGER _start;
+static LARGE_INTEGER _counterFrequency;
+#else
+static struct timeval _start;
+#endif
+
+static char versionCstr[1024];
+System::OutOfMemoryCallback System::outOfMemoryCallback = NULL;
+
+#ifdef G3D_OSX
+long System::m_OSXCPUSpeed;
+double System::m_secondsPerNS;
+#endif
+
+/** The Real-World time of System::getTick() time 0. Set by initTime */
+static RealTime realWorldGetTickTime0;
+
+
+static unsigned int maxSupportedCPUIDLevel = 0;
+static unsigned int maxSupportedExtendedLevel = 0;
+
+/** Checks if the CPUID command is available on the processor (called from init) */
+static void checkForCPUID();
+
+/** ReadRead the standard processor extensions. Called from init(). */
+static void getStandardProcessorExtensions();
+
+/** Called from init */
+static void initTime();
+
+
+std::string System::findDataFile
+(const std::string& full,
+ bool errorIfNotFound) {
+
+ if (fileExists(full)) {
+ return full;
+ }
+
+ std::string initialAppDataDir(g_appDataDir);
+
+ std::string name = filenameBaseExt(full);
+ std::string originalPath = filenamePath(full);
+
+ // Search several paths
+ Array<std::string> pathBase;
+
+ int backlen = 4;
+
+ // add what should be the current working directory
+ pathBase.append("");
+
+ // add application specified data directory to be searched first
+ pathBase.append(initialAppDataDir);
+
+ // try walking back along the directory tree
+ std::string prev = "";
+ for (int i = 0; i < backlen; ++i) {
+ pathBase.append(originalPath + prev);
+ prev = prev + "../";
+ }
+
+ prev = "../";
+ for (int i = 0; i < backlen; ++i) {
+ pathBase.append(prev);
+ prev = prev + "../";
+ }
+
+ // Hard-code in likely install directories
+ int ver = G3D_VER;
+ std::string lname = format("G3D-%d.%02d", ver / 10000, (ver / 100) % 100);
+
+ if (G3D_VER % 100 != 0) {
+ lname = lname + format("-b%02d/", ver % 100);
+ } else {
+ lname = lname + "/";
+ }
+
+ // Look in some other likely places
+# ifdef G3D_WIN32
+ std::string lpath = "libraries/G3D/";
+ pathBase.append(std::string("c:/") + lpath);
+ pathBase.append(std::string("d:/") + lpath);
+ pathBase.append(std::string("e:/") + lpath);
+ pathBase.append(std::string("f:/") + lpath);
+ pathBase.append(std::string("g:/") + lpath);
+ pathBase.append(std::string("x:/") + lpath);
+# endif
+# if defined(G3D_LINUX)
+ pathBase.append("/usr/local/");
+ pathBase.append("/course/cs224/");
+ pathBase.append("/map/gfx0/common/games/");
+# endif
+# if defined(G3D_FREEBSD)
+ pathBase.append("/usr/local/");
+ pathBase.append("/usr/local/371/");
+ pathBase.append("/usr/cs-local/371/");
+# endif
+# if defined(G3D_OSX)
+ pathBase.append("/usr/local/" + lname);
+ pathBase.append("/Volumes/McGuire/Projects/");
+# endif
+
+ // Add the library name to all variations
+ int N = pathBase.size();
+ for (int i = 0; i < N; ++i) {
+ pathBase.append(pathBase[i] + lname);
+ pathBase.append(pathBase[i] + "G3D/");
+ }
+
+ Array<std::string> subDir;
+ subDir.append("", "font/", "sky/", "gui/");
+ subDir.append("image/", "quake2/", "quake2/players/");
+ subDir.append("quake3/", "SuperShader/", "ifs/", "3ds/");
+ subDir.append("quake2/speedway/");
+
+ Array<std::string> path;
+ for (int p = 0; p < pathBase.size(); ++p) {
+ for (int s = 0; s < subDir.size(); ++s) {
+ path.append(pathBase[p] + subDir[s]);
+ path.append(pathBase[p] + "data/" + subDir[s]);
+ path.append(pathBase[p] + "data-files/" + subDir[s]);
+ }
+ }
+
+ for (int i = 0; i < path.length(); ++i) {
+ std::string filename = path[i] + name;
+ if (fileExists(filename)) {
+ logPrintf("\nWARNING: Could not find '%s' so '%s' "
+ "was substituted.\n", full.c_str(),
+ filename.c_str());
+ return filename;
+ }
+ }
+
+ if (errorIfNotFound) {
+ // Generate an error message
+ std::string locations;
+ for (int i = 0; i < path.size(); ++i) {
+ locations += path[i] + name + "\n";
+ }
+ alwaysAssertM(false, "Could not find '" + full + "' in:\n" + locations);
+ }
+ // Not found
+ return "";
+}
+
+
+void System::setAppDataDir(const std::string& path) {
+ // just copy the path, it needs to be valid
+ strncpy(g_appDataDir, path.c_str(), sizeof(g_appDataDir));
+}
+
+
+std::string demoFindData(bool errorIfNotFound) {
+ // Directories that might contain the data
+ Array<std::string> potential;
+
+ // Look back up the directory tree
+ std::string x = "../";
+ std::string f = "";
+ for (int i = 0; i < 6; ++i) {
+ potential.append(f);
+ f = f + x;
+ }
+
+ // Hard-code in likely install directories
+ int ver = G3D_VER;
+ std::string lname = format("G3D-%d.%02d", ver / 10000, (ver / 100) % 100);
+
+ if (G3D_VER % 100 != 0) {
+ lname = lname + format("-b%02d/", ver % 100);
+ } else {
+ lname = lname + "/";
+ }
+
+ std::string lpath = "libraries/" + lname;
+ #ifdef G3D_WIN32
+ potential.append(std::string("c:/") + lpath);
+ potential.append(std::string("d:/") + lpath);
+ potential.append(std::string("e:/") + lpath);
+ potential.append(std::string("f:/") + lpath);
+ potential.append(std::string("g:/") + lpath);
+ potential.append(std::string("x:/") + lpath);
+ #elif defined(G3D_LINUX)
+ potential.append("/usr/local/" + lname);
+ potential.append("/course/cs224/");
+ potential.append("/map/gfx0/common/games/");
+ #elif defined(G3D_FREEBSD)
+ potential.append("/usr/local/" + lname);
+ potential.append("/usr/local/371/")
+ potential.append("/usr/cs-local/371/")
+ #elif defined(G3D_OSX)
+ potential.append("/usr/local/" + lname);
+ potential.append("/Volumes/McGuire/Projects/");
+ potential.append("/Volumes/McGuire/Projects/G3D/");
+ #endif
+
+ // Scan all potentials for the font directory
+ for (int p = 0; p < potential.size(); ++p) {
+ std::string path = potential[p];
+ //debugPrintf("Looking at: %sdata\n", path.c_str());
+ if (fileExists(path + "data") && fileExists(path + "data/font")) {
+ return path + "data/";
+ }
+ if (fileExists(path + "data-files") && fileExists(path + "data-files/font")) {
+ return path + "data-files/";
+ }
+ }
+
+ if (errorIfNotFound) {
+ const char* choice[] = {"Exit"};
+
+ prompt("Demo Error", "The demo could not locate the data directory. "
+ "The data is required to run this demo. If you have not downloaded "
+ "the data zipfile, get it from http://g3d-cpp.sf.net. If you have "
+ "downloaded it, it needs to be no more than 4 directories above the "
+ "demo directory.", choice, 1, true);
+ }
+
+ return "";
+}
+
+bool System::hasCPUID() {
+ init();
+ return g_cpuInfo.m_hasCPUID;
+}
+
+bool System::hasRDTSC() {
+ init();
+ return g_cpuInfo.m_hasRDTSC;
+}
+
+
+bool System::hasSSE() {
+ init();
+ return g_cpuInfo.m_hasSSE;
+}
+
+
+bool System::hasSSE2() {
+ init();
+ return g_cpuInfo.m_hasSSE2;
+}
+
+bool System::hasSSE3() {
+ init();
+ return g_cpuInfo.m_hasSSE3;
+}
+
+bool System::hasMMX() {
+ init();
+ return g_cpuInfo.m_hasMMX;
+}
+
+
+bool System::has3DNow() {
+ init();
+ return g_cpuInfo.m_has3DNOW;
+}
+
+
+const std::string& System::cpuVendor() {
+ init();
+ static const std::string _cpuVendor = g_cpuInfo.m_cpuVendorStr;
+ return _cpuVendor;
+}
+
+
+G3DEndian System::machineEndian() {
+ init();
+ return _machineEndian;
+}
+
+const std::string& System::operatingSystem() {
+ init();
+ static const std::string _operatingSystem =_operatingSystemCstr;
+ return _operatingSystem;
+}
+
+
+const std::string& System::cpuArchitecture() {
+ init();
+ static const std::string _cpuArch = _cpuArchCstr;
+ return _cpuArch;
+}
+
+const std::string& System::build() {
+ const static std::string b =
+# ifdef _DEBUG
+ "Debug";
+# else
+ "Release";
+# endif
+
+ return b;
+}
+
+const std::string& System::version() {
+ init();
+
+ static const std::string _version = versionCstr;
+ return _version;
+}
+
+
+void System::init() {
+ // Cannot use most G3D data structures or utility functions in here because
+ // they are not initialized.
+
+ static bool initialized = false;
+
+ if (initialized) {
+ return;
+ }
+
+ initialized = true;
+
+ if ((G3D_VER % 100) != 0) {
+ sprintf(versionCstr, "G3D %d.%02d beta %d",
+ G3D_VER / 10000,
+ (G3D_VER / 100) % 100,
+ G3D_VER % 100);
+ } else {
+ sprintf(versionCstr, "G3D %d.%02d",
+ G3D_VER / 10000,
+ (G3D_VER / 100) % 100);
+ }
+
+ // First of all we check if the CPUID command is available
+ checkForCPUID();
+
+ // Figure out if this machine is little or big endian.
+ {
+ int32 a = 1;
+ if (*(uint8*)&a == 1) {
+ _machineEndian = G3D_LITTLE_ENDIAN;
+ } else {
+ _machineEndian = G3D_BIG_ENDIAN;
+ }
+ }
+
+# ifdef G3D_NOT_OSX_PPC
+ // Process the CPUID information
+ if (g_cpuInfo.m_hasCPUID) {
+ // We read the standard CPUID level 0x00000000 which should
+ // be available on every x86 processor. This fills out
+ // a string with the processor vendor tag.
+ unsigned int eaxreg = 0, ebxreg = 0, ecxreg = 0, edxreg = 0;
+
+ CALL_CPUID(0x00, eaxreg, ebxreg, ecxreg, edxreg);
+
+ // Then we connect the single register values to the vendor string
+ *((unsigned int*) g_cpuInfo.m_cpuVendorStr) = ebxreg;
+ *((unsigned int*) (g_cpuInfo.m_cpuVendorStr + 4)) = edxreg;
+ *((unsigned int*) (g_cpuInfo.m_cpuVendorStr + 8)) = ecxreg;
+ g_cpuInfo.m_cpuVendorStr[12] = '\0';
+
+ // We can also read the max. supported standard CPUID level
+ maxSupportedCPUIDLevel = eaxreg & 0xFFFF;
+
+ // Then we read the ext. CPUID level 0x80000000
+ CALL_CPUID(0x80000000, eaxreg, ebxreg, ecxreg, edxreg);
+
+ // ...to check the max. supported extended CPUID level
+ maxSupportedExtendedLevel = eaxreg;
+
+ // Then we switch to the specific processor vendors.
+ // Fill out _cpuArch based on this information. It will
+ // be overwritten by the next block of code on Windows,
+ // but on Linux will stand.
+ switch (ebxreg) {
+ case 0x756E6547: // GenuineIntel
+ strcpy(_cpuArchCstr, "Intel Processor");
+ break;
+
+ case 0x68747541: // AuthenticAMD
+ strcpy(_cpuArchCstr, "AMD Processor");
+ break;
+
+ case 0x69727943: // CyrixInstead
+ strcpy(_cpuArchCstr, "Cyrix Processor");
+ break;
+
+ default:
+ strcpy(_cpuArchCstr, "Unknown Processor Vendor");
+ break;
+ }
+ }
+ #endif // G3D_NOT_OSX_PPC
+
+ #ifdef G3D_WIN32
+ bool success = RegistryUtil::readInt32
+ ("HKEY_LOCAL_MACHINE\\HARDWARE\\DESCRIPTION\\System\\CentralProcessor\\0", "~MHz", g_cpuInfo.m_cpuSpeed);
+
+ SYSTEM_INFO systemInfo;
+ GetSystemInfo(&systemInfo);
+ char* arch;
+ switch (systemInfo.wProcessorArchitecture) {
+ case PROCESSOR_ARCHITECTURE_INTEL:
+ arch = "Intel";
+ break;
+
+ case PROCESSOR_ARCHITECTURE_MIPS:
+ arch = "MIPS";
+ break;
+
+ case PROCESSOR_ARCHITECTURE_ALPHA:
+ arch = "Alpha";
+ break;
+
+ case PROCESSOR_ARCHITECTURE_PPC:
+ arch = "Power PC";
+ break;
+
+ default:
+ arch = "Unknown";
+ }
+
+ uint32 maxAddr = (uint32)systemInfo.lpMaximumApplicationAddress;
+ sprintf(_cpuArchCstr, "%d x %d-bit %s processor",
+ systemInfo.dwNumberOfProcessors,
+ (int)(::log((double)maxAddr) / ::log(2.0) + 2.0),
+ arch);
+ // _CPUSpeed / (1024.0 * 1024));
+
+ OSVERSIONINFO osVersionInfo;
+ osVersionInfo.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
+ success = GetVersionEx(&osVersionInfo) != 0;
+
+ if (success) {
+ sprintf(_operatingSystemCstr, "Windows %d.%d build %d Platform %d %s",
+ osVersionInfo.dwMajorVersion,
+ osVersionInfo.dwMinorVersion,
+ osVersionInfo.dwBuildNumber,
+ osVersionInfo.dwPlatformId,
+ osVersionInfo.szCSDVersion);
+ } else {
+ strcpy(_operatingSystemCstr, "Windows");
+ }
+
+ #elif defined(G3D_LINUX)
+
+ {
+ // Shell out to the 'uname' command
+ FILE* f = popen("uname -a", "r");
+
+ int len = 100;
+ char* r = (char*)::malloc(len * sizeof(char));
+ fgets(r, len, f);
+ // Remove trailing newline
+ if (r[strlen(r) - 1] == '\n') {
+ r[strlen(r) - 1] = '\0';
+ }
+ fclose(f);
+
+ strcpy(_operatingSystemCstr, r);
+ ::free(r);
+ }
+
+ #elif defined(G3D_OSX)
+
+ // Operating System:
+ SInt32 macVersion;
+ Gestalt(gestaltSystemVersion, &macVersion);
+
+ int major = (macVersion >> 8) & 0xFF;
+ int minor = (macVersion >> 4) & 0xF;
+ int revision = macVersion & 0xF;
+
+ sprintf(_operatingSystemCstr, "OS X %x.%x.%x", major, minor, revision);
+
+ // Clock Cycle Timing Information:
+ Gestalt('pclk', &System::m_OSXCPUSpeed);
+ g_cpuInfo.m_cpuSpeed = iRound((double)m_OSXCPUSpeed / (1024 * 1024));
+ m_secondsPerNS = 1.0 / 1.0e9;
+
+ // System Architecture:
+ const NXArchInfo* pInfo = NXGetLocalArchInfo();
+
+ if (pInfo) {
+ strcpy(_cpuArchCstr, pInfo->description);
+
+ switch (pInfo->cputype) {
+ case CPU_TYPE_POWERPC:
+ switch(pInfo->cpusubtype){
+ case CPU_SUBTYPE_POWERPC_750:
+ case CPU_SUBTYPE_POWERPC_7400:
+ case CPU_SUBTYPE_POWERPC_7450:
+ strcpy(g_cpuInfo.m_cpuVendorStr, "Motorola");
+ break;
+ case CPU_SUBTYPE_POWERPC_970:
+ strcpy(g_cpuInfo.m_cpuVendorStr, "IBM");
+ break;
+ }
+ break;
+
+ case CPU_TYPE_I386:
+ strcpy(g_cpuInfo.m_cpuVendorStr, "Intel");
+ break;
+ }
+ }
+ #endif
+
+ initTime();
+
+ getStandardProcessorExtensions();
+}
+
+static void checkForCPUID() {
+ unsigned int bitChanged = 0;
+
+ // We've to check if we can toggle the flag register bit 21.
+ // If we can't the processor does not support the CPUID command.
+
+#if defined(_MSC_VER)
+ __asm {
+ push eax
+ push ebx
+ pushfd
+ pushfd
+ pop eax
+ mov ebx, eax
+ xor eax, 0x00200000
+ push eax
+ popfd
+ pushfd
+ pop eax
+ popfd
+ xor eax, ebx
+ mov bitChanged, eax
+ pop ebx
+ pop eax
+ }
+#elif defined(__GNUC__) && defined(i386) && !defined(G3D_OSX_INTEL)
+ // 32-bit g++
+ __asm__ (
+ "pushfl # Get original EFLAGS \n"
+ "pushfl \n"
+ "popl %%eax \n"
+ "movl %%eax, %%ecx \n"
+ "xorl $0x200000, %%eax # Flip ID bit in EFLAGS \n"
+ "pushl %%eax # Save new EFLAGS value on stack \n"
+ "popfl # Replace current EFLAGS value \n"
+ "pushfl # Get new EFLAGS \n"
+ "popl %%eax # Store new EFLAGS in EAX \n"
+ "popfl \n"
+ "xorl %%ecx, %%eax # Can not toggle ID bit, \n"
+ "movl %%eax, %0 # We have CPUID support \n"
+ : "=m" (bitChanged)
+ : // No inputs
+ : "%eax", "%ecx"
+ );
+#elif defined(__GNUC__) && defined(__x86_64__) && !defined(G3D_OSX_INTEL)
+ // x86_64 has SSE and CPUID
+
+ bitChanged = 1;
+#else
+ // Unknown architecture
+ bitChanged = 0;
+#endif
+
+ g_cpuInfo.m_hasCPUID = ((bitChanged == 0) ? false : true);
+}
+
+void getStandardProcessorExtensions() {
+#if !defined(G3D_OSX) || defined(G3D_OSX_INTEL)
+ if (! g_cpuInfo.m_hasCPUID) {
+ return;
+ }
+
+ unsigned int eaxreg = 0, ebxreg = 0, ecxreg = 0;
+ unsigned int features = 0;
+
+ // http://www.amd.com/us-en/assets/content_type/white_papers_and_tech_docs/25481.pdf
+ // call cpuid with function 0x01 in EAX
+
+ // Invoking CPUID with '1' in EAX fills out edx with a bit string.
+ // The bits of this value indicate the presence or absence of
+ // useful processor features.
+ CALL_CPUID(0x01, eaxreg, ebxreg, ecxreg, features);
+
+ #define checkBit(var, bit) ((var & (1 << bit)) ? true : false)
+
+ g_cpuInfo.m_hasRDTSC = checkBit(features, 16);
+ g_cpuInfo.m_hasMMX = checkBit(features, 23);
+ g_cpuInfo.m_hasSSE = checkBit(features, 25);
+ g_cpuInfo.m_hasSSE2 = checkBit(features, 26);
+ g_cpuInfo.m_hasSSE3 = checkBit(ecxreg, 0);
+
+ if (maxSupportedExtendedLevel >= 0x80000001) {
+ // function 0x80000001 changes bit 31 of edx to 3dnow support flag
+ CALL_CPUID(0x80000001, eaxreg, ebxreg, ecxreg, features);
+ g_cpuInfo.m_has3DNOW = checkBit(features, 31);
+ } else {
+ g_cpuInfo.m_has3DNOW = false;
+ }
+
+ #undef checkBit
+#endif
+}
+
+#if defined(SSE)
+
+// Copy in 128 bytes chunks, where each chunk contains 8*float32x4 = 8 * 4 * 4 bytes = 128 bytes
+//
+//
+void memcpySSE2(void* dst, const void* src, int nbytes) {
+ int remainingBytes = nbytes;
+
+ if (nbytes > 128) {
+
+ // Number of chunks
+ int N = nbytes / 128;
+
+ float* restrict d = (float*)dst;
+ const float* restrict s = (const float*)src;
+
+ // Finish when the destination pointer has moved 8N elements
+ float* stop = d + (N * 8 * 4);
+
+ while (d < stop) {
+ // Inner loop unrolled 8 times
+ const __m128 r0 = _mm_loadu_ps(s);
+ const __m128 r1 = _mm_loadu_ps(s + 4);
+ const __m128 r2 = _mm_loadu_ps(s + 8);
+ const __m128 r3 = _mm_loadu_ps(s + 12);
+ const __m128 r4 = _mm_loadu_ps(s + 16);
+ const __m128 r5 = _mm_loadu_ps(s + 20);
+ const __m128 r6 = _mm_loadu_ps(s + 24);
+ const __m128 r7 = _mm_loadu_ps(s + 28);
+
+ _mm_storeu_ps(d, r0);
+ _mm_storeu_ps(d + 4, r1);
+ _mm_storeu_ps(d + 8, r2);
+ _mm_storeu_ps(d + 12, r3);
+ _mm_storeu_ps(d + 16, r4);
+ _mm_storeu_ps(d + 20, r5);
+ _mm_storeu_ps(d + 24, r6);
+ _mm_storeu_ps(d + 28, r7);
+
+ s += 32;
+ d += 32;
+ }
+
+ remainingBytes -= N * 8 * 4 * 4;
+ }
+
+ if (remainingBytes > 0) {
+ // Memcpy the rest
+ memcpy((uint8*)dst + (nbytes - remainingBytes), (const uint8*)src + (nbytes - remainingBytes), remainingBytes);
+ }
+}
+#else
+
+ // Fall back to memcpy
+ void memcpySSE2(void *dst, const void *src, int nbytes) {
+ memcpy(dst, src, nbytes);
+ }
+
+#endif
+
+#if defined(G3D_WIN32) && defined(SSE)
+/** Michael Herf's fast memcpy */
+void memcpyMMX(void* dst, const void* src, int nbytes) {
+ int remainingBytes = nbytes;
+
+ if (nbytes > 64) {
+ _asm {
+ mov esi, src
+ mov edi, dst
+ mov ecx, nbytes
+ shr ecx, 6 // 64 bytes per iteration
+
+ loop1:
+ movq mm1, 0[ESI] // Read in source data
+ movq mm2, 8[ESI]
+ movq mm3, 16[ESI]
+ movq mm4, 24[ESI]
+ movq mm5, 32[ESI]
+ movq mm6, 40[ESI]
+ movq mm7, 48[ESI]
+ movq mm0, 56[ESI]
+
+ movntq 0[EDI], mm1 // Non-temporal stores
+ movntq 8[EDI], mm2
+ movntq 16[EDI], mm3
+ movntq 24[EDI], mm4
+ movntq 32[EDI], mm5
+ movntq 40[EDI], mm6
+ movntq 48[EDI], mm7
+ movntq 56[EDI], mm0
+
+ add esi, 64
+ add edi, 64
+ dec ecx
+ jnz loop1
+
+ emms
+ }
+ remainingBytes -= ((nbytes >> 6) << 6);
+ }
+
+ if (remainingBytes > 0) {
+ // Memcpy the rest
+ memcpy((uint8*)dst + (nbytes - remainingBytes), (const uint8*)src + (nbytes - remainingBytes), remainingBytes);
+ }
+}
+
+#else
+ // Fall back to memcpy
+ void memcpyMMX(void *dst, const void *src, int nbytes) {
+ memcpy(dst, src, nbytes);
+ }
+
+#endif
+
+
+void System::memcpy(void* dst, const void* src, size_t numBytes) {
+ if (System::hasSSE2() && System::hasMMX()) {
+ G3D::memcpyMMX(dst, src, numBytes);
+ } else if (System::hasSSE() && System::hasMMX()) {
+ G3D::memcpyMMX(dst, src, numBytes);
+ } else {
+ ::memcpy(dst, src, numBytes);
+ }
+}
+
+
+/** Michael Herf's fastest memset. n32 must be filled with the same
+ character repeated. */
+#if defined(G3D_WIN32) && defined(SSE)
+
+// On x86 processors, use MMX
+void memfill(void *dst, int n32, unsigned long i) {
+
+ int originalSize = i;
+ int bytesRemaining = i;
+
+ if (i > 16) {
+
+ bytesRemaining = i % 16;
+ i -= bytesRemaining;
+ __asm {
+ movq mm0, n32
+ punpckldq mm0, mm0
+ mov edi, dst
+
+ loopwrite:
+
+ movntq 0[edi], mm0
+ movntq 8[edi], mm0
+
+ add edi, 16
+ sub i, 16
+ jg loopwrite
+
+ emms
+ }
+ }
+
+ if (bytesRemaining > 0) {
+ ::memset((uint8*)dst + (originalSize - bytesRemaining), n32, bytesRemaining);
+ }
+}
+
+#else
+
+// For non x86 processors, we fall back to the standard memset
+void memfill(void *dst, int n32, unsigned long i) {
+ ::memset(dst, n32, i);
+}
+
+#endif
+
+
+void System::memset(void* dst, uint8 value, size_t numBytes) {
+ if (System::hasSSE() && System::hasMMX()) {
+ uint32 v = value;
+ v = v + (v << 8) + (v << 16) + (v << 24);
+ G3D::memfill(dst, v, numBytes);
+ } else {
+ ::memset(dst, value, numBytes);
+ }
+}
+
+
+std::string& System::appName() {
+ static std::string n = filenameBase(currentProgramFilename());
+ return n;
+}
+
+
+std::string System::currentProgramFilename() {
+ char filename[2048];
+
+ #ifdef G3D_WIN32
+ {
+ GetModuleFileNameA(NULL, filename, sizeof(filename));
+ }
+ #else
+ {
+ int ret = readlink("/proc/self/exe", filename, sizeof(filename));
+
+ // In case of an error, leave the handling up to the caller
+ if (ret == -1) {
+ return "";
+ }
+
+ debugAssert((int)sizeof(filename) > ret);
+
+ // Ensure proper NULL termination
+ filename[ret] = 0;
+ }
+ #endif
+
+ return filename;
+}
+
+
+void System::sleep(RealTime t) {
+
+ // Overhead of calling this function.
+ static const RealTime OVERHEAD = .000006;
+
+ RealTime now = time();
+ RealTime wakeupTime = now + t - OVERHEAD;
+
+ RealTime remainingTime = wakeupTime - now;
+ RealTime sleepTime = 0;
+
+ while (remainingTime > 0) {
+
+
+ if (remainingTime > 0.001) {
+ // Safe to use Sleep with a time... sleep for half the remaining time
+ sleepTime = max(remainingTime * .5, 0.0005);
+ } else if (remainingTime > 0.0001) {
+ // Safe to use Sleep with a zero time;
+ // causes the program to yield only
+ // the current time slice, and then return.
+ sleepTime = 0;
+ } else {
+ // Not safe to use Sleep; busy wait
+ sleepTime = -1;
+ }
+
+ if (sleepTime >= 0) {
+ #ifdef G3D_WIN32
+ // Translate to milliseconds
+ Sleep((int)(sleepTime * 1e3));
+ #else
+ // Translate to microseconds
+ usleep((int)(sleepTime * 1e6));
+ #endif
+ }
+
+ now = time();
+ remainingTime = wakeupTime - now;
+ }
+}
+
+
+void System::consoleClearScreen() {
+ #ifdef G3D_WIN32
+ system("cls");
+ #else
+ system("clear");
+ #endif
+}
+
+
+bool System::consoleKeyPressed() {
+ #ifdef G3D_WIN32
+
+ return _kbhit() != 0;
+
+ #else
+
+ static const int STDIN = 0;
+ static bool initialized = false;
+
+ if (! initialized) {
+ // Use termios to turn off line buffering
+ termios term;
+ tcgetattr(STDIN, &term);
+ term.c_lflag &= ~ICANON;
+ tcsetattr(STDIN, TCSANOW, &term);
+ setbuf(stdin, NULL);
+ initialized = true;
+ }
+
+ #ifdef G3D_LINUX
+
+ int bytesWaiting;
+ ioctl(STDIN, FIONREAD, &bytesWaiting);
+ return bytesWaiting;
+
+ #else
+
+ timeval timeout;
+ fd_set rdset;
+
+ FD_ZERO(&rdset);
+ FD_SET(STDIN, &rdset);
+ timeout.tv_sec = 0;
+ timeout.tv_usec = 0;
+
+ return select(STDIN + 1, &rdset, NULL, NULL, &timeout);
+ #endif
+ #endif
+}
+
+
+int System::consoleReadKey() {
+ #ifdef G3D_WIN32
+ return _getch();
+ #else
+ char c;
+ read(0, &c, 1);
+ return c;
+ #endif
+}
+
+
+void initTime() {
+ #ifdef G3D_WIN32
+ if (QueryPerformanceFrequency(&_counterFrequency)) {
+ QueryPerformanceCounter(&_start);
+ }
+
+ struct _timeb t;
+ _ftime(&t);
+
+ realWorldGetTickTime0 = (RealTime)t.time - t.timezone * G3D::MINUTE + (t.dstflag ? G3D::HOUR : 0);
+
+ #else
+ gettimeofday(&_start, NULL);
+ // "sse" = "seconds since epoch". The time
+ // function returns the seconds since the epoch
+ // GMT (perhaps more correctly called UTC).
+ time_t gmt = time(NULL);
+
+ // No call to free or delete is needed, but subsequent
+ // calls to asctime, ctime, mktime, etc. might overwrite
+ // local_time_vals.
+ tm* localTimeVals = localtime(&gmt);
+
+ time_t local = gmt;
+
+ if (localTimeVals) {
+ // tm_gmtoff is already corrected for daylight savings.
+ local = local + localTimeVals->tm_gmtoff;
+ }
+
+ realWorldGetTickTime0 = local;
+ #endif
+}
+
+
+RealTime System::time() {
+ init();
+ #ifdef G3D_WIN32
+ LARGE_INTEGER now;
+ QueryPerformanceCounter(&now);
+
+ return ((RealTime)(now.QuadPart - _start.QuadPart) /
+ _counterFrequency.QuadPart) + realWorldGetTickTime0;
+ #else
+ // Linux resolution defaults to 100Hz.
+ // There is no need to do a separate RDTSC call as gettimeofday
+ // actually uses RDTSC when on systems that support it, otherwise
+ // it uses the system clock.
+ struct timeval now;
+ gettimeofday(&now, NULL);
+
+ return (now.tv_sec - _start.tv_sec) +
+ (now.tv_usec - _start.tv_usec) / 1e6
+ + realWorldGetTickTime0;
+ #endif
+}
+
+
+////////////////////////////////////////////////////////////////
+
+#define REALPTR_TO_USERPTR(x) ((uint8*)(x) + sizeof (void *))
+#define USERPTR_TO_REALPTR(x) ((uint8*)(x) - sizeof (void *))
+#define REALBLOCK_SIZE(x) ((x) + sizeof (void *))
+
+class BufferPool {
+public:
+
+ /** Only store buffers up to these sizes (in bytes) in each pool->
+ Different pools have different management strategies.
+
+ A large block is preallocated for tiny buffers; they are used with
+ tremendous frequency. Other buffers are allocated as demanded.
+ Tiny buffers are 128 bytes long because that seems to align well with
+ cache sizes on many machines.
+ */
+ enum {tinyBufferSize = 128, smallBufferSize = 1024, medBufferSize = 4096};
+
+ /**
+ Most buffers we're allowed to store.
+ 128000 * 128 = 16 MB (preallocated)
+ 2048 * 1024 = 2 MB (allocated on demand)
+ 1024 * 4096 = 4 MB (allocated on demand)
+ */
+ enum {maxTinyBuffers = 128000, maxSmallBuffers = 2048, maxMedBuffers = 1024};
+
+private:
+
+ class MemBlock {
+ public:
+ void* ptr;
+ size_t bytes;
+
+ inline MemBlock() : ptr(NULL), bytes(0) {}
+ inline MemBlock(void* p, size_t b) : ptr(p), bytes(b) {}
+ };
+
+ MemBlock smallPool[maxSmallBuffers];
+ int smallPoolSize;
+
+ MemBlock medPool[maxMedBuffers];
+ int medPoolSize;
+
+ /** The tiny pool is a single block of storage into which all tiny
+ objects are allocated. This provides better locality for
+ small objects and avoids the search time, since all tiny
+ blocks are exactly the same size. */
+ void* tinyPool[maxTinyBuffers];
+ int tinyPoolSize;
+
+ /** Pointer to the data in the tiny pool */
+ void* tinyHeap;
+
+# ifdef G3D_WIN32
+ CRITICAL_SECTION mutex;
+# else
+ pthread_mutex_t mutex;
+# endif
+
+ /** Provide synchronization between threads */
+ void lock() {
+# ifdef G3D_WIN32
+ EnterCriticalSection(&mutex);
+# else
+ pthread_mutex_lock(&mutex);
+# endif
+ }
+
+ void unlock() {
+# ifdef G3D_WIN32
+ LeaveCriticalSection(&mutex);
+# else
+ pthread_mutex_unlock(&mutex);
+# endif
+ }
+
+ /**
+ Malloc out of the tiny heap. Returns NULL if allocation failed.
+ */
+ inline void* tinyMalloc(size_t bytes) {
+ // Note that we ignore the actual byte size
+ // and create a constant size block.
+ (void)bytes;
+ assert(tinyBufferSize >= bytes);
+
+ void* ptr = NULL;
+
+ if (tinyPoolSize > 0) {
+ --tinyPoolSize;
+
+ // Return the old last pointer from the freelist
+ ptr = tinyPool[tinyPoolSize];
+
+# ifdef G3D_DEBUG
+ if (tinyPoolSize > 0) {
+ assert(tinyPool[tinyPoolSize - 1] != ptr);
+ // "System::malloc heap corruption detected: "
+ // "the last two pointers on the freelist are identical (during tinyMalloc).");
+ }
+# endif
+
+ // NULL out the entry to help detect corruption
+ tinyPool[tinyPoolSize] = NULL;
+ }
+
+ return ptr;
+ }
+
+ /** Returns true if this is a pointer into the tiny heap. */
+ bool inTinyHeap(void* ptr) {
+ return
+ (ptr >= tinyHeap) &&
+ (ptr < (uint8*)tinyHeap + maxTinyBuffers * tinyBufferSize);
+ }
+
+ void tinyFree(void* ptr) {
+ assert(ptr);
+ assert(tinyPoolSize < maxTinyBuffers);
+ // "Tried to free a tiny pool buffer when the tiny pool freelist is full.");
+
+# ifdef G3D_DEBUG
+ if (tinyPoolSize > 0) {
+ void* prevOnHeap = tinyPool[tinyPoolSize - 1];
+ assert(prevOnHeap != ptr);
+// "System::malloc heap corruption detected: "
+// "the last two pointers on the freelist are identical (during tinyFree).");
+ }
+# endif
+
+ assert(tinyPool[tinyPoolSize] == NULL);
+
+ // Put the pointer back into the free list
+ tinyPool[tinyPoolSize] = ptr;
+ ++tinyPoolSize;
+
+ }
+
+ void flushPool(MemBlock* pool, int& poolSize) {
+ for (int i = 0; i < poolSize; ++i) {
+ ::free(pool[i].ptr);
+ pool[i].ptr = NULL;
+ pool[i].bytes = 0;
+ }
+ poolSize = 0;
+ }
+
+
+ /** Allocate out of a specific pool-> Return NULL if no suitable
+ memory was found.
+
+ */
+ void* malloc(MemBlock* pool, int& poolSize, size_t bytes) {
+
+ // OPT: find the smallest block that satisfies the request.
+
+ // See if there's something we can use in the buffer pool->
+ // Search backwards since usually we'll re-use the last one.
+ for (int i = (int)poolSize - 1; i >= 0; --i) {
+ if (pool[i].bytes >= bytes) {
+ // We found a suitable entry in the pool->
+
+ // No need to offset the pointer; it is already offset
+ void* ptr = pool[i].ptr;
+
+ // Remove this element from the pool
+ --poolSize;
+ pool[i] = pool[poolSize];
+
+ return ptr;
+ }
+ }
+
+ return NULL;
+ }
+
+public:
+
+ /** Count of memory allocations that have occurred. */
+ int totalMallocs;
+ int mallocsFromTinyPool;
+ int mallocsFromSmallPool;
+ int mallocsFromMedPool;
+
+ /** Amount of memory currently allocated (according to the application).
+ This does not count the memory still remaining in the buffer pool,
+ but does count extra memory required for rounding off to the size
+ of a buffer.
+ Primarily useful for detecting leaks.*/
+ // TODO: make me an atomic int!
+ volatile int bytesAllocated;
+
+ BufferPool() {
+ totalMallocs = 0;
+
+ mallocsFromTinyPool = 0;
+ mallocsFromSmallPool = 0;
+ mallocsFromMedPool = 0;
+
+ bytesAllocated = true;
+
+ tinyPoolSize = 0;
+ tinyHeap = NULL;
+
+ smallPoolSize = 0;
+
+ medPoolSize = 0;
+
+
+ // Initialize the tiny heap as a bunch of pointers into one
+ // pre-allocated buffer.
+ tinyHeap = ::malloc(maxTinyBuffers * tinyBufferSize);
+ for (int i = 0; i < maxTinyBuffers; ++i) {
+ tinyPool[i] = (uint8*)tinyHeap + (tinyBufferSize * i);
+ }
+ tinyPoolSize = maxTinyBuffers;
+
+# ifdef G3D_WIN32
+ InitializeCriticalSection(&mutex);
+# else
+ pthread_mutex_init(&mutex, NULL);
+# endif
+ }
+
+
+ ~BufferPool() {
+ ::free(tinyHeap);
+# ifdef G3D_WIN32
+ DeleteCriticalSection(&mutex);
+# else
+ // No destruction on pthreads
+# endif
+ }
+
+
+ void* realloc(void* ptr, size_t bytes) {
+ if (ptr == NULL) {
+ return malloc(bytes);
+ }
+
+ if (inTinyHeap(ptr)) {
+ if (bytes <= tinyBufferSize) {
+ // The old pointer actually had enough space.
+ return ptr;
+ } else {
+ // Free the old pointer and malloc
+
+ void* newPtr = malloc(bytes);
+ System::memcpy(newPtr, ptr, tinyBufferSize);
+ tinyFree(ptr);
+ return newPtr;
+
+ }
+ } else {
+ // In one of our heaps.
+
+ // See how big the block really was
+ size_t realSize = *(uint32*)USERPTR_TO_REALPTR(ptr);
+ if (bytes <= realSize) {
+ // The old block was big enough.
+ return ptr;
+ }
+
+ // Need to reallocate
+ void* newPtr = malloc(bytes);
+ System::memcpy(newPtr, ptr, realSize);
+ free(ptr);
+ return newPtr;
+ }
+ }
+
+
+ void* malloc(size_t bytes) {
+ lock();
+ ++totalMallocs;
+
+ if (bytes <= tinyBufferSize) {
+
+ void* ptr = tinyMalloc(bytes);
+
+ if (ptr) {
+ ++mallocsFromTinyPool;
+ unlock();
+ return ptr;
+ }
+
+ }
+
+ // Failure to allocate a tiny buffer is allowed to flow
+ // through to a small buffer
+ if (bytes <= smallBufferSize) {
+
+ void* ptr = malloc(smallPool, smallPoolSize, bytes);
+
+ if (ptr) {
+ ++mallocsFromSmallPool;
+ unlock();
+ return ptr;
+ }
+
+ } else if (bytes <= medBufferSize) {
+ // Note that a small allocation failure does *not* fall
+ // through into a medium allocation because that would
+ // waste the medium buffer's resources.
+
+ void* ptr = malloc(medPool, medPoolSize, bytes);
+
+ if (ptr) {
+ ++mallocsFromMedPool;
+ unlock();
+ debugAssertM(ptr != NULL, "BufferPool::malloc returned NULL");
+ return ptr;
+ }
+ }
+
+ bytesAllocated += REALBLOCK_SIZE(bytes);
+ unlock();
+
+ // Heap allocate
+
+ // Allocate 4 extra bytes for our size header (unfortunate,
+ // since malloc already added its own header).
+ void* ptr = ::malloc(REALBLOCK_SIZE(bytes));
+
+ if (ptr == NULL) {
+ // Flush memory pools to try and recover space
+ flushPool(smallPool, smallPoolSize);
+ flushPool(medPool, medPoolSize);
+ ptr = ::malloc(REALBLOCK_SIZE(bytes));
+ }
+
+ if (ptr == NULL) {
+ if ((System::outOfMemoryCallback != NULL) &&
+ (System::outOfMemoryCallback(REALBLOCK_SIZE(bytes), true) == true)) {
+ // Re-attempt the malloc
+ ptr = ::malloc(REALBLOCK_SIZE(bytes));
+ }
+ }
+
+ if (ptr == NULL) {
+ if (System::outOfMemoryCallback != NULL) {
+ // Notify the application
+ System::outOfMemoryCallback(REALBLOCK_SIZE(bytes), false);
+ }
+# ifdef G3D_DEBUG
+ debugPrintf("::malloc(%d) returned NULL\n", REALBLOCK_SIZE(bytes));
+# endif
+ debugAssertM(ptr != NULL,
+ "::malloc returned NULL. Either the "
+ "operating system is out of memory or the "
+ "heap is corrupt.");
+ return NULL;
+ }
+
+ *(uint32*)ptr = bytes;
+
+ return REALPTR_TO_USERPTR(ptr);
+ }
+
+
+ void free(void* ptr) {
+ if (ptr == NULL) {
+ // Free does nothing on null pointers
+ return;
+ }
+
+ assert(isValidPointer(ptr));
+
+ if (inTinyHeap(ptr)) {
+ lock();
+ tinyFree(ptr);
+ unlock();
+ return;
+ }
+
+ uint32 bytes = *(uint32*)USERPTR_TO_REALPTR(ptr);
+
+ lock();
+ if (bytes <= smallBufferSize) {
+ if (smallPoolSize < maxSmallBuffers) {
+ smallPool[smallPoolSize] = MemBlock(ptr, bytes);
+ ++smallPoolSize;
+ unlock();
+ return;
+ }
+ } else if (bytes <= medBufferSize) {
+ if (medPoolSize < maxMedBuffers) {
+ medPool[medPoolSize] = MemBlock(ptr, bytes);
+ ++medPoolSize;
+ unlock();
+ return;
+ }
+ }
+ bytesAllocated -= REALBLOCK_SIZE(bytes);
+ unlock();
+
+ // Free; the buffer pools are full or this is too big to store.
+ ::free(USERPTR_TO_REALPTR(ptr));
+ }
+
+ std::string performance() const {
+ if (totalMallocs > 0) {
+ int pooled = mallocsFromTinyPool +
+ mallocsFromSmallPool +
+ mallocsFromMedPool;
+
+ int total = totalMallocs;
+
+ return format("malloc performance: %5.1f%% <= %db, %5.1f%% <= %db, "
+ "%5.1f%% <= %db, %5.1f%% > %db",
+ 100.0 * mallocsFromTinyPool / total,
+ BufferPool::tinyBufferSize,
+ 100.0 * mallocsFromSmallPool / total,
+ BufferPool::smallBufferSize,
+ 100.0 * mallocsFromMedPool / total,
+ BufferPool::medBufferSize,
+ 100.0 * (1.0 - (double)pooled / total),
+ BufferPool::medBufferSize);
+ } else {
+ return "No System::malloc calls made yet.";
+ }
+ }
+
+ std::string status() const {
+ return format("preallocated shared buffers: %5d/%d x %db",
+ maxTinyBuffers - tinyPoolSize, maxTinyBuffers, tinyBufferSize);
+ }
+};
+
+// Dynamically allocated because we need to ensure that
+// the buffer pool is still around when the last global variable
+// is deallocated.
+static BufferPool* bufferpool = NULL;
+
+std::string System::mallocPerformance() {
+#ifndef NO_BUFFERPOOL
+ return bufferpool->performance();
+#else
+ return "NO_BUFFERPOOL";
+#endif
+}
+
+std::string System::mallocStatus() {
+#ifndef NO_BUFFERPOOL
+ return bufferpool->status();
+#else
+ return "NO_BUFFERPOOL";
+#endif
+}
+
+
+void System::resetMallocPerformanceCounters() {
+#ifndef NO_BUFFERPOOL
+ bufferpool->totalMallocs = 0;
+ bufferpool->mallocsFromMedPool = 0;
+ bufferpool->mallocsFromSmallPool = 0;
+ bufferpool->mallocsFromTinyPool = 0;
+#endif
+}
+
+
+#ifndef NO_BUFFERPOOL
+inline void initMem() {
+ // Putting the test here ensures that the system is always
+ // initialized, even when globals are being allocated.
+ static bool initialized = false;
+ if (! initialized) {
+ bufferpool = new BufferPool();
+ initialized = true;
+ }
+}
+#endif
+
+
+void* System::malloc(size_t bytes) {
+#ifndef NO_BUFFERPOOL
+ initMem();
+ return bufferpool->malloc(bytes);
+#else
+ return ::malloc(bytes);
+#endif
+}
+
+void* System::calloc(size_t n, size_t x) {
+#ifndef NO_BUFFERPOOL
+ void* b = System::malloc(n * x);
+ debugAssertM(b != NULL, "System::malloc returned NULL");
+ debugAssertM(isValidHeapPointer(b), "System::malloc returned an invalid pointer");
+ System::memset(b, 0, n * x);
+ return b;
+#else
+ return ::calloc(n, x);
+#endif
+}
+
+
+void* System::realloc(void* block, size_t bytes) {
+#ifndef NO_BUFFERPOOL
+ initMem();
+ return bufferpool->realloc(block, bytes);
+#else
+ return ::realloc(block, bytes);
+#endif
+}
+
+
+void System::free(void* p) {
+#ifndef NO_BUFFERPOOL
+ bufferpool->free(p);
+#else
+ return ::free(p);
+#endif
+}
+
+
+void* System::alignedMalloc(size_t bytes, size_t alignment) {
+
+ alwaysAssertM(isPow2(alignment), "alignment must be a power of 2");
+
+ // We must align to at least a word boundary.
+ alignment = iMax(alignment, sizeof(void *));
+
+ // Pad the allocation size with the alignment size and the
+ // size of the redirect pointer.
+ size_t totalBytes = bytes + alignment + sizeof(void*);
+
+ size_t truePtr = (size_t)System::malloc(totalBytes);
+
+ if (truePtr == 0) {
+ // malloc returned NULL
+ return NULL;
+ }
+
+ debugAssert(isValidHeapPointer((void*)truePtr));
+ #ifdef G3D_WIN32
+ // The blocks we return will not be valid Win32 debug heap
+ // pointers because they are offset
+ // debugAssert(_CrtIsValidPointer((void*)truePtr, totalBytes, TRUE) );
+ #endif
+
+ // The return pointer will be the next aligned location (we must at least
+ // leave space for the redirect pointer, however).
+ size_t alignedPtr = truePtr + sizeof(void*);
+
+ // 2^n - 1 has the form 1111... in binary.
+ uint32 bitMask = (alignment - 1);
+
+ // Advance forward until we reach an aligned location.
+ while ((alignedPtr & bitMask) != 0) {
+ alignedPtr += sizeof(void*);
+ }
+
+ debugAssert(alignedPtr - truePtr + bytes <= totalBytes);
+
+ // Immediately before the aligned location, write the true array location
+ // so that we can free it correctly.
+ size_t* redirectPtr = (size_t *)(alignedPtr - sizeof(void *));
+ redirectPtr[0] = truePtr;
+
+ debugAssert(isValidHeapPointer((void*)truePtr));
+
+ #ifdef G3D_WIN32
+ debugAssert( _CrtIsValidPointer((void*)alignedPtr, bytes, TRUE) );
+ #endif
+ return (void *)alignedPtr;
+}
+
+
+void System::alignedFree(void* _ptr) {
+ if (_ptr == NULL) {
+ return;
+ }
+
+ size_t alignedPtr = (size_t)_ptr;
+
+ // Back up one word from the pointer the user passed in.
+ // We now have a pointer to a pointer to the true start
+ // of the memory block.
+ size_t* redirectPtr = (size_t*)(alignedPtr - sizeof(void *));
+
+ // Dereference that pointer so that ptr = true start
+ void* truePtr = (void*)redirectPtr[0];
+
+ debugAssert(isValidHeapPointer((void*)truePtr));
+ System::free(truePtr);
+}
+
+
+void System::setEnv(const std::string& name, const std::string& value) {
+ std::string cmd = name + "=" + value;
+# ifdef G3D_WIN32
+ _putenv(cmd.c_str());
+# else
+ // Many linux implementations of putenv expect char*
+ putenv(const_cast<char*>(cmd.c_str()));
+# endif
+}
+
+const char* System::getEnv(const std::string& name) {
+ return getenv(name.c_str());
+}
+
+static void var(TextOutput& t, const std::string& name, const std::string& val) {
+ t.writeSymbols(name,"=");
+ t.writeString(val);
+ t.writeNewline();
+}
+
+
+static void var(TextOutput& t, const std::string& name, const bool val) {
+ t.writeSymbols(name, "=", val ? "Yes" : "No");
+ t.writeNewline();
+}
+
+
+static void var(TextOutput& t, const std::string& name, const int val) {
+ t.writeSymbols(name,"=");
+ t.writeNumber(val);
+ t.writeNewline();
+}
+
+void System::describeSystem(
+ std::string& s) {
+
+ TextOutput t;
+ describeSystem(t);
+ t.commitString(s);
+}
+
+void System::describeSystem(
+ TextOutput& t) {
+
+ t.writeSymbols("App", "{");
+ t.writeNewline();
+ t.pushIndent();
+ var(t, "Name", System::currentProgramFilename());
+ char cwd[1024];
+ getcwd(cwd, 1024);
+ var(t, "cwd", std::string(cwd));
+ t.popIndent();
+ t.writeSymbols("}");
+ t.writeNewline();
+ t.writeNewline();
+
+ t.writeSymbols("OS", "{");
+ t.writeNewline();
+ t.pushIndent();
+ var(t, "Name", System::operatingSystem());
+ t.popIndent();
+ t.writeSymbols("}");
+ t.writeNewline();
+ t.writeNewline();
+
+ t.writeSymbols("CPU", "{");
+ t.writeNewline();
+ t.pushIndent();
+ var(t, "Vendor", System::cpuVendor());
+ var(t, "Architecture", System::cpuArchitecture());
+ var(t, "hasCPUID", System::hasCPUID());
+ var(t, "hasMMX", System::hasMMX());
+ var(t, "hasSSE", System::hasSSE());
+ var(t, "hasSSE2", System::hasSSE2());
+ var(t, "hasSSE3", System::hasSSE3());
+ var(t, "has3DNow", System::has3DNow());
+ var(t, "hasRDTSC", System::hasRDTSC());
+ t.popIndent();
+ t.writeSymbols("}");
+ t.writeNewline();
+ t.writeNewline();
+
+ t.writeSymbols("G3D", "{");
+ t.writeNewline();
+ t.pushIndent();
+ var(t, "Link version", G3D_VER);
+ var(t, "Compile version", System::version());
+ t.popIndent();
+ t.writeSymbols("}");
+ t.writeNewline();
+ t.writeNewline();
+}
+
+
+int System::cpuSpeedMHz() {
+ return g_cpuInfo.m_cpuSpeed;
+}
+
+
+void System::setClipboardText(const std::string& s) {
+# ifdef G3D_WIN32
+ if (OpenClipboard(NULL)) {
+ HGLOBAL hMem = GlobalAlloc(GHND | GMEM_DDESHARE, s.size() + 1);
+ if (hMem) {
+ char *pMem = (char*)GlobalLock(hMem);
+ strcpy(pMem, s.c_str());
+ GlobalUnlock(hMem);
+
+ EmptyClipboard();
+ SetClipboardData(CF_TEXT, hMem);
+ }
+
+ CloseClipboard();
+ GlobalFree(hMem);
+ }
+# endif
+}
+
+
+std::string System::getClipboardText() {
+ std::string s;
+
+# ifdef G3D_WIN32
+ if (OpenClipboard(NULL)) {
+ HANDLE h = GetClipboardData(CF_TEXT);
+
+ if (h) {
+ char* temp = (char*)GlobalLock(h);
+ if (temp) {
+ s = temp;
+ }
+ temp = NULL;
+ GlobalUnlock(h);
+ }
+ CloseClipboard();
+ }
+# endif
+ return s;
+}
+
+
+std::string System::currentDateString() {
+ time_t t1;
+ ::time(&t1);
+ tm* t = localtime(&t1);
+ return format("%d-%02d-%02d", t->tm_year + 1900, t->tm_mon + 1, t->tm_mday);
+}
+
+
+} // namespace
diff --git a/externals/g3dlite/G3D.lib/source/TextInput.cpp b/externals/g3dlite/G3D.lib/source/TextInput.cpp
new file mode 100644
index 00000000000..d5dc14fb6a0
--- /dev/null
+++ b/externals/g3dlite/G3D.lib/source/TextInput.cpp
@@ -0,0 +1,988 @@
+/**
+ @file TextInput.cpp
+
+ @author Morgan McGuire, graphics3d.com
+
+ @cite Based on a lexer written by Aaron Orenstein.
+
+ @created 2001-11-27
+ @edited 2008-07-14
+ */
+
+#include "G3D/fileutils.h"
+#include "G3D/TextInput.h"
+#include "G3D/BinaryInput.h"
+#include "G3D/stringutils.h"
+
+#ifdef _MSC_VER
+# pragma warning (push)
+// conversion from 'int' to 'char', possible loss of data (TODO: fix underlying problems)
+# pragma warning (disable: 4244)
+#endif
+
+namespace G3D {
+
+double Token::number() const {
+ if (_type == NUMBER) {
+ std::string s = toLower(_string);
+ if (s == "-1.#ind00") {
+ return nan();
+ }
+
+ if (s == "1.#inf00") {
+ return inf();
+ }
+
+ if (s == "-1.#inf00") {
+ return -inf();
+ }
+
+ double n;
+ if ((_string.length() > 2) &&
+ (_string[0] == '0') &&
+ (_string[1] == 'x')) {
+ // Hex
+ uint32 i;
+ sscanf(_string.c_str(), "%x", &i);
+ n = i;
+ } else {
+ sscanf(_string.c_str(), "%lg", &n);
+ }
+ return n;
+ } else {
+ return 0.0;
+ }
+}
+
+
+TextInput::Settings::Settings ()
+ : cComments(true), cppComments(true), escapeSequencesInStrings(true),
+ otherCommentCharacter('\0'), otherCommentCharacter2('\0'),
+ signedNumbers(true), singleQuotedStrings(true), sourceFileName(),
+ startingLineNumberOffset(0), msvcSpecials(true), proofSymbols(false),
+ caseSensitive(true)
+{
+ trueSymbols.insert("true");
+ falseSymbols.insert("false");
+}
+
+
+Token TextInput::peek() {
+ if (stack.size() == 0) {
+ Token t = nextToken();
+ push(t);
+ }
+
+ return stack.front();
+}
+
+
+int TextInput::peekLineNumber() {
+ return peek().line();
+}
+
+
+int TextInput::peekCharacterNumber() {
+ return peek().character();
+}
+
+
+Token TextInput::read() {
+ if (stack.size() > 0) {
+ Token t = stack.front();
+ stack.pop_front();
+ return t;
+ } else {
+ return nextToken();
+ }
+}
+
+static void toUpper(Set<std::string>& set) {
+ Array<std::string> symbols;
+ set.getMembers(symbols);
+ set.clear();
+ for (int i = 0; i < symbols.size(); ++i) {
+ set.insert(toUpper(symbols[i]));
+ }
+}
+
+void TextInput::init() {
+ currentCharOffset = 0;
+ charNumber = 1;
+ lineNumber = 1 + options.startingLineNumberOffset;
+
+ if (! options.caseSensitive) {
+ // Convert true and false symbols to all uppercase for fast comparisons
+ toUpper(options.trueSymbols);
+ toUpper(options.falseSymbols);
+ }
+}
+
+
+void TextInput::push(const Token& t) {
+ stack.push_front(t);
+}
+
+
+bool TextInput::hasMore() {
+ return (peek()._type != Token::END);
+}
+
+
+int TextInput::eatInputChar() {
+ // Don't go off the end
+ if (currentCharOffset >= (unsigned int)buffer.length()) {
+ return EOF;
+ }
+
+ unsigned char c = buffer[currentCharOffset];
+ ++currentCharOffset;
+
+ // update lineNumber and charNumber to reflect the location of the *next*
+ // character which will be read.
+ //
+ // We update even for CR because the user is allowed to do arbitrarily
+ // stupid things, like put a bunch of literal CRs inside a quoted string.
+ //
+ // We eat all whitespace between tokens, so they should never see a
+ // lineNumber that points to a CR. However, if they have some kind of
+ // syntax error in a token that appears *after* a quoted string
+ // containing CRs, they should get a correct character number.
+
+ // TODO: http://sourceforge.net/tracker/index.php?func=detail&aid=1341266&group_id=76879&atid=548565
+
+ if (c == '\n') {
+ ++lineNumber;
+ charNumber = 1;
+ } else {
+ ++charNumber;
+ }
+
+ return c;
+}
+
+int TextInput::peekInputChar(unsigned int distance) {
+ // Don't go off the end
+ if ((currentCharOffset + distance) >= (unsigned int)buffer.length()) {
+ return EOF;
+ }
+
+ unsigned char c = buffer[currentCharOffset + distance];
+ return c;
+}
+
+
+Token TextInput::nextToken() {
+ Token t;
+
+ t._line = lineNumber;
+ t._character = charNumber;
+ t._type = Token::END;
+ t._extendedType = Token::END_TYPE;
+
+ int c = peekInputChar();
+ if (c == EOF) {
+ return t;
+ }
+
+ bool whitespaceDone = false;
+ while (! whitespaceDone) {
+ whitespaceDone = true;
+
+ // Consume whitespace
+ while (isWhiteSpace(c)) {
+ c = eatAndPeekInputChar();
+ }
+
+ int c2 = peekInputChar(1);
+ if ((options.cppComments && c == '/' && c2 == '/')
+ || (options.otherCommentCharacter != '\0'
+ && c == options.otherCommentCharacter)
+ || (options.otherCommentCharacter2 != '\0'
+ && c == options.otherCommentCharacter2)) {
+
+ // Single line comment, consume to newline or EOF.
+
+ do {
+ c = eatAndPeekInputChar();
+ } while (! isNewline(c) && c != EOF);
+
+ // There is whitespace after the comment (in particular, the
+ // newline that terminates the comment). There might also be
+ // whitespace at the start of the next line.
+ whitespaceDone = false;
+
+ } else if (options.cComments && (c == '/') && (c2 == '*')) {
+
+ // consume both start-comment chars, can't let the trailing one
+ // help close the comment.
+ eatInputChar();
+ eatInputChar();
+
+ // Multi-line comment, consume to end-marker or EOF.
+ c = peekInputChar();
+ c2 = peekInputChar(1);
+ while (! ((c == '*') && (c2 == '/')) && (c != EOF)) {
+ eatInputChar();
+ c = c2;
+ c2 = peekInputChar(1);
+ }
+ eatInputChar(); // eat closing '*'
+ eatInputChar(); // eat closing '/'
+
+ c = peekInputChar();
+
+ // May be whitespace after comment.
+ whitespaceDone = false;
+ }
+
+ } // while (! whitespaceDone)
+
+ t._line = lineNumber;
+ t._character = charNumber;
+
+ // handle EOF
+ if (c == EOF) {
+ return t;
+ }
+
+ // Extended ASCII parses as itself, except for EOF
+ if (c > 127 && c < 255) {
+ t._type = Token::SYMBOL;
+ t._extendedType = Token::SYMBOL_TYPE;
+ t._string = c;
+ c = eatAndPeekInputChar();
+ }
+
+
+ // Perform appropriate setup for a symbol (including setting up the token
+ // string to start with c), eat the input character, and overwrite
+ // 'c' with the peeked next input character.
+#define SETUP_SYMBOL(c) \
+ { \
+ t._type = Token::SYMBOL; \
+ t._extendedType = Token::SYMBOL_TYPE; \
+ t._string = c; \
+ c = eatAndPeekInputChar(); \
+ }
+
+ switch (c) {
+
+ case '@': // Simple symbols -> just themselves.
+ case '(':
+ case ')':
+ case ',':
+ case ';':
+ case '{':
+ case '}':
+ case '[':
+ case ']':
+ case '#':
+ case '$':
+ case '?':
+ case '%':
+ SETUP_SYMBOL(c);
+ return t;
+
+ case '-': // negative number, -, --, -=, or ->
+ SETUP_SYMBOL(c);
+
+ switch (c) {
+ case '>': // ->
+ case '-': // --
+ case '=': // -=
+ t._string += c;
+ eatInputChar();
+ return t;
+ }
+
+ if (options.signedNumbers
+ && (isDigit(c) || (c == '.' && isDigit(peekInputChar(1))))) {
+
+ // Negative number. 'c' is still the first digit, and is
+ // the next input char.
+
+ goto numLabel;
+ }
+
+ // plain -
+ return t;
+
+ case '+': // positive number, +, ++, or +=
+ SETUP_SYMBOL(c);
+
+ switch (c) {
+ case '+': // ++
+ case '=': // +=
+ t._string += c;
+ eatInputChar();
+ return t;
+ }
+
+ if (options.signedNumbers
+ && (isDigit(c) || (c == '.' && isDigit(peekInputChar(1))))) {
+
+ // Positive number. 'c' is still the first digit, and is
+ // the next input char.
+
+ goto numLabel;
+ }
+
+ return t;
+
+ case ':': // : or :: or ::> or ::= or := or :>
+ SETUP_SYMBOL(c);
+
+ if (c == ':') {
+ t._string += c;
+ eatInputChar();
+
+ if (options.proofSymbols) {
+ c = peekInputChar(0);
+
+ if ((c == '>') || (c == '=')) {
+ t._string += c;
+ eatInputChar();
+ }
+ }
+ }
+ else if (options.proofSymbols && (c == '=' || c == '>')) {
+ t._string += c;
+ eatInputChar();
+ }
+ return t;
+
+ case '=': // = or == or =>
+ SETUP_SYMBOL(c);
+
+ if (c == '=') {
+ t._string += c;
+ eatInputChar();
+ return t;
+ } else if (options.proofSymbols && (c == '>')) {
+ t._string += c;
+ eatInputChar();
+ return t;
+ }
+ return t;
+
+ case '*': // * or *=
+ case '/': // / or /=
+ case '!': // ! or !=
+ case '~': // ~ or ~=
+ case '^': // ^ or ^=
+ SETUP_SYMBOL(c);
+
+ if (c == '=') {
+ t._string += c;
+ eatInputChar();
+ return t;
+ }
+ return t;
+
+ case '>': // >, >>,or >=
+ case '<': // <<, <<, or <= or <- or <:
+ case '|': // ||, ||, or |= or |-
+ case '&': // &, &&, or &=
+ {
+ int orig_c = c;
+ SETUP_SYMBOL(c);
+
+ if ((c == '=') || (orig_c == c)) {
+ t._string += c;
+ eatInputChar();
+ return t;
+ } else if (options.proofSymbols) {
+ if ((orig_c == '<') && (c == '-')) {
+ t._string += c;
+ eatInputChar();
+ } else if ((orig_c == '|') && (c == '-')) {
+ t._string += c;
+ eatInputChar();
+ } else if ((orig_c == '<') && (c == ':')) {
+ t._string += c;
+
+ c = eatAndPeekInputChar();
+
+ if (c == ':') {
+ t._string += c;
+ eatInputChar();
+ }
+ }
+ }
+ }
+ return t;
+
+ case '\\': // backslash or escaped comment char.
+ SETUP_SYMBOL(c);
+
+ if ((options.otherCommentCharacter != '\0'
+ && c == options.otherCommentCharacter)
+ || (options.otherCommentCharacter2 != '\0'
+ && c == options.otherCommentCharacter2)) {
+
+ // escaped comment character. Return the raw comment
+ // char (no backslash).
+
+ t._string = c;
+ eatInputChar();
+ return t;
+ }
+ return t;
+
+ case '.': // number, ., .., or ...
+ if (isDigit(peekInputChar(1))) {
+ // We're parsing a float that began without a leading zero
+ goto numLabel;
+ }
+
+ SETUP_SYMBOL(c);
+
+ if (c == '.') { // .. or ...
+ t._string += c;
+ c = eatAndPeekInputChar();
+
+ if (c == '.') { // ...
+ t._string += c;
+ eatInputChar();
+ }
+ return t;
+ }
+
+ return t;
+
+ } // switch (c)
+
+#undef SETUP_SYMBOL
+
+numLabel:
+ if (isDigit(c) || (c == '.')) {
+
+ // A number. Note-- single dots have been
+ // parsed already, so a . indicates a number
+ // less than 1 in floating point form.
+
+ // [0-9]*(\.[0-9]) or [0-9]+ or 0x[0-9,A-F]+
+
+ if (t._string != "-") {
+ // If we picked up a leading "-" sign above, keep it,
+ // otherwise drop the string parsed thus far
+ t._string = "";
+ }
+ t._type = Token::NUMBER;
+ if (c == '.') {
+ t._extendedType = Token::FLOATING_POINT_TYPE;
+ } else {
+ t._extendedType = Token::INTEGER_TYPE;
+ }
+
+ if ((c == '0') && (peekInputChar(1) == 'x')) {
+ // Hex number
+ t._string += "0x";
+
+ // skip the 0x
+ eatInputChar();
+ eatInputChar();
+
+ c = peekInputChar();
+ while (isDigit(c) || ((c >= 'A') && (c <= 'F')) || ((c >= 'a') && (c <= 'f'))) {
+ t._string += c;
+ c = eatAndPeekInputChar();
+ }
+
+ } else {
+ // Non-hex number
+
+ // Read the part before the decimal.
+ while (isDigit(c)) {
+ t._string += c;
+ c = eatAndPeekInputChar();
+ }
+
+ // True if we are reading a floating-point special type
+ bool isSpecial = false;
+
+ // Read the decimal, if one exists
+ if (c == '.') {
+ t._extendedType = Token::FLOATING_POINT_TYPE;
+
+ // The '.' character was a decimal point, not the start of a
+ // method or range operator
+ t._string += c;
+ c = eatAndPeekInputChar();
+
+ // Floating point specials (msvc format only)
+ if (options.msvcSpecials && (c == '#')) {
+ isSpecial = true;
+ // We are reading a floating point special value
+ // of the form -1.#IND00, -1.#INF00, or 1.#INF00
+ c = eatAndPeekInputChar();
+ char test = c;
+ if (! options.caseSensitive) {
+ test = toupper(c);
+ }
+ if (test != 'I') {
+ throw BadMSVCSpecial
+ (
+ "Incorrect floating-point special (inf or nan) "
+ "format.",
+ t.line(), charNumber);
+ }
+ c = eatAndPeekInputChar();
+ test = c;
+ if (! options.caseSensitive) {
+ test = toupper(c);
+ }
+ if (test != 'N') {
+ throw BadMSVCSpecial
+ (
+ "Incorrect floating-point special (inf or nan) "
+ "format.",
+ t.line(), charNumber);
+ }
+ t._string += "#IN";
+ c = eatAndPeekInputChar();
+ test = c;
+ if (! options.caseSensitive) {
+ test = toupper(c);
+ }
+ if ((test != 'F') && (test != 'D')) {
+ throw BadMSVCSpecial
+ (
+ "Incorrect floating-point special (inf or nan) "
+ "format.",
+ t.line(), charNumber);
+ }
+ t._string += c;
+ for (int j = 0; j < 2; ++j) {
+ c = eatAndPeekInputChar();
+ if (c != '0') {
+ throw BadMSVCSpecial
+ (
+ "Incorrect floating-point special (inf or"
+ "nan) format.",
+ t.line(), charNumber);
+ }
+ t._string += (char)c;
+ }
+
+ } else {
+
+ // Read the part after the decimal
+ while (isDigit((char)c)) {
+ t._string += (char)c;
+ c = eatAndPeekInputChar();
+ }
+ }
+ }
+
+ if (! isSpecial && ((c == 'e') || (c == 'E'))) {
+ // Read exponent
+ t._extendedType = Token::FLOATING_POINT_TYPE;
+ t._string += c;
+
+ c = eatAndPeekInputChar();
+ if ((c == '-') || (c == '+')) {
+ t._string += c;
+ c = eatAndPeekInputChar();
+ }
+
+ while (isDigit(c)) {
+ t._string += c;
+ c = eatAndPeekInputChar();
+ }
+ }
+ }
+ return t;
+
+ } else if (isLetter(c) || (c == '_')) {
+ // Identifier or keyword
+ // [A-Za-z_][A-Za-z_0-9]*
+
+ t._type = Token::SYMBOL;
+ t._extendedType = Token::SYMBOL_TYPE;
+ t._string = "";
+ do {
+ t._string += c;
+ c = eatAndPeekInputChar();
+ } while (isLetter(c) || isDigit(c) || (c == '_'));
+
+ // See if this symbol is actually a boolean
+ if ((options.trueSymbols.size() > 0) || (options.falseSymbols.size() > 0)) {
+ std::string str = t._string;
+ if (! options.caseSensitive) {
+ str = toUpper(str);
+ }
+ if (options.trueSymbols.contains(str)) {
+ t._type = Token::BOOLEAN;
+ t._extendedType = Token::BOOLEAN_TYPE;
+ t._bool = true;
+ } else if (options.falseSymbols.contains(str)) {
+ t._type = Token::BOOLEAN;
+ t._extendedType = Token::BOOLEAN_TYPE;
+ t._bool = false;
+ }
+ }
+
+ return t;
+
+ } else if (c == '\"') {
+
+ // Discard the double-quote.
+ eatInputChar();
+
+ // Double quoted string
+ parseQuotedString('\"', t);
+ return t;
+
+ } else if (c == '\'') {
+
+ // Discard the single-quote.
+ eatInputChar();
+
+ if (options.singleQuotedStrings) {
+ // Single quoted string
+ parseQuotedString('\'', t);
+ } else {
+ t._string = c;
+ t._type = Token::SYMBOL;
+ t._extendedType = Token::SYMBOL_TYPE;
+ }
+ return t;
+
+ } // end of special case tokens
+
+ if (c == EOF) {
+ t._type = Token::END;
+ t._extendedType = Token::END_TYPE;
+ t._string = "";
+ return t;
+ }
+
+ // Some unknown token
+ debugAssertM(false,
+ format("Unrecognized token type beginning with character '%c' (ASCII %d)",
+ c, c));
+ return t;
+}
+
+
+void TextInput::parseQuotedString(unsigned char delimiter, Token& t) {
+
+ t._type = Token::STRING;
+
+ if (delimiter == '\'') {
+ t._extendedType = Token::SINGLE_QUOTED_TYPE;
+ } else {
+ t._extendedType = Token::DOUBLE_QUOTED_TYPE;
+ }
+
+ while (true) {
+ // We're definitely going to consume the next input char, so we get
+ // it right now. This makes the condition handling below a bit easier.
+ int c = eatInputChar();
+
+ if (c == EOF) {
+ // END inside a quoted string. (We finish the string.)
+ break;
+ }
+
+ if (options.escapeSequencesInStrings && (c == '\\')) {
+ // An escaped character. We're definitely going to consume it,
+ // so we get it (and consume it) now.
+
+ c = eatInputChar();
+
+ switch (c) {
+ case 'r':
+ t._string += '\r';
+ break;
+ case 'n':
+ t._string += '\n';
+ break;
+ case 't':
+ t._string += '\t';
+ break;
+ case '0':
+ t._string += '\0';
+ break;
+
+ case '\\':
+ case '\"':
+ case '\'':
+ t._string += (char)c;
+ break;
+
+ default:
+ if (((c == options.otherCommentCharacter) &&
+ (options.otherCommentCharacter != '\0')) ||
+ ((c == options.otherCommentCharacter2) &&
+ (options.otherCommentCharacter2 != '\0'))) {
+ t._string += c;
+ }
+ // otherwise, some illegal escape sequence; skip it.
+ break;
+
+ } // switch
+
+ } else if (c == delimiter) {
+ // End of the string. Already consumed the character.
+ break;
+ } else {
+ // All other chars, go on to the string. Already consumed the
+ // character.
+ t._string += (char)c;
+ }
+
+ }
+}
+
+bool TextInput::readBoolean() {
+ Token t(read());
+
+ if (t._type == Token::BOOLEAN) {
+ return t.boolean();
+ }
+
+ // Push initial token back, and throw an error. We intentionally
+ // indicate that the wrong type is the type of the initial token.
+ // Logically, the number started there.
+ push(t);
+ throw WrongTokenType(options.sourceFileName, t.line(), t.character(),
+ Token::BOOLEAN, t._type);
+}
+
+double TextInput::readNumber() {
+ Token t(read());
+
+ if (t._type == Token::NUMBER) {
+ return t.number();
+ }
+
+ // Even if signedNumbers is disabled, readNumber attempts to
+ // read a signed number, so we handle that case here.
+ if (! options.signedNumbers
+ && (t._type == Token::SYMBOL)
+ && ((t._string == "-")
+ || (t._string == "+"))) {
+
+ Token t2(read());
+
+ if ((t2._type == Token::NUMBER)
+ && (t2._character == t._character + 1)) {
+
+ if (t._string == "-") {
+ return -t2.number();
+ } else {
+ return t2.number();
+ }
+ }
+
+ // push back the second token.
+ push(t2);
+ }
+
+ // Push initial token back, and throw an error. We intentionally
+ // indicate that the wrong type is the type of the initial token.
+ // Logically, the number started there.
+ push(t);
+ throw WrongTokenType(options.sourceFileName, t.line(), t.character(),
+ Token::NUMBER, t._type);
+}
+
+
+Token TextInput::readStringToken() {
+ Token t(read());
+
+ if (t._type == Token::STRING) { // fast path
+ return t;
+ }
+
+ push(t);
+ throw WrongTokenType(options.sourceFileName, t.line(), t.character(),
+ Token::STRING, t._type);
+}
+
+std::string TextInput::readString() {
+ return readStringToken()._string;
+}
+
+void TextInput::readString(const std::string& s) {
+ Token t(readStringToken());
+
+ if (t._string == s) { // fast path
+ return;
+ }
+
+ push(t);
+ throw WrongString(options.sourceFileName, t.line(), t.character(),
+ s, t._string);
+}
+
+
+Token TextInput::readSymbolToken() {
+ Token t(read());
+
+ if (t._type == Token::SYMBOL) { // fast path
+ return t;
+ }
+
+ push(t);
+ throw WrongTokenType(options.sourceFileName, t.line(), t.character(),
+ Token::SYMBOL, t._type);
+}
+
+
+std::string TextInput::readSymbol() {
+ return readSymbolToken()._string;
+}
+
+void TextInput::readSymbol(const std::string& symbol) {
+ Token t(readSymbolToken());
+
+ if (t._string == symbol) { // fast path
+ return;
+ }
+
+ push(t);
+ throw WrongSymbol(options.sourceFileName, t.line(), t.character(),
+ symbol, t._string);
+}
+
+
+TextInput::TextInput(const std::string& filename, const Settings& opt) : options(opt) {
+ init();
+ std::string input = readWholeFile(filename);
+
+ if (options.sourceFileName.empty()) {
+ options.sourceFileName = filename;
+ }
+ int n = input.size();
+ buffer.resize(n);
+ System::memcpy(buffer.getCArray(), input.c_str(), n);
+}
+
+
+TextInput::TextInput(FS fs, const std::string& str, const Settings& opt) : options(opt) {
+ (void)fs;
+ init();
+ if (options.sourceFileName.empty()) {
+ if (str.length() < 14) {
+ options.sourceFileName = std::string("\"") + str + "\"";
+ } else {
+ options.sourceFileName = std::string("\"") + str.substr(0, 10) + "...\"";
+ }
+ }
+ buffer.resize(str.length()); // we don't bother copying trailing NUL.
+ System::memcpy(buffer.getCArray(), str.c_str(), buffer.size());
+}
+
+
+const std::string& TextInput::filename() const {
+ return options.sourceFileName;
+}
+
+///////////////////////////////////////////////////////////////////////////////////
+
+TextInput::TokenException::TokenException(
+ const std::string& src,
+ int ln,
+ int ch) : sourceFile(src), line(ln), character(ch) {
+
+ message = format("%s(%d) : ", sourceFile.c_str(), line);
+}
+
+///////////////////////////////////////////////////////////////////////////////////
+
+static const char* tokenTypeToString(Token::Type t) {
+ switch (t) {
+ case Token::SYMBOL:
+ return "Token::SYMBOL";
+ case Token::STRING:
+ return "Token::STRING";
+ case Token::NUMBER:
+ return "Token::NUMBER";
+ case Token::END:
+ return "Token::END";
+ default:
+ debugAssertM(false, "Fell through switch");
+ return "?";
+ }
+}
+
+TextInput::WrongTokenType::WrongTokenType(
+ const std::string& src,
+ int ln,
+ int ch,
+ Token::Type e,
+ Token::Type a) :
+ TokenException(src, ln, ch), expected(e), actual(a) {
+
+ message += format("Expected token of type %s, found type %s.",
+ tokenTypeToString(e), tokenTypeToString(a));
+}
+
+
+TextInput::BadMSVCSpecial::BadMSVCSpecial(
+ const std::string& src,
+ int ln,
+ int ch) :
+ TokenException(src, ln, ch) {
+}
+
+
+TextInput::WrongSymbol::WrongSymbol(
+ const std::string& src,
+ int ln,
+ int ch,
+ const std::string& e,
+ const std::string& a) :
+ TokenException(src, ln, ch), expected(e), actual(a) {
+
+ message += format("Expected symbol '%s', found symbol '%s'.",
+ e.c_str(), a.c_str());
+}
+
+
+TextInput::WrongString::WrongString(
+ const std::string& src,
+ int ln,
+ int ch,
+ const std::string& e,
+ const std::string& a) :
+ TokenException(src, ln, ch), expected(e), actual(a) {
+
+ message += format("Expected string '%s', found string '%s'.",
+ e.c_str(), a.c_str());
+}
+
+
+void deserialize(bool& b, TextInput& ti) {
+ b = ti.readSymbol() == "true";
+}
+
+
+void deserialize(int& b, TextInput& ti) {
+ b = iRound(ti.readNumber());
+}
+
+
+void deserialize(uint8& b, TextInput& ti) {
+ b = (uint8)iRound(ti.readNumber());
+}
+
+
+void deserialize(double& b, TextInput& ti) {
+ b = ti.readNumber();
+}
+
+
+void deserialize(float& b, TextInput& ti) {
+ b = (float)ti.readNumber();
+}
+
+} // namespace
+
+#ifdef _MSC_VER
+# pragma warning (pop)
+#endif
diff --git a/externals/g3dlite/G3D.lib/source/TextOutput.cpp b/externals/g3dlite/G3D.lib/source/TextOutput.cpp
new file mode 100644
index 00000000000..f7a40d9798b
--- /dev/null
+++ b/externals/g3dlite/G3D.lib/source/TextOutput.cpp
@@ -0,0 +1,452 @@
+/**
+ @file TextOutput.cpp
+
+ @maintainer Morgan McGuire, morgan@graphics3d.com
+ @created 2004-06-21
+ @edited 2006-08-14
+
+ Copyright 2000-2006, Morgan McGuire.
+ All rights reserved.
+ */
+
+#include "G3D/TextOutput.h"
+#include "G3D/Log.h"
+#include "G3D/fileutils.h"
+
+namespace G3D {
+
+TextOutput::TextOutput(const TextOutput::Settings& opt) :
+ startingNewLine(true),
+ currentColumn(0),
+ inDQuote(false),
+ filename(""),
+ indentLevel(0)
+{
+ setOptions(opt);
+}
+
+
+TextOutput::TextOutput(const std::string& fil, const TextOutput::Settings& opt) :
+ startingNewLine(true),
+ currentColumn(0),
+ inDQuote(false),
+ filename(fil),
+ indentLevel(0)
+{
+
+ setOptions(opt);
+}
+
+
+void TextOutput::setIndentLevel(int i) {
+ indentLevel = i;
+
+ // If there were more pops than pushes, don't let that take us below 0 indent.
+ // Don't ever indent more than the number of columns.
+ indentSpaces =
+ iClamp(option.spacesPerIndent * indentLevel,
+ 0,
+ option.numColumns - 1);
+}
+
+
+void TextOutput::setOptions(const Settings& _opt) {
+ option = _opt;
+
+ debugAssert(option.numColumns > 1);
+
+ setIndentLevel(indentLevel);
+
+ newline = (option.newlineStyle == Settings::NEWLINE_WINDOWS) ? "\r\n" : "\n";
+}
+
+
+void TextOutput::pushIndent() {
+ setIndentLevel(indentLevel + 1);
+}
+
+
+void TextOutput::popIndent() {
+ setIndentLevel(indentLevel - 1);
+}
+
+
+static std::string escape(const std::string& string) {
+ std::string result = "";
+
+ for (std::string::size_type i = 0; i < string.length(); ++i) {
+ char c = string.at(i);
+ switch (c) {
+ case '\0':
+ result += "\\0";
+ break;
+
+ case '\r':
+ result += "\\r";
+ break;
+
+ case '\n':
+ result += "\\n";
+ break;
+
+ case '\t':
+ result += "\\t";
+ break;
+
+ case '\\':
+ result += "\\\\";
+ break;
+
+ default:
+ result += c;
+ }
+ }
+
+ return result;
+}
+
+void TextOutput::writeString(const std::string& string) {
+ // Convert special characters to escape sequences
+ this->printf("\"%s\"", escape(string).c_str());
+}
+
+
+void TextOutput::writeBoolean(bool b) {
+ this->printf("%s ", b ? option.trueSymbol.c_str() : option.falseSymbol.c_str());
+}
+
+void TextOutput::writeNumber(double n) {
+ this->printf("%f ", n);
+}
+
+
+void TextOutput::writeNumber(int n) {
+ this->printf("%d ", n);
+}
+
+
+void TextOutput::writeSymbol(const std::string& string) {
+ if (string.size() > 0) {
+ // TODO: check for legal symbols?
+ this->printf("%s ", string.c_str());
+ }
+}
+
+void TextOutput::writeSymbols(
+ const std::string& a,
+ const std::string& b,
+ const std::string& c,
+ const std::string& d,
+ const std::string& e,
+ const std::string& f) {
+
+ writeSymbol(a);
+ writeSymbol(b);
+ writeSymbol(c);
+ writeSymbol(d);
+ writeSymbol(e);
+ writeSymbol(f);
+}
+
+
+void TextOutput::printf(const std::string formatString, ...) {
+ va_list argList;
+ va_start(argList, formatString);
+ this->vprintf(formatString.c_str(), argList);
+ va_end(argList);
+}
+
+
+void TextOutput::printf(const char* formatString, ...) {
+ va_list argList;
+ va_start(argList, formatString);
+ this->vprintf(formatString, argList);
+ va_end(argList);
+}
+
+
+void TextOutput::convertNewlines(const std::string& in, std::string& out) {
+ // TODO: can be significantly optimized in cases where
+ // single characters are copied in order by walking through
+ // the array and copying substrings as needed.
+
+ if (option.convertNewlines) {
+ out = "";
+ for (uint32 i = 0; i < in.size(); ++i) {
+ if (in[i] == '\n') {
+ // Unix newline
+ out += newline;
+ } else if ((in[i] == '\r') && (i + 1 < in.size()) && (in[i + 1] == '\n')) {
+ // Windows newline
+ out += newline;
+ ++i;
+ } else {
+ out += in[i];
+ }
+ }
+ } else {
+ out = in;
+ }
+}
+
+
+void TextOutput::writeNewline() {
+ for (uint32 i = 0; i < newline.size(); ++i) {
+ indentAppend(newline[i]);
+ }
+}
+
+
+void TextOutput::writeNewlines(int numLines) {
+ for (int i = 0; i < numLines; ++i) {
+ writeNewline();
+ }
+}
+
+
+void TextOutput::wordWrapIndentAppend(const std::string& str) {
+ // TODO: keep track of the last space character we saw so we don't
+ // have to always search.
+
+ if ((option.wordWrap == Settings::WRAP_NONE) ||
+ (currentColumn + (int)str.size() <= option.numColumns)) {
+ // No word-wrapping is needed
+
+ // Add one character at a time.
+ // TODO: optimize for strings without newlines to add multiple
+ // characters.
+ for (uint32 i = 0; i < str.size(); ++i) {
+ indentAppend(str[i]);
+ }
+ return;
+ }
+
+ // Number of columns to wrap against
+ int cols = option.numColumns - indentSpaces;
+
+ // Copy forward until we exceed the column size,
+ // and then back up and try to insert newlines as needed.
+ for (uint32 i = 0; i < str.size(); ++i) {
+
+ indentAppend(str[i]);
+ if ((str[i] == '\r') && (i + 1 < str.size()) && (str[i + 1] == '\n')) {
+ // \r\n, we need to hit the \n to enter word wrapping.
+ ++i;
+ indentAppend(str[i]);
+ }
+
+ if (currentColumn >= cols) {
+ debugAssertM(str[i] != '\n' && str[i] != '\r',
+ "Should never enter word-wrapping on a newline character");
+
+ // True when we're allowed to treat a space as a space.
+ bool unquotedSpace = option.allowWordWrapInsideDoubleQuotes || ! inDQuote;
+
+ // Cases:
+ //
+ // 1. Currently in a series of spaces that ends with a newline
+ // strip all spaces and let the newline
+ // flow through.
+ //
+ // 2. Currently in a series of spaces that does not end with a newline
+ // strip all spaces and replace them with single newline
+ //
+ // 3. Not in a series of spaces
+ // search backwards for a space, then execute case 2.
+
+ // Index of most recent space
+ uint32 lastSpace = data.size() - 1;
+
+ // How far back we had to look for a space
+ uint32 k = 0;
+ uint32 maxLookBackward = currentColumn - indentSpaces;
+
+ // Search backwards (from current character), looking for a space.
+ while ((k < maxLookBackward) &&
+ (lastSpace > 0) &&
+ (! ((data[lastSpace] == ' ') && unquotedSpace))) {
+ --lastSpace;
+ ++k;
+
+ if ((data[lastSpace] == '\"') && !option.allowWordWrapInsideDoubleQuotes) {
+ unquotedSpace = ! unquotedSpace;
+ }
+ }
+
+ if (k == maxLookBackward) {
+ // We couldn't find a series of spaces
+
+ if (option.wordWrap == Settings::WRAP_ALWAYS) {
+ // Strip the last character we wrote, force a newline,
+ // and replace the last character;
+ data.pop();
+ writeNewline();
+ indentAppend(str[i]);
+ } else {
+ // Must be Settings::WRAP_WITHOUT_BREAKING
+ //
+ // Don't write the newline; we'll come back to
+ // the word wrap code after writing another character
+ }
+ } else {
+ // We found a series of spaces. If they continue
+ // to the new string, strip spaces off both. Otherwise
+ // strip spaces from data only and insert a newline.
+
+ // Find the start of the spaces. firstSpace is the index of the
+ // first non-space, looking backwards from lastSpace.
+ uint32 firstSpace = lastSpace;
+ while ((k < maxLookBackward) &&
+ (firstSpace > 0) &&
+ (data[firstSpace] == ' ')) {
+ --firstSpace;
+ ++k;
+ }
+
+ if (k == maxLookBackward) {
+ ++firstSpace;
+ }
+
+ if (lastSpace == (uint32)data.size() - 1) {
+ // Spaces continued up to the new string
+ data.resize(firstSpace + 1);
+ writeNewline();
+
+ // Delete the spaces from the new string
+ while ((i < str.size() - 1) && (str[i + 1] == ' ')) {
+ ++i;
+ }
+ } else {
+ // Spaces were somewhere in the middle of the old string.
+ // replace them with a newline.
+
+ // Copy over the characters that should be saved
+ Array<char> temp;
+ for (uint32 j = lastSpace + 1; j < (uint32)data.size(); ++j) {
+ char c = data[j];
+
+ if (c == '\"') {
+ // Undo changes to quoting (they will be re-done
+ // when we paste these characters back on).
+ inDQuote = !inDQuote;
+ }
+ temp.append(c);
+ }
+
+ // Remove those characters and replace with a newline.
+ data.resize(firstSpace + 1);
+ writeNewline();
+
+ // Write them back
+ for (uint32 j = 0; j < (uint32)temp.size(); ++j) {
+ indentAppend(temp[j]);
+ }
+
+ // We are now free to continue adding from the
+ // new string, which may or may not begin with spaces.
+
+ } // if spaces included new string
+ } // if hit indent
+ } // if line exceeded
+ } // iterate over str
+}
+
+
+void TextOutput::indentAppend(char c) {
+
+ if (startingNewLine) {
+ for (int j = 0; j < indentSpaces; ++j) {
+ data.push(' ');
+ }
+ startingNewLine = false;
+ currentColumn = indentSpaces;
+ }
+
+ data.push(c);
+
+ // Don't increment the column count on return character
+ // newline is taken care of below.
+ if (c != '\r') {
+ ++currentColumn;
+ }
+
+ if (c == '\"') {
+ inDQuote = ! inDQuote;
+ }
+
+ startingNewLine = (c == '\n');
+ if (startingNewLine) {
+ currentColumn = 0;
+ }
+}
+
+
+void TextOutput::vprintf(const char* formatString, va_list argPtr) {
+ std::string str = vformat(formatString, argPtr);
+
+ std::string clean;
+ convertNewlines(str, clean);
+ wordWrapIndentAppend(clean);
+}
+
+
+void TextOutput::commit(bool flush) {
+ std::string p = filenamePath(filename);
+ if (! fileExists(p, false)) {
+ createDirectory(p);
+ }
+
+ FILE* f = fopen(filename.c_str(), "wb");
+ debugAssert(f);
+ fwrite(data.getCArray(), 1, data.size(), f);
+ if (flush) {
+ fflush(f);
+ }
+ fclose(f);
+}
+
+
+void TextOutput::commitString(std::string& out) {
+ // Null terminate
+ data.push('\0');
+ out = data.getCArray();
+ data.pop();
+}
+
+
+std::string TextOutput::commitString() {
+ std::string str;
+ commitString(str);
+ return str;
+}
+
+
+
+/////////////////////////////////////////////////////////////////////
+
+void serialize(const float& b, TextOutput& to) {
+ to.writeNumber(b);
+}
+
+
+void serialize(const bool& b, TextOutput& to) {
+ to.writeSymbol(b ? "true" : "false");
+}
+
+
+void serialize(const int& b, TextOutput& to) {
+ to.writeNumber(b);
+}
+
+
+void serialize(const uint8& b, TextOutput& to) {
+ to.writeNumber(b);
+}
+
+
+void serialize(const double& b, TextOutput& to) {
+ to.writeNumber(b);
+}
+
+
+}
diff --git a/externals/g3dlite/G3D.lib/source/ThreadSet.cpp b/externals/g3dlite/G3D.lib/source/ThreadSet.cpp
new file mode 100644
index 00000000000..59060892247
--- /dev/null
+++ b/externals/g3dlite/G3D.lib/source/ThreadSet.cpp
@@ -0,0 +1,147 @@
+#include "G3D/ThreadSet.h"
+
+namespace G3D {
+
+int ThreadSet::size() const {
+ ThreadSet* me = const_cast<ThreadSet*>(this);
+ me->m_lock.lock();
+ int s = m_thread.size();
+ me->m_lock.unlock();
+ return s;
+}
+
+
+int ThreadSet::numStarted() const {
+ ThreadSet* me = const_cast<ThreadSet*>(this);
+ me->m_lock.lock();
+ int count = 0;
+ for (int i = 0; i < m_thread.size(); ++i) {
+ if (m_thread[i]->started()) {
+ ++count;
+ }
+ }
+ me->m_lock.unlock();
+ return count;
+}
+
+
+void ThreadSet::start() const {
+ ThreadSet* me = const_cast<ThreadSet*>(this);
+ me->m_lock.lock();
+ for (int i = 0; i < m_thread.size(); ++i) {
+ if (! m_thread[i]->started()) {
+ m_thread[i]->start();
+ }
+ }
+ me->m_lock.unlock();
+}
+
+
+void ThreadSet::terminate() const {
+ ThreadSet* me = const_cast<ThreadSet*>(this);
+ me->m_lock.lock();
+ for (int i = 0; i < m_thread.size(); ++i) {
+ if (m_thread[i]->started()) {
+ m_thread[i]->terminate();
+ }
+ }
+ me->m_lock.unlock();
+}
+
+
+void ThreadSet::waitForCompletion() const {
+ ThreadSet* me = const_cast<ThreadSet*>(this);
+ me->m_lock.lock();
+ for (int i = 0; i < m_thread.size(); ++i) {
+ if (m_thread[i]->started()) {
+ m_thread[i]->waitForCompletion();
+ }
+ }
+ me->m_lock.unlock();
+}
+
+
+int ThreadSet::removeCompleted() {
+ m_lock.lock();
+ for (int i = 0; i < m_thread.size(); ++i) {
+ if (m_thread[i]->completed()) {
+ m_thread.fastRemove(i);
+ --i;
+ }
+ }
+
+ int s = m_thread.size();
+ m_lock.unlock();
+ return s;
+}
+
+
+void ThreadSet::clear() {
+ m_lock.lock();
+ m_thread.clear();
+ m_lock.unlock();
+}
+
+
+int ThreadSet::insert(const ThreadRef& t) {
+ m_lock.lock();
+ bool found = false;
+ for (int i = 0; i < m_thread.size() && ! found; ++i) {
+ found = (m_thread[i] == t);
+ }
+ if (! found) {
+ m_thread.append(t);
+ }
+ int s = m_thread.size();
+ m_lock.unlock();
+ return s;
+}
+
+
+bool ThreadSet::remove(const ThreadRef& t) {
+ m_lock.lock();
+ bool found = false;
+ for (int i = 0; i < m_thread.size() && ! found; ++i) {
+ found = (m_thread[i] == t);
+ if (found) {
+ m_thread.fastRemove(i);
+ }
+ }
+ m_lock.unlock();
+ return found;
+}
+
+
+bool ThreadSet::contains(const ThreadRef& t) const {
+ ThreadSet* me = const_cast<ThreadSet*>(this);
+ me->m_lock.lock();
+ bool found = false;
+ for (int i = 0; i < m_thread.size() && ! found; ++i) {
+ found = (m_thread[i] == t);
+ }
+ me->m_lock.unlock();
+ return found;
+}
+
+
+ThreadSet::Iterator ThreadSet::begin() {
+ return m_thread.begin();
+}
+
+
+ThreadSet::Iterator ThreadSet::end() {
+ return m_thread.end();
+}
+
+
+ThreadSet::ConstIterator ThreadSet::begin() const {
+ return m_thread.begin();
+}
+
+
+ThreadSet::ConstIterator ThreadSet::end() const {
+ return m_thread.end();
+}
+
+
+} // namespace G3D
diff --git a/externals/g3dlite/G3D.lib/source/Triangle.cpp b/externals/g3dlite/G3D.lib/source/Triangle.cpp
new file mode 100644
index 00000000000..ad264b1f72a
--- /dev/null
+++ b/externals/g3dlite/G3D.lib/source/Triangle.cpp
@@ -0,0 +1,135 @@
+/**
+ @file Triangle.cpp
+
+ @maintainer Morgan McGuire, graphics3d.com
+
+ @created 2001-04-06
+ @edited 2006-01-20
+
+ Copyright 2000-2006, Morgan McGuire.
+ All rights reserved.
+ */
+
+#include "G3D/platform.h"
+#include "G3D/Triangle.h"
+#include "G3D/Plane.h"
+#include "G3D/BinaryInput.h"
+#include "G3D/BinaryOutput.h"
+#include "G3D/debugAssert.h"
+#include "G3D/AABox.h"
+
+namespace G3D {
+
+
+void Triangle::init(const Vector3& v0, const Vector3& v1, const Vector3& v2) {
+
+ _plane = Plane(v0, v1, v2);
+ _vertex[0] = v0;
+ _vertex[1] = v1;
+ _vertex[2] = v2;
+
+ static int next[] = {1,2,0};
+
+ for (int i = 0; i < 3; ++i) {
+ const Vector3& e = _vertex[next[i]] - _vertex[i];
+ edgeMagnitude[i] = e.magnitude();
+
+ if (edgeMagnitude[i] == 0) {
+ edgeDirection[i] = Vector3::zero();
+ } else {
+ edgeDirection[i] = e / (float)edgeMagnitude[i];
+ }
+ }
+
+ _edge01 = _vertex[1] - _vertex[0];
+ _edge02 = _vertex[2] - _vertex[0];
+
+ _primaryAxis = _plane.normal().primaryAxis();
+ _area = 0.5f * edgeDirection[0].cross(edgeDirection[2]).magnitude() * (edgeMagnitude[0] * edgeMagnitude[2]);
+ //0.5f * (_vertex[1] - _vertex[0]).cross(_vertex[2] - _vertex[0]).dot(_plane.normal());
+}
+
+
+Triangle::Triangle() {
+ init(Vector3::zero(), Vector3::zero(), Vector3::zero());
+}
+
+
+Triangle::Triangle(const Vector3& v0, const Vector3& v1, const Vector3& v2) {
+ init(v0, v1, v2);
+}
+
+
+Triangle::~Triangle() {
+}
+
+
+Triangle::Triangle(class BinaryInput& b) {
+ deserialize(b);
+}
+
+
+void Triangle::serialize(class BinaryOutput& b) {
+ _vertex[0].serialize(b);
+ _vertex[1].serialize(b);
+ _vertex[2].serialize(b);
+}
+
+
+void Triangle::deserialize(class BinaryInput& b) {
+ _vertex[0].deserialize(b);
+ _vertex[1].deserialize(b);
+ _vertex[2].deserialize(b);
+ init(_vertex[0], _vertex[1], _vertex[2]);
+}
+
+
+float Triangle::area() const {
+ return _area;
+}
+
+
+const Vector3& Triangle::normal() const {
+ return _plane.normal();
+}
+
+
+const Plane& Triangle::plane() const {
+ return _plane;
+}
+
+
+Vector3 Triangle::center() const {
+ return (_vertex[0] + _vertex[1] + _vertex[2]) / 3.0;
+}
+
+Vector3 Triangle::randomPoint() const {
+ // Choose a random point in the parallelogram
+
+ float s = uniformRandom();
+ float t = uniformRandom();
+
+ if (t > 1.0f - s) {
+ // Outside the triangle; reflect about the
+ // diagonal of the parallelogram
+ t = 1.0f - t;
+ s = 1.0f - s;
+ }
+
+ return _edge01 * s + _edge02 * t + _vertex[0];
+}
+
+
+void Triangle::getBounds(AABox& out) const {
+ Vector3 lo = _vertex[0];
+ Vector3 hi = lo;
+
+ for (int i = 1; i < 3; ++i) {
+ lo = lo.min(_vertex[i]);
+ hi = hi.max(_vertex[i]);
+ }
+
+ out = AABox(lo, hi);
+}
+
+} // G3D
diff --git a/externals/g3dlite/G3D.lib/source/UprightFrame.cpp b/externals/g3dlite/G3D.lib/source/UprightFrame.cpp
new file mode 100644
index 00000000000..78b2c0bb0bb
--- /dev/null
+++ b/externals/g3dlite/G3D.lib/source/UprightFrame.cpp
@@ -0,0 +1,132 @@
+/**
+ @file UprightFrame.cpp
+ Box class
+
+ @maintainer Morgan McGuire, matrix@graphics3d.com
+
+ @created 2007-05-02
+ @edited 2007-05-05
+*/
+
+#include "G3D/UprightFrame.h"
+#include "G3D/BinaryInput.h"
+#include "G3D/BinaryOutput.h"
+
+namespace G3D {
+
+UprightFrame::UprightFrame(const CoordinateFrame& cframe) {
+ Vector3 look = cframe.lookVector();
+
+ yaw = G3D::pi() + atan2(look.x, look.z);
+ pitch = asin(look.y);
+
+ translation = cframe.translation;
+}
+
+
+CoordinateFrame UprightFrame::toCoordinateFrame() const {
+ CoordinateFrame cframe;
+
+ Matrix3 P(Matrix3::fromAxisAngle(Vector3::unitX(), pitch));
+ Matrix3 Y(Matrix3::fromAxisAngle(Vector3::unitY(), yaw));
+
+ cframe.rotation = Y * P;
+ cframe.translation = translation;
+
+ return cframe;
+}
+
+
+UprightFrame UprightFrame::operator+(const UprightFrame& other) const {
+ return UprightFrame(translation + other.translation, pitch + other.pitch, yaw + other.yaw);
+}
+
+
+UprightFrame UprightFrame::operator*(const float k) const {
+ return UprightFrame(translation * k, pitch * k, yaw * k);
+}
+
+
+void UprightFrame::unwrapYaw(UprightFrame* a, int N) {
+ // Use the first point to establish the wrapping convention
+ for (int i = 1; i < N; ++i) {
+ const float prev = a[i - 1].yaw;
+ float& cur = a[i].yaw;
+
+ // No two angles should be more than pi (i.e., 180-degrees) apart.
+ if (abs(cur - prev) > G3D::pi()) {
+ // These angles must have wrapped at zero, causing them
+ // to be interpolated the long way.
+
+ // Find canonical [0, 2pi] versions of these numbers
+ float p = wrap(prev, twoPi());
+ float c = wrap(cur, twoPi());
+
+ // Find the difference -pi < diff < pi between the current and previous values
+ float diff = c - p;
+ if (diff < -G3D::pi()) {
+ diff += twoPi();
+ } else if (diff > G3D::pi()) {
+ diff -= twoPi();
+ }
+
+ // Offset the current from the previous by the difference
+ // between them.
+ cur = prev + diff;
+ }
+ }
+}
+
+
+void UprightFrame::serialize(class BinaryOutput& b) const {
+ translation.serialize(b);
+ b.writeFloat32(pitch);
+ b.writeFloat32(yaw);
+}
+
+
+void UprightFrame::deserialize(class BinaryInput& b) {
+ translation.deserialize(b);
+ pitch = b.readFloat32();
+ yaw = b.readFloat32();
+}
+
+
+void UprightSpline::serialize(class BinaryOutput& b) const {
+ b.writeBool8(cyclic);
+
+ b.writeInt32(control.size());
+ for (int i = 0; i < control.size(); ++i) {
+ control[i].serialize(b);
+ }
+ b.writeInt32(time.size());
+ for (int i = 0; i < time.size(); ++i) {
+ b.writeFloat32(time[i]);
+ }
+}
+
+
+void UprightSpline::deserialize(class BinaryInput& b) {
+ cyclic = b.readBool8();
+
+ control.resize(b.readInt32());
+ for (int i = 0; i < control.size(); ++i) {
+ control[i].deserialize(b);
+ }
+
+ if (b.hasMore()) {
+ time.resize(b.readInt32());
+ for (int i = 0; i < time.size(); ++i) {
+ time[i] = b.readFloat32();
+ }
+ debugAssert(time.size() == control.size());
+ } else {
+ // Import legacy path
+ time.resize(control.size());
+ for (int i = 0; i < time.size(); ++i) {
+ time[i] = i;
+ }
+ }
+}
+
+}
diff --git a/externals/g3dlite/G3D.lib/source/Vector2.cpp b/externals/g3dlite/G3D.lib/source/Vector2.cpp
new file mode 100644
index 00000000000..6b7f96a764e
--- /dev/null
+++ b/externals/g3dlite/G3D.lib/source/Vector2.cpp
@@ -0,0 +1,211 @@
+/**
+ @file Vector2.cpp
+
+ 2D vector class, used for texture coordinates primarily.
+
+ @maintainer Morgan McGuire, matrix@graphics3d.com
+
+ @cite Portions based on Dave Eberly'x Magic Software Library
+ at http://www.magic-software.com
+
+ @created 2001-06-02
+ @edited 2006-01-16
+ */
+
+#include "G3D/platform.h"
+#include <stdlib.h>
+#include "G3D/Vector2.h"
+#include "G3D/g3dmath.h"
+#include "G3D/format.h"
+#include "G3D/BinaryInput.h"
+#include "G3D/BinaryOutput.h"
+#include "G3D/TextInput.h"
+#include "G3D/TextOutput.h"
+
+namespace G3D {
+
+const Vector2& Vector2::zero() {
+ static Vector2 v(0, 0);
+ return v;
+}
+
+const Vector2& Vector2::unitX() {
+ static Vector2 v(1, 0);
+ return v;
+}
+
+const Vector2& Vector2::unitY() {
+ static Vector2 v(0, 1);
+ return v;
+}
+
+const Vector2& Vector2::inf() {
+ static Vector2 v((float)G3D::inf(), (float)G3D::inf());
+ return v;
+}
+
+
+const Vector2& Vector2::nan() {
+ static Vector2 v((float)G3D::nan(), (float)G3D::nan());
+ return v;
+}
+
+
+const Vector2& Vector2::minFinite() {
+ static Vector2 v(-FLT_MAX, -FLT_MAX);
+ return v;
+}
+
+
+const Vector2& Vector2::maxFinite() {
+ static Vector2 v(FLT_MAX, FLT_MAX);
+ return v;
+}
+
+
+size_t Vector2::hashCode() const {
+ unsigned int xhash = (*(int*)(void*)(&x));
+ unsigned int yhash = (*(int*)(void*)(&y));
+
+ return xhash + (yhash * 37);
+}
+
+
+Vector2::Vector2(BinaryInput& b) {
+ deserialize(b);
+}
+
+
+void Vector2::deserialize(BinaryInput& b) {
+ x = b.readFloat32();
+ y = b.readFloat32();
+}
+
+
+void Vector2::serialize(BinaryOutput& b) const {
+ b.writeFloat32(x);
+ b.writeFloat32(y);
+}
+
+
+void Vector2::deserialize(TextInput& t) {
+ t.readSymbol("(");
+ x = (float)t.readNumber();
+ t.readSymbol(",");
+ y = (float)t.readNumber();
+ t.readSymbol(")");
+}
+
+
+void Vector2::serialize(TextOutput& t) const {
+ t.writeSymbol("(");
+ t.writeNumber(x);
+ t.writeSymbol(",");
+ t.writeNumber(y);
+ t.writeSymbol(")");
+}
+
+//----------------------------------------------------------------------------
+
+Vector2 Vector2::random() {
+ Vector2 result;
+
+ do {
+ result = Vector2(uniformRandom(-1, 1), uniformRandom(-1, 1));
+
+ } while (result.squaredLength() >= 1.0f);
+
+ result.unitize();
+
+ return result;
+}
+
+//----------------------------------------------------------------------------
+Vector2 Vector2::operator/ (float fScalar) const {
+ Vector2 kQuot;
+
+ if ( fScalar != 0.0f ) {
+ float fInvScalar = 1.0f / fScalar;
+ kQuot.x = fInvScalar * x;
+ kQuot.y = fInvScalar * y;
+ return kQuot;
+ } else {
+ return Vector2::inf();
+ }
+}
+
+//----------------------------------------------------------------------------
+Vector2& Vector2::operator/= (float fScalar) {
+ if (fScalar != 0.0f) {
+ float fInvScalar = 1.0f / fScalar;
+ x *= fInvScalar;
+ y *= fInvScalar;
+ } else {
+ x = (float)G3D::inf();
+ y = (float)G3D::inf();
+ }
+
+ return *this;
+}
+
+//----------------------------------------------------------------------------
+float Vector2::unitize (float fTolerance) {
+ float fLength = length();
+
+ if (fLength > fTolerance) {
+ float fInvLength = 1.0f / fLength;
+ x *= fInvLength;
+ y *= fInvLength;
+ } else {
+ fLength = 0.0;
+ }
+
+ return fLength;
+}
+
+//----------------------------------------------------------------------------
+
+std::string Vector2::toString() const {
+ return G3D::format("(%g, %g)", x, y);
+}
+
+// 2-char swizzles
+
+Vector2 Vector2::xx() const { return Vector2 (x, x); }
+Vector2 Vector2::yx() const { return Vector2 (y, x); }
+Vector2 Vector2::xy() const { return Vector2 (x, y); }
+Vector2 Vector2::yy() const { return Vector2 (y, y); }
+
+// 3-char swizzles
+
+Vector3 Vector2::xxx() const { return Vector3 (x, x, x); }
+Vector3 Vector2::yxx() const { return Vector3 (y, x, x); }
+Vector3 Vector2::xyx() const { return Vector3 (x, y, x); }
+Vector3 Vector2::yyx() const { return Vector3 (y, y, x); }
+Vector3 Vector2::xxy() const { return Vector3 (x, x, y); }
+Vector3 Vector2::yxy() const { return Vector3 (y, x, y); }
+Vector3 Vector2::xyy() const { return Vector3 (x, y, y); }
+Vector3 Vector2::yyy() const { return Vector3 (y, y, y); }
+
+// 4-char swizzles
+
+Vector4 Vector2::xxxx() const { return Vector4 (x, x, x, x); }
+Vector4 Vector2::yxxx() const { return Vector4 (y, x, x, x); }
+Vector4 Vector2::xyxx() const { return Vector4 (x, y, x, x); }
+Vector4 Vector2::yyxx() const { return Vector4 (y, y, x, x); }
+Vector4 Vector2::xxyx() const { return Vector4 (x, x, y, x); }
+Vector4 Vector2::yxyx() const { return Vector4 (y, x, y, x); }
+Vector4 Vector2::xyyx() const { return Vector4 (x, y, y, x); }
+Vector4 Vector2::yyyx() const { return Vector4 (y, y, y, x); }
+Vector4 Vector2::xxxy() const { return Vector4 (x, x, x, y); }
+Vector4 Vector2::yxxy() const { return Vector4 (y, x, x, y); }
+Vector4 Vector2::xyxy() const { return Vector4 (x, y, x, y); }
+Vector4 Vector2::yyxy() const { return Vector4 (y, y, x, y); }
+Vector4 Vector2::xxyy() const { return Vector4 (x, x, y, y); }
+Vector4 Vector2::yxyy() const { return Vector4 (y, x, y, y); }
+Vector4 Vector2::xyyy() const { return Vector4 (x, y, y, y); }
+Vector4 Vector2::yyyy() const { return Vector4 (y, y, y, y); }
+
+
+
+} // namespace
diff --git a/externals/g3dlite/G3D.lib/source/Vector2int16.cpp b/externals/g3dlite/G3D.lib/source/Vector2int16.cpp
new file mode 100644
index 00000000000..04efee2b4b7
--- /dev/null
+++ b/externals/g3dlite/G3D.lib/source/Vector2int16.cpp
@@ -0,0 +1,47 @@
+/**
+ @file Vector2int16.cpp
+
+ @author Morgan McGuire, matrix@graphics3d.com
+
+ @created 2003-08-09
+ @edited 2006-01-29
+ */
+
+#include "G3D/platform.h"
+#include "G3D/g3dmath.h"
+#include "G3D/Vector2int16.h"
+#include "G3D/Vector2.h"
+#include "G3D/BinaryInput.h"
+#include "G3D/BinaryOutput.h"
+
+namespace G3D {
+
+Vector2int16::Vector2int16(const class Vector2& v) {
+ x = (int16)iFloor(v.x + 0.5);
+ y = (int16)iFloor(v.y + 0.5);
+}
+
+
+Vector2int16::Vector2int16(class BinaryInput& bi) {
+ deserialize(bi);
+}
+
+
+void Vector2int16::serialize(class BinaryOutput& bo) const {
+ bo.writeInt16(x);
+ bo.writeInt16(y);
+}
+
+
+void Vector2int16::deserialize(class BinaryInput& bi) {
+ x = bi.readInt16();
+ y = bi.readInt16();
+}
+
+
+Vector2int16 Vector2int16::clamp(const Vector2int16& lo, const Vector2int16& hi) {
+ return Vector2int16(iClamp(x, lo.x, hi.x), iClamp(y, lo.y, hi.y));
+}
+
+
+}
diff --git a/externals/g3dlite/G3D.lib/source/Vector3.cpp b/externals/g3dlite/G3D.lib/source/Vector3.cpp
new file mode 100644
index 00000000000..bc52640d297
--- /dev/null
+++ b/externals/g3dlite/G3D.lib/source/Vector3.cpp
@@ -0,0 +1,493 @@
+/**
+ @file Vector3.cpp
+
+ 3D vector class
+
+ @maintainer Morgan McGuire, matrix@graphics3d.com
+
+ @cite Portions based on Dave Eberly's Magic Software Library at http://www.magic-software.com
+
+ @created 2001-06-02
+ @edited 2006-01-30
+ */
+
+#include <limits>
+#include <stdlib.h>
+#include "G3D/Vector3.h"
+#include "G3D/g3dmath.h"
+#include "G3D/stringutils.h"
+#include "G3D/BinaryInput.h"
+#include "G3D/BinaryOutput.h"
+#include "G3D/TextInput.h"
+#include "G3D/TextOutput.h"
+#include "G3D/Vector3int16.h"
+#include "G3D/Matrix3.h"
+#include "G3D/Vector2.h"
+#include "G3D/Vector4int8.h"
+
+namespace G3D {
+
+Vector3 Vector3::dummy;
+
+
+Vector3::Vector3(const Vector4int8& v) : x(v.x / 127.0f), y(v.y / 127.0f), z(v.z / 127.0f) {}
+
+Vector3::Vector3(const class Vector2& v, float _z) : x(v.x), y(v.y), z(_z) {
+}
+
+
+Vector3::Axis Vector3::primaryAxis() const {
+
+ Axis a = X_AXIS;
+
+ double nx = abs(x);
+ double ny = abs(y);
+ double nz = abs(z);
+
+ if (nx > ny) {
+ if (nx > nz) {
+ a = X_AXIS;
+ } else {
+ a = Z_AXIS;
+ }
+ } else {
+ if (ny > nz) {
+ a = Y_AXIS;
+ } else {
+ a = Z_AXIS;
+ }
+ }
+
+ return a;
+}
+
+
+size_t Vector3::hashCode() const {
+ unsigned int xhash = (*(int*)(void*)(&x));
+ unsigned int yhash = (*(int*)(void*)(&y));
+ unsigned int zhash = (*(int*)(void*)(&z));
+
+ return xhash + (yhash * 37) + (zhash * 101);
+}
+
+std::ostream& operator<<(std::ostream& os, const Vector3& v) {
+ return os << v.toString();
+}
+
+
+//----------------------------------------------------------------------------
+
+double frand() {
+ return rand() / (double) RAND_MAX;
+}
+
+Vector3::Vector3(TextInput& t) {
+ deserialize(t);
+}
+
+Vector3::Vector3(BinaryInput& b) {
+ deserialize(b);
+}
+
+
+Vector3::Vector3(const class Vector3int16& v) {
+ x = v.x;
+ y = v.y;
+ z = v.z;
+}
+
+
+void Vector3::deserialize(BinaryInput& b) {
+ x = b.readFloat32();
+ y = b.readFloat32();
+ z = b.readFloat32();
+}
+
+
+void Vector3::deserialize(TextInput& t) {
+ t.readSymbol("(");
+ x = (float)t.readNumber();
+ t.readSymbol(",");
+ y = (float)t.readNumber();
+ t.readSymbol(",");
+ z = (float)t.readNumber();
+ t.readSymbol(")");
+}
+
+
+void Vector3::serialize(TextOutput& t) const {
+ t.writeSymbol("(");
+ t.writeNumber(x);
+ t.writeSymbol(",");
+ t.writeNumber(y);
+ t.writeSymbol(",");
+ t.writeNumber(z);
+ t.writeSymbol(")");
+}
+
+
+void Vector3::serialize(BinaryOutput& b) const {
+ b.writeFloat32(x);
+ b.writeFloat32(y);
+ b.writeFloat32(z);
+}
+
+
+Vector3 Vector3::random() {
+ Vector3 result;
+
+ do {
+ result = Vector3(uniformRandom(-1.0, 1.0),
+ uniformRandom(-1.0, 1.0),
+ uniformRandom(-1.0, 1.0));
+ } while (result.squaredMagnitude() >= 1.0f);
+
+ result.unitize();
+
+ return result;
+}
+
+//----------------------------------------------------------------------------
+Vector3 Vector3::operator/ (float fScalar) const {
+ Vector3 kQuot;
+
+ if ( fScalar != 0.0 ) {
+ float fInvScalar = 1.0f / fScalar;
+ kQuot.x = fInvScalar * x;
+ kQuot.y = fInvScalar * y;
+ kQuot.z = fInvScalar * z;
+ return kQuot;
+ } else {
+ return Vector3::inf();
+ }
+}
+
+//----------------------------------------------------------------------------
+Vector3& Vector3::operator/= (float fScalar) {
+ if (fScalar != 0.0) {
+ float fInvScalar = 1.0f / fScalar;
+ x *= fInvScalar;
+ y *= fInvScalar;
+ z *= fInvScalar;
+ } else {
+ x = (float)G3D::inf();
+ y = (float)G3D::inf();
+ z = (float)G3D::inf();
+ }
+
+ return *this;
+}
+
+//----------------------------------------------------------------------------
+float Vector3::unitize (float fTolerance) {
+ float fMagnitude = magnitude();
+
+ if (fMagnitude > fTolerance) {
+ float fInvMagnitude = 1.0f / fMagnitude;
+ x *= fInvMagnitude;
+ y *= fInvMagnitude;
+ z *= fInvMagnitude;
+ } else {
+ fMagnitude = 0.0f;
+ }
+
+ return fMagnitude;
+}
+
+//----------------------------------------------------------------------------
+
+Vector3 Vector3::reflectAbout(const Vector3& normal) const {
+
+ Vector3 out;
+
+ Vector3 N = normal.direction();
+
+ // 2 * normal.dot(this) * normal - this
+ return N * 2 * this->dot(N) - *this;
+}
+
+//----------------------------------------------------------------------------
+Vector3 Vector3::cosRandom(const Vector3& normal) {
+ double e1 = G3D::uniformRandom(0, 1);
+ double e2 = G3D::uniformRandom(0, 1);
+
+ // Angle from normal
+ double theta = acos(sqrt(e1));
+
+ // Angle about normal
+ double phi = 2 * pi() * e2;
+
+ // Make a coordinate system
+ Vector3 U = normal.direction();
+ Vector3 V = Vector3::unitX();
+
+ if (abs(U.dot(V)) > .9) {
+ V = Vector3::unitY();
+ }
+
+ Vector3 W = U.cross(V).direction();
+ V = W.cross(U);
+
+ // Convert to rectangular form
+ return cos(theta) * U + sin(theta) * (cos(phi) * V + sin(phi) * W);
+}
+//----------------------------------------------------------------------------
+
+Vector3 Vector3::hemiRandom(const Vector3& normal) {
+ Vector3 V = Vector3::random();
+
+ if (V.dot(normal) < 0) {
+ return -V;
+ } else {
+ return V;
+ }
+}
+
+//----------------------------------------------------------------------------
+
+Vector3 Vector3::reflectionDirection(const Vector3& normal) const {
+ return -reflectAbout(normal).direction();
+}
+
+//----------------------------------------------------------------------------
+
+Vector3 Vector3::refractionDirection(
+ const Vector3& normal,
+ float iInside,
+ float iOutside) const {
+
+ // From pg. 24 of Henrik Wann Jensen. Realistic Image Synthesis
+ // Using Photon Mapping. AK Peters. ISBN: 1568811470. July 2001.
+
+ // Invert the directions from Wann Jensen's formulation
+ // and normalize the vectors.
+ const Vector3 W = -direction();
+ Vector3 N = normal.direction();
+
+ float h1 = iOutside;
+ float h2 = iInside;
+
+ if (normal.dot(*this) > 0.0f) {
+ h1 = iInside;
+ h2 = iOutside;
+ N = -N;
+ }
+
+ const float hRatio = h1 / h2;
+ const float WdotN = W.dot(N);
+
+ float det = 1.0f - (float)square(hRatio) * (1.0f - (float)square(WdotN));
+
+ if (det < 0) {
+ // Total internal reflection
+ return Vector3::zero();
+ } else {
+ return -hRatio * (W - WdotN * N) - N * sqrt(det);
+ }
+}
+
+//----------------------------------------------------------------------------
+void Vector3::orthonormalize (Vector3 akVector[3]) {
+ // If the input vectors are v0, v1, and v2, then the Gram-Schmidt
+ // orthonormalization produces vectors u0, u1, and u2 as follows,
+ //
+ // u0 = v0/|v0|
+ // u1 = (v1-(u0*v1)u0)/|v1-(u0*v1)u0|
+ // u2 = (v2-(u0*v2)u0-(u1*v2)u1)/|v2-(u0*v2)u0-(u1*v2)u1|
+ //
+ // where |A| indicates length of vector A and A*B indicates dot
+ // product of vectors A and B.
+
+ // compute u0
+ akVector[0].unitize();
+
+ // compute u1
+ float fDot0 = akVector[0].dot(akVector[1]);
+ akVector[1] -= akVector[0] * fDot0;
+ akVector[1].unitize();
+
+ // compute u2
+ float fDot1 = akVector[1].dot(akVector[2]);
+ fDot0 = akVector[0].dot(akVector[2]);
+ akVector[2] -= akVector[0] * fDot0 + akVector[1] * fDot1;
+ akVector[2].unitize();
+}
+
+//----------------------------------------------------------------------------
+void Vector3::generateOrthonormalBasis (Vector3& rkU, Vector3& rkV,
+ Vector3& rkW, bool bUnitLengthW) {
+ if ( !bUnitLengthW )
+ rkW.unitize();
+
+ if ( G3D::abs(rkW.x) >= G3D::abs(rkW.y)
+ && G3D::abs(rkW.x) >= G3D::abs(rkW.z) ) {
+ rkU.x = -rkW.y;
+ rkU.y = + rkW.x;
+ rkU.z = 0.0;
+ } else {
+ rkU.x = 0.0;
+ rkU.y = + rkW.z;
+ rkU.z = -rkW.y;
+ }
+
+ rkU.unitize();
+ rkV = rkW.cross(rkU);
+}
+
+//----------------------------------------------------------------------------
+
+std::string Vector3::toString() const {
+ return G3D::format("(%g, %g, %g)", x, y, z);
+}
+
+
+//----------------------------------------------------------------------------
+
+Matrix3 Vector3::cross() const {
+ return Matrix3( 0, -z, y,
+ z, 0, -x,
+ -y, x, 0);
+}
+
+
+void serialize(const Vector3::Axis& a, class BinaryOutput& bo) {
+ bo.writeUInt8((uint8)a);
+}
+
+void deserialize(Vector3::Axis& a, class BinaryInput& bi) {
+ a = (Vector3::Axis)bi.readUInt8();
+}
+
+//----------------------------------------------------------------------------
+// 2-char swizzles
+
+Vector2 Vector3::xx() const { return Vector2 (x, x); }
+Vector2 Vector3::yx() const { return Vector2 (y, x); }
+Vector2 Vector3::zx() const { return Vector2 (z, x); }
+Vector2 Vector3::xy() const { return Vector2 (x, y); }
+Vector2 Vector3::yy() const { return Vector2 (y, y); }
+Vector2 Vector3::zy() const { return Vector2 (z, y); }
+Vector2 Vector3::xz() const { return Vector2 (x, z); }
+Vector2 Vector3::yz() const { return Vector2 (y, z); }
+Vector2 Vector3::zz() const { return Vector2 (z, z); }
+
+// 3-char swizzles
+
+Vector3 Vector3::xxx() const { return Vector3 (x, x, x); }
+Vector3 Vector3::yxx() const { return Vector3 (y, x, x); }
+Vector3 Vector3::zxx() const { return Vector3 (z, x, x); }
+Vector3 Vector3::xyx() const { return Vector3 (x, y, x); }
+Vector3 Vector3::yyx() const { return Vector3 (y, y, x); }
+Vector3 Vector3::zyx() const { return Vector3 (z, y, x); }
+Vector3 Vector3::xzx() const { return Vector3 (x, z, x); }
+Vector3 Vector3::yzx() const { return Vector3 (y, z, x); }
+Vector3 Vector3::zzx() const { return Vector3 (z, z, x); }
+Vector3 Vector3::xxy() const { return Vector3 (x, x, y); }
+Vector3 Vector3::yxy() const { return Vector3 (y, x, y); }
+Vector3 Vector3::zxy() const { return Vector3 (z, x, y); }
+Vector3 Vector3::xyy() const { return Vector3 (x, y, y); }
+Vector3 Vector3::yyy() const { return Vector3 (y, y, y); }
+Vector3 Vector3::zyy() const { return Vector3 (z, y, y); }
+Vector3 Vector3::xzy() const { return Vector3 (x, z, y); }
+Vector3 Vector3::yzy() const { return Vector3 (y, z, y); }
+Vector3 Vector3::zzy() const { return Vector3 (z, z, y); }
+Vector3 Vector3::xxz() const { return Vector3 (x, x, z); }
+Vector3 Vector3::yxz() const { return Vector3 (y, x, z); }
+Vector3 Vector3::zxz() const { return Vector3 (z, x, z); }
+Vector3 Vector3::xyz() const { return Vector3 (x, y, z); }
+Vector3 Vector3::yyz() const { return Vector3 (y, y, z); }
+Vector3 Vector3::zyz() const { return Vector3 (z, y, z); }
+Vector3 Vector3::xzz() const { return Vector3 (x, z, z); }
+Vector3 Vector3::yzz() const { return Vector3 (y, z, z); }
+Vector3 Vector3::zzz() const { return Vector3 (z, z, z); }
+
+// 4-char swizzles
+
+Vector4 Vector3::xxxx() const { return Vector4 (x, x, x, x); }
+Vector4 Vector3::yxxx() const { return Vector4 (y, x, x, x); }
+Vector4 Vector3::zxxx() const { return Vector4 (z, x, x, x); }
+Vector4 Vector3::xyxx() const { return Vector4 (x, y, x, x); }
+Vector4 Vector3::yyxx() const { return Vector4 (y, y, x, x); }
+Vector4 Vector3::zyxx() const { return Vector4 (z, y, x, x); }
+Vector4 Vector3::xzxx() const { return Vector4 (x, z, x, x); }
+Vector4 Vector3::yzxx() const { return Vector4 (y, z, x, x); }
+Vector4 Vector3::zzxx() const { return Vector4 (z, z, x, x); }
+Vector4 Vector3::xxyx() const { return Vector4 (x, x, y, x); }
+Vector4 Vector3::yxyx() const { return Vector4 (y, x, y, x); }
+Vector4 Vector3::zxyx() const { return Vector4 (z, x, y, x); }
+Vector4 Vector3::xyyx() const { return Vector4 (x, y, y, x); }
+Vector4 Vector3::yyyx() const { return Vector4 (y, y, y, x); }
+Vector4 Vector3::zyyx() const { return Vector4 (z, y, y, x); }
+Vector4 Vector3::xzyx() const { return Vector4 (x, z, y, x); }
+Vector4 Vector3::yzyx() const { return Vector4 (y, z, y, x); }
+Vector4 Vector3::zzyx() const { return Vector4 (z, z, y, x); }
+Vector4 Vector3::xxzx() const { return Vector4 (x, x, z, x); }
+Vector4 Vector3::yxzx() const { return Vector4 (y, x, z, x); }
+Vector4 Vector3::zxzx() const { return Vector4 (z, x, z, x); }
+Vector4 Vector3::xyzx() const { return Vector4 (x, y, z, x); }
+Vector4 Vector3::yyzx() const { return Vector4 (y, y, z, x); }
+Vector4 Vector3::zyzx() const { return Vector4 (z, y, z, x); }
+Vector4 Vector3::xzzx() const { return Vector4 (x, z, z, x); }
+Vector4 Vector3::yzzx() const { return Vector4 (y, z, z, x); }
+Vector4 Vector3::zzzx() const { return Vector4 (z, z, z, x); }
+Vector4 Vector3::xxxy() const { return Vector4 (x, x, x, y); }
+Vector4 Vector3::yxxy() const { return Vector4 (y, x, x, y); }
+Vector4 Vector3::zxxy() const { return Vector4 (z, x, x, y); }
+Vector4 Vector3::xyxy() const { return Vector4 (x, y, x, y); }
+Vector4 Vector3::yyxy() const { return Vector4 (y, y, x, y); }
+Vector4 Vector3::zyxy() const { return Vector4 (z, y, x, y); }
+Vector4 Vector3::xzxy() const { return Vector4 (x, z, x, y); }
+Vector4 Vector3::yzxy() const { return Vector4 (y, z, x, y); }
+Vector4 Vector3::zzxy() const { return Vector4 (z, z, x, y); }
+Vector4 Vector3::xxyy() const { return Vector4 (x, x, y, y); }
+Vector4 Vector3::yxyy() const { return Vector4 (y, x, y, y); }
+Vector4 Vector3::zxyy() const { return Vector4 (z, x, y, y); }
+Vector4 Vector3::xyyy() const { return Vector4 (x, y, y, y); }
+Vector4 Vector3::yyyy() const { return Vector4 (y, y, y, y); }
+Vector4 Vector3::zyyy() const { return Vector4 (z, y, y, y); }
+Vector4 Vector3::xzyy() const { return Vector4 (x, z, y, y); }
+Vector4 Vector3::yzyy() const { return Vector4 (y, z, y, y); }
+Vector4 Vector3::zzyy() const { return Vector4 (z, z, y, y); }
+Vector4 Vector3::xxzy() const { return Vector4 (x, x, z, y); }
+Vector4 Vector3::yxzy() const { return Vector4 (y, x, z, y); }
+Vector4 Vector3::zxzy() const { return Vector4 (z, x, z, y); }
+Vector4 Vector3::xyzy() const { return Vector4 (x, y, z, y); }
+Vector4 Vector3::yyzy() const { return Vector4 (y, y, z, y); }
+Vector4 Vector3::zyzy() const { return Vector4 (z, y, z, y); }
+Vector4 Vector3::xzzy() const { return Vector4 (x, z, z, y); }
+Vector4 Vector3::yzzy() const { return Vector4 (y, z, z, y); }
+Vector4 Vector3::zzzy() const { return Vector4 (z, z, z, y); }
+Vector4 Vector3::xxxz() const { return Vector4 (x, x, x, z); }
+Vector4 Vector3::yxxz() const { return Vector4 (y, x, x, z); }
+Vector4 Vector3::zxxz() const { return Vector4 (z, x, x, z); }
+Vector4 Vector3::xyxz() const { return Vector4 (x, y, x, z); }
+Vector4 Vector3::yyxz() const { return Vector4 (y, y, x, z); }
+Vector4 Vector3::zyxz() const { return Vector4 (z, y, x, z); }
+Vector4 Vector3::xzxz() const { return Vector4 (x, z, x, z); }
+Vector4 Vector3::yzxz() const { return Vector4 (y, z, x, z); }
+Vector4 Vector3::zzxz() const { return Vector4 (z, z, x, z); }
+Vector4 Vector3::xxyz() const { return Vector4 (x, x, y, z); }
+Vector4 Vector3::yxyz() const { return Vector4 (y, x, y, z); }
+Vector4 Vector3::zxyz() const { return Vector4 (z, x, y, z); }
+Vector4 Vector3::xyyz() const { return Vector4 (x, y, y, z); }
+Vector4 Vector3::yyyz() const { return Vector4 (y, y, y, z); }
+Vector4 Vector3::zyyz() const { return Vector4 (z, y, y, z); }
+Vector4 Vector3::xzyz() const { return Vector4 (x, z, y, z); }
+Vector4 Vector3::yzyz() const { return Vector4 (y, z, y, z); }
+Vector4 Vector3::zzyz() const { return Vector4 (z, z, y, z); }
+Vector4 Vector3::xxzz() const { return Vector4 (x, x, z, z); }
+Vector4 Vector3::yxzz() const { return Vector4 (y, x, z, z); }
+Vector4 Vector3::zxzz() const { return Vector4 (z, x, z, z); }
+Vector4 Vector3::xyzz() const { return Vector4 (x, y, z, z); }
+Vector4 Vector3::yyzz() const { return Vector4 (y, y, z, z); }
+Vector4 Vector3::zyzz() const { return Vector4 (z, y, z, z); }
+Vector4 Vector3::xzzz() const { return Vector4 (x, z, z, z); }
+Vector4 Vector3::yzzz() const { return Vector4 (y, z, z, z); }
+Vector4 Vector3::zzzz() const { return Vector4 (z, z, z, z); }
+
+
+
+
+
+
+} // namespace
diff --git a/externals/g3dlite/G3D.lib/source/Vector3int16.cpp b/externals/g3dlite/G3D.lib/source/Vector3int16.cpp
new file mode 100644
index 00000000000..8d8bb00bbef
--- /dev/null
+++ b/externals/g3dlite/G3D.lib/source/Vector3int16.cpp
@@ -0,0 +1,49 @@
+/**
+ @file Vector3int16.cpp
+
+ @author Morgan McGuire, matrix@graphics3d.com
+
+ @created 2003-04-07
+ @edited 2006-01-17
+ */
+
+#include "G3D/platform.h"
+#include "G3D/g3dmath.h"
+#include "G3D/Vector3int16.h"
+#include "G3D/Vector3.h"
+#include "G3D/BinaryInput.h"
+#include "G3D/BinaryOutput.h"
+#include "G3D/format.h"
+
+namespace G3D {
+
+Vector3int16::Vector3int16(const class Vector3& v) {
+ x = (int16)iFloor(v.x + 0.5);
+ y = (int16)iFloor(v.y + 0.5);
+ z = (int16)iFloor(v.z + 0.5);
+}
+
+
+Vector3int16::Vector3int16(class BinaryInput& bi) {
+ deserialize(bi);
+}
+
+
+void Vector3int16::serialize(class BinaryOutput& bo) const {
+ bo.writeInt16(x);
+ bo.writeInt16(y);
+ bo.writeInt16(z);
+}
+
+
+void Vector3int16::deserialize(class BinaryInput& bi) {
+ x = bi.readInt16();
+ y = bi.readInt16();
+ z = bi.readInt16();
+}
+
+std::string Vector3int16::toString() const {
+ return G3D::format("(%d, %d, %d)", x, y, z);
+}
+
+}
diff --git a/externals/g3dlite/G3D.lib/source/Vector3int32.cpp b/externals/g3dlite/G3D.lib/source/Vector3int32.cpp
new file mode 100644
index 00000000000..1328ba1aba5
--- /dev/null
+++ b/externals/g3dlite/G3D.lib/source/Vector3int32.cpp
@@ -0,0 +1,57 @@
+/**
+ @file Vector3int32.cpp
+
+ @author Morgan McGuire, matrix@graphics3d.com
+
+ @created 2008-07-01
+ @edited 2008-07-01
+ */
+
+#include "G3D/platform.h"
+#include "G3D/g3dmath.h"
+#include "G3D/Vector3int32.h"
+#include "G3D/Vector3int16.h"
+#include "G3D/Vector3.h"
+#include "G3D/BinaryInput.h"
+#include "G3D/BinaryOutput.h"
+#include "G3D/format.h"
+
+namespace G3D {
+
+Vector3int32::Vector3int32(const class Vector3& v) {
+ x = (int32)iFloor(v.x + 0.5);
+ y = (int32)iFloor(v.y + 0.5);
+ z = (int32)iFloor(v.z + 0.5);
+}
+
+
+Vector3int32::Vector3int32(const class Vector3int16& v) {
+ x = v.x;
+ y = v.y;
+ z = v.z;
+}
+
+
+Vector3int32::Vector3int32(class BinaryInput& bi) {
+ deserialize(bi);
+}
+
+
+void Vector3int32::serialize(class BinaryOutput& bo) const {
+ bo.writeInt32(x);
+ bo.writeInt32(y);
+ bo.writeInt32(z);
+}
+
+
+void Vector3int32::deserialize(class BinaryInput& bi) {
+ x = bi.readInt32();
+ y = bi.readInt32();
+ z = bi.readInt32();
+}
+
+std::string Vector3int32::toString() const {
+ return G3D::format("(%d, %d, %d)", x, y, z);
+}
+
+}
diff --git a/externals/g3dlite/G3D.lib/source/Vector4.cpp b/externals/g3dlite/G3D.lib/source/Vector4.cpp
new file mode 100644
index 00000000000..f562f2d6877
--- /dev/null
+++ b/externals/g3dlite/G3D.lib/source/Vector4.cpp
@@ -0,0 +1,475 @@
+/**
+ @file Vector4.cpp
+
+ @maintainer Morgan McGuire, matrix@graphics3d.com
+
+ @created 2001-07-09
+ @edited 2007-02-29
+ */
+
+#include <stdlib.h>
+#include <limits>
+#include "G3D/Vector4.h"
+#include "G3D/Color4.h"
+#include "G3D/g3dmath.h"
+#include "G3D/stringutils.h"
+#include "G3D/BinaryInput.h"
+#include "G3D/BinaryOutput.h"
+#include "G3D/Vector4int8.h"
+#include "G3D/Matrix4.h"
+
+namespace G3D {
+
+Vector4::Vector4(const Vector4int8& v) : x(v.x / 127.0f), y(v.y / 127.0f), z(v.z / 127.0f), w(v.w / 127.0f) {
+}
+
+size_t Vector4::hashCode() const {
+ unsigned int xhash = (*(int*)(void*)(&x));
+ unsigned int yhash = (*(int*)(void*)(&y));
+ unsigned int zhash = (*(int*)(void*)(&z));
+ unsigned int whash = (*(int*)(void*)(&w));
+
+ return xhash + (yhash * 37) + (zhash * 101) + (whash * 241);
+}
+
+
+Vector4::Vector4(const class Color4& c) {
+ x = c.r;
+ y = c.g;
+ z = c.b;
+ w = c.a;
+}
+
+
+Vector4::Vector4(const Vector2& v1, const Vector2& v2) {
+ x = v1.x;
+ y = v1.y;
+ z = v2.x;
+ w = v2.y;
+}
+
+
+Vector4::Vector4(const Vector2& v1, float fz, float fw) {
+ x = v1.x;
+ y = v1.y;
+ z = fz;
+ w = fw;
+}
+
+Vector4::Vector4(BinaryInput& b) {
+ deserialize(b);
+}
+
+
+void Vector4::deserialize(BinaryInput& b) {
+ x = b.readFloat32();
+ y = b.readFloat32();
+ z = b.readFloat32();
+ w = b.readFloat32();
+}
+
+
+void Vector4::serialize(BinaryOutput& b) const {
+ b.writeFloat32(x);
+ b.writeFloat32(y);
+ b.writeFloat32(z);
+ b.writeFloat32(w);
+}
+
+//----------------------------------------------------------------------------
+
+Vector4 Vector4::operator*(const Matrix4& M) const {
+ Vector4 result;
+ for (int i = 0; i < 4; ++i) {
+ result[i] = 0.0f;
+ for (int j = 0; j < 4; ++j) {
+ result[i] += (*this)[j] * M[j][i];
+ }
+ }
+ return result;
+}
+
+
+Vector4 Vector4::operator/ (float fScalar) const {
+ Vector4 kQuot;
+
+ if ( fScalar != 0.0 ) {
+ float fInvScalar = 1.0f / fScalar;
+ kQuot.x = fInvScalar * x;
+ kQuot.y = fInvScalar * y;
+ kQuot.z = fInvScalar * z;
+ kQuot.w = fInvScalar * w;
+ return kQuot;
+ } else {
+ return Vector4::inf();
+ }
+}
+
+//----------------------------------------------------------------------------
+Vector4& Vector4::operator/= (float fScalar) {
+ if (fScalar != 0.0f) {
+ float fInvScalar = 1.0f / fScalar;
+ x *= fInvScalar;
+ y *= fInvScalar;
+ z *= fInvScalar;
+ w *= fInvScalar;
+ } else {
+ *this = Vector4::inf();
+ }
+
+ return *this;
+}
+
+
+//----------------------------------------------------------------------------
+
+std::string Vector4::toString() const {
+ return G3D::format("(%g, %g, %g, %g)", x, y, z, w);
+}
+// 2-char swizzles
+
+Vector2 Vector4::xx() const { return Vector2 (x, x); }
+Vector2 Vector4::yx() const { return Vector2 (y, x); }
+Vector2 Vector4::zx() const { return Vector2 (z, x); }
+Vector2 Vector4::wx() const { return Vector2 (w, x); }
+Vector2 Vector4::xy() const { return Vector2 (x, y); }
+Vector2 Vector4::yy() const { return Vector2 (y, y); }
+Vector2 Vector4::zy() const { return Vector2 (z, y); }
+Vector2 Vector4::wy() const { return Vector2 (w, y); }
+Vector2 Vector4::xz() const { return Vector2 (x, z); }
+Vector2 Vector4::yz() const { return Vector2 (y, z); }
+Vector2 Vector4::zz() const { return Vector2 (z, z); }
+Vector2 Vector4::wz() const { return Vector2 (w, z); }
+Vector2 Vector4::xw() const { return Vector2 (x, w); }
+Vector2 Vector4::yw() const { return Vector2 (y, w); }
+Vector2 Vector4::zw() const { return Vector2 (z, w); }
+Vector2 Vector4::ww() const { return Vector2 (w, w); }
+
+// 3-char swizzles
+
+Vector3 Vector4::xxx() const { return Vector3 (x, x, x); }
+Vector3 Vector4::yxx() const { return Vector3 (y, x, x); }
+Vector3 Vector4::zxx() const { return Vector3 (z, x, x); }
+Vector3 Vector4::wxx() const { return Vector3 (w, x, x); }
+Vector3 Vector4::xyx() const { return Vector3 (x, y, x); }
+Vector3 Vector4::yyx() const { return Vector3 (y, y, x); }
+Vector3 Vector4::zyx() const { return Vector3 (z, y, x); }
+Vector3 Vector4::wyx() const { return Vector3 (w, y, x); }
+Vector3 Vector4::xzx() const { return Vector3 (x, z, x); }
+Vector3 Vector4::yzx() const { return Vector3 (y, z, x); }
+Vector3 Vector4::zzx() const { return Vector3 (z, z, x); }
+Vector3 Vector4::wzx() const { return Vector3 (w, z, x); }
+Vector3 Vector4::xwx() const { return Vector3 (x, w, x); }
+Vector3 Vector4::ywx() const { return Vector3 (y, w, x); }
+Vector3 Vector4::zwx() const { return Vector3 (z, w, x); }
+Vector3 Vector4::wwx() const { return Vector3 (w, w, x); }
+Vector3 Vector4::xxy() const { return Vector3 (x, x, y); }
+Vector3 Vector4::yxy() const { return Vector3 (y, x, y); }
+Vector3 Vector4::zxy() const { return Vector3 (z, x, y); }
+Vector3 Vector4::wxy() const { return Vector3 (w, x, y); }
+Vector3 Vector4::xyy() const { return Vector3 (x, y, y); }
+Vector3 Vector4::yyy() const { return Vector3 (y, y, y); }
+Vector3 Vector4::zyy() const { return Vector3 (z, y, y); }
+Vector3 Vector4::wyy() const { return Vector3 (w, y, y); }
+Vector3 Vector4::xzy() const { return Vector3 (x, z, y); }
+Vector3 Vector4::yzy() const { return Vector3 (y, z, y); }
+Vector3 Vector4::zzy() const { return Vector3 (z, z, y); }
+Vector3 Vector4::wzy() const { return Vector3 (w, z, y); }
+Vector3 Vector4::xwy() const { return Vector3 (x, w, y); }
+Vector3 Vector4::ywy() const { return Vector3 (y, w, y); }
+Vector3 Vector4::zwy() const { return Vector3 (z, w, y); }
+Vector3 Vector4::wwy() const { return Vector3 (w, w, y); }
+Vector3 Vector4::xxz() const { return Vector3 (x, x, z); }
+Vector3 Vector4::yxz() const { return Vector3 (y, x, z); }
+Vector3 Vector4::zxz() const { return Vector3 (z, x, z); }
+Vector3 Vector4::wxz() const { return Vector3 (w, x, z); }
+Vector3 Vector4::xyz() const { return Vector3 (x, y, z); }
+Vector3 Vector4::yyz() const { return Vector3 (y, y, z); }
+Vector3 Vector4::zyz() const { return Vector3 (z, y, z); }
+Vector3 Vector4::wyz() const { return Vector3 (w, y, z); }
+Vector3 Vector4::xzz() const { return Vector3 (x, z, z); }
+Vector3 Vector4::yzz() const { return Vector3 (y, z, z); }
+Vector3 Vector4::zzz() const { return Vector3 (z, z, z); }
+Vector3 Vector4::wzz() const { return Vector3 (w, z, z); }
+Vector3 Vector4::xwz() const { return Vector3 (x, w, z); }
+Vector3 Vector4::ywz() const { return Vector3 (y, w, z); }
+Vector3 Vector4::zwz() const { return Vector3 (z, w, z); }
+Vector3 Vector4::wwz() const { return Vector3 (w, w, z); }
+Vector3 Vector4::xxw() const { return Vector3 (x, x, w); }
+Vector3 Vector4::yxw() const { return Vector3 (y, x, w); }
+Vector3 Vector4::zxw() const { return Vector3 (z, x, w); }
+Vector3 Vector4::wxw() const { return Vector3 (w, x, w); }
+Vector3 Vector4::xyw() const { return Vector3 (x, y, w); }
+Vector3 Vector4::yyw() const { return Vector3 (y, y, w); }
+Vector3 Vector4::zyw() const { return Vector3 (z, y, w); }
+Vector3 Vector4::wyw() const { return Vector3 (w, y, w); }
+Vector3 Vector4::xzw() const { return Vector3 (x, z, w); }
+Vector3 Vector4::yzw() const { return Vector3 (y, z, w); }
+Vector3 Vector4::zzw() const { return Vector3 (z, z, w); }
+Vector3 Vector4::wzw() const { return Vector3 (w, z, w); }
+Vector3 Vector4::xww() const { return Vector3 (x, w, w); }
+Vector3 Vector4::yww() const { return Vector3 (y, w, w); }
+Vector3 Vector4::zww() const { return Vector3 (z, w, w); }
+Vector3 Vector4::www() const { return Vector3 (w, w, w); }
+
+// 4-char swizzles
+
+Vector4 Vector4::xxxx() const { return Vector4 (x, x, x, x); }
+Vector4 Vector4::yxxx() const { return Vector4 (y, x, x, x); }
+Vector4 Vector4::zxxx() const { return Vector4 (z, x, x, x); }
+Vector4 Vector4::wxxx() const { return Vector4 (w, x, x, x); }
+Vector4 Vector4::xyxx() const { return Vector4 (x, y, x, x); }
+Vector4 Vector4::yyxx() const { return Vector4 (y, y, x, x); }
+Vector4 Vector4::zyxx() const { return Vector4 (z, y, x, x); }
+Vector4 Vector4::wyxx() const { return Vector4 (w, y, x, x); }
+Vector4 Vector4::xzxx() const { return Vector4 (x, z, x, x); }
+Vector4 Vector4::yzxx() const { return Vector4 (y, z, x, x); }
+Vector4 Vector4::zzxx() const { return Vector4 (z, z, x, x); }
+Vector4 Vector4::wzxx() const { return Vector4 (w, z, x, x); }
+Vector4 Vector4::xwxx() const { return Vector4 (x, w, x, x); }
+Vector4 Vector4::ywxx() const { return Vector4 (y, w, x, x); }
+Vector4 Vector4::zwxx() const { return Vector4 (z, w, x, x); }
+Vector4 Vector4::wwxx() const { return Vector4 (w, w, x, x); }
+Vector4 Vector4::xxyx() const { return Vector4 (x, x, y, x); }
+Vector4 Vector4::yxyx() const { return Vector4 (y, x, y, x); }
+Vector4 Vector4::zxyx() const { return Vector4 (z, x, y, x); }
+Vector4 Vector4::wxyx() const { return Vector4 (w, x, y, x); }
+Vector4 Vector4::xyyx() const { return Vector4 (x, y, y, x); }
+Vector4 Vector4::yyyx() const { return Vector4 (y, y, y, x); }
+Vector4 Vector4::zyyx() const { return Vector4 (z, y, y, x); }
+Vector4 Vector4::wyyx() const { return Vector4 (w, y, y, x); }
+Vector4 Vector4::xzyx() const { return Vector4 (x, z, y, x); }
+Vector4 Vector4::yzyx() const { return Vector4 (y, z, y, x); }
+Vector4 Vector4::zzyx() const { return Vector4 (z, z, y, x); }
+Vector4 Vector4::wzyx() const { return Vector4 (w, z, y, x); }
+Vector4 Vector4::xwyx() const { return Vector4 (x, w, y, x); }
+Vector4 Vector4::ywyx() const { return Vector4 (y, w, y, x); }
+Vector4 Vector4::zwyx() const { return Vector4 (z, w, y, x); }
+Vector4 Vector4::wwyx() const { return Vector4 (w, w, y, x); }
+Vector4 Vector4::xxzx() const { return Vector4 (x, x, z, x); }
+Vector4 Vector4::yxzx() const { return Vector4 (y, x, z, x); }
+Vector4 Vector4::zxzx() const { return Vector4 (z, x, z, x); }
+Vector4 Vector4::wxzx() const { return Vector4 (w, x, z, x); }
+Vector4 Vector4::xyzx() const { return Vector4 (x, y, z, x); }
+Vector4 Vector4::yyzx() const { return Vector4 (y, y, z, x); }
+Vector4 Vector4::zyzx() const { return Vector4 (z, y, z, x); }
+Vector4 Vector4::wyzx() const { return Vector4 (w, y, z, x); }
+Vector4 Vector4::xzzx() const { return Vector4 (x, z, z, x); }
+Vector4 Vector4::yzzx() const { return Vector4 (y, z, z, x); }
+Vector4 Vector4::zzzx() const { return Vector4 (z, z, z, x); }
+Vector4 Vector4::wzzx() const { return Vector4 (w, z, z, x); }
+Vector4 Vector4::xwzx() const { return Vector4 (x, w, z, x); }
+Vector4 Vector4::ywzx() const { return Vector4 (y, w, z, x); }
+Vector4 Vector4::zwzx() const { return Vector4 (z, w, z, x); }
+Vector4 Vector4::wwzx() const { return Vector4 (w, w, z, x); }
+Vector4 Vector4::xxwx() const { return Vector4 (x, x, w, x); }
+Vector4 Vector4::yxwx() const { return Vector4 (y, x, w, x); }
+Vector4 Vector4::zxwx() const { return Vector4 (z, x, w, x); }
+Vector4 Vector4::wxwx() const { return Vector4 (w, x, w, x); }
+Vector4 Vector4::xywx() const { return Vector4 (x, y, w, x); }
+Vector4 Vector4::yywx() const { return Vector4 (y, y, w, x); }
+Vector4 Vector4::zywx() const { return Vector4 (z, y, w, x); }
+Vector4 Vector4::wywx() const { return Vector4 (w, y, w, x); }
+Vector4 Vector4::xzwx() const { return Vector4 (x, z, w, x); }
+Vector4 Vector4::yzwx() const { return Vector4 (y, z, w, x); }
+Vector4 Vector4::zzwx() const { return Vector4 (z, z, w, x); }
+Vector4 Vector4::wzwx() const { return Vector4 (w, z, w, x); }
+Vector4 Vector4::xwwx() const { return Vector4 (x, w, w, x); }
+Vector4 Vector4::ywwx() const { return Vector4 (y, w, w, x); }
+Vector4 Vector4::zwwx() const { return Vector4 (z, w, w, x); }
+Vector4 Vector4::wwwx() const { return Vector4 (w, w, w, x); }
+Vector4 Vector4::xxxy() const { return Vector4 (x, x, x, y); }
+Vector4 Vector4::yxxy() const { return Vector4 (y, x, x, y); }
+Vector4 Vector4::zxxy() const { return Vector4 (z, x, x, y); }
+Vector4 Vector4::wxxy() const { return Vector4 (w, x, x, y); }
+Vector4 Vector4::xyxy() const { return Vector4 (x, y, x, y); }
+Vector4 Vector4::yyxy() const { return Vector4 (y, y, x, y); }
+Vector4 Vector4::zyxy() const { return Vector4 (z, y, x, y); }
+Vector4 Vector4::wyxy() const { return Vector4 (w, y, x, y); }
+Vector4 Vector4::xzxy() const { return Vector4 (x, z, x, y); }
+Vector4 Vector4::yzxy() const { return Vector4 (y, z, x, y); }
+Vector4 Vector4::zzxy() const { return Vector4 (z, z, x, y); }
+Vector4 Vector4::wzxy() const { return Vector4 (w, z, x, y); }
+Vector4 Vector4::xwxy() const { return Vector4 (x, w, x, y); }
+Vector4 Vector4::ywxy() const { return Vector4 (y, w, x, y); }
+Vector4 Vector4::zwxy() const { return Vector4 (z, w, x, y); }
+Vector4 Vector4::wwxy() const { return Vector4 (w, w, x, y); }
+Vector4 Vector4::xxyy() const { return Vector4 (x, x, y, y); }
+Vector4 Vector4::yxyy() const { return Vector4 (y, x, y, y); }
+Vector4 Vector4::zxyy() const { return Vector4 (z, x, y, y); }
+Vector4 Vector4::wxyy() const { return Vector4 (w, x, y, y); }
+Vector4 Vector4::xyyy() const { return Vector4 (x, y, y, y); }
+Vector4 Vector4::yyyy() const { return Vector4 (y, y, y, y); }
+Vector4 Vector4::zyyy() const { return Vector4 (z, y, y, y); }
+Vector4 Vector4::wyyy() const { return Vector4 (w, y, y, y); }
+Vector4 Vector4::xzyy() const { return Vector4 (x, z, y, y); }
+Vector4 Vector4::yzyy() const { return Vector4 (y, z, y, y); }
+Vector4 Vector4::zzyy() const { return Vector4 (z, z, y, y); }
+Vector4 Vector4::wzyy() const { return Vector4 (w, z, y, y); }
+Vector4 Vector4::xwyy() const { return Vector4 (x, w, y, y); }
+Vector4 Vector4::ywyy() const { return Vector4 (y, w, y, y); }
+Vector4 Vector4::zwyy() const { return Vector4 (z, w, y, y); }
+Vector4 Vector4::wwyy() const { return Vector4 (w, w, y, y); }
+Vector4 Vector4::xxzy() const { return Vector4 (x, x, z, y); }
+Vector4 Vector4::yxzy() const { return Vector4 (y, x, z, y); }
+Vector4 Vector4::zxzy() const { return Vector4 (z, x, z, y); }
+Vector4 Vector4::wxzy() const { return Vector4 (w, x, z, y); }
+Vector4 Vector4::xyzy() const { return Vector4 (x, y, z, y); }
+Vector4 Vector4::yyzy() const { return Vector4 (y, y, z, y); }
+Vector4 Vector4::zyzy() const { return Vector4 (z, y, z, y); }
+Vector4 Vector4::wyzy() const { return Vector4 (w, y, z, y); }
+Vector4 Vector4::xzzy() const { return Vector4 (x, z, z, y); }
+Vector4 Vector4::yzzy() const { return Vector4 (y, z, z, y); }
+Vector4 Vector4::zzzy() const { return Vector4 (z, z, z, y); }
+Vector4 Vector4::wzzy() const { return Vector4 (w, z, z, y); }
+Vector4 Vector4::xwzy() const { return Vector4 (x, w, z, y); }
+Vector4 Vector4::ywzy() const { return Vector4 (y, w, z, y); }
+Vector4 Vector4::zwzy() const { return Vector4 (z, w, z, y); }
+Vector4 Vector4::wwzy() const { return Vector4 (w, w, z, y); }
+Vector4 Vector4::xxwy() const { return Vector4 (x, x, w, y); }
+Vector4 Vector4::yxwy() const { return Vector4 (y, x, w, y); }
+Vector4 Vector4::zxwy() const { return Vector4 (z, x, w, y); }
+Vector4 Vector4::wxwy() const { return Vector4 (w, x, w, y); }
+Vector4 Vector4::xywy() const { return Vector4 (x, y, w, y); }
+Vector4 Vector4::yywy() const { return Vector4 (y, y, w, y); }
+Vector4 Vector4::zywy() const { return Vector4 (z, y, w, y); }
+Vector4 Vector4::wywy() const { return Vector4 (w, y, w, y); }
+Vector4 Vector4::xzwy() const { return Vector4 (x, z, w, y); }
+Vector4 Vector4::yzwy() const { return Vector4 (y, z, w, y); }
+Vector4 Vector4::zzwy() const { return Vector4 (z, z, w, y); }
+Vector4 Vector4::wzwy() const { return Vector4 (w, z, w, y); }
+Vector4 Vector4::xwwy() const { return Vector4 (x, w, w, y); }
+Vector4 Vector4::ywwy() const { return Vector4 (y, w, w, y); }
+Vector4 Vector4::zwwy() const { return Vector4 (z, w, w, y); }
+Vector4 Vector4::wwwy() const { return Vector4 (w, w, w, y); }
+Vector4 Vector4::xxxz() const { return Vector4 (x, x, x, z); }
+Vector4 Vector4::yxxz() const { return Vector4 (y, x, x, z); }
+Vector4 Vector4::zxxz() const { return Vector4 (z, x, x, z); }
+Vector4 Vector4::wxxz() const { return Vector4 (w, x, x, z); }
+Vector4 Vector4::xyxz() const { return Vector4 (x, y, x, z); }
+Vector4 Vector4::yyxz() const { return Vector4 (y, y, x, z); }
+Vector4 Vector4::zyxz() const { return Vector4 (z, y, x, z); }
+Vector4 Vector4::wyxz() const { return Vector4 (w, y, x, z); }
+Vector4 Vector4::xzxz() const { return Vector4 (x, z, x, z); }
+Vector4 Vector4::yzxz() const { return Vector4 (y, z, x, z); }
+Vector4 Vector4::zzxz() const { return Vector4 (z, z, x, z); }
+Vector4 Vector4::wzxz() const { return Vector4 (w, z, x, z); }
+Vector4 Vector4::xwxz() const { return Vector4 (x, w, x, z); }
+Vector4 Vector4::ywxz() const { return Vector4 (y, w, x, z); }
+Vector4 Vector4::zwxz() const { return Vector4 (z, w, x, z); }
+Vector4 Vector4::wwxz() const { return Vector4 (w, w, x, z); }
+Vector4 Vector4::xxyz() const { return Vector4 (x, x, y, z); }
+Vector4 Vector4::yxyz() const { return Vector4 (y, x, y, z); }
+Vector4 Vector4::zxyz() const { return Vector4 (z, x, y, z); }
+Vector4 Vector4::wxyz() const { return Vector4 (w, x, y, z); }
+Vector4 Vector4::xyyz() const { return Vector4 (x, y, y, z); }
+Vector4 Vector4::yyyz() const { return Vector4 (y, y, y, z); }
+Vector4 Vector4::zyyz() const { return Vector4 (z, y, y, z); }
+Vector4 Vector4::wyyz() const { return Vector4 (w, y, y, z); }
+Vector4 Vector4::xzyz() const { return Vector4 (x, z, y, z); }
+Vector4 Vector4::yzyz() const { return Vector4 (y, z, y, z); }
+Vector4 Vector4::zzyz() const { return Vector4 (z, z, y, z); }
+Vector4 Vector4::wzyz() const { return Vector4 (w, z, y, z); }
+Vector4 Vector4::xwyz() const { return Vector4 (x, w, y, z); }
+Vector4 Vector4::ywyz() const { return Vector4 (y, w, y, z); }
+Vector4 Vector4::zwyz() const { return Vector4 (z, w, y, z); }
+Vector4 Vector4::wwyz() const { return Vector4 (w, w, y, z); }
+Vector4 Vector4::xxzz() const { return Vector4 (x, x, z, z); }
+Vector4 Vector4::yxzz() const { return Vector4 (y, x, z, z); }
+Vector4 Vector4::zxzz() const { return Vector4 (z, x, z, z); }
+Vector4 Vector4::wxzz() const { return Vector4 (w, x, z, z); }
+Vector4 Vector4::xyzz() const { return Vector4 (x, y, z, z); }
+Vector4 Vector4::yyzz() const { return Vector4 (y, y, z, z); }
+Vector4 Vector4::zyzz() const { return Vector4 (z, y, z, z); }
+Vector4 Vector4::wyzz() const { return Vector4 (w, y, z, z); }
+Vector4 Vector4::xzzz() const { return Vector4 (x, z, z, z); }
+Vector4 Vector4::yzzz() const { return Vector4 (y, z, z, z); }
+Vector4 Vector4::zzzz() const { return Vector4 (z, z, z, z); }
+Vector4 Vector4::wzzz() const { return Vector4 (w, z, z, z); }
+Vector4 Vector4::xwzz() const { return Vector4 (x, w, z, z); }
+Vector4 Vector4::ywzz() const { return Vector4 (y, w, z, z); }
+Vector4 Vector4::zwzz() const { return Vector4 (z, w, z, z); }
+Vector4 Vector4::wwzz() const { return Vector4 (w, w, z, z); }
+Vector4 Vector4::xxwz() const { return Vector4 (x, x, w, z); }
+Vector4 Vector4::yxwz() const { return Vector4 (y, x, w, z); }
+Vector4 Vector4::zxwz() const { return Vector4 (z, x, w, z); }
+Vector4 Vector4::wxwz() const { return Vector4 (w, x, w, z); }
+Vector4 Vector4::xywz() const { return Vector4 (x, y, w, z); }
+Vector4 Vector4::yywz() const { return Vector4 (y, y, w, z); }
+Vector4 Vector4::zywz() const { return Vector4 (z, y, w, z); }
+Vector4 Vector4::wywz() const { return Vector4 (w, y, w, z); }
+Vector4 Vector4::xzwz() const { return Vector4 (x, z, w, z); }
+Vector4 Vector4::yzwz() const { return Vector4 (y, z, w, z); }
+Vector4 Vector4::zzwz() const { return Vector4 (z, z, w, z); }
+Vector4 Vector4::wzwz() const { return Vector4 (w, z, w, z); }
+Vector4 Vector4::xwwz() const { return Vector4 (x, w, w, z); }
+Vector4 Vector4::ywwz() const { return Vector4 (y, w, w, z); }
+Vector4 Vector4::zwwz() const { return Vector4 (z, w, w, z); }
+Vector4 Vector4::wwwz() const { return Vector4 (w, w, w, z); }
+Vector4 Vector4::xxxw() const { return Vector4 (x, x, x, w); }
+Vector4 Vector4::yxxw() const { return Vector4 (y, x, x, w); }
+Vector4 Vector4::zxxw() const { return Vector4 (z, x, x, w); }
+Vector4 Vector4::wxxw() const { return Vector4 (w, x, x, w); }
+Vector4 Vector4::xyxw() const { return Vector4 (x, y, x, w); }
+Vector4 Vector4::yyxw() const { return Vector4 (y, y, x, w); }
+Vector4 Vector4::zyxw() const { return Vector4 (z, y, x, w); }
+Vector4 Vector4::wyxw() const { return Vector4 (w, y, x, w); }
+Vector4 Vector4::xzxw() const { return Vector4 (x, z, x, w); }
+Vector4 Vector4::yzxw() const { return Vector4 (y, z, x, w); }
+Vector4 Vector4::zzxw() const { return Vector4 (z, z, x, w); }
+Vector4 Vector4::wzxw() const { return Vector4 (w, z, x, w); }
+Vector4 Vector4::xwxw() const { return Vector4 (x, w, x, w); }
+Vector4 Vector4::ywxw() const { return Vector4 (y, w, x, w); }
+Vector4 Vector4::zwxw() const { return Vector4 (z, w, x, w); }
+Vector4 Vector4::wwxw() const { return Vector4 (w, w, x, w); }
+Vector4 Vector4::xxyw() const { return Vector4 (x, x, y, w); }
+Vector4 Vector4::yxyw() const { return Vector4 (y, x, y, w); }
+Vector4 Vector4::zxyw() const { return Vector4 (z, x, y, w); }
+Vector4 Vector4::wxyw() const { return Vector4 (w, x, y, w); }
+Vector4 Vector4::xyyw() const { return Vector4 (x, y, y, w); }
+Vector4 Vector4::yyyw() const { return Vector4 (y, y, y, w); }
+Vector4 Vector4::zyyw() const { return Vector4 (z, y, y, w); }
+Vector4 Vector4::wyyw() const { return Vector4 (w, y, y, w); }
+Vector4 Vector4::xzyw() const { return Vector4 (x, z, y, w); }
+Vector4 Vector4::yzyw() const { return Vector4 (y, z, y, w); }
+Vector4 Vector4::zzyw() const { return Vector4 (z, z, y, w); }
+Vector4 Vector4::wzyw() const { return Vector4 (w, z, y, w); }
+Vector4 Vector4::xwyw() const { return Vector4 (x, w, y, w); }
+Vector4 Vector4::ywyw() const { return Vector4 (y, w, y, w); }
+Vector4 Vector4::zwyw() const { return Vector4 (z, w, y, w); }
+Vector4 Vector4::wwyw() const { return Vector4 (w, w, y, w); }
+Vector4 Vector4::xxzw() const { return Vector4 (x, x, z, w); }
+Vector4 Vector4::yxzw() const { return Vector4 (y, x, z, w); }
+Vector4 Vector4::zxzw() const { return Vector4 (z, x, z, w); }
+Vector4 Vector4::wxzw() const { return Vector4 (w, x, z, w); }
+Vector4 Vector4::xyzw() const { return Vector4 (x, y, z, w); }
+Vector4 Vector4::yyzw() const { return Vector4 (y, y, z, w); }
+Vector4 Vector4::zyzw() const { return Vector4 (z, y, z, w); }
+Vector4 Vector4::wyzw() const { return Vector4 (w, y, z, w); }
+Vector4 Vector4::xzzw() const { return Vector4 (x, z, z, w); }
+Vector4 Vector4::yzzw() const { return Vector4 (y, z, z, w); }
+Vector4 Vector4::zzzw() const { return Vector4 (z, z, z, w); }
+Vector4 Vector4::wzzw() const { return Vector4 (w, z, z, w); }
+Vector4 Vector4::xwzw() const { return Vector4 (x, w, z, w); }
+Vector4 Vector4::ywzw() const { return Vector4 (y, w, z, w); }
+Vector4 Vector4::zwzw() const { return Vector4 (z, w, z, w); }
+Vector4 Vector4::wwzw() const { return Vector4 (w, w, z, w); }
+Vector4 Vector4::xxww() const { return Vector4 (x, x, w, w); }
+Vector4 Vector4::yxww() const { return Vector4 (y, x, w, w); }
+Vector4 Vector4::zxww() const { return Vector4 (z, x, w, w); }
+Vector4 Vector4::wxww() const { return Vector4 (w, x, w, w); }
+Vector4 Vector4::xyww() const { return Vector4 (x, y, w, w); }
+Vector4 Vector4::yyww() const { return Vector4 (y, y, w, w); }
+Vector4 Vector4::zyww() const { return Vector4 (z, y, w, w); }
+Vector4 Vector4::wyww() const { return Vector4 (w, y, w, w); }
+Vector4 Vector4::xzww() const { return Vector4 (x, z, w, w); }
+Vector4 Vector4::yzww() const { return Vector4 (y, z, w, w); }
+Vector4 Vector4::zzww() const { return Vector4 (z, z, w, w); }
+Vector4 Vector4::wzww() const { return Vector4 (w, z, w, w); }
+Vector4 Vector4::xwww() const { return Vector4 (x, w, w, w); }
+Vector4 Vector4::ywww() const { return Vector4 (y, w, w, w); }
+Vector4 Vector4::zwww() const { return Vector4 (z, w, w, w); }
+Vector4 Vector4::wwww() const { return Vector4 (w, w, w, w); }
+
+
+}; // namespace
diff --git a/externals/g3dlite/G3D.lib/source/Vector4int8.cpp b/externals/g3dlite/G3D.lib/source/Vector4int8.cpp
new file mode 100644
index 00000000000..29ca0d678fc
--- /dev/null
+++ b/externals/g3dlite/G3D.lib/source/Vector4int8.cpp
@@ -0,0 +1,58 @@
+/**
+ @file Vector4int8.cpp
+
+ Homogeneous vector class.
+
+ @maintainer Morgan McGuire, matrix@graphics3d.com
+
+ @created 2007-02-09
+ @edited 2007-02-09
+
+ Copyright 2000-2007, Morgan McGuire.
+ All rights reserved.
+ */
+
+#include "G3D/platform.h"
+#include "G3D/Vector4int8.h"
+#include "G3D/Vector3.h"
+#include "G3D/Vector4.h"
+#include "G3D/BinaryInput.h"
+#include "G3D/BinaryOutput.h"
+#include <string>
+
+namespace G3D {
+
+Vector4int8::Vector4int8(const Vector4& source) {
+ x = iClamp(iRound(source.x), -128, 127);
+ y = iClamp(iRound(source.y), -128, 127);
+ z = iClamp(iRound(source.z), -128, 127);
+ w = iClamp(iRound(source.w), -128, 127);
+}
+
+Vector4int8::Vector4int8(const Vector3& source, int8 w) : w(w) {
+ x = iClamp(iRound(source.x), -128, 127);
+ y = iClamp(iRound(source.y), -128, 127);
+ z = iClamp(iRound(source.z), -128, 127);
+}
+
+Vector4int8::Vector4int8(class BinaryInput& b) {
+ deserialize(b);
+}
+
+void Vector4int8::serialize(class BinaryOutput& b) const {
+ // Intentionally write individual bytes to avoid endian issues
+ b.writeInt8(x);
+ b.writeInt8(y);
+ b.writeInt8(z);
+ b.writeInt8(w);
+}
+
+void Vector4int8::deserialize(class BinaryInput& b) {
+ x = b.readInt8();
+ y = b.readInt8();
+ z = b.readInt8();
+ w = b.readInt8();
+}
+
+} // namespace G3D
+
diff --git a/externals/g3dlite/G3D.lib/source/WinMain.cpp b/externals/g3dlite/G3D.lib/source/WinMain.cpp
new file mode 100644
index 00000000000..94653de4d43
--- /dev/null
+++ b/externals/g3dlite/G3D.lib/source/WinMain.cpp
@@ -0,0 +1,155 @@
+/*
+ Dervied from SDL_main.c, which was placed in the public domain by Sam Lantinga 4/13/98
+
+ The WinMain function -- calls your program's main() function
+*/
+
+#include "G3D/platform.h"
+
+#ifdef G3D_WIN32
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <cctype>
+
+#ifdef main
+# ifndef _WIN32_WCE_EMULATION
+# undef main
+# endif /* _WIN32_WCE_EMULATION */
+#endif /* main */
+
+#if defined(_WIN32_WCE) && _WIN32_WCE < 300
+/* seems to be undefined in Win CE although in online help */
+#define isspace(a) (((CHAR)a == ' ') || ((CHAR)a == '\t'))
+#endif /* _WIN32_WCE < 300 */
+
+// Turn off the G3D for loop scoping for C++
+#ifdef for
+# undef for
+#endif
+
+extern int main(int argc, const char** argv);
+
+/* Parse a command line buffer into arguments */
+static int ParseCommandLine(char *cmdline, char **argv) {
+ char *bufp;
+ int argc;
+
+ argc = 0;
+ for (bufp = cmdline; *bufp;) {
+ /* Skip leading whitespace */
+ while (isspace(*bufp)) {
+ ++bufp;
+ }
+ /* Skip over argument */
+ if (*bufp == '"') {
+ ++bufp;
+ if (*bufp) {
+ if (argv) {
+ argv[argc] = bufp;
+ }
+ ++argc;
+ }
+ /* Skip over word */
+ while (*bufp && (*bufp != '"')) {
+ ++bufp;
+ }
+ } else {
+ if (*bufp) {
+ if (argv) {
+ argv[argc] = bufp;
+ }
+ ++argc;
+ }
+ /* Skip over word */
+ while (*bufp && !isspace(*bufp)) {
+ ++bufp;
+ }
+ }
+ if (*bufp) {
+ if (argv) {
+ *bufp = '\0';
+ }
+ ++bufp;
+ }
+ }
+ if (argv) {
+ argv[argc] = NULL;
+ }
+ return (argc);
+}
+
+/* Show an error message */
+static void ShowError(const char *title, const char *message) {
+/* If USE_MESSAGEBOX is defined, you need to link with user32.lib */
+#ifdef USE_MESSAGEBOX
+ MessageBox(NULL, message, title, MB_ICONEXCLAMATION | MB_OK);
+#else
+ fprintf(stderr, "%s: %s\n", title, message);
+#endif
+}
+
+/* Pop up an out of memory message, returns to Windows */
+static BOOL OutOfMemory(void) {
+ ShowError("Fatal Error", "Out of memory - aborting");
+ return FALSE;
+}
+
+
+int WINAPI G3D_WinMain(HINSTANCE hInst, HINSTANCE hPrev, LPSTR szCmdLine, int sw) {
+ char **argv;
+ int argc;
+ int status;
+ char *cmdline;
+# ifdef _WIN32_WCE
+ wchar_t *bufp;
+ int nLen;
+# else
+ char *bufp;
+ size_t nLen;
+# endif
+
+#ifdef _WIN32_WCE
+#error WinCE not supported
+ /*
+ nLen = wcslen(szCmdLine) + 128 + 1;
+ bufp = SDL_stack_alloc(wchar_t, nLen * 2);
+ wcscpy(bufp, TEXT("\""));
+ GetModuleFileName(NULL, bufp + 1, 128 - 3);
+ wcscpy(bufp + wcslen(bufp), TEXT("\" "));
+ wcsncpy(bufp + wcslen(bufp), szCmdLine, nLen - wcslen(bufp));
+ nLen = wcslen(bufp) + 1;
+ cmdline = SDL_stack_alloc(char, nLen);
+ if (cmdline == NULL) {
+ return OutOfMemory();
+ }
+ WideCharToMultiByte(CP_ACP, 0, bufp, -1, cmdline, nLen, NULL, NULL);
+ */
+#else
+ /* Grab the command line */
+ bufp = GetCommandLineA();
+ nLen = strlen(bufp) + 1;
+ cmdline = (char*)malloc(sizeof(char) * nLen);
+ if (cmdline == NULL) {
+ return OutOfMemory();
+ }
+ strncpy(cmdline, bufp, nLen);
+#endif
+
+ /* Parse it into argv and argc */
+ argc = ParseCommandLine(cmdline, NULL);
+ argv = (char**)malloc(sizeof(char*) * (argc + 1));
+ if (argv == NULL) {
+ return OutOfMemory();
+ }
+ ParseCommandLine(cmdline, argv);
+
+ /* Run the main program */
+ status = main(argc, (const char**)argv);
+ free(argv);
+ free(cmdline);
+
+ return status;
+}
+
+#endif // if Win32
diff --git a/externals/g3dlite/G3D.lib/source/debugAssert.cpp b/externals/g3dlite/G3D.lib/source/debugAssert.cpp
new file mode 100644
index 00000000000..4b24546a661
--- /dev/null
+++ b/externals/g3dlite/G3D.lib/source/debugAssert.cpp
@@ -0,0 +1,392 @@
+/**
+ @file debugAssert.cpp
+
+ Windows implementation of assertion routines.
+
+ @maintainer Morgan McGuire, graphics3d.com
+
+ @created 2001-08-26
+ @edited 2006-02-02
+ */
+
+#include "G3D/debugAssert.h"
+#include "G3D/platform.h"
+#ifdef G3D_WIN32
+ #include <tchar.h>
+#endif
+#include "G3D/format.h"
+#include "G3D/prompt.h"
+#include <string>
+#include "G3D/debugPrintf.h"
+#include "G3D/Log.h"
+
+#include <cstdlib>
+
+#ifdef _MSC_VER
+ // disable: "C++ exception handler used"
+# pragma warning (push)
+# pragma warning (disable : 4530)
+#endif
+
+using namespace std;
+
+namespace G3D { namespace _internal {
+
+ConsolePrintHook _consolePrintHook;
+AssertionHook _debugHook = _handleDebugAssert_;
+AssertionHook _failureHook = _handleErrorCheck_;
+
+#ifdef G3D_LINUX
+ Display* x11Display = NULL;
+ Window x11Window = 0;
+#endif
+
+
+#ifdef G3D_WIN32
+static void postToClipboard(const char *text) {
+ if (OpenClipboard(NULL)) {
+ HGLOBAL hMem = GlobalAlloc(GHND | GMEM_DDESHARE, strlen(text) + 1);
+ if (hMem) {
+ char *pMem = (char*)GlobalLock(hMem);
+ strcpy(pMem, text);
+ GlobalUnlock(hMem);
+
+ EmptyClipboard();
+ SetClipboardData(CF_TEXT, hMem);
+ }
+
+ CloseClipboard();
+ GlobalFree(hMem);
+ }
+}
+#endif
+
+/**
+ outTitle should be set before the call
+ */
+static void createErrorMessage(
+ const char* expression,
+ const std::string& message,
+ const char* filename,
+ int lineNumber,
+ std::string& outTitle,
+ std::string& outMessage) {
+
+ std::string le = "";
+ const char* newline = "\n";
+
+ #ifdef G3D_WIN32
+ newline = "\r\n";
+
+ // The last error value. (Which is preserved across the call).
+ DWORD lastErr = GetLastError();
+
+ // The decoded message from FormatMessage
+ LPTSTR formatMsg = NULL;
+
+ if (NULL == formatMsg) {
+ FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER |
+ FORMAT_MESSAGE_IGNORE_INSERTS |
+ FORMAT_MESSAGE_FROM_SYSTEM,
+ NULL,
+ lastErr,
+ 0,
+ (LPTSTR)&formatMsg,
+ 0,
+ NULL);
+ }
+
+ // Make sure the message got translated into something.
+ LPTSTR realLastErr;
+ if (NULL != formatMsg) {
+ realLastErr = formatMsg;
+ } else {
+ realLastErr = _T("Last error code does not exist.");
+ }
+
+ if (lastErr != 0) {
+ le = G3D::format("Last Error (0x%08X): %s\r\n\r\n", lastErr, (LPCSTR)realLastErr);
+ }
+
+ // Get rid of the allocated memory from FormatMessage.
+ if (NULL != formatMsg) {
+ LocalFree((LPVOID)formatMsg);
+ }
+
+ char modulePath[MAX_PATH];
+ GetModuleFileNameA(NULL, modulePath, MAX_PATH);
+
+ const char* moduleName = strrchr(modulePath, '\\');
+ outTitle = outTitle + string(" - ") + string(moduleName ? (moduleName + 1) : modulePath);
+
+ #endif
+
+ // Build the message.
+ outMessage =
+ G3D::format("%s%s%sExpression: %s%s%s:%d%s%s%s",
+ message.c_str(), newline, newline, expression, newline,
+ filename, lineNumber, newline, newline, le.c_str());
+}
+
+
+bool _handleDebugAssert_(
+ const char* expression,
+ const std::string& message,
+ const char* filename,
+ int lineNumber,
+ bool& ignoreAlways,
+ bool useGuiPrompt) {
+
+ std::string dialogTitle = "Assertion Failure";
+ std::string dialogText = "";
+ createErrorMessage(expression, message, filename, lineNumber, dialogTitle, dialogText);
+
+ #ifdef G3D_WIN32
+ DWORD lastErr = GetLastError();
+ postToClipboard(dialogText.c_str());
+ debugPrintf("\n%s\n", dialogText.c_str());
+ #endif
+
+ const int cBreak = 0;
+ const int cIgnore = 1;
+ const int cIgnoreAlways = 2;
+ const int cAbort = 3;
+
+ static const char* choices[] = {"Debug", "Ignore", "Ignore Always", "Exit"};
+
+ // Log the error
+ Log::common()->print(std::string("\n**************************\n\n") + dialogTitle + "\n" + dialogText);
+
+ int result = G3D::prompt(dialogTitle.c_str(), dialogText.c_str(), (const char**)choices, 4, useGuiPrompt);
+
+# ifdef G3D_WIN32
+ // Put the incoming last error back.
+ SetLastError(lastErr);
+# endif
+
+ switch (result) {
+ // -1 shouldn't actually occur because it means
+ // that we're in release mode.
+ case -1:
+ case cBreak:
+ return true;
+ break;
+
+ case cIgnore:
+ return false;
+ break;
+
+ case cIgnoreAlways:
+ ignoreAlways = true;
+ return false;
+ break;
+
+ case cAbort:
+ exit(-1);
+ break;
+ }
+
+ // Should never get here
+ return false;
+}
+
+
+bool _handleErrorCheck_(
+ const char* expression,
+ const std::string& message,
+ const char* filename,
+ int lineNumber,
+ bool& ignoreAlways,
+ bool useGuiPrompt) {
+
+ (void)ignoreAlways;
+
+ std::string dialogTitle = "Critical Error";
+ std::string dialogText = "";
+
+ createErrorMessage(expression, message, filename, lineNumber, dialogTitle, dialogText);
+
+ // Log the error
+ Log::common()->print(std::string("\n**************************\n\n") + dialogTitle + "\n" + dialogText);
+ #ifdef G3D_WIN32
+ DWORD lastErr = GetLastError();
+ postToClipboard(dialogText.c_str());
+ debugPrintf("\n%s\n", dialogText.c_str());
+ #endif
+
+ static const char* choices[] = {"Ok"};
+
+ std::string m =
+ std::string("An internal error has occured in your program and it will now close. Details about the error have been reported in \"") +
+ Log::getCommonLogFilename() + "\".";
+
+ int result = G3D::prompt("Error", m.c_str(), (const char**)choices, 1, useGuiPrompt);
+ (void)result;
+
+ return true;
+}
+
+
+#ifdef G3D_WIN32
+static HCURSOR oldCursor;
+static RECT oldCursorRect;
+static POINT oldCursorPos;
+static int oldShowCursorCount;
+#endif
+
+void _releaseInputGrab_() {
+ #ifdef G3D_WIN32
+
+ GetCursorPos(&oldCursorPos);
+
+ // Stop hiding the cursor if the application hid it.
+ oldShowCursorCount = ShowCursor(true) - 1;
+
+ if (oldShowCursorCount < -1) {
+ for (int c = oldShowCursorCount; c < -1; ++c) {
+ ShowCursor(true);
+ }
+ }
+
+ // Set the default cursor in case the application
+ // set the cursor to NULL.
+ oldCursor = GetCursor();
+ SetCursor(LoadCursor(NULL, IDC_ARROW));
+
+ // Allow the cursor full access to the screen
+ GetClipCursor(&oldCursorRect);
+ ClipCursor(NULL);
+
+ #elif defined(G3D_LINUX)
+ if (x11Display != NULL) {
+ XUngrabPointer(x11Display, CurrentTime);
+ XUngrabKeyboard(x11Display, CurrentTime);
+ if (x11Window != 0) {
+ //XUndefineCursor(x11Display, x11Window);
+ // TODO: Note that we leak this cursor; it should be
+ // freed in the restore code.
+ Cursor c = XCreateFontCursor(x11Display, 68);
+ XDefineCursor(x11Display, x11Window, c);
+ }
+ XSync(x11Display, false);
+ XAllowEvents(x11Display, AsyncPointer, CurrentTime);
+ XFlush(x11Display);
+ }
+ #elif defined(G3D_OSX)
+ // TODO: OS X
+ #endif
+}
+
+
+void _restoreInputGrab_() {
+ #ifdef G3D_WIN32
+
+ // Restore the old clipping region
+ ClipCursor(&oldCursorRect);
+
+ SetCursorPos(oldCursorPos.x, oldCursorPos.y);
+
+ // Restore the old cursor
+ SetCursor(oldCursor);
+
+ // Restore old visibility count
+ if (oldShowCursorCount < 0) {
+ for (int c = 0; c > oldShowCursorCount; --c) {
+ ShowCursor(false);
+ }
+ }
+
+ #elif defined(G3D_LINUX)
+ // TODO: Linux
+ #elif defined(G3D_OSX)
+ // TODO: OS X
+ #endif
+}
+
+
+}; // internal namespace
+
+void setAssertionHook(AssertionHook hook) {
+ G3D::_internal::_debugHook = hook;
+}
+
+AssertionHook assertionHook() {
+ return G3D::_internal::_debugHook;
+}
+
+void setFailureHook(AssertionHook hook) {
+ G3D::_internal::_failureHook = hook;
+}
+
+AssertionHook failureHook() {
+ return G3D::_internal::_failureHook;
+}
+
+
+void setConsolePrintHook(ConsolePrintHook h) {
+ G3D::_internal::_consolePrintHook = h;
+}
+
+ConsolePrintHook consolePrintHook() {
+ return G3D::_internal::_consolePrintHook;
+}
+
+
+std::string __cdecl debugPrint(const std::string& s) {
+# ifdef G3D_WIN32
+ const int MAX_STRING_LEN = 1024;
+
+ // Windows can't handle really long strings sent to
+ // the console, so we break the string.
+ if (s.size() < MAX_STRING_LEN) {
+ OutputDebugStringA(s.c_str());
+ } else {
+ for (unsigned int i = 0; i < s.size(); i += MAX_STRING_LEN) {
+ std::string sub = s.substr(i, MAX_STRING_LEN);
+ OutputDebugStringA(sub.c_str());
+ }
+ }
+# else
+ fprintf(stderr, "%s", s.c_str());
+ fflush(stderr);
+# endif
+
+ return s;
+}
+
+std::string __cdecl debugPrintf(const char* fmt ...) {
+ va_list argList;
+ va_start(argList, fmt);
+ std::string s = G3D::vformat(fmt, argList);
+ va_end(argList);
+
+ return debugPrint(consolePrint(s));
+}
+
+std::string consolePrint(const std::string& s) {
+ FILE* L = Log::common()->getFile();
+ fprintf(L, "%s", s.c_str());
+
+ if (consolePrintHook()) {
+ consolePrintHook()(s);
+ }
+
+ fflush(L);
+ return s;
+}
+
+
+std::string __cdecl consolePrintf(const char* fmt ...) {
+ va_list argList;
+ va_start(argList, fmt);
+ std::string s = G3D::vformat(fmt, argList);
+ va_end(argList);
+
+ return consolePrint(s);
+}
+
+} // namespace
+
+#ifdef _MSC_VER
+# pragma warning (pop)
+#endif
diff --git a/externals/g3dlite/G3D.lib/source/fileutils.cpp b/externals/g3dlite/G3D.lib/source/fileutils.cpp
new file mode 100644
index 00000000000..3a5e8c715c8
--- /dev/null
+++ b/externals/g3dlite/G3D.lib/source/fileutils.cpp
@@ -0,0 +1,1092 @@
+/**
+ @file fileutils.cpp
+
+ @author Morgan McGuire, graphics3d.com
+
+ @author 2002-06-06
+ @edited 2008-01-20
+ */
+
+#include "G3D/platform.h"
+#include "G3D/fileutils.h"
+#include "G3D/BinaryInput.h"
+#include "G3D/g3dmath.h"
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <zip/zip.h>
+#include <zip/unzip.h>
+#include "G3D/stringutils.h"
+#include "G3D/Set.h"
+
+#include <cstring>
+
+#ifdef G3D_WIN32
+ // Needed for _getcwd
+ #include <direct.h>
+ #include <io.h>
+#else
+ #include <dirent.h>
+ #include <fnmatch.h>
+ #include <unistd.h>
+ #define _getcwd getcwd
+#endif
+#include <stdio.h>
+#include "G3D/BinaryOutput.h"
+
+#ifdef G3D_WIN32
+ //for _mkdir and _stat
+# include <direct.h>
+#else
+# define _stat stat
+#endif
+
+
+namespace G3D {
+
+namespace _internal {
+ Set<std::string> currentFilesUsed;
+}
+
+std::string pathConcat(const std::string& dirname, const std::string& file) {
+ // Ensure that the directory ends in a slash
+ if ((dirname.size() != 0) &&
+ (dirname[dirname.size() - 1] != '/') &&
+ (dirname[dirname.size() - 1] != '\\') &&
+ (dirname[dirname.size() - 1] != ':')) {
+ return dirname + '/' + file;
+ } else {
+ return dirname + file;
+ }
+}
+
+std::string resolveFilename(const std::string& filename) {
+ if (filename.size() >= 1) {
+ if ((filename[0] == '/') || (filename[0] == '\\')) {
+ // Already resolved
+ return filename;
+ } else {
+
+ #ifdef G3D_WIN32
+ if ((filename.size() >= 2) && (filename[1] == ':')) {
+ // There is a drive spec on the front.
+ if ((filename.size() >= 3) && ((filename[2] == '\\') ||
+ (filename[2] == '/'))) {
+ // Already fully qualified
+ return filename;
+ } else {
+ // The drive spec is relative to the
+ // working directory on that drive.
+ debugAssertM(false, "Files of the form d:path are"
+ " not supported (use a fully qualified"
+ " name).");
+ return filename;
+ }
+ }
+ #endif
+ }
+ }
+
+ char buffer[1024];
+
+ // Prepend the working directory.
+ _getcwd(buffer, 1024);
+
+ return format("%s/%s", buffer, filename.c_str());
+}
+
+bool zipfileExists(const std::string& filename) {
+ std::string outZipfile;
+ std::string outInternalFile;
+ return zipfileExists(filename, outZipfile, outInternalFile);
+}
+
+std::string readWholeFile(
+ const std::string& filename) {
+
+ _internal::currentFilesUsed.insert(filename);
+
+ std::string s;
+
+ debugAssert(filename != "");
+ if (fileExists(filename, false)) {
+
+ int64 length = fileLength(filename);
+
+ char* buffer = (char*)System::alignedMalloc(length + 1, 16);
+ debugAssert(buffer);
+ FILE* f = fopen(filename.c_str(), "rb");
+ debugAssert(f);
+ int ret = fread(buffer, 1, length, f);
+ debugAssert(ret == length);(void)ret;
+ fclose(f);
+
+ buffer[length] = '\0';
+ s = std::string(buffer);
+
+ System::alignedFree(buffer);
+
+ } else if (zipfileExists(filename)) {
+
+ void* zipBuffer;
+ size_t length;
+ zipRead(filename, zipBuffer, length);
+
+ char* buffer = (char*)System::alignedMalloc(length + 1, 16);
+ System::memcpy(buffer,zipBuffer, length + 1);
+ zipClose(zipBuffer);
+
+ buffer[length] = '\0';
+ s = std::string(buffer);
+ System::alignedFree(buffer);
+ } else {
+ debugAssertM(false, filename + " not found");
+ }
+
+ return s;
+}
+
+
+void zipRead(const std::string& file,
+ void*& data,
+ size_t& length) {
+ std::string zip, desiredFile;
+
+ if (zipfileExists(file, zip, desiredFile)) {
+ unzFile f = unzOpen(zip.c_str());
+ {
+ unzLocateFile(f, desiredFile.c_str(), 2);
+ unz_file_info info;
+ unzGetCurrentFileInfo(f, &info, NULL, 0, NULL, 0, NULL, 0);
+ length = info.uncompressed_size;
+ // sets machines up to use MMX, if they want
+ data = System::alignedMalloc(length, 16);
+ unzOpenCurrentFile(f);
+ {
+ int test = unzReadCurrentFile(f, data, length);
+ debugAssertM((size_t)test == length,
+ desiredFile + " was corrupt because it unzipped to the wrong size.");
+ (void)test;
+ }
+ unzCloseCurrentFile(f);
+ }
+ unzClose(f);
+ } else {
+ data = NULL;
+ }
+}
+
+
+void zipClose(void* data) {
+ System::alignedFree(data);
+}
+
+
+int64 fileLength(const std::string& filename) {
+ struct _stat st;
+ int result = _stat(filename.c_str(), &st);
+
+ if (result == -1) {
+ std::string zip, contents;
+ if(zipfileExists(filename, zip, contents)){
+ int64 requiredMem;
+
+ unzFile f = unzOpen(zip.c_str());
+ {
+ unzLocateFile(f, contents.c_str(), 2);
+ unz_file_info info;
+ unzGetCurrentFileInfo(f, &info, NULL, 0, NULL, 0, NULL, 0);
+ requiredMem = info.uncompressed_size;
+ }
+ unzClose(f);
+ return requiredMem;
+ } else {
+ return -1;
+ }
+ }
+
+ return st.st_size;
+}
+
+/** Used by robustTmpfile. Returns nonzero if fread, fwrite, and fseek all
+succeed on the file.
+ @author Morgan McGuire, morgan@graphics3d.com */
+static int isFileGood(FILE* f) {
+
+ int x, n, result;
+
+ /* Must be a valid file handle */
+ if (f == NULL) {
+ return 0;
+ }
+
+ /* Try to write */
+ x = 1234;
+ n = fwrite(&x, sizeof(int), 1, f);
+
+ if (n != 1) {
+ return 0;
+ }
+
+ /* Seek back to the beginning */
+ result = fseek(f, 0, SEEK_SET);
+ if (result != 0) {
+ return 0;
+ }
+
+ /* Read */
+ n = fread(&x, sizeof(int), 1, f);
+ if (n != 1) {
+ return 0;
+ }
+
+ /* Seek back to the beginning again */
+ fseek(f, 0, SEEK_SET);
+
+ return 1;
+}
+
+FILE* createTempFile() {
+ FILE* t = NULL;
+
+//# ifdef G3D_WIN32
+ t = tmpfile();
+//# else
+// // On Unix, tmpfile generates a warning for any code that links against it.
+// const char* tempfilename = "/tmp/g3dtemp.XXXXXXXX";
+// mktemp(tempfilename);
+// t = fopen(tempfilename, "w");
+//# endif
+
+# ifdef _WIN32
+ char* n = NULL;
+# endif
+ char name[256];
+
+ if (isFileGood(t)) {
+ return t;
+ }
+
+# ifdef G3D_WIN32
+ /* tmpfile failed; try the tmpnam routine */
+ t = fopen(tmpnam(NULL), "w+");
+ if (isFileGood(t)) {
+ return t;
+ }
+
+ n = _tempnam("c:/tmp/", "t");
+ /* Try to create something in C:\tmp */
+ t = fopen(n, "w+");
+ if (isFileGood(t)) {
+ return t;
+ }
+
+ /* Try c:\temp */
+ n = _tempnam("c:/temp/", "t");
+ t = fopen(n, "w+");
+ if (isFileGood(t)) {
+ return t;
+ }
+
+ /* try the current directory */
+ n = _tempnam("./", "t");
+ t = fopen(n, "w+");
+ if (isFileGood(t)) {
+ return t;
+ }
+
+ sprintf(name, "%s/tmp%d", "c:/temp", rand());
+ t = fopen(name, "w+");
+ if (isFileGood(t)) {
+ return t;
+ }
+
+ /* Try some hardcoded paths */
+ sprintf(name, "%s/tmp%d", "c:/tmp", rand());
+ t = fopen(name, "w+");
+ if (isFileGood(t)) {
+ return t;
+ }
+# else
+ sprintf(name, "%s/tmp%d", "/tmp", rand());
+ t = fopen(name, "w+");
+ if (isFileGood(t)) {
+ return t;
+ }
+#endif
+
+ sprintf(name, "tmp%d", rand());
+ t = fopen(name, "w+");
+ if (isFileGood(t)) {
+ return t;
+ }
+
+ fprintf(stderr, "Unable to create a temporary file; robustTmpfile returning NULL\n");
+
+ return NULL;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+void writeWholeFile(
+ const std::string& filename,
+ const std::string& str,
+ bool flush) {
+
+ // Make sure the directory exists.
+ std::string root, base, ext, path;
+ Array<std::string> pathArray;
+ parseFilename(filename, root, pathArray, base, ext);
+
+ path = root + stringJoin(pathArray, '/');
+ if (! fileExists(path, false)) {
+ createDirectory(path);
+ }
+
+ FILE* file = fopen(filename.c_str(), "wb");
+
+ debugAssert(file);
+
+ fwrite(str.c_str(), str.size(), 1, file);
+
+ if (flush) {
+ fflush(file);
+ }
+ fclose(file);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+/**
+ Creates the directory (which may optionally end in a /)
+ and any parents needed to reach it.
+ */
+void createDirectory(
+ const std::string& dir) {
+
+ if (dir == "") {
+ return;
+ }
+
+ std::string d;
+
+ // Add a trailing / if there isn't one.
+ switch (dir[dir.size() - 1]) {
+ case '/':
+ case '\\':
+ d = dir;
+ break;
+
+ default:
+ d = dir + "/";
+ }
+
+ // If it already exists, do nothing
+ if (fileExists(d.substr(0, d.size() - 1)), false) {
+ return;
+ }
+
+ // Parse the name apart
+ std::string root, base, ext;
+ Array<std::string> path;
+
+ std::string lead;
+ parseFilename(d, root, path, base, ext);
+ debugAssert(base == "");
+ debugAssert(ext == "");
+
+ // Begin with an extra period so "c:\" becomes "c:\.\" after
+ // appending a path and "c:" becomes "c:.\", not root: "c:\"
+ std::string p = root + ".";
+
+ // Create any intermediate that doesn't exist
+ for (int i = 0; i < path.size(); ++i) {
+ p += "/" + path[i];
+ if (! fileExists(p, false)) {
+ // Windows only requires one argument to mkdir,
+ // where as unix also requires the permissions.
+# ifndef G3D_WIN32
+ mkdir(p.c_str(), 0777);
+# else
+ _mkdir(p.c_str());
+# endif
+ }
+ }
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+bool fileExists(
+ const std::string& filename,
+ const bool lookInZipfiles) {
+
+ if (filename == "") {
+ return true;
+ }
+
+
+ // Useful for debugging
+ //char curdir[1024]; _getcwd(curdir, 1024);
+
+ struct _stat st;
+ int ret = _stat(filename.c_str(), &st);
+
+ // _stat returns zero on success
+ bool exists = (ret == 0);
+
+ if (exists) {
+ // Exists
+ return true;
+ } else if (lookInZipfiles) {
+ // Does not exist standalone, but might exist in a zipfile
+
+ // These output arguments will be ignored
+ std::string zipDir, internalPath;
+ return zipfileExists(filename, zipDir, internalPath);
+ } else {
+ // Does not exist
+ return false;
+ }
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+/* Helper methods for zipfileExists()*/
+// Given a string (the drive) and an array (the path), computes the directory
+static void _zip_resolveDirectory(std::string& completeDir, const std::string& drive, const Array<std::string>& path, const int length){
+ completeDir = drive;
+ int tempLength;
+ // if the given length is longer than the array, we correct it
+ if(length > path.length()){
+ tempLength = path.length();
+ } else{
+ tempLength = length;
+ }
+
+ for(int t = 0; t < tempLength; ++t){
+ if(t > 0){
+ completeDir += "/";
+ }
+ completeDir += path[t];
+ }
+}
+
+
+// assumes that zipDir references a .zip file
+static bool _zip_zipContains(const std::string& zipDir, const std::string& desiredFile){
+ unzFile f = unzOpen(zipDir.c_str());
+ //the last parameter, an int, determines case sensitivity:
+ //1 is sensitive, 2 is not, 0 is default
+ int test = unzLocateFile(f, desiredFile.c_str(), 2);
+ unzClose(f);
+ if(test == UNZ_END_OF_LIST_OF_FILE){
+ return false;
+ }
+ return true;
+}
+
+
+// If no zipfile exists, outZipfile and outInternalFile are unchanged
+bool zipfileExists(const std::string& filename, std::string& outZipfile,
+ std::string& outInternalFile){
+ if (fileExists(filename, false)) {
+ // Not inside a zipfile if the file itself exists
+ return false;
+ } else {
+ Array<std::string> path;
+ std::string drive, base, ext, zipfile, infile;
+ parseFilename(filename, drive, path, base, ext);
+
+ // Put the filename back together
+ if ((base != "") && (ext != "")) {
+ infile = base + "." + ext;
+ } else {
+ infile = base + ext;
+ }
+
+ // Remove "." from path
+ for (int i = 0; i < path.length(); ++i) {
+ if (path[i] == ".") {
+ path.remove(i);
+ --i;
+ }
+ }
+
+ // Remove ".." from path
+ for (int i = 1; i < path.length(); ++i) {
+ if ((path[i] == "..") && (i > 0) && (path[i - 1] != "..")) {
+ // Remove both i and i - 1
+ path.remove(i - 1, 2);
+ i -= 2;
+ }
+ }
+
+ // Walk the path backwards, accumulating pieces onto the infile until
+ // we find a zipfile that contains it
+ for (int t = 0; t < path.length(); ++t){
+ _zip_resolveDirectory(zipfile, drive, path, path.length() - t);
+ if (t > 0) {
+ infile = path[path.length() - t] + "/" + infile;
+ }
+
+ if (fileExists(zipfile, false)) {
+ // test if it actually is a zipfile
+ // if not, return false, a bad
+ // directory structure has been given,
+ // not a .zip
+ if (isZipfile(zipfile)){
+
+ if (_zip_zipContains(zipfile, infile)){
+ outZipfile = zipfile;
+ outInternalFile = infile;
+ return true;
+ } else {
+ return false;
+ }
+ } else {
+ // the directory structure was valid but did not point to a .zip
+ return false;
+ }
+ }
+
+ }
+ }
+
+ // not a valid directory structure ever,
+ // obviously no .zip was found within the path
+ return false;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+std::string generateFilenameBase(const std::string& prefix) {
+ Array<std::string> exist;
+
+ // Note "template" is a reserved word in C++
+ std::string templat = prefix + System::currentDateString();
+ getFiles(templat + "*", exist);
+
+ // Remove extensions
+ for (int i = 0; i < exist.size(); ++i) {
+ exist[i] = filenameBase(exist[i]);
+ }
+
+ int num = 0;
+ std::string result;
+ templat += "_%03d";
+ do {
+ result = format(templat.c_str(), num);
+ ++num;
+ } while (exist.contains(result));
+
+ return result;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+void copyFile(
+ const std::string& source,
+ const std::string& dest) {
+
+ #ifdef G3D_WIN32
+ CopyFileA(source.c_str(), dest.c_str(), FALSE);
+ #else
+ // TODO: don't use BinaryInput and BinaryOutput
+ // Read it all in, then dump it out
+ BinaryInput in(source, G3D_LITTLE_ENDIAN);
+ BinaryOutput out(dest, G3D_LITTLE_ENDIAN);
+ out.writeBytes(in.getCArray(), in.size());
+ out.commit(false);
+ #endif
+}
+
+//////////////////////////////////////////////////////////////////////////////
+
+void parseFilename(
+ const std::string& filename,
+ std::string& root,
+ Array<std::string>& path,
+ std::string& base,
+ std::string& ext) {
+
+ std::string f = filename;
+
+ root = "";
+ path.clear();
+ base = "";
+ ext = "";
+
+ if (f == "") {
+ // Empty filename
+ return;
+ }
+
+ // See if there is a root/drive spec.
+ if ((f.size() >= 2) && (f[1] == ':')) {
+
+ if ((f.size() > 2) && isSlash(f[2])) {
+
+ // e.g. c:\foo
+ root = f.substr(0, 3);
+ f = f.substr(3, f.size() - 3);
+
+ } else {
+
+ // e.g. c:foo
+ root = f.substr(2);
+ f = f.substr(2, f.size() - 2);
+
+ }
+
+ } else if ((f.size() >= 2) & isSlash(f[0]) && isSlash(f[1])) {
+
+ // e.g. //foo
+ root = f.substr(0, 2);
+ f = f.substr(2, f.size() - 2);
+
+ } else if (isSlash(f[0])) {
+
+ root = f.substr(0, 1);
+ f = f.substr(1, f.size() - 1);
+
+ }
+
+ // Pull the extension off
+ {
+ // Find the period
+ size_t i = f.rfind('.');
+
+ if (i != std::string::npos) {
+ // Make sure it is after the last slash!
+ size_t j = iMax(f.rfind('/'), f.rfind('\\'));
+ if ((j == std::string::npos) || (i > j)) {
+ ext = f.substr(i + 1, f.size() - i - 1);
+ f = f.substr(0, i);
+ }
+ }
+ }
+
+ // Pull the basename off
+ {
+ // Find the last slash
+ size_t i = iMax(f.rfind('/'), f.rfind('\\'));
+
+ if (i == std::string::npos) {
+
+ // There is no slash; the basename is the whole thing
+ base = f;
+ f = "";
+
+ } else if ((i != std::string::npos) && (i < f.size() - 1)) {
+
+ base = f.substr(i + 1, f.size() - i - 1);
+ f = f.substr(0, i);
+
+ }
+ }
+
+ // Parse what remains into path.
+ size_t prev, cur = 0;
+
+ while (cur < f.size()) {
+ prev = cur;
+
+ // Allow either slash
+ size_t i = f.find('/', prev + 1);
+ size_t j = f.find('\\', prev + 1);
+ if (i == std::string::npos) {
+ i = f.size();
+ }
+
+ if (j == std::string::npos) {
+ j = f.size();
+ }
+
+ cur = iMin(i, j);
+
+ if (cur == std::string::npos) {
+ cur = f.size();
+ }
+
+ path.append(f.substr(prev, cur - prev));
+ ++cur;
+ }
+}
+
+
+/**
+ Helper for getFileList and getDirectoryList.
+
+ @param wantFiles If false, returns the directories, otherwise
+ returns the files.
+ @param includePath If true, the names include paths
+ */
+static void getFileOrDirListNormal
+(
+ const std::string& filespec,
+ Array<std::string>& files,
+ bool wantFiles,
+ bool includePath) {
+
+ bool test = wantFiles ? true : false;
+
+ std::string path = "";
+
+ // Find the place where the path ends and the file-spec begins
+ size_t i = filespec.rfind('/');
+ size_t j = filespec.rfind('\\');
+
+ // Drive letters on Windows can separate a path
+ size_t k = filespec.rfind(':');
+
+ if (((j != std::string::npos) && (j > i)) ||
+ (i == std::string::npos)) {
+ i = j;
+ }
+
+ if (((k != std::string::npos) && (k > i)) ||
+ (i == std::string::npos)) {
+ i = k;
+ }
+
+ // If there is a path, pull it off
+ if (i != std::string::npos) {
+ path = filespec.substr(0, i + 1);
+ }
+
+ std::string prefix = path;
+
+ if (path.size() > 0) {
+ // Strip the trailing character
+ path = path.substr(0, path.size() - 1);
+ }
+
+# ifdef G3D_WIN32
+ {
+ struct _finddata_t fileinfo;
+
+ long handle = _findfirst(filespec.c_str(), &fileinfo);
+ int result = handle;
+
+ while (result != -1) {
+ if ((((fileinfo.attrib & _A_SUBDIR) == 0) == test) &&
+ strcmp(fileinfo.name, ".") &&
+ strcmp(fileinfo.name, "..")) {
+
+ if (includePath) {
+ files.append(prefix + fileinfo.name);
+ } else {
+ files.append(fileinfo.name);
+ }
+ }
+
+ result = _findnext(handle, &fileinfo);
+ }
+ }
+# else
+ {
+ if (path == "") {
+ // Empty paths don't work on Unix
+ path = ".";
+ }
+
+ // Unix implementation
+ DIR* dir = opendir(path.c_str());
+
+ if (dir != NULL) {
+ struct dirent* entry = readdir(dir);
+
+ while (entry != NULL) {
+
+ // Exclude '.' and '..'
+ if ((strcmp(entry->d_name, ".") != 0) &&
+ (strcmp(entry->d_name, "..") != 0)) {
+
+ // Form a name with a path
+ std::string filename = prefix + entry->d_name;
+ // See if this is a file or a directory
+ struct _stat st;
+ bool exists = _stat(filename.c_str(), &st) != -1;
+
+ if (exists &&
+
+ // Make sure it has the correct type
+ (((st.st_mode & S_IFDIR) == 0) == test) &&
+
+ // Make sure it matches the wildcard
+ (fnmatch(filespec.c_str(),
+ filename.c_str(),
+ FNM_PATHNAME) == 0)) {
+
+ if (includePath) {
+ files.append(filename);
+ } else {
+ files.append(entry->d_name);
+ }
+ }
+ }
+
+ entry = readdir(dir);
+ }
+ closedir(dir);
+ }
+ }
+# endif
+}
+
+/**
+
+ c:/temp/foo.zip/plainsky\\*
+" path "
+ "prefix "
+
+ @param path The zipfile name (no trailing slash)
+ @param prefix Directory inside the zipfile. No leading slash, must have trailing slash if non-empty.
+ @param file Name inside the zipfile that we are testing to see if it matches prefix + "*"
+ */
+static void _zip_addEntry(const std::string& path,
+ const std::string& prefix,
+ const std::string& file,
+ Set<std::string>& files,
+ bool wantFiles,
+ bool includePath) {
+
+ // Make certain we are within the desired parent folder (prefix)
+ if (beginsWith(file, prefix)) {
+ // validityTest was prefix/file
+
+ // Extract everything to the right of the prefix
+ std::string s = file.substr(prefix.length());
+
+ if (s == "") {
+ // This was the name of the prefix
+ return;
+ }
+
+ // See if there are any slashes
+ size_t slashPos = s.find('/');
+
+ bool add = false;
+
+ if (slashPos == std::string::npos) {
+ // No slashes, so s must be a file
+ add = wantFiles;
+ } else if (! wantFiles) {
+ // Not all zipfiles list directories as explicit entries.
+ // Because of this, if we're looking for directories and see
+ // any path longer than prefix, we must add the subdirectory.
+ // The Set will fix duplicates for us.
+ s = s.substr(0, slashPos);
+ add = true;
+ }
+
+ if (add) {
+ if (includePath) {
+ files.insert(path + "/" + prefix + s);
+ } else {
+ files.insert(s);
+ }
+ }
+ }
+}
+
+
+static void getFileOrDirListZip(const std::string& path,
+ const std::string& prefix,
+ Array<std::string>& files,
+ bool wantFiles,
+ bool includePath){
+ unzFile f = unzOpen(path.c_str());
+
+ enum {MAX_STRING_LENGTH=1024};
+ char filename[MAX_STRING_LENGTH];
+ Set<std::string> fileSet;
+
+ do {
+
+ // prefix is valid, either "" or a subfolder
+ unzGetCurrentFileInfo(f, NULL, filename, MAX_STRING_LENGTH, NULL, 0, NULL, 0);
+ _zip_addEntry(path, prefix, filename, fileSet, wantFiles, includePath);
+
+ } while (unzGoToNextFile(f) != UNZ_END_OF_LIST_OF_FILE);
+
+ unzClose(f);
+
+ fileSet.getMembers(files);
+}
+
+
+static void determineFileOrDirList(
+ const std::string& filespec,
+ Array<std::string>& files,
+ bool wantFiles,
+ bool includePath) {
+
+ // if it is a .zip, prefix will specify the folder within
+ // whose contents we want to see
+ std::string prefix = "";
+ std::string path = filenamePath(filespec);
+
+ if ((path.size() > 0) && isSlash(path[path.size() - 1])) {
+ // Strip the trailing slash
+ path = path.substr(0, path.length() -1);
+ }
+
+ if (fileExists(path, false)) {
+ if (isZipfile(path)) {
+ // .zip should only work if * is specified as the Base + Ext
+ // Here, we have been asked for the root's contents
+ debugAssertM(filenameBaseExt(filespec) == "*", "Can only call getFiles/getDirs on zipfiles using '*' wildcard");
+ getFileOrDirListZip(path, prefix, files, wantFiles, includePath);
+ } else {
+ // It is a normal directory
+ getFileOrDirListNormal(filespec, files, wantFiles, includePath);
+ }
+ } else if (zipfileExists(filenamePath(filespec), path, prefix)) {
+ // .zip should only work if * is specified as the Base + Ext
+ // Here, we have been asked for the contents of a folder within the .zip
+ debugAssertM(filenameBaseExt(filespec) == "*", "Can only call getFiles/getDirs on zipfiles using '*' wildcard");
+ getFileOrDirListZip(path, prefix, files, wantFiles, includePath);
+ }
+}
+
+
+void getFiles(
+ const std::string& filespec,
+ Array<std::string>& files,
+ bool includePath) {
+
+ determineFileOrDirList(filespec, files, true, includePath);
+}
+
+
+void getDirs(
+ const std::string& filespec,
+ Array<std::string>& files,
+ bool includePath) {
+
+ determineFileOrDirList(filespec, files, false, includePath);
+}
+
+
+std::string filenameBaseExt(const std::string& filename) {
+ int i = filename.rfind("/");
+ int j = filename.rfind("\\");
+
+ if ((j > i) && (j >= 0)) {
+ i = j;
+ }
+
+# ifdef G3D_WIN32
+ j = filename.rfind(":");
+ if ((i == -1) && (j >= 0)) {
+ i = j;
+ }
+# endif
+
+ if (i == -1) {
+ return filename;
+ } else {
+ return filename.substr(i + 1, filename.length() - i);
+ }
+}
+
+
+std::string filenameBase(const std::string& s) {
+ std::string drive;
+ std::string base;
+ std::string ext;
+ Array<std::string> path;
+
+ parseFilename(s, drive, path, base, ext);
+ return base;
+}
+
+
+std::string filenameExt(const std::string& filename) {
+ int i = filename.rfind(".");
+ if (i >= 0) {
+ return filename.substr(i + 1, filename.length() - i);
+ } else {
+ return "";
+ }
+}
+
+
+std::string filenamePath(const std::string& filename) {
+ int i = filename.rfind("/");
+ int j = filename.rfind("\\");
+
+ if ((j > i) && (j >= 0)) {
+ i = j;
+ }
+
+# ifdef G3D_WIN32
+ j = filename.rfind(":");
+ if ((i == -1) && (j >= 0)) {
+ i = j;
+ }
+# endif
+
+ if (i == -1) {
+ return "";
+ } else {
+ return filename.substr(0, i+1);
+ }
+}
+
+
+bool isZipfile(const std::string& filename) {
+
+ FILE* f = fopen(filename.c_str(), "r");
+ if (f == NULL) {
+ return false;
+ }
+ uint8 header[4];
+ fread(header, 4, 1, f);
+
+ const uint8 zipHeader[4] = {0x50, 0x4b, 0x03, 0x04};
+ for (int i = 0; i < 4; ++i) {
+ if (header[i] != zipHeader[i]) {
+ fclose(f);
+ return false;
+ }
+ }
+
+ fclose(f);
+ return true;
+}
+
+
+bool isDirectory(const std::string& filename) {
+ struct _stat st;
+ bool exists = _stat(filename.c_str(), &st) != -1;
+ return exists && ((st.st_mode & S_IFDIR) != 0);
+}
+
+
+bool filenameContainsWildcards(const std::string& filename) {
+ return (filename.find('*') != std::string::npos) || (filename.find('?') != std::string::npos);
+}
+
+
+bool fileIsNewer(const std::string& src, const std::string& dst) {
+ struct _stat sts;
+ bool sexists = _stat(src.c_str(), &sts) != -1;
+
+ struct _stat dts;
+ bool dexists = _stat(dst.c_str(), &dts) != -1;
+
+ return sexists && ((! dexists) || (sts.st_mtime > dts.st_mtime));
+}
+
+
+Array<std::string> filesUsed() {
+ Array<std::string> f;
+ _internal::currentFilesUsed.getMembers(f);
+ return f;
+}
+
+}
+
+#ifndef G3D_WIN32
+ #undef _stat
+#endif
diff --git a/externals/g3dlite/G3D.lib/source/filter.cpp b/externals/g3dlite/G3D.lib/source/filter.cpp
new file mode 100644
index 00000000000..f6c0467820f
--- /dev/null
+++ b/externals/g3dlite/G3D.lib/source/filter.cpp
@@ -0,0 +1,32 @@
+/**
+ @file filter.cpp
+
+ @author Morgan McGuire, matrix@graphics3d.com
+ @created 2007-03-01
+ @edited 2007-03-01
+
+ Copyright 2000-2007, Morgan McGuire.
+ All rights reserved.
+ */
+#include "G3D/filter.h"
+
+namespace G3D {
+
+void gaussian1D(Array<float>& coeff, int N, float std) {
+ coeff.resize(N);
+ float sum = 0.0f;
+ for (int i = 0; i < N; ++i) {
+ float x = i - (N - 1) / 2.0f;
+ float p = -square(x / std) / 2.0f;
+ float y = exp(p);
+ coeff[i] = y;
+ sum += y;
+ }
+
+ for (int i = 0; i < N; ++i) {
+ coeff[i] /= sum;
+ }
+}
+
+
+} // namespace
diff --git a/externals/g3dlite/G3D.lib/source/format.cpp b/externals/g3dlite/G3D.lib/source/format.cpp
new file mode 100644
index 00000000000..d9d1b516393
--- /dev/null
+++ b/externals/g3dlite/G3D.lib/source/format.cpp
@@ -0,0 +1,164 @@
+/**
+ @file format.cpp
+
+ @author Morgan McGuire, graphics3d.com
+
+ @created 2000-09-09
+ @edited 2006-08-14
+*/
+
+#include "G3D/format.h"
+#include "G3D/platform.h"
+#include "G3D/System.h"
+
+#ifdef _MSC_VER
+ // disable: "C++ exception handler used"
+# pragma warning (push)
+# pragma warning (disable : 4530)
+#endif // _MSC_VER
+
+// If your platform does not have vsnprintf, you can find a
+// implementation at http://www.ijs.si/software/snprintf/
+
+namespace G3D {
+
+std::string __cdecl format(const char* fmt,...) {
+ va_list argList;
+ va_start(argList,fmt);
+ std::string result = vformat(fmt, argList);
+ va_end(argList);
+
+ return result;
+}
+
+#if defined(_MSC_VER) && (_MSC_VER >= 1300)
+// Both MSVC seems to use the non-standard vsnprintf
+// so we are using vscprintf to determine buffer size, however
+// only MSVC7 and up headers include vscprintf for some reason.
+std::string vformat(const char *fmt, va_list argPtr) {
+ // We draw the line at a 1MB string.
+ const int maxSize = 1000000;
+
+ // If the string is less than 161 characters,
+ // allocate it on the stack because this saves
+ // the malloc/free time.
+ const int bufSize = 161;
+ char stackBuffer[bufSize];
+
+ // MSVC does not support va_copy
+ int actualSize = _vscprintf(fmt, argPtr) + 1;
+
+ if (actualSize > bufSize) {
+
+ // Now use the heap.
+ char* heapBuffer = NULL;
+
+ if (actualSize < maxSize) {
+
+ heapBuffer = (char*)System::malloc(maxSize + 1);
+ _vsnprintf(heapBuffer, maxSize, fmt, argPtr);
+ heapBuffer[maxSize] = '\0';
+ } else {
+ heapBuffer = (char*)System::malloc(actualSize);
+ vsprintf(heapBuffer, fmt, argPtr);
+ }
+
+ std::string formattedString(heapBuffer);
+ System::free(heapBuffer);
+ return formattedString;
+ } else {
+
+ vsprintf(stackBuffer, fmt, argPtr);
+ return std::string(stackBuffer);
+ }
+}
+
+#elif defined(_MSC_VER) && (_MSC_VER < 1300)
+
+std::string vformat(const char *fmt, va_list argPtr) {
+ // We draw the line at a 1MB string.
+ const int maxSize = 1000000;
+
+ // If the string is less than 161 characters,
+ // allocate it on the stack because this saves
+ // the malloc/free time.
+ const int bufSize = 161;
+ char stackBuffer[bufSize];
+
+ // MSVC6 doesn't support va_copy, however it also seems to compile
+ // correctly if we just pass our argument list along. Note that
+ // this whole code block is only compiled if we're on MSVC6 anyway
+ int actualWritten = _vsnprintf(stackBuffer, bufSize, fmt, argPtr);
+
+ // Not a big enough buffer, bufSize characters written
+ if (actualWritten == -1) {
+
+ int heapSize = 512;
+ double powSize = 1.0;
+ char* heapBuffer = (char*)System::malloc(heapSize);
+
+ while ((_vsnprintf(heapBuffer, heapSize, fmt, argPtr) == -1) &&
+ (heapSize < maxSize)) {
+
+ heapSize = iCeil(heapSize * ::pow((double)2.0, powSize++));
+ heapBuffer = (char*)System::realloc(heapBuffer, heapSize);
+ }
+
+ heapBuffer[heapSize-1] = '\0';
+
+ std::string heapString(heapBuffer);
+ System::free(heapBuffer);
+
+ return heapString;
+ } else {
+
+ return std::string(stackBuffer);
+ }
+}
+
+#else
+
+// glibc 2.1 has been updated to the C99 standard
+std::string vformat(const char* fmt, va_list argPtr) {
+ // If the string is less than 161 characters,
+ // allocate it on the stack because this saves
+ // the malloc/free time. The number 161 is chosen
+ // to support two lines of text on an 80 character
+ // console (plus the null terminator).
+ const int bufSize = 161;
+ char stackBuffer[bufSize];
+
+ va_list argPtrCopy;
+ va_copy(argPtrCopy, argPtr);
+ int numChars = vsnprintf(stackBuffer, bufSize, fmt, argPtrCopy);
+ va_end(argPtrCopy);
+
+ if (numChars >= bufSize) {
+ // We didn't allocate a big enough string.
+ char* heapBuffer = (char*)System::malloc((numChars + 1) * sizeof(char));
+
+ debugAssert(heapBuffer);
+ int numChars2 = vsnprintf(heapBuffer, numChars + 1, fmt, argPtr);
+ debugAssert(numChars2 == numChars);
+ (void)numChars2;
+
+ std::string result(heapBuffer);
+
+ System::free(heapBuffer);
+
+ return result;
+
+ } else {
+
+ return std::string(stackBuffer);
+
+ }
+}
+
+#endif
+
+} // namespace
+
+#ifdef _MSC_VER
+# pragma warning (pop)
+#endif
diff --git a/externals/g3dlite/G3D.lib/source/g3dmath.cpp b/externals/g3dlite/G3D.lib/source/g3dmath.cpp
new file mode 100644
index 00000000000..95c9e16cc24
--- /dev/null
+++ b/externals/g3dlite/G3D.lib/source/g3dmath.cpp
@@ -0,0 +1,70 @@
+/**
+ @file g3dmath.cpp
+
+ @author Morgan McGuire, graphics3d.com
+
+ @created 2001-06-02
+ @edited 2004-02-24
+ */
+
+#include "G3D/g3dmath.h"
+#include <stdlib.h>
+
+namespace G3D {
+
+float gaussRandom(float mean, float stdev) {
+
+ // Using Box-Mueller method from http://www.taygeta.com/random/gaussian.html
+ // Modified to specify standard deviation and mean of distribution
+ float w, x1, x2;
+
+ // Loop until w is less than 1 so that log(w) is negative
+ do {
+ x1 = uniformRandom(-1.0, 1.0);
+ x2 = uniformRandom(-1.0, 1.0);
+
+ w = float(square(x1) + square(x2));
+ } while (w > 1.0f);
+
+ // Transform to gassian distribution
+ // Multiply by sigma (stdev ^ 2) and add mean.
+ return x2 * (float)square(stdev) * sqrtf((-2.0f * logf(w) ) / w) + mean;
+}
+
+
+int highestBit(uint32 x) {
+ // Binary search.
+ int base = 0;
+
+ if (x & 0xffff0000) {
+ base = 16;
+ x >>= 16;
+ }
+ if (x & 0x0000ff00) {
+ base += 8;
+ x >>= 8;
+ }
+ if (x & 0x000000f0) {
+ base += 4;
+ x >>= 4;
+ }
+
+ static const int lut[] = {-1,0,1,1,2,2,2,2,3,3,3,3,3,3,3,3};
+ return base + lut[x];
+}
+
+
+int iRandom(int low, int high) {
+ int r = iFloor(low + (high - low + 1) * (double)rand() / RAND_MAX);
+
+ // There is a *very small* chance of generating
+ // a number larger than high.
+ if (r > high) {
+ return high;
+ } else {
+ return r;
+ }
+}
+
+
+}
diff --git a/externals/g3dlite/G3D.lib/source/license.cpp b/externals/g3dlite/G3D.lib/source/license.cpp
new file mode 100644
index 00000000000..b362ad3f45f
--- /dev/null
+++ b/externals/g3dlite/G3D.lib/source/license.cpp
@@ -0,0 +1,70 @@
+/**
+ @file license.cpp
+
+ @author Morgan McGuire, graphics3d.com
+
+ @created 2004-04-15
+ @edited 2004-04-15
+*/
+
+#include "G3D/format.h"
+#include <string>
+
+namespace G3D {
+
+std::string license() {
+ return format(
+
+"This software is based in part on the PNG Reference Library which is\n"
+"Copyright (c) 2004 Glenn Randers-Pehrson\n\n"
+"This software is based in part on the work of the Independent JPEG Group.\n\n"
+"This software is based on part on the FFmpeg libavformat and libavcodec libraries\n"
+"(\"FFmpeg\", http://ffmpeg.mplayerhq.hu), which are included under the terms of the\n"
+"GNU Lesser General Public License (LGPL), (http://www.gnu.org/copyleft/lesser.html).\n\n"
+"%s"
+"This program uses the G3D Library (http://g3d-cpp.sf.net), which\n"
+"is licensed under the \"BSD\" Open Source license. The Graphics3D library\n"
+"source code is Copyright © 2000-2008, Morgan McGuire, All rights reserved.\n"
+"The BSD license requires the following statement regarding G3D:\n"
+"\n"
+"Redistribution and use in source and binary forms, with or without\n"
+"modification, are permitted provided that the following conditions\n"
+"are met:\n"
+"\n"
+"Redistributions of source code must retain the above copyright\n"
+"notice, this list of conditions and the following disclaimer.\n"
+"\n"
+"Redistributions in binary form must reproduce the above copyright\n"
+"notice, this list of conditions and the following disclaimer in the\n"
+"documentation and/or other materials provided with the distribution.\n"
+"\n"
+"Neither the name of Morgan McGuire, Brown University, Williams College, nor the names\n"
+"of the G3D contributors may be used to endorse or promote products derived\n"
+"from this software without specific prior written permission.\n"
+"\n"
+"THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS\n"
+"\"AS IS\" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT\n"
+"LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR\n"
+"A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT\n"
+"OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,\n"
+"SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT\n"
+"LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,\n"
+"DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY\n"
+"THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n"
+"(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n"
+"OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n"
+"\n\n"
+"G3D VERSION %d\n",
+
+#ifdef G3D_WIN32
+ "" // Win32 doesn't use SDL
+#else
+ "This software uses the Simple DirectMedia Layer library (\"SDL\",\n"
+ "http://www.libsdl.org), which is included under the terms of the\n"
+ "GNU Lesser General Public License, (http://www.gnu.org/copyleft/lesser.html).\n\n"
+#endif
+,
+G3D_VER);
+}
+
+}
diff --git a/externals/g3dlite/G3D.lib/source/prompt.cpp b/externals/g3dlite/G3D.lib/source/prompt.cpp
new file mode 100644
index 00000000000..1fb3bd4cfee
--- /dev/null
+++ b/externals/g3dlite/G3D.lib/source/prompt.cpp
@@ -0,0 +1,716 @@
+/**
+ @file prompt.cpp
+
+ @author Morgan McGuire, morgan@graphics3d.com
+ @cite Windows dialog interface by Max McGuire, mmcguire@ironlore.com
+ @cite Font setting code by Kurt Miller, kurt@flipcode.com
+
+ @created 2000-08-26
+ @edited 2005-01-14
+ */
+
+#include "G3D/prompt.h"
+#include "G3D/platform.h"
+
+#include <stdio.h>
+
+#ifdef G3D_WIN32
+# include <sstream>
+# include <conio.h>
+#else
+# define _getch getchar
+#endif
+
+#ifdef G3D_OSX
+# include <Carbon/Carbon.h>
+#endif
+
+namespace G3D {
+
+#ifdef G3D_WIN32
+
+namespace _internal {
+/**
+ Generic Win32 dialog facility.
+ @author Max McGuire
+ */
+class DialogTemplate {
+public:
+
+ DialogTemplate(LPCSTR caption, DWORD style,
+ int x, int y, int w, int h,
+ LPCSTR font = NULL, WORD fontSize = 8) {
+
+ usedBufferLength = sizeof(DLGTEMPLATE);
+ totalBufferLength = usedBufferLength;
+
+ dialogTemplate = (DLGTEMPLATE*)malloc(totalBufferLength);
+
+ dialogTemplate->style = style;
+
+ if (font != NULL) {
+ dialogTemplate->style |= DS_SETFONT;
+ }
+
+ dialogTemplate->x = (short)x;
+ dialogTemplate->y = (short)y;
+ dialogTemplate->cx = (short)w;
+ dialogTemplate->cy = (short)h;
+ dialogTemplate->cdit = 0;
+
+ dialogTemplate->dwExtendedStyle = 0;
+
+ // The dialog box doesn't have a menu or a special class
+ AppendData("\0", 2);
+ AppendData("\0", 2);
+
+ // Add the dialog's caption to the template
+
+ AppendString(caption);
+
+ if (font != NULL) {
+ AppendData(&fontSize, sizeof(WORD));
+ AppendString(font);
+ }
+ }
+
+ void AddComponent(LPCSTR type, LPCSTR caption, DWORD style, DWORD exStyle, int x, int y, int w, int h, WORD id) {
+
+ DLGITEMTEMPLATE item;
+
+ item.style = style;
+ item.x = (short)x;
+ item.y = (short)y;
+ item.cx = (short)w;
+ item.cy = (short)h;
+ item.id = id;
+
+ item.dwExtendedStyle = exStyle;
+
+ AppendData(&item, sizeof(DLGITEMTEMPLATE));
+
+ AppendString(type);
+ AppendString(caption);
+
+ WORD creationDataLength = 0;
+ AppendData(&creationDataLength, sizeof(WORD));
+
+ // Increment the component count
+ dialogTemplate->cdit++;
+
+ }
+
+
+ void AddButton(LPCSTR caption, DWORD style, DWORD exStyle, int x, int y, int w, int h, WORD id) {
+
+ AddStandardComponent(0x0080, caption, style, exStyle, x, y, w, h, id);
+
+ WORD creationDataLength = 0;
+ AppendData(&creationDataLength, sizeof(WORD));
+
+ }
+
+
+ void AddEditBox(LPCSTR caption, DWORD style, DWORD exStyle, int x, int y, int w, int h, WORD id) {
+
+ AddStandardComponent(0x0081, caption, style, exStyle, x, y, w, h, id);
+
+ WORD creationDataLength = 0;
+ AppendData(&creationDataLength, sizeof(WORD));
+
+ }
+
+
+ void AddStatic(LPCSTR caption, DWORD style, DWORD exStyle, int x, int y, int w, int h, WORD id) {
+
+ AddStandardComponent(0x0082, caption, style, exStyle, x, y, w, h, id);
+
+ WORD creationDataLength = 0;
+ AppendData(&creationDataLength, sizeof(WORD));
+
+ }
+
+
+ void AddListBox(LPCSTR caption, DWORD style, DWORD exStyle, int x, int y, int w, int h, WORD id) {
+
+ AddStandardComponent(0x0083, caption, style, exStyle, x, y, w, h, id);
+
+ WORD creationDataLength = sizeof(WORD) + 5 * sizeof(WCHAR);
+ AppendData(&creationDataLength, sizeof(WORD));
+
+ AppendString("TEST");
+
+ }
+
+
+ void AddScrollBar(LPCSTR caption, DWORD style, DWORD exStyle, int x, int y, int w, int h, WORD id) {
+
+ AddStandardComponent(0x0084, caption, style, exStyle, x, y, w, h, id);
+
+ WORD creationDataLength = 0;
+ AppendData(&creationDataLength, sizeof(WORD));
+
+ }
+
+
+ void AddComboBox(LPCSTR caption, DWORD style, DWORD exStyle, int x, int y, int w, int h, WORD id) {
+
+ AddStandardComponent(0x0085, caption, style, exStyle, x, y, w, h, id);
+
+ WORD creationDataLength = 0;
+ AppendData(&creationDataLength, sizeof(WORD));
+
+ }
+
+
+ /**
+ *
+ * Returns a pointer to the Win32 dialog template which the object
+ * represents. This pointer may become invalid if additional components
+ * are added to the template.
+ *
+ */
+ operator const DLGTEMPLATE*() const {
+ return dialogTemplate;
+ }
+
+ virtual ~DialogTemplate() {
+ free(dialogTemplate);
+ }
+
+protected:
+
+ void AddStandardComponent(WORD type, LPCSTR caption, DWORD style, DWORD exStyle,
+ int x, int y, int w, int h, WORD id, LPSTR font = NULL, WORD fontSize = 8) {
+
+ DLGITEMTEMPLATE item;
+
+ // DWORD align the beginning of the component data
+
+ AlignData(sizeof(DWORD));
+
+ item.style = style;
+ if (font != NULL) {
+ item.style |= DS_SETFONT;
+ }
+ item.x = (short)x;
+ item.y = (short)y;
+ item.cx = (short)w;
+ item.cy = (short)h;
+ item.id = id;
+
+ item.dwExtendedStyle = exStyle;
+
+ AppendData(&item, sizeof(DLGITEMTEMPLATE));
+
+ WORD preType = 0xFFFF;
+
+ AppendData(&preType, sizeof(WORD));
+ AppendData(&type, sizeof(WORD));
+
+ AppendString(caption);
+
+ if (font != NULL) {
+ AppendData(&fontSize, sizeof(WORD));
+ AppendString(font);
+ }
+
+ // Increment the component count
+ dialogTemplate->cdit++;
+ }
+
+
+ void AlignData(int size) {
+
+ int paddingSize = usedBufferLength % size;
+
+ if (paddingSize != 0) {
+ EnsureSpace(paddingSize);
+ usedBufferLength += paddingSize;
+ }
+
+ }
+
+ void AppendString(LPCSTR string) {
+
+ int length = MultiByteToWideChar(CP_ACP, 0, string, -1, NULL, 0);
+
+ WCHAR* wideString = (WCHAR*)malloc(sizeof(WCHAR) * length);
+ MultiByteToWideChar(CP_ACP, 0, string, -1, wideString, length);
+
+ AppendData(wideString, length * sizeof(WCHAR));
+ free(wideString);
+
+ }
+
+ void AppendData(const void* data, int dataLength) {
+
+ EnsureSpace(dataLength);
+
+ memcpy((char*)dialogTemplate + usedBufferLength, data, dataLength);
+ usedBufferLength += dataLength;
+
+ }
+
+ void EnsureSpace(int length) {
+ if (length + usedBufferLength > totalBufferLength) {
+ totalBufferLength += length * 2;
+
+ void* newBuffer = malloc(totalBufferLength);
+ memcpy(newBuffer, dialogTemplate, usedBufferLength);
+
+ free(dialogTemplate);
+ dialogTemplate = (DLGTEMPLATE*)newBuffer;
+ }
+ }
+
+private:
+
+ DLGTEMPLATE* dialogTemplate;
+
+ int totalBufferLength;
+ int usedBufferLength;
+
+};
+
+
+struct PromptParams {
+ const char* message;
+ const char* title;
+};
+
+/**
+ * Constants for controls.
+ */
+#define IDC_MESSAGE 1000
+#define IDC_BUTTON0 2000
+
+INT_PTR CALLBACK PromptDlgProc(HWND hDlg, UINT msg,
+ WPARAM wParam, LPARAM lParam) {
+ switch(msg) {
+ case WM_INITDIALOG:
+ {
+ PromptParams *params = (PromptParams*)lParam;
+ ::SetWindowTextA(::GetDlgItem(hDlg, IDC_MESSAGE), params->message);
+
+ ::SetFocus(::GetDlgItem(hDlg, IDC_BUTTON0));
+
+ SetWindowTextA(hDlg, params->title);
+
+ HFONT hfont =
+ CreateFontA(16, 0, 0, 0, FW_NORMAL,
+ FALSE, FALSE, FALSE,
+ ANSI_CHARSET,OUT_DEFAULT_PRECIS,CLIP_DEFAULT_PRECIS,
+ PROOF_QUALITY, FIXED_PITCH | FF_MODERN, "Courier New");
+
+ SendDlgItemMessage(hDlg, IDC_MESSAGE, WM_SETFONT, (WPARAM)hfont, MAKELPARAM(TRUE,0));
+
+
+ break;
+ }
+ case WM_COMMAND:
+ {
+ int choiceNumber = LOWORD(wParam) - IDC_BUTTON0;
+ if ((choiceNumber >= 0) && (choiceNumber < 10)) {
+ EndDialog(hDlg, choiceNumber);
+ return TRUE;
+ }
+ }
+
+ break;
+
+ case WM_NCDESTROY:
+ // Under SDL 1.2.6 we get a NCDESTROY message for no reason and the
+ // window is immediately closed. This is here to debug the problem.
+ (void)0;
+ break;
+
+ }
+
+ return FALSE;
+}
+
+}; // namespace _internal
+
+
+using namespace _internal;
+
+/**
+ * Show a dialog prompt.
+ */
+static int guiPrompt(
+ const char* windowTitle,
+ const char* prompt,
+ const char** choice,
+ int numChoices) {
+
+ int width = 280;
+ int height = 128;
+
+ const int buttonSpacing = 2;
+ const int buttonWidth =
+ (width - buttonSpacing * 2 -
+ buttonSpacing * (numChoices - 1)) / numChoices;
+ const int buttonHeight = 13;
+
+
+ DialogTemplate dialogTemplate(
+ windowTitle,
+ WS_CAPTION | DS_CENTER | WS_SYSMENU,
+ 10, 10, width, height,
+ "Tahoma");
+
+ dialogTemplate.AddEditBox(
+ "Edit", WS_VISIBLE | ES_READONLY | ES_OEMCONVERT | ES_MULTILINE | WS_TABSTOP, WS_EX_STATICEDGE,
+ 2, 2, width - 4, height - buttonHeight - 7, IDC_MESSAGE);
+
+ int i;
+ for (i = 0; i < numChoices; i++) {
+
+ int x = buttonSpacing + i * (buttonWidth + buttonSpacing);
+ int y = height - buttonHeight - buttonSpacing;
+
+ dialogTemplate.AddButton(choice[i], WS_VISIBLE | WS_TABSTOP, 0,
+ x, y, buttonWidth, buttonHeight, IDC_BUTTON0 + (WORD)i);
+
+ }
+
+ // Convert all single \n characters to \r\n for proper printing
+ int strLen = 0;
+ const char* pStr = prompt;
+
+ while (*pStr != '\0') {
+ if ((*pStr == '\n') && (pStr != prompt)) {
+ if (*(pStr - 1) != '\r') {
+ ++strLen;
+ }
+ }
+ ++strLen;
+ ++pStr;
+ }
+
+ char* newStr = (char*)malloc(strLen + 1);
+
+ const char* pStr2 = prompt;
+ char* pNew = newStr;
+
+ while (*pStr2 != '\0') {
+ if ((*pStr2 == '\n') && (pStr2 != prompt)) {
+ if (*(pStr2 - 1) != '\r') {
+ *pNew = '\r';
+ ++pNew;
+ }
+ }
+ *pNew = *pStr2;
+ ++pNew;
+ ++pStr2;
+ }
+
+ *pNew = '\0';
+
+ PromptParams params;
+ params.message = newStr;;
+ params.title = windowTitle;
+
+ HMODULE module = GetModuleHandle(0);
+ int ret = DialogBoxIndirectParam(module, dialogTemplate, NULL, (DLGPROC) PromptDlgProc, (DWORD)&params);
+
+ free(newStr);
+
+ /*
+ For debugging when DialogBoxIndirectParam fails:
+
+ // The last error value. (Which is preserved across the call).
+ DWORD lastErr = GetLastError();
+
+ // The decoded message from FormatMessage
+ LPTSTR formatMsg = NULL;
+
+ if (NULL == formatMsg) {
+ FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER |
+ FORMAT_MESSAGE_IGNORE_INSERTS |
+ FORMAT_MESSAGE_FROM_SYSTEM,
+ NULL,
+ lastErr,
+ 0,
+ (LPTSTR)&formatMsg,
+ 0,
+ NULL);
+ }
+
+ // Make sure the message got translated into something.
+ LPTSTR realLastErr;
+ if (NULL != formatMsg) {
+ realLastErr = formatMsg;
+ } else {
+ realLastErr = "Last error code does not exist.";
+ }
+
+ // Get rid of the allocated memory from FormatMessage.
+ if (NULL != formatMsg) {
+ LocalFree((LPVOID)formatMsg);
+ }
+ */
+
+ return ret;
+}
+
+#endif
+
+
+/**
+ * Show a prompt on stdout
+ */
+static int textPrompt(
+ const char* windowTitle,
+ const char* prompt,
+ const char** choice,
+ int numChoices) {
+
+ printf("\n___________________________________________________\n");
+ printf("%s\n", windowTitle);
+ printf("%s", prompt);
+
+ if (numChoices > 10) {
+ numChoices = 10;
+ }
+
+ int c = -1;
+ if (numChoices > 1) {
+ printf("\n");
+ printf("Choose an option by number:");
+
+ while ((c < 0) || (c >= numChoices)) {
+ printf("\n");
+
+ for (int i = 0; i < numChoices; i++) {
+ if (numChoices <= 3) {
+ printf(" (%d) %s ", i, choice[i]);
+ } else {
+ printf(" (%d) %s\n", i, choice[i]);
+ }
+ }
+
+ printf("\n> ");
+ c = _getch() - '0';
+
+ if ((c < 0) || (c >= numChoices)) {
+ printf("'%d' is not a valid choice.", c);
+ } else {
+ printf("%d", c);
+ }
+ }
+
+ } else if (numChoices == 1) {
+
+ printf("\nPress any key for '%s'...", choice[0]);
+ _getch();
+ c = 0;
+
+ } else {
+
+ printf("\nPress any key...");
+ _getch();
+ c = 0;
+ }
+
+ printf("\n___________________________________________________\n");
+ return c;
+}
+
+#ifdef G3D_OSX
+
+// See http://developer.apple.com/documentation/Carbon/Reference/Carbon_Event_Manager_Ref/index.html
+
+#define CARBON_COMMANDID_START 128
+#define CARBON_BUTTON_SPACING 12
+#define CARBON_BUTTON_HEIGHT 20
+#define CARBON_BUTTON_MINWIDTH 69
+#define CARBON_WINDOW_PADDING 20
+
+struct CallbackData {
+ WindowRef refWindow;
+
+ /** Index of this particular button */
+ int myIndex;
+
+ /** Buttons store their index into here when pressed. */
+ int* whichButton;
+};
+
+/**
+ Assumes that userData is a pointer to a carbon_evt_data_t.
+
+ */
+static pascal OSStatus DoCommandEvent(EventHandlerCallRef handlerRef, EventRef event, void* userData) {
+ // See http://developer.apple.com/documentation/Carbon/Conceptual/HandlingWindowsControls/index.html
+
+ CallbackData& callbackData = *(CallbackData*)userData;
+
+# pragma unused(handlerRef)
+
+ callbackData.whichButton[0] = callbackData.myIndex;
+
+ // If we get here we can close the window
+ QuitAppModalLoopForWindow(callbackData.refWindow);
+
+ // Return noErr to indicate that we handled the event
+ return noErr;
+}
+
+static int guiPrompt
+(const char* windowTitle,
+ const char* prompt,
+ const char** choice,
+ int numChoices) {
+
+ WindowRef window;
+
+ int iNumButtonRows = 0;
+ int iButtonWidth = -1;
+ OSStatus err = noErr;
+
+ // Determine number of rows of buttons
+ while (iButtonWidth < CARBON_BUTTON_MINWIDTH) {
+ ++iNumButtonRows;
+ iButtonWidth =
+ (550 - (CARBON_WINDOW_PADDING*2 +
+ (CARBON_BUTTON_SPACING*numChoices))) /
+ (numChoices/iNumButtonRows);
+ }
+
+ // Window Variables
+ Rect rectWin = {0, 0, 200 + ((iNumButtonRows-1) * (CARBON_BUTTON_HEIGHT+CARBON_BUTTON_SPACING)), 550}; // top, left, bottom, right
+ CFStringRef szWindowTitle = CFStringCreateWithCString(kCFAllocatorDefault, windowTitle, kCFStringEncodingUTF8);
+
+ window = NULL;
+
+ err = CreateNewWindow(kMovableAlertWindowClass, kWindowStandardHandlerAttribute|kWindowCompositingAttribute, &rectWin, &window);
+ err = SetWindowTitleWithCFString(window, szWindowTitle);
+ err = SetThemeWindowBackground(window, kThemeBrushAlertBackgroundActive, false);
+ assert(err == noErr);
+
+ // Event Handler Variables
+ EventTypeSpec buttonSpec[] = {{ kEventClassControl, kEventControlHit }, { kEventClassCommand, kEventCommandProcess }};
+ EventHandlerUPP buttonHandler = NewEventHandlerUPP(DoCommandEvent);
+
+ // Static Text Variables
+ Rect rectStatic = {20, 20, 152, 530};
+ CFStringRef szStaticText = CFStringCreateWithCString(kCFAllocatorDefault, prompt, kCFStringEncodingUTF8);
+ ControlRef refStaticText = NULL;
+ err = CreateStaticTextControl(window, &rectStatic, szStaticText, NULL, &refStaticText);
+
+ // Button Variables
+ Rect bounds[numChoices];
+ CFStringRef caption[numChoices];
+ ControlRef button[numChoices];
+
+ int whichButton=-1;
+ CallbackData callbackData[numChoices];
+
+ // Create the Buttons and assign event handlers
+ for (int i = 0; i < numChoices; ++i) {
+ bounds[i].top = 160 + ((CARBON_BUTTON_HEIGHT+CARBON_BUTTON_SPACING)*(i%iNumButtonRows));
+ bounds[i].right = 530 - ((iButtonWidth+CARBON_BUTTON_SPACING)*(i/iNumButtonRows));
+ bounds[i].left = bounds[i].right - iButtonWidth;
+ bounds[i].bottom = bounds[i].top + CARBON_BUTTON_HEIGHT;
+
+ // Convert the button captions to Apple strings
+ caption[i] = CFStringCreateWithCString(kCFAllocatorDefault, choice[i], kCFStringEncodingUTF8);
+
+ err = CreatePushButtonControl(window, &bounds[i], caption[i], &button[i]);
+ assert(err == noErr);
+
+ err = SetControlCommandID(button[i], CARBON_COMMANDID_START + i);
+ assert(err == noErr);
+
+ callbackData[i].refWindow = window;
+ callbackData[i].myIndex = i;
+ callbackData[i].whichButton = &whichButton;
+
+ err = InstallControlEventHandler(button[i], buttonHandler,
+ GetEventTypeCount(buttonSpec), buttonSpec,
+ &callbackData[i], NULL);
+ assert(err == noErr);
+ }
+
+ // Show Dialog
+ err = RepositionWindow(window, NULL, kWindowCenterOnMainScreen);
+ ShowWindow(window);
+ BringToFront(window);
+ err = ActivateWindow(window, true);
+
+ // Hack to get our window/process to the front...
+ ProcessSerialNumber psn = { 0, kCurrentProcess};
+ TransformProcessType(&psn, kProcessTransformToForegroundApplication);
+ SetFrontProcess (&psn);
+
+ // Run in Modal State
+ err = RunAppModalLoopForWindow(window);
+
+ // Dispose of Button Related Data
+ for (int i = 0; i < numChoices; ++i) {
+ // Dispose of controls
+ DisposeControl(button[i]);
+
+ // Release CFStrings
+ CFRelease(caption[i]);
+ }
+
+ // Dispose of Other Controls
+ DisposeControl(refStaticText);
+
+ // Dispose of Event Handlers
+ DisposeEventHandlerUPP(buttonHandler);
+
+ // Dispose of Window
+ DisposeWindow(window);
+
+ // Release CFStrings
+ CFRelease(szWindowTitle);
+ CFRelease(szStaticText);
+
+ // Return Selection
+ return whichButton;
+}
+
+#endif
+
+int prompt(
+ const char* windowTitle,
+ const char* prompt,
+ const char** choice,
+ int numChoices,
+ bool useGui) {
+
+ #ifdef G3D_WIN32
+ if (useGui) {
+ // Build the message box
+ return guiPrompt(windowTitle, prompt, choice, numChoices);
+ }
+ #endif
+
+ #ifdef G3D_OSX
+ if (useGui){
+ //Will default to text prompt if numChoices > 4
+ return guiPrompt(windowTitle, prompt, choice, numChoices);
+ }
+ #endif
+ return textPrompt(windowTitle, prompt, choice, numChoices);
+}
+
+
+void msgBox(
+ const std::string& message,
+ const std::string& title) {
+
+ const char *choice[] = {"Ok"};
+ prompt(title.c_str(), message.c_str(), choice, 1, true);
+}
+
+#ifndef G3D_WIN32
+ #undef _getch
+#endif
+
+};// namespace
+
diff --git a/externals/g3dlite/G3D.lib/source/stringutils.cpp b/externals/g3dlite/G3D.lib/source/stringutils.cpp
new file mode 100644
index 00000000000..a21fc1f377c
--- /dev/null
+++ b/externals/g3dlite/G3D.lib/source/stringutils.cpp
@@ -0,0 +1,231 @@
+/**
+ @file stringutils.cpp
+
+ @maintainer Morgan McGuire, matrix@graphics3d.com
+
+ @created 2000-09-09
+ @edited 2008-01-10
+*/
+
+#include "G3D/platform.h"
+#include "G3D/stringutils.h"
+#include "G3D/BinaryInput.h"
+#include <algorithm>
+
+namespace G3D {
+
+#ifdef _MSC_VER
+ // disable: "C++ exception handler used"
+# pragma warning (push)
+# pragma warning (disable : 4530)
+#endif
+#ifdef G3D_WIN32
+ const char* NEWLINE = "\r\n";
+#else
+ const char* NEWLINE = "\n";
+ static bool iswspace(int ch) { return (ch==' ' || ch=='\t' || ch=='\n'); }
+#endif
+
+bool beginsWith(
+ const std::string& test,
+ const std::string& pattern) {
+
+ if (test.size() >= pattern.size()) {
+ for (int i = 0; i < (int)pattern.size(); ++i) {
+ if (pattern[i] != test[i]) {
+ return false;
+ }
+ }
+ return true;
+ } else {
+ return false;
+ }
+}
+
+
+bool endsWith(
+ const std::string& test,
+ const std::string& pattern) {
+
+ if (test.size() >= pattern.size()) {
+ int te = test.size() - 1;
+ int pe = pattern.size() - 1;
+ for (int i = pattern.size() - 1; i >= 0; --i) {
+ if (pattern[pe - i] != test[te - i]) {
+ return false;
+ }
+ }
+ return true;
+ } else {
+ return false;
+ }
+}
+
+
+std::string wordWrap(
+ const std::string& input,
+ int numCols) {
+
+ std::string output;
+ size_t c = 0;
+ int len;
+
+ // Don't make lines less than this length
+ int minLength = numCols / 4;
+ size_t inLen = input.size();
+
+ bool first = true;
+ while (c < inLen) {
+ if (first) {
+ first = false;
+ } else {
+ output += NEWLINE;
+ }
+
+ if ((int)inLen - (int)c - 1 < numCols) {
+ // The end
+ output += input.substr(c, inLen - c);
+ break;
+ }
+
+ len = numCols;
+
+ // Look at character c + numCols, see if it is a space.
+ while ((len > minLength) &&
+ (input[c + len] != ' ')) {
+ len--;
+ }
+
+ if (len == minLength) {
+ // Just crop
+ len = numCols;
+
+ }
+
+ output += input.substr(c, len);
+ c += len;
+ if (c < input.size()) {
+ // Collapse multiple spaces.
+ while ((input[c] == ' ') && (c < input.size())) {
+ c++;
+ }
+ }
+ }
+
+ return output;
+}
+
+
+int stringCompare(
+ const std::string& s1,
+ const std::string& s2) {
+
+ return stringPtrCompare(&s1, &s2);
+}
+
+
+int stringPtrCompare(
+ const std::string* s1,
+ const std::string* s2) {
+
+ return s1->compare(*s2);
+}
+
+
+std::string toUpper(const std::string& x) {
+ std::string result = x;
+ std::transform(result.begin(), result.end(), result.begin(), toupper);
+ return result;
+}
+
+
+std::string toLower(const std::string& x) {
+ std::string result = x;
+ std::transform(result.begin(), result.end(), result.begin(), tolower);
+ return result;
+}
+
+
+Array<std::string> stringSplit(
+ const std::string& x,
+ char splitChar) {
+
+ Array<std::string> out;
+
+ // Pointers to the beginning and end of the substring
+ const char* start = x.c_str();
+ const char* stop = start;
+
+ while ((stop = strchr(start, splitChar))) {
+ out.append(std::string(start, stop - start));
+ start = stop + 1;
+ }
+
+ // Append the last one
+ out.append(std::string(start));
+
+ return out;
+}
+
+
+std::string stringJoin(
+ const Array<std::string>& a,
+ char joinChar) {
+
+ std::string out;
+
+ for (int i = 0; i < (int)a.size() - 1; ++i) {
+ out += a[i] + joinChar;
+ }
+
+ if (a.size() > 0) {
+ return out + a.last();
+ } else {
+ return out;
+ }
+}
+
+
+std::string stringJoin(
+ const Array<std::string>& a,
+ const std::string& joinStr) {
+
+ std::string out;
+
+ for (int i = 0; i < (int)a.size() - 1; ++i) {
+ out += a[i] + joinStr;
+ }
+
+ if (a.size() > 0) {
+ return out + a.last();
+ } else {
+ return out;
+ }
+}
+
+
+std::string trimWhitespace(
+ const std::string& s) {
+
+ size_t left = 0;
+
+ // Trim from left
+ while ((left < s.length()) && iswspace(s[left])) {
+ ++left;
+ }
+
+ int right = s.length() - 1;
+ // Trim from right
+ while ((right > (int)left) && iswspace(s[right])) {
+ --right;
+ }
+
+ return s.substr(left, right - left + 1);
+}
+
+}; // namespace
+
+#undef NEWLINE
+#ifdef _MSC_VER
+# pragma warning (pop)
+#endif
diff --git a/externals/g3dlite/G3D.lib/source/uint128.cpp b/externals/g3dlite/G3D.lib/source/uint128.cpp
new file mode 100644
index 00000000000..450009a5cff
--- /dev/null
+++ b/externals/g3dlite/G3D.lib/source/uint128.cpp
@@ -0,0 +1,155 @@
+/**
+ @file uint128.cpp
+
+ @maintainer Morgan McGuire, matrix@graphics3d.com
+ @author Kyle Whitson
+
+ @created 2008-07-17
+ @edited 2008-07-17
+ */
+
+#include "G3D/uint128.h"
+
+namespace G3D {
+
+/** Adds two 64-bit integers, placing the result and the overflow into 64-bit integers.*/
+static void addAndCarry(const uint64& _a, const uint64& _b, uint64& carry, uint64& result) {
+
+ // Break each number into 4 32-bit chunks. Since we are using uints, right-shifting will fill with zeros.
+ // This eliminates the need to and with 0xFFFFFFFF.
+ uint32 a [2] = {_a & 0xFFFFFFFF, _a >> 32};
+ uint32 b [2] = {_b & 0xFFFFFFFF, _b >> 32};
+
+ uint64 tmp = uint64(a[0]) + b[0];
+
+ result = tmp & 0xFFFFFFFF;
+ uint32 c = tmp >> 32;
+
+ tmp = uint64(c) + a[1] + b[1];
+ result += tmp << 32;
+ carry = (tmp >> 32);
+}
+
+/** Multiplies two unsigned 64-bit integers, placing the result into one 64-bit int and the overflow into another.*/
+void multiplyAndCarry(const uint64& _a, const uint64& _b, uint64& carry, uint64& result) {
+
+ // Break each number into 4 32-bit chunks. Since we are using uints, right-shifting will fill with zeros.
+ // This eliminates the need to and with 0xFFFFFFFF.
+ uint32 a [2] = {_a & 0xFFFFFFFF, _a >> 32};
+ uint32 b [2] = {_b & 0xFFFFFFFF, _b >> 32};
+
+ uint64 prod [2][2];
+ for(int i = 0; i < 2; ++i) {
+ for(int j = 0; j < 2; ++j) {
+ prod[i][j] = uint64(a[i]) * b[j];
+ }
+ }
+
+ // The product of the low bits of a and b will always fit into the result
+ result = prod[0][0];
+
+ // The product of the high bits of a and b will never fit into the result
+ carry = prod[1][1];
+
+ // The high 32 bits of prod[0][1] and prod[1][0] will never fit into the result
+ carry += prod[0][1] >> 32;
+ carry += prod[1][0] >> 32;
+
+ uint64 tmp;
+ addAndCarry(result, (prod[0][1] << 32), tmp, result);
+ carry += tmp;
+ addAndCarry(result, (prod[1][0] << 32), tmp, result);
+ carry += tmp;
+}
+
+
+uint128::uint128(const uint64& hi, const uint64& lo) : hi(hi), lo(lo) {
+}
+
+uint128::uint128(const uint64& lo) : hi(0), lo(lo) {
+}
+
+uint128& uint128::operator+=(const uint128& x) {
+
+ G3D::uint64 carry;
+ addAndCarry(lo, x.lo, carry, lo);
+
+ // Adding the carry will change hi. Save the old hi bits in case this == x.
+ const uint64 xHi = x.hi;
+ hi += carry;
+ hi += xHi;
+ return *this;
+}
+
+uint128& uint128::operator*=(const uint128& x) {
+
+ // The low bits will get overwritten when doing the multiply, so back up both (in case &x == this)
+ const uint64 oldLo = lo;
+ const uint64 oldXLo = x.lo;
+
+ G3D::uint64 carry;
+ multiplyAndCarry(oldLo, oldXLo, carry, lo);
+
+ // Overflow doesn't matter here because the result is going into hi - any overflow will exceed the capacity of a 128-bit number
+ // Note: hi * x.hi will always overflow, since (x * 2^64) * (y * 2^64) = x*y*(2^128). The largest number expressable in 128 bits is
+ // 2^128 - 1.
+ hi = carry + (oldLo * x.hi) + (hi * oldXLo);
+
+ return *this;
+}
+
+uint128& uint128::operator^=(const uint128& x) {
+ hi ^= x.hi;
+ lo ^= x.lo;
+ return *this;
+}
+
+uint128& uint128::operator&=(const uint128& x) {
+ hi &= x.hi;
+ lo &= x.lo;
+ return *this;
+}
+
+uint128& uint128::operator|=(const uint128& x) {
+ hi |= x.hi;
+ lo |= x.lo;
+ return *this;
+}
+
+bool uint128::operator==(const uint128& x) {
+ return (hi == x.hi) && (lo == x.lo);
+}
+
+uint128& uint128::operator>>=(const int x) {
+
+ //Before shifting, mask out the bits that will be shifted out of hi.
+ //Put a 1 in the first bit that will not be lost in the shift, then subtract 1 to get the mask.
+ uint64 mask = ((uint64)1L << x) - 1;
+ uint64 tmp = hi & mask;
+ hi >>= x;
+
+ //Shift lo and add the bits shifted down from hi
+ lo = (lo >> x) + (tmp << (64 - x));
+
+ return *this;
+}
+
+uint128& uint128::operator<<=(const int x) {
+
+ //Before shifting, mask out the bits that will be shifted out of lo.
+ //Put a 1 in the last bit that will be lost in the shift, then subtract 1 to get the logical inverse of the mask.
+ //A bitwise NOT will then produce the correct mask.
+ uint64 mask = ~((((uint64)1L) << (64 - x)) - 1);
+ uint64 tmp = lo & mask;
+ lo <<= x;
+
+ //Shift hi and add the bits shifted up from lo
+ hi = (hi << x) + (tmp >> (64 - x));
+
+ return *this;
+}
+
+uint128 uint128::operator&(const uint128& x) {
+ return uint128(hi & x.hi, lo & x.lo);
+}
+}
diff --git a/externals/g3dlite/delme b/externals/g3dlite/delme
deleted file mode 100644
index e69de29bb2d..00000000000
--- a/externals/g3dlite/delme
+++ /dev/null
diff --git a/externals/g3dlite/doc-files/changelog.dox b/externals/g3dlite/doc-files/changelog.dox
new file mode 100644
index 00000000000..fd52b99f271
--- /dev/null
+++ b/externals/g3dlite/doc-files/changelog.dox
@@ -0,0 +1,1872 @@
+/**
+ @page changelog Change Log
+
+<P>
+
+ Major version numbers introduce API changes that are not backwards
+ compatible. Minor versions are backwards compatible to the
+ previous major release, except for critical bug fixes. Deprecated functionality
+ will be supported until (at least) the next major release.
+
+
+<hr>
+
+ <P>
+ Changes in 7.01:
+ <ul>
+ <li> Video file reading and writing via FFmpeg added
+ <li> Added computeBounds method to ArticulatedModel::Part that calls computeBounds on each TriList. Changed updateAll to automatically call computeBounds [Kyle]
+ <li> Added constructor to Matrix4 to construct a matrix from an upper-left 3x3 submatrix and an upper-right 3x1 submatrix [Kyle]
+ <li> <b>Incompatible change</b>: RegistryUtil functions now require an explicit value parameter instead of extracting the value from the key string.
+ <li> <b>Incompatible change</b>:GApp now calls the onLogic handler before the simulation handlers but after the user input and network handlers
+ <li> <b>Incompatible change</b>: Changed GHashCode and other functors to traits. See \link guidenewuser \endlink. Added typedefs and adapters to make this mostly backwards compatible.
+ <li> Added parallax occlusion mapping to G3D::SuperShader (specify Material::parallaxSteps > 1)
+ <li> Added normal mapping to G3D::SuperShader (specify Material::parallaxSteps == 0)
+ <li> G3D::Texture resizes textures that exceed the device maximum size
+ <li> G3D::Array now allows control over MIN_ELEMENTS and MIN_BYTES using template parameters
+ <li> Clarified G3D::Any file format in documentation
+ <li> G3D::Texture::PreProcess::gammaAdjust
+ <li> G3D::ShadowMap::lightProjection(), G3D::ShadowMap::lightFrame()
+ <li> Added Barycentric coordinates to CollisionDetection::isPointInTriangle
+ <li> G3D::RenderDevice::beginOpenGL, G3D::RenderDevice::endOpenGL
+ <li> PointAABSPTree::clearData
+ <li> AABSPTree -> KDTree
+ <li> GApp now allows the MidgetManager to process events in onEvent before the GApp::onEvent executes
+ <li> Added spotlight support to SuperShader
+ <li> Switched SuperShader to use ph
+ <li> Adjusted standard deviation used in G3D::GaussianBlur to provide smoother filtering
+ <li> Put HashTrait and EqualsTrait in their own headers separate from Table.h
+ <li> ArticulatedModel::facet
+ <li> Renamed GWindow to G3D::OSWindow
+ <li> ReferenceCountedPointer now asserts that the pointer is not NULL on method invocation
+ <li> G3D::ShadowMap now computes appropriate matrices for spot lights
+ <li> Added ImageFormat::convert [Danny and Kyle]
+ <li> 3- and 4- argument min and max
+ <li> G3D::GaussianBlur now correctly sets the output viewport
+ <li> G3D::Framebuffer::clear
+ <li> IFSModel and ArticulatedModel now load Princeton Shape Benchmark OFF files.
+ <li> G3D::Any CoordinateFrame now serialized using angles
+ <li> CameraControlWindow now prints angles in degrees
+ <li> ImageXXX classes now have a format() method
+ <li> OSWindow::create
+ <li> MeshAlg::toIndexedTriList now supports TRIANGLE_FAN input.
+ <li> Tuned Table and hash functions for performance [Kyle & Morgan]
+ <li> GEvent::toString
+ <li> G3D::TextInput now treats characters with ASCII code greater than 127 as symbols
+ <li> G3D::ThreadSet
+ <li> G3D::Texture::white
+ <li> G3D::Matrix4::upper3x3
+ <li> G3D::Matrix4::homoMul
+ <li> ArticulateModel::fromFile now takes a Matrix4 instead of a CoordinateFrame to allow arbitrary linear transformations.
+ <li> ArticulatedModel::createCornellBox
+ <li> Material::createDiffuse
+ <li> ImageFormat::convert
+ <li> G3D::filenameBase
+ <li> Removed SDL_SysWMEvent, which was never supported by GEvent anyway
+ <li> Removed TextureFormat::SAME_AS_SCREEN to break dependence on OpenGL
+ <li> TextureFormat has been renamed to G3D::ImageFormat and moved into G3D.lib
+ <li> Added variable time control points to G3D::Spline
+ <li> Gui controls now have configureable GuiControl::setCaptionSize
+ <li> Gui controls now default to no indent if the caption is "" (use " " for indent with no caption)
+ <li> G3D::GuiContainer
+ <li> G3D::GThread::started
+ <li> buildg3d installation arguments changed--see
+ <li> G3D::Vector3int32
+ <li> GuiButton now accepts an optional callback function/method
+ <li> FileDialog now accepts an extra "note" argument
+ <li> FileDialog::getFilename non-static to support subclassing
+ <li> System::currentDateString
+ <li> Expanded G3D::ArticulatedModel documentation
+ <li> Build system now executes on multiple processors (about 1.8x speedup for dual-core)
+ <li> Build system now caches dependencies (about 5x speedup for small incremental builds)
+ <li> Patched LOAD_EXTENSION to work around gcc pointer-to-function cast issues
+ <li> Tool buttons added to a G3D::GuiPane automatically align to the previous one.
+ <li> Added invisible GuiPane style
+ <li> G3D::uint128 [Kyle]
+ <li> Increased BSPMap rendering by 10% by reducing state changes
+ <li> Added prompt argument to FileDialog::getFilename
+ <li> G3D::PosedModel::getBoxBounds on an array
+ <li> G3D::PosedModel::getSphereBounds on an array
+ <li> Changed RenderDevice::screenshot to save .png instead of .jpg files
+ <li> G3D::SuperShader now supports a customMap and customConstant for experimenting with shaders.
+ <li> G3D::SuperShader now does not ever light the "back" of a bump-mapped poly, even if the bumps should create a light-facing surface
+ <li> G3D::Material promoted to its own class (was G3D::SuperShader::Material)
+ <li> G3D::Matrix2
+ <li> G3D::VertexAndPixelShader::ArgList::size
+ <li> G3D::pathConcat
+ <li> G3D::WidgetManager::moveWidgetToBack
+ <li> SuperShader / NonShadowed.pix now uses arrays of lights instead of separate variables
+ <li> Reduced cost of release-mode Shader argument validation
+ <li> G3D::PosedModel::sortAndRender now performs view-frustum culling of objects
+ <li> G3D::Draw::lighting for visualizing light sources
+ <li> G3D::SuperShader::Pass::purgeCache
+ <li> G3D::GuiSlider::setRange
+ <li> G3D::GuiPane::addPane no longer takes a
+ <li> G3D::VertexAndPixelShader::ArgList::remove
+ <li> Optimized G3D::Matrix::pseudoInverse; now about 2x faster
+ <li> G3D::GLight::effectSphere
+ <li> G3D::GuiWindow::moveTo
+ <li> G3D::GuiWindow::setEnabled, enabled
+ <li> G3D::GuiButton now sizes to its caption
+ <li> G3D::GuiSlider now fires events on change and drag
+ <li> G3D::Shader arguments (in G3D::VertexAndPixelShader::ArgList) may now be "optional"
+ <li> G3D::GLight::point now has quadratic attenuation by default.
+ <li> G3D::ImageFormat::name
+ <li> g3dmath.h now includes inttypes.h on gcc and simulates it on visual studio
+ <li> G3D::RenderDevice::cullFace
+ <li> G3D::LineSegment2D::intersection
+ <li> G3D::BinaryInput::setEndian
+ <li> G3D::GEvent::MOUSE_BUTTON_CLICK
+ <li> Generalized ShadowMap to work with spotlights as well as directional lights
+ <li> G3D::GLCaps::supportsTexture, G3D::GLCaps::supportsRenderBuffer
+ <li> Opaque G3D::ArticulatedModels now support more than 2 non-shadow casting light sources
+ <li> Added proof symbol parsing to TextInput [Corey Taylor]
+ <li> Added G3D::AABox::corner() to match G3D::Box::corner() [Corey Taylor]
+ <li> OS X: G3D::CarbonWindow [Casey]
+ <li> OS X: iCompile now generates OS X application bundles and dmg files
+ <li> OS X build no longer depends on X11
+ <li> G3D::FileDialog
+ <li> G3D::Table now allows overriding the default equality operator for keys
+ <li> <b>Incompatible change</b>: GApp::onBeforeSimulation now allows mutation of the timesteps
+ <li> <b>Incompatible change</b>: Merged GApp::simTime and idealSimTime (the sim time is now idealized)
+ <li> cmake now generates project files for Xcode, MinGW, and all Visual Studio versions [Corey Taylor]
+ <li> OS X: icompile and buildg3d now generate universal binaries on Intel machines
+ <li> G3D::PosedModel::objectSpaceTangents
+ <li> G3D::IFSModel::fromData
+ <li> G3D::MeshAlg::generateGrid
+ <li> G3D::BinaryOutput::ok()
+ <li> G3D::generateFilenameBase
+ <li> G3D::IFSModel::fromFile now defaults to not welding for improved performance
+ <li> G3D::IFSModel members are now protected to allow subclassing
+ <li> Removed G3D::uint in favor of G3D::uint32 [Corey Taylor]
+ <li> Added G3D::GMaterial(TextureRef) constructor
+ <li> Made G3D::GMaterial fields floats
+ <li> G3D::GuiControl::setCaption, G3D::GuiWindow::setCaption
+ <li> G3D::GuiControl can now be subclassed for custom user-defined controls
+ <li> G3D::GuiTheme::renderCanvas
+ <li> G3D::GuiTheme::pauseRendering, G3D::GuiTheme::resumeRendering
+ <li> G3D::PosedModel::sortAndRender
+ <li> G3D::Framebuffer can now attach cube map faces
+ <li> System::describeSystem now prints current working directory and application name
+ <li> Added /usr/local/-G3D dir- to system data file path [Kai Schroeder]
+ <li> Various patches for detecting new CPUs in System.cpp [Corey Taylor]
+ <li> g3d_Index macro now available in G3D::Shader GLSL code
+ <li> G3D::BackgroundWidget
+ <li> G3D::TriangleShape
+ <li> Fix: <B>incompatible change</b> OSWindow::Settings::asynchronous is now spelled correclty, with two "n"s
+ <li> Fix: Fixes for point-in-triangle and moving-sphere-fixed-tri; previous code projected onto the wrong axes, so barycentric coords were wrong for nearly vertical triangles.
+ <li> Fix: Changed some doubles to floats in G3D::Triangle
+ <li> Fix: Changed all of the isXXX(char) methods to take unsigned char arguments so that they can parse extended symbols
+ <li> Fix: AABSPTree::deserializeStructure was missing a return statement [Derex]
+ <li> Fix: Draw::plane was drawing the plane reflected through the origin [Macklin Chaffee]
+ <li> Fix: Added template parameters to friends in AABSPTree and PointAABSPTree [expiring_frog]
+ <li> Fix: System::findDataFile uses data directory set by GApp
+ <li> Fix: AtomicInt32 decrement returns int32 instead of uint32
+ <li> Fix: OS X function keys now work correctly under CarbonWindow
+ <li> Fix: OS X modifier keys now work correctly under CarbonWindow
+ <li> Fix: OS X arrow keys now work correctly under CarbonWindow
+ <li> Fix: Rewrote buildg3d to fix many longstanding bugs, including mismatched 'bin' directories and confusion about the 'install' target
+ <li> Fix: gfxmeter reports now format correctly regardless of monitor width
+ <li> Fix: patch to initialize correctly on the Mesa library, which crashes when requesting DEPTH24_STENCIL8
+ <li> Fix: stringSplit now works correctly for adjacent split characters [Akita Noek]
+ <li> Fix: Draw::axes labels now obey current viewport
+ <li> Fix: GuiWindow now loses focus when hidden
+ <li> Fix: GFont::draw2D now computes correct horizontal bounds on text
+ <li> Fix: GuiPane no longer renders when invisible
+ <li> Fix: Clicking off of all GuiWindows makes none of them have focus
+ <li> Fix: Win32Window now allows windows programmatically positioned anywhere on a multiple monitor screen
+ <li> Fix: Win32Window now does not fail when dragging a GL context between multiple monitors
+ <li> Fix: SuperShader now correctly lights bump-mapped surfaces in tangent space [Kyle Whitson]
+ <li> Fix: GuiPane now renders its caption
+ <li> Fix: Rect2D::border now grows the correct way (positive = grow)
+ <li> Fix: Added % operator to TextInput
+ <li> Fix: Added multi-line printing to GConsole
+ <li> Fix: G3D::Texture can now create empty cube maps
+ <li> Fix: G3D::Table iterator now correctly parameterized on hashfunction and equality function as well as key and value
+ <li> Fix: G3D::Table now passes Values by reference when setting them, avoiding one copy
+ <li> Fix: various Framebuffer/empty texture initialization bugs on ATI cards
+ <li> Fix: uniform arrays for GLSL
+ <li> Fix: All aliasing warnings have been fixed no longer needs -fno-strict-aliasing [Corey Taylor]
+ <li> Fix: [ 1829704 ] debugAssert in Array::operator[](unsigned int n) wrong
+ <li> Fix: GuiWindow::pack now recursively packs all child panes
+ <li> Fix: patch to continue build when javac isn't found on both windows and linux [Corey Taylor]
+ <li> Fix: [ 1599139 ] Fixes to make buildg3d work on non C:\ windows systems [Patrick Burke]
+ <li> Fix: Added faster overloads of GImage::stripAlpha() and GImage::insertRedAsAlpha() [Corey Taylor]
+ <li> Fix: GImage::save() with odd-widths bmp files [Corey Taylor]
+ <li> Fix: Draw::capule renders properly (capule was not visible) [Corey Taylor]
+ <li> Fix: Patched ShadowMap to work around ATI and OS X driver shadow map bugs.
+ <b>Incompatible change:</b>Required changing several interfaces to take ShadowMapRef arguments.
+ <li> Fix: GCamera::Frustum was facing backwards
+ <li> Fix: [ 1774479 ] texture glformats wrong (caused incorrect font rendering on Intel)
+ <li> Fix: ArticulatedModel static methods do not force loading of shaders unless an ArticulatedModel has actually been loaded.
+ <li> Fix: RenderDevice::setAlphaWrite/setColorWrite implemented correctly
+ <li> Fix: Implemented ImageFormat::fromCode
+ <li> Fix: [ 1766509 ] Texture doesn't handle 3D textures correctly
+ <li> Fix: Separate bool, float, and int back ends for GLSL shaders
+ </ul>
+
+<hr>
+ <P>
+ Changes in 7.00:
+ <ul>
+ <li> Upgraded to iCompile 0.5.3. Requires users to delete their old ice.txt and ~/.icompile files
+ <li> Key to toggle the user camera is now F2 (was Tab)
+ <li> Support for g++ 4.1 [Corey]
+ <li> Patches for 64-bit compilation [Corey]
+ <li> WinMain is now compiled as C++ code (fixed some "manifest" errors on certain VC8 installations)
+ <li> G3D::ShadowMap
+ <li> Cleaned up and documented G3D::GCamera project/unproject API [Jeff Marsceill]
+ <li> Made G3D::MD2Model::Pose easier to use
+ <li> G3D::Matrix
+ <li> Added G3D::Texture methods for reading back color and depth textures
+ <li> G3D::RenderDevice::getFixedFunctionLighting
+ <li> G3D::debugPrintf -> G3D::consolePrintf
+ <li> G3D::GApp::debugPrintf -> G3D::screenPrintf
+ <li> Reduced ArticulatedModel and MD2Model push/popState calls
+ <li> G3D::Shader now defines platform and graphics vendor macros
+ <li> User camera control requires right mouse click to move
+ <li> G3D::AnyVal::fromFile, G3D::AnyVal::load, G3D::AnyVal::save
+ <li> G3D::AnyVal now uses copy-on-mutate for fast Array/Table copies
+ <li> G3D::AnyVal no longer separates table entries with ";"
+ <li> G3D::AnyVal now provides casts to basic data types
+ <li> G3D::AnyVal G3D::Rect2D and G3D::AABox types
+ <li> G3D::NetListener, G3D::ReliableConduit, and G3D::LightweightConduit can now be created without an explicit NetworkDevice pointer
+ <li> GApp::onGraphics now takes posed model arguments
+ <li> G3D::RenderDevice::beginFrame no longer executes a pushState-- state will carry over between frames
+ <li> G3D::GUniqueID
+ <li> GApp::onPose
+ <li> All classes that read from files can now read data inside zipfiles.
+ <li> Changed G3D::hashCode(T) to ::GHashCode<T>(T) [Corey]
+ <li> Removed "G3D::" from the printed portion of the documentation index to make it easier to read.
+ <li> OSWindow::fireEvent for inserting user events into the queue
+ <li> G3D::logPrintf
+ <li> System::findDataFile
+ <li> On OS X, G3D::FirstPersonManipulator now treats ctrl-click as right click
+ <li> Increased mouse sensitivity of G3D::FirstPersonManipulator on OS X
+ <li> System::getClipboardText, System::setClipboardText
+ <li> Widget::window() accessor
+ <li> Replaced SDLEvent with G3D::GEvent
+ <li> Increased GFont rendering performance, added GFont::send2DQuads for fast rendering of large amounts of text
+ <li> G3D::GFont now supports fonts with 256 characters (.fnt file format version 2)
+ <li> Upgraded to the OpenGL 2.0 glext/glxext/wglext headers
+ <li> G3D::GuiTheme, G3D::GuiText
+ <li> G3D::UprightFrame, G3D::UprightSpline
+ <li> G3D::Spline
+ <li> G3D::RenderDevice::clip2D
+ <li> G3D::Win32Window now returns events for up to 5 mouse buttons
+ <li> Significantly changed GEvent delivery and Widget mechanisms to incorporate notions of focus--see upgrade.html
+ <li> Motion events (joystick/mouse) can no longer be cancelled by Widget::onEvent
+ <li> Mouse motion events for all platforms
+ <li> G3D::PosedModel::Ref::sendGeometry
+ <li> G3D::RenderDevice::pushState(FramebufferRef)
+ <li> G3D::Texture::PreProcess::computeNormalMap
+ <li> direct.h is now included by fileutils.h on Win32 to ensure that chdir, mkdir, etc. are available
+ <li> Changed distribution directories to place include, lib, and bin under a directory named after the platform
+ and all other files one directory up.
+ <li> G3D::GCamera::unproject
+ <li> Made G3D::Ray non-virtual for efficiency
+ <li> RenderDevice::alphaWrite now defaults to true
+ <li> Changed G3D install directory from g3d-7_00 to g3d-7.00
+ <li> On a GL 2.0 or greater driver, G3D::GLCaps now assumes the presence of all GL2 extensions even if they are not explicitly listed by the card [Corey Taylor]
+ <li> debugPrintf now flushes stderr on Unix systems
+ <li> G3D::Lighting::fromSky
+ <li> G3D::Texture::newGLTexture2D [Corey Taylor]
+ <li> Added 10-bit cinema texture formats [Corey Taylor]
+ <li> Added G3D::ArticulatedModel to the core (includes 3DS loading)
+ <li> Added G3D::SuperShader to the core
+ <li> Merged GApp and GApplet into GApp to make the common case easier to implement
+ <li> Removed GApp::debugMode options, removed "debug" prefix from most fields.
+ <li> G3D::zipfileExists for testing if a filename path contains a .zip to be opened [Eric]
+ <li> G3D::isZipfile tests the header of a file to see if it is a .zip [Eric]
+ <li> G3D::zipRead and G3D::zipClose to open and close .zip files [Eric]
+ <li> G3D::fileExists supports filename paths that contain .zip [Eric]
+ <li> G3D::fileLength supports filename paths that contain .zip [Eric]
+ <li> G3D::getFiles and G3D::getDirs support filename paths that contain .zip [Eric]
+ <li> Texture::isSupportedImage - static method, returns true if a filename exists and is compatible with Texture [Eric]
+ <li> Viewer (tool) - Allows drag and drop viewing of many supported file formats [Eric]
+ <li> GFXMeter (tool) - Updated for 7.00 compatibility: GApplet structure removed [Eric]
+ <li> OSWindow::Settings has an allowMaximize field. Win32Window will have an activated 'Maximize' button if true [Eric]
+ <li> GApp2::debugCamera -> GApp2::defaultCamera
+ <li> GApp2::debugController -> GApp2::defaultController
+ <li> Made GApp2::debugText private
+ <li> Added g3d_WorldLight0 to the G3D shader extensions
+ <li> Added Shader support for GL_FLOAT_MAT3_ARB uniforms
+ <li> Tab (command completion) no longer auto-repeats in GConsole
+ <li> GConsole now limits the key repeat rate to the framerate
+ <li> Removed GApp::Settings::useNetwork because it was no longer needed--Win32 does not trigger firewall checks anymore
+ <li> Optimized AABSPTree balance and queries; now about 20% faster than 6.10, but requires 30 bytes more memory per member
+ <li> G3D::Array::pop now shrinks the underlying array by default (Array::popRemove does not shrink the array and is faster)
+ <li> Fast O(<i>n</i>), non-destructive G3D::Array::partition and G3D::Array::medianPartition
+ <li> Increased ReliableConduit read attempts before timeout from 10 to 100
+ <li> G3D::GImage::pixel1 now returns G3D::Color1uint8*
+ <li> G3D::Color1, G3D::Color1uint8
+ <li> G3D::Image1, G3D::Image1uint8
+ <li> G3D::Image3, G3D::Image3uint8
+ <li> G3D::Image4, G3D::Image4uint8
+ <li> BinaryInput::flipEndian32, BinaryInput::flipEndian16
+ <li> Texture::createEmpty now intializes invertY = true, which is usually desirable for Framebuffer rendering.
+ <li> G3D::Map2D
+ <li> G3D::Vector4int8
+ <li> G3D::PointShape
+ <li> G3D::GImage::RGBAtoRGB
+ <li> Added GLEW compatibility [nico]
+ <li> Added data-files directory to the locations searched for G3D demo data
+ <li> On Win32, assertions now print ot the Output window as well as the dialog box.
+ <li> On Win32, $TEMP is now used for the logfile location instead of c:\tmp
+ <li> G3D::GKey replaces old SDL key enumeration
+ <li> Decreased memory requirements and increased balance speed of G3D::AABSPTree by adding a level of indirection
+ between tree nodes and the data stored in them.
+ <li> Added OSWindow::Settings::caption
+ <li> <b>Win32 programs must call the macro <b><code>G3D_START_AT_MAIN();</code></b> at top-level if they do not define WinMain themselves.</b>
+ <li> Win32 switched from MBCS to UNICODE for the binaries (G3D sources compile under either, but UNICODE is the VC8 default)
+ <li> Replaced SDL event types with G3D event types
+ <li> Added support for GL_ARB_point_sprite
+ <li> Win32 programs must call the macro <b><code>G3D_START_AT_MAIN();</code></b> at top-level if they do not define WinMain themselves.
+ <li> Win32 switched from MBCS to UNICODE for the binaries (G3D sources compile under either)
+ <li> Changed G3D::GApp::main to return an int
+ <li> Added header support for GL_EXT_geometry_shader4 ("Geometry shaders")
+ <li> Changed IFSModel::create to IFSModel::fromFile
+ <li> G3D::BSPMap for loading Quake3 .bsp files
+ <li> G3D::TextInput::Settings::caseSensitive
+ <li> G3D::TextInput::readBoolean, G3D::TextInput::Settings::trueSymbols, G3D::TextInput::Settings::falseSymbols, G3D::TextOutput::Settings::trueSymbol, G3D::TextOutput::Settings::falseSymbol, G3D::TextOutput::writeBoolean, G3D::Token::BOOLEAN_TYPE
+ <li> G3D::TextInput::Settings::msvcSpecials now defaults to true
+ <li> Made the input to G3D::tessellateComplexPolygon a constant array reference
+ <li> Removed SDL from Win32 build
+ <li> Increased maximum ReliableConduit message size to 60 MB
+ <li> Removed error macro
+ <li> G3D::GConsole
+ <li> Rect2D::lerp
+ <li> Added GL_EXT_packed_depth_stencil
+ <li> Added G3D::ImageFormat::DEPTH24_STENCIL8 packed stencil mode
+ <li> getOpenGLState now includes GL_LIGHT_MODEL_TWO_SIDE value
+ <li> Made ThirdPersonManipulator constructor protected (use ThirdPersonManipulator::create now)
+ <li> G3D::ToneMap
+ <li> Changed Renderbuffer::createEmpty argument order to match Texture::createEmpty
+ <li> G3D::IFSModel can now remove degenerate faces on load
+ <li> G3D::MD2Model::textureFromFile
+ <li> Replaced AABSPTree::beginRayIntersection with simpler AABSPTree::intersectRay interface.
+ <li> G3D::MD2Model now uses floating point texture coordinates, which makes it easier to
+ write pixel shaders for MD2Models
+ <li> G3D::GImage::computeNormalMap now accepts a scale factor indicating how steep the input bump map is
+ <li> Added G3D::Shader support for the GLSL #version directive
+ <li> Optimized G3D::GImage::computeNormalMap to use primarily integer math
+ <li> G3D::AABox::contains(AABox)
+ <li> Added large file (>2GB) support to BinaryInput [Peter]
+ <li> Renamed graphics3d.h to G3D/G3D.h
+ <li> Renamed GLG3D.h to GLG3D/GLG3D.h
+ <li> Renamed G3DAll.h to G3D/G3DAll.h
+ <li> G3D::Quat::unitize now normalizes in place
+ <li> Added G3D::Quat::operator*=
+ <li> Removed G3D::FirstPersonManipulator's constructor--use the static G3D::FirstPersonManipulator::create method now.
+ <li> Changed BSPMap leaf bounds from Box to AABox--50% improvement in frustum culling speed
+ <li> Optimized BSPMap rendering performance
+ <li> Removed all deprecated APIs
+ <li> All accessors of the form "getXXX" that take no arguments are now named just "xxx"
+ <li> G3D::Sky::create is now named G3D::Sky::fromFile and no longer accepts a G3D::RenderDevice. G3D::Sky::render now requires a RenderDevice.
+ <li> G3D::GFont::fromFile no longer accepts a G3D::RenderDevice and G3D::GFont::draw2D now requires a RenderDevice.
+ <li> Removed an assertion in BinaryInput requiring that compressed buffers be copied on construction [Nick Bray]
+ <li> Removed CoordinateFrame::zLookDirection (use -1)
+ <li> Removed Capsule::endPoint (use point)
+ <li> Removed CoordinateFrame::getStrafeVector (use rightVector)
+ <li> Removed static constants (use equivalent lower-case methods)
+ <li> Removed Cylinder::getPoint1 (use point)
+ <li> Fix: BSPMap::getStartingPosition now works correctly on all maps [Jeff]
+ <li> Fix: Workaround for ATI drivers that do not support zero-area scissor region
+ <li> Fix: GL_EXT_texture_3D -> GL_EXT_texture3D
+ <li> Fix: ARB_texture_cube_map and EXT_texture_cube_map are now aliases on all cards
+ <li> Fix: EXT_texture_edge_clamp and SGIS_texture_edge_clamp are now aliases on all cards
+ <li> Fix: G3D::Sky now turns off lighting
+ <li> Fix: Win32Window now returns mousebutton and motion events according to the GEvent spec
+ <li> Fix: Win32Window now operates correctly in fullscreen mode
+ <li> Fix: Fixed TGA decode to load from the middle of a binaryinput
+ <li> Fix: Added texture coordinates to posed MD2Models
+ <li> Fix: prompt/debugAssert now correctly responds to button presses on OS X
+ <li> Fix: GConsole now filename completes after the first word
+ <li> Fix: RenderDevice::getDepthBufferValue now checks for the presence of a depth buffer.
+ <li> Fix: G3D::AABSPTree now correctly handles members with infinite bounds on ray intersection tests
+ <li> Fix: Removed use of tmpfile on Unix
+ <li> Fix: Java ReliableConduit now properly waits if the buffer is full when sending
+ <li> Fix: [ 1607693 ] Triangles/Second display is now correct (rates were too low in 6.10)
+ <li> Fix: NetworkDevice now does not perform a test broadcast during initialization
+ <li> Fix: G3D::LightweightConduit::ok is now false if any error occurs during initialization
+ <li> Fix: BSPMAP for cards without glMultiDrawElementsEXT
+ <li> Fix: [ 1584335 ] ReliableConduit incorrectly assumes it's ok
+ <li> Fix: RenderDevide::push2D no longer resets the frameBuffer.
+ <li> Fix: Framebuffer logic for counting number of attachments was broken
+ <li> Fix: [ 1581986 ] Matrix3::fromAxisAngle now normalizes the input vector
+ <li> Fix: [1490643] Linux/FreeBSD/OS X binaries are now compiled with -fno-strict-aliasing, which fixes some
+ memory corruption problems that occurred with full optimizations.
+ <li> Fix: Fixed all warnings on gcc-4.1 and VC8.
+ <li> Fix: Matrix and CoordinateFrame serializers inside G3D::AnyVal dropped data
+ <li> Fix: [ 1535759 ] valgrind finds initialization/deletion errors in TextOutput, Matrix [Chris Demetriou]
+ <li> Fix: Patched MD2Model to automatically reduce animation times to less than 100000; large time were overflowing double->int conversion and causing animations to appear scrambled.
+ <li> Fix: [ 1535292 ] global Table hashCode overloads broken [Chris Demetriou]
+ <li> Fix: [ 1535736 ] Fixed System.cpp memory allocator to compile on 64-bit machines correctly [Chris Demetriou]
+ </ul>
+
+ <hr>
+ <P>
+ Changes in 6.10:
+ <UL>
+ <LI> Optimized G3D::CoordinateFrame::pointToObjectSpace to be fully inlined via left-multiplication
+ <LI> G3D::Matrix4::orthogonalProjection from a G3D::Rect2D
+ <LI> G3D::RenderDevice::swapBuffers
+ <LI> G3D::AnyVal
+ <LI> G3D::ThirdPersonManipulator
+ <LI> Added support for GL_SGIS_texture_lod in Texture.
+ <LI> Fix: [ 1490655 ] MeshAlg::Edge::containVertex goes off the end of the array
+ <LI> Fix: [ 1511729 ] NVIDIA rectangle generates errors in mipmap code
+ <LI> Fix: [ 1507296 ] RenderDevice must swapBuffer on resize
+ </UL>
+<hr>
+ <P>
+ Changes in 6.09:
+ <UL>
+ <LI> glDepthBoundsEXT
+ <LI> G3D::Quat::sameRotation
+ <LI> Full loading of the GL_ATI_separate_stencil extension, support within RenderDevice
+ <LI> platform.h undefines WIN32_LEAN_AND_MEAN, NOMINMAX after it has defined them
+ <LI> G3D::Texture::Settings::maxMipMap
+ <LI> Renamed Texture::Parameters to Texture::Settings (backwards compatible typedef added)
+ <LI> Optimized IFSModel rendering by increasing internal VAR cache size and reducing the number of state changes.
+ Can now render more than 1000 IFSModels at 30 fps on GeForce 7800.
+ <LI> G3D::System::mallocStatus
+ <LI> Range checking on Vector2int16::operator[]
+ <li> GImage::BAYER_G8R8_B8G8_to_R8G8B8_MHC, GImage::BAYER_B8G8_G8R8_to_R8G8B8_MHC
+ <LI> IFSModel and MD2Model now allocated their posed models using System::malloc
+ <LI> Increased the memory maintained by G3D::System for buffer pools up to a total of 13 MB:
+ 8 MB tiny (preallocated), 1 MB small (on demand), 4 MB medium (on demand). This was observed to
+ dramatically increase performance (15x) in real programs that were
+ performance limited by memory allocation time.
+ <LI> NetworkDevice now uses Winsock2.0 on Windows (controlled by the G3D_WINSOCK_MAJOR_VERSION/G3D_WINSOCK_MINOR_VERSION settings in NetAddress.h)
+ <LI> G3D::Manipulator
+ <LI> G3D::GApplet now runs installed G3D::GModules (except for graphics, which is left to the progrmamer)
+ <LI> G3D::GApp::addWidget, G3D::GApplet::addWidget, G3D::GApp::removeWidget, G3D::GApplet::removeWidget
+ <LI> G3D::Widget, G3D::WidgetManager
+ <LI> G3D::System::getEnv()
+ <LI> G3D::PosedModel2D
+ <LI> G3D::DXCaps
+ <LI> Increased precision of several Quat operations
+ <LI> G3D::Quat::fuzzyEq
+ <LI> G3D::Quat::operator-
+ <LI> G3D::LineSegment::length, G3D::LineSegment::point
+ <LI> Increased fuzzyEpsilon by a factor of 10 to take into account the new float32 focus of the APIs
+ <LI> G3D::RegistryUtil
+ <LI> G3D::LineSegment2D
+ <LI> G3D::ConvexPolygon2D
+ <LI> G3D::AxesShape
+ <LI> contrib/shaders/showDepth
+ <LI> G3D::Crypto with MD5 and CRC32 hashes
+ <LI> TextureManager::findTexture, TextureManager::cacheTexture [Erik]
+ <LI> Win32Window::_directInput created on-demand [Erik]
+ <LI> WeakReferenceCountedPointer has more comparison operators [Erik]
+ <LI> GImage::resolveFormat utility function [Erik]
+ <LI> GLCaps supports MESA
+ <LI> G3D::Win32Window and G3D::SDLWindow now release input capture and make the mouse visible on destruction
+ <LI> G3D::OSWindow::setInputCaptureCount, G3D::OSWindow::inputCaptureCount, G3D::OSWindow::incInputCaptureCount, G3D::OSWindow::decInputCaptureCount
+ <LI> GImage::makeCheckerboard
+ <LI> G3D::Vector3::one()
+ <LI> G3D::Shader now supports g3d_size(sampler2D) and g3d_invSize(sampler2D) extensions in GLSL shaders.
+ <LI> Renamed GAppSettings to G3D::GApp::Settings (old name is supported but deprecated)
+ <LI> Renamed GWindowSettings to G3D::OSWindow::Settings (old name is supported but deprecated)
+ <LI> Renamed TextInput::Options to G3D::TextInput::Options (old name is supported but deprecated)
+ <LI> G3D::FPManualController::setAutoActive for World of Warcraft style controller
+ <LI> G3D::isSlash, G3D::isQuote
+ <LI> G3D::GApplet::onEvent can now consume (i.e., prevent G3D::GApp from seeing) the event
+ <LI> G3D::CoordinateFrame::fuzzyIsIdentity, G3D::CoordinateFrame::isIdentity, G3D::CoordinateFrame::fuzzyEq
+ <LI> Matrix3::isOrthonormal
+ <LI> [1421201] Removed excess gl (NVIDIA) headers
+ <LI> Win32Window destructor now releases the mouse if it was captured and the current GL context is that window and the window was not created from an existing HDC/HWND
+ <LI> Fix: com.graphics3d.g3d.ReliableConduit now correctly selects on the waiting socket
+ <LI> Fix: [ 1166057 ] AABSPTree::beginBoxIntersection
+ <LI> Fix: GLCaps::supports(ImageFormat) now returns correct results on all cards
+ <LI> Fix: Shadow map rendering of default PosedModels now enables lighting
+ <LI> Fix: G3D::UserInput now restores the mouse position after pureDeltaMouse is turned off
+ <LI> Fix: G3D::Win32Window now clips precisely to the client area during an input grab.
+ <LI> Fix: [ 1383042 ] free static variables on shutdown
+ <LI> Fix: [1449115 ] Texture loading for odd-byte rows
+ <LI> Fix: G3D::Win32Window now produces correct character and scan codes for key events
+ <LI> Fix: G3D::GApplet::onEvent calls GApplet::processEvent by default
+ <LI> Fix: [ 1444320 ] TextInput parsed ".1" as "1" instead of "0.1"
+ <LI> Fix: G3D::Shape::type is now const
+ <LI> Fix: 0 --> 0.0f FrameBuffer.h [Erik]
+ <LI> Fix: Fixed Texture read-back dimensions for cube-map
+ <LI> Fix: Missing #include in SkyParameters.h [Erik]
+ <LI> Fix: Quad triangle counts are now accurate (were off by factor of 4 in 6.08)
+ <LI> Fix: contrib/ArticulatedModel now correctly masks all components using the diffuse alpha in fixed function mode
+ <LI> Fix: G3D::CoordinateFrame::getHeading was flipped front-to-back
+ <LI> Fix: [ 1404487 ] Missing Alt key up/down events on Win32
+ <LI> Fix: [ 1484924 ] collisionTimeForMovingPointFixedBox normals
+ </UL>
+ <P>
+<hr>
+ Changes in 6.08:
+ <UL>
+ <LI> Moved Win32 linker statements out of platform.h for IncrediBuild compatibility.
+ <LI> G3D::Texture and G3D::Sky now accept a rescaling factor
+ <LI> Added GFont::fromMemory() [Corey]
+ <LI> Added optional argument to Quat::slerp() for slerp/lerp angle threshold. [Corey]
+ <LI> Across-the-board performance optimizations. Most apps should render 10% faster.
+ Includes removal of Milestones when using VBO VAR [Nick Bray], GFont::draw2D and
+ Draw::rect2D stripped down to raw OpenGL, consistent internal use of float,
+ increased RenderDevice state change optimization.
+ <LI> Minimized header interdependencies (GLG3D headers no longer include all of G3D)
+ <LI> Added GThread and GMutex classes. [Corey]
+ <LI> Added ImageFormat::fromCode(). [Corey]
+ <LI> Added Plane::distance() and Plane::closestPoint() helper methods. [Corey]
+ <LI> G3D::ImageFormat::code, G3D::ImageFormat::colorSpace
+ <LI> <B>incompatible change</B> G3D::MeshAlg::computeTangentSpace basis now computes a right-handed coordinate frame,
+ where the binormal direction is the negative of the direction it faced in G3D 6.07.
+ <LI> Exposed G3D::RenderDevice::beforePrimitive and G3D::RenderDevice::afterPrimitive to end-user code for
+ integrating raw OpenGL calls.
+ <LI> G3D::Framebuffer and G3D::Renderbuffer to implement the Framebuffer_object extension [Dan Hilferty]
+ <LI> G3D::Shader::hasArgument
+ <LI> G3D::Texture::getImage
+ <LI> Changed SECOND, MINUTE, DAY, HOUR, SUNRISE, SUNSET, MIDNIGHT, METER, KILOMETER to enum values instead of #defines
+ <LI> G3D::Texture::Parameters; deprecated most Texture constructors in favor of ones that use this class
+ <LI> Moved most image manipulation routines into GImage.
+ <LI> G3D::GImage now allocates the underlying buffer in multiples of bytes to allow slight overflor for MMX algorithms
+ <LI> G3D::GImage::BAYER_R8G8_G8R8_to_R8G8B8_MHC
+ <LI> G3D::GImage::R8G8B8_to_Y8U8V8
+ <LI> G3D::GImage::Y8U8V8_to_R8G8B8
+ <LI> G3D::GImage now supports PPM binary
+ <LI> Various Rect2D helpers [Nick Bray]
+ <LI> ConvexPolyhedron improved clipping [Nick Bray]
+ <LI> G3D::System::build
+ <LI> G3D::System::calloc
+ <LI> G3D::GImage::convertToRGBA
+ <LI> contrib/AVI can read most AVI files on Windows.
+ <LI> contrib/wxGWindow now uses wxWidgets 2.6.2
+ <LI> G3D_DEBUG now controls whether debug code is enabled; it defaults to the value of _DEBUG
+ <LI> zlib upgraded to 1.2.3 [Corey]
+ <LI> zlib now statically linked on Win32 (no longer requires zlib1.dll at runtime) [Corey]
+ <LI> G3D::MeshShape
+ <LI> Changed std::string hashCode to use CRC32 to reduce collisions
+ <LI> G3D::crc32
+ <LI> Added occlusion query #defines [Nick Bray]
+ <LI> G3D::Win32Window now shares textures and vertex buffers across all GL contexts
+ <LI> G3D::Win32Window now enforces single-threading among GL contexts
+ <LI> G3D::GLCaps::slowVBO
+ <LI> G3D::VARArea now uses main memory vertex buffers on cards with slow VBO implementations.
+ <LI> G3D::Matrix3::toString [Peter]
+ <LI> G3D::Matrix4::toString [Peter]
+ <LI> G3D::Color3::fromHSV [Peter]
+ <LI> G3D::Color3::toHSV [Peter]
+ <LI> G3D::Color3::jetColorMap [Peter]
+ <LI> Optimized G3D::iRound (now faster than casting!)
+ <LI> G3D::MD2Model::create now accepts a scale factor
+ <LI> #G3D_DEPRECATED macro
+ <LI> #G3D_CHECK_PRINTF_ARGS, #G3D_CHECK_VPRINTF_ARGS macros to allow
+ checking of printf argument strings under gcc at compile time with
+ -Wformat.
+ <LI> G3D::TextInput::filename
+ <LI> G3D::TextInput::Options::msvcSpecials
+ <LI> G3D::TextInput::Options::startingLineNumberOffset
+ <LI> G3D::TextInput::readSymbolToken [cgd]
+ <LI> G3D::TextInput::readStringToken [cgd]
+ <LI> G3D_DEPRECATED macro
+ <LI> Threadsafe G3D::ReferenceCountedPointer
+ <LI> G3D::AtomicInt32
+ <LI> G3D::GThread [Corey]
+ <LI> G3D::Array::popDiscard
+ <LI> Optimized multi-argument Array::append
+ <LI> G3D::GFont 2x faster than in G3D 6.07
+ <LI> G3D::RenderDevice::pushState 2x faster than in G3D 6.07
+ <LI> G3D::RenderDevice::pushState no longer stores GL texgen and fog information
+ <LI> G3D::Draw::fastRect2D
+ <LI> G3D::System::outOfMemoryCallback
+ <LI> G3D::Queue::fastClear [Chris Demetriou]
+ <LI> G3D::Rect2D::x0y1 and x1y0
+ <LI> G3D::GLCaps bug tests now run in a separate GL context [Erik Cassel]
+ <LI> G3D::GApplet tracks real and simulation time.
+ <LI> contrib/Q3Map updated to correctly render instanced objects [Alex Rice]
+ <LI> G3D::OSWindow subclasses now required to invoke OSWindow::loadExtensions
+ <LI> G3D::Quat::log for non-unit quats and for real-only quats.
+ <LI> G3D::GApplet::doUserInput
+ <LI> G3D::GApp prints time for each component
+ <LI> G3D::Stopwatch
+ <LI> G3D::OSWindow::renderDevice()
+ <LI> G3D::OSWindow::current()
+ <LI> G3D::GLCaps::hasBug_redBlueMipmapSwap and workaround for G3D::Texture on Radeon 7500
+ <LI> Fix: CollisionDetection::penetrationDepthForFixedSphereFixedPlane() contact point and normal values. [Corey]
+ <LI> Fix: Quat::slerp has invalid shortest path [Corey]
+ <LI> Fix: G3D::drawFeatureEdges now uses correctly normalized face edges (and offers a crease angle)
+ <LI> Fix: G3D::SDLWindow now releases the mouse on Linux during an assertion.
+ <LI> Fix: All keys are reset to up when Win32Window loses focus. [Corey]
+ <LI> Fix: gaussRandom is unit gaussian [Corey]
+ <LI> Fix: [ 1418276 ] 6.08: Unsupported format for depth texture
+ <LI> Fix: Ignoring extra/unused set Shader arguments. [Corey]
+ <LI> Fix: [ 1229205 ] uniform texture array (Could not set indexed array uniforms). [Corey]
+ <LI> Fix: <B>incompatible change</B> BinaryInput/BinaryOutput copy constructors and assignments were accessible. [Corey]
+ <LI> Fix: RenderDevice::screenshotPic would corrupt GImage's heap. [Corey]
+ <LI> Fix: Alt-Tab window switching caused an invalid Alt key state. [Corey]
+ <LI> Fix: Incorrect window size event in Win32Window sent to OpenGL. [Corey]
+ <LI> Fix: [ 1227915 ] Textures don't bind on ATI under GLSL.
+ <LI> Fix: [ 1358477 ] ray-plane intersection bug [Dan Keefe]
+ <LI> Fix: [ 1370665 ] hash_map moved to stdext in VC8 (2005)
+ <LI> Fix: ToneMap extended to use DIM_2D_NPOT instead of DIM_2D_RECT
+ <LI> Fix: Texture::copyFromScreen now works with DIM_2D_NPOT textures
+ <LI> Fix: Wrapped debugAssertM in do {} while (0) to ensure correct compilation in single-line statements [ERik Cassel]
+ <LI> Fix: G3D::Draw::cylinder now renders the bottom correctly
+ <LI> Fix: Array::front now compiles under gcc
+ <LI> Fix: G3D::Ray::distance used to measure against the origin [David]
+ <LI> Fix: [ 1293151 ] ArticulatedModel clipping on Radeon -- disabled auto-mipmap generation on mobile radeon 9xxx
+ <LI> Fix: G3D::TextInput now parses ^=, character 255 correctly [cgd]
+ <LI> Fix: G3D::TextInput now reports line numbers correctly with raw newlines [cgd]
+ <LI> Fix: .ICO files with transparency loaded incorrectly [Corey]
+ <LI> Fix: G3D::Draw::rect2DBorder inner border was 1 pixel too thick.
+ <LI> Fix: [ 1326173 ] Win32Window::init should call makeCurrent.[Erik Cassel]
+ <LI> Fix: [ 1326423 ] G3D::Queue::_copy broken [Chris Demetriou]
+ <LI> Fix: [ 1313293 ] 6.08: TextInput gets symbol extendedType() wrong [Chris Demetriou]
+ <LI> Fix: IFSModel::save, for PLY2 forgot newlines [Peter]
+ <LI> Fix: Quat(Matrix3) now computes trace correctly (gave negative quats in some cases)
+ <LI> Fix: Setting RenderDevice::polygonOffset now always produces a depth shift,
+ even for faces perpendicular to the view axis.
+ <LI> Fix: GImage now auto-resolves formats for files with 1 character base names
+ <LI> Fix: WeakReferenceCountedPointer cycle bug
+ <LI> Fix: Corrected lag encountered when using some ReliableConduit constructors [Dan Keefe]
+ </UL>
+
+ <P>
+<hr>
+ Changes in 6.07:
+ <UL>
+ <LI> G3D::OSWindow::makeCurrent
+ <LI> Win32 release binaries now built with no debug information (used to have line numbers)
+ <LI> AABox::AABox enforces the constraint low <= high
+ <LI> Optimized G3D::Array, Table, Queue, and Set for performance. Now significantly (up to 10x) faster
+ than their std::counterparts.
+ <LI> G3D::Vector3(Vector2, float) constructor
+ <LI> G3D::Vector2::fastDirection
+ <LI> G3D::TextInput::Options::cComments
+ <LI> G3D::TextInput::Options::escapeSequencesInStrings
+ <LI> G3D::TextInput::Options::otherCommentCharacter2
+ <LI> G3D::TextInput::WrongString
+ <LI> GLCaps::supports_GL_ATI_separate_stencil
+ <LI> GLCaps can now test a card/driver and detect specific bugs:
+ <ul><li>G3D::GLCaps::hasBug_glMultiTexCoord3fvARB
+ <LI> G3D::GLCaps::hasBug_normalMapTexGen
+ </ul>
+ <LI> G3D::ReferenceCountedPointer::downcast for non VC6 compilers
+ <LI> Improved G3D::ReferenceCountedPointer documentation to make subclassing features clearer
+ <LI> Moved typedef for uint into G3D namespace and into g3d (was in glg3d)
+ <LI> G3D::Shape
+ <LI> G3D::Cylinder
+ <LI> G3D::System::malloc, G3D::System::realloc, G3D::System::free for fast allocation of small objects
+ <LI> G3D::Draw::plane
+ <LI> G3D::Draw::cylinder
+ <LI> G3D::gaussRandom
+ <LI> GCamera deserialize(BinaryInput) & serialize(BinaryOutput) functions [Peter]
+ <LI> G3D::GApp now writes a description of the whole system to the log to aid debugging.
+ <LI> [ 1217928 ] OpenGL occlusion query entry points are loaded on initialization
+ <LI> New texture interpolation modes: BILINEAR_MIPMAP, NEAREST_MIPMAP, NEAREST_NO_MIPMAP
+ <LI> New texture formats:
+ <UL>
+ <LI> G3D::ImageFormat::L16;
+ <LI> G3D::ImageFormat::L16F;
+ <LI> G3D::ImageFormat::L32F;
+ <LI> G3D::ImageFormat::A16;
+ <LI> G3D::ImageFormat::A16F;
+ <LI> G3D::ImageFormat::A32F;
+ <LI> G3D::ImageFormat::LA4;
+ <LI> G3D::ImageFormat::LA16;
+ <LI> G3D::ImageFormat::LA16F;
+ <LI> G3D::ImageFormat::LA32F;
+ <LI> G3D::ImageFormat::RGB16;
+ <LI> G3D::ImageFormat::RGB16F;
+ <LI> G3D::ImageFormat::RGB32F;
+ <LI> G3D::ImageFormat::RGBA16;
+ <LI> G3D::ImageFormat::RGBA16F;
+ <LI> G3D::ImageFormat::RGBA32F;
+ </UL>
+ <LI> isValidPointer and isValidHeapPointer no longer check the Win32 debug heap in order to support offset and padded memory blocks.
+ <LI> Restructured unit tests
+ <LI> G3D::CoordinateFrame::lookRay [David Baszucki]
+ <LI> G3D::System::describeSystem, G3D::NetworkDevice::describeSystem, G3D::RenderDevice::describeSystem
+ <LI> G3D::Array performance tuning for short arrays and arrays of small objects
+ <LI> Added glext.h entries for GL_ARB_draw_buffers, GL_ARB_texture_rectangle,
+ GL_ARB_color_buffer_float, GL_ARB_half_float_pixel, GL_ARB_texture_float,
+ and GL_ARB_pixel_buffer_object extensions
+ <LI> IFSModel::create added weld option, defaults to true (to keep compatibility). [Peter]
+ <LI> G3D::RenderDevice::alphaTestReference, RenderDevice::alphaTest
+ <LI> G3D::VAR::set
+ <LI> G3D::Log::vprintf
+ <LI> G3D::WeakReferenceCountedPointer
+ <LI> GCC 4.0 build support added [Corey]
+ <LI> G3D::CoordinateFrame::lookAt now gives a valid output even when look == up
+ <LI> contrib/GChunk
+ <LI> GLCaps now loads GL_EXT_framebuffer_object functions
+ <LI> Added MSVC 6 support for C99 restrict keyword
+ <LI> G3D::Win32Window properly resizes viewport on window resize [Corey]
+ <LI> G3D::BinaryFormat, G3D::byteSize, G3D::binaryFormatOf
+ <LI> Removed dead ManualCameraControllerHelper code
+ <li> Added consistent area and volume methods to geometric primitives, deprecated old methods.
+ <LI> Fast G3D::BinaryInput::read / G3D::BinaryOutput::write methods for arrays
+ <LI> Enabled cube mapping on Radeon mobility cards and added a workaround to the known problems with texcoords on those cards.
+ <LI> Can now create G3D::Win32Window with existing HWND and HDC [Corey]
+ <LI> G3D::VertexAndPixelShader::ArgList::set(std::string, Array<T>)-- [ 1192401 ] Shader support arrays
+ <LI> Fix: SDLWindow used std::string's instead of C strings in printf and format inside some exception handling code. [Peter]
+ <LI> G3D::X11Window (same as SDLWindow in this release)
+ <LI> Fix: [ 1277854 ] Win32Window fails on 24-bit modes
+ <LI> RFE: [ 1242466 ] Inline Matrix3 methods
+ <LI> Fix: [ 1226272 ] end caps of capsules in wrong position
+ <LI> Fix: G3D::ImageFormat::LA8 now has 8-bits per channel
+ <LI> Fix: [ 1124491 ] Remove GL_SAMPLER_2DRECT_ARB
+ <LI> Fix: [ 1257113 ] G3D::Queue problems comining pushFront and pushBack
+ <LI> Fix: MeshAlg::Weld now linear time (was O(n^2) due to a bug)
+ <LI> Fix: [ 1298873 ] fast & correct CoordinateFrame::lerp
+ </UL>
+
+ <P>
+<hr>
+ Changes in 6.06:
+ <UL>
+ <LI> G3D::Lighting::emissiveScale
+ <LI> G3D::RenderDevice::drawBuffer
+ <LI> G3D::RenderDevice::debugNumMinorStateChanges, debugNumMinorOpenGLStateChanges, debugNumMajorStateChanges, debugNumMajorOpenGLStateChanges.
+ <LI> In stereo mode, Texture::copyFromScreen automatically chooses the left/right buffer to read based on the current glDrawBuffer
+ <LI> contrib/ArticulatedModel/ToneMap
+ <LI> Lazy state changes for shaders
+ <LI> 50% performance improvement for G3D::BinaryInput, G3D::BinaryOutput when machine endian matches file endian
+ <LI> Textures load with default of maxAnisotroy = 2.0
+ <LI> maxAnisotropy argument to G3D::Texture constructors.
+ <LI> GLCaps now loads GL_ATI_fragment_shader extension
+ <LI> contrib/ArticulatedModel now supports rigid body hierarchies
+ <LI> Added TEX_SUBTRACT, TEX_ADD_SIGNED, TEX_DOT3, TEX_DOT3_RGBA modes for G3D::RenderDevice::setTextureCombineMode
+ <LI> G3D::RenderDevice now cleans up all static G3D::VARArea s when it shuts down
+ <LI> FIX: [ 1208157 ] GLSL slow on ATI
+ <LI> FIX: Off-by-one on viewport scale for 2D rendering
+ <LI> FIX: MeshAlg::computeTangentSpaceBasis now works correctly
+ <LI> FIX: 6.05 enabled all fixed function lights by default. This caused major performance problems on some cards.
+ <LI> FIX: Extended cube map workaround to all Radeon Mobility cards
+ <LI> FIX: Added check for glBlendEq before calling in RenderDevice
+ <LI> FIX: Added a test for GL_EXT_texture_env_add in RenderDevice
+ <LI> FIX: [ 1191817 ] unsigned warnings in BinaryInput
+ </UL>
+
+<hr>
+ <P>
+ Changes in 6.05:
+ <UL>
+ <LI> G3D::BAYER_G8B8_R8G8_to_R8G8B8_MHC
+ <LI> G3D::Quarter_R8G8B8_to_BAYER_G8B8_R8G8
+ <LI> G3D::BAYER_G8B8_R8G8_to_Quarter_R8G8B8
+ <LI> contrib/Matrix
+ <LI> contrib/Java
+ <LI> Texture::alphaOnlyVersion
+ <LI> Draw::sphere speed improved over 25% with single quad strip (improves Draw::capsule) [Corey]
+ <LI> Allow 1-channel GImage saving - BMP (expanded to RGB), PNG [Corey]
+ <LI> Allow 1-channel GImage loading - PNG [Corey]
+ <LI> Added shader and framebuffer extensions to glext.h
+ <LI> All files used during current execution are available via G3D::getFiles() [Corey]
+ <LI> Implemented OSX version of glGetCurrentContext with CGL. [Corey + Derek]
+ <LI> ReferenceCountedObject is-in-heap checks were removed to allow better multiple and virtual inheritance for reference counted objects. ReferenceCountedPointer still appropriately checks does an is-in-heap check on assignment. [Corey]
+ <LI> Added Dev C++ compatability
+ <LI> glGetAttribLocationARB
+ <LI> Changed GLight == operator to not use memcpy (was causing issues due to byte padding on some compilers)
+ <LI> Made CoordinateFrame destructor non-virtual (eliminates vtable)
+ <LI> Added new FAQ documentation
+ <LI> Added support to G3D::BinaryInput and G3D::BinaryOutput
+ reading and writing huge (larger than available memory) files.
+ Files are still restricted to about 2 GB total, and compressed
+ files must fit entirely in memory.
+ <LI> Tweaked allocation strategy for small G3D::Array
+ <LI> G3D::Texture::rect2DBounds, G3D::Texture::vector2Bounds
+ <LI> G3D::Vector4 * G3D::Vector4, Vector4 / Vector4
+ <LI> G3D::Array::operator=(std::vector)
+ <LI> G3D::Sky::getEnvironmentMap now returns the top texture on machines
+ that don't support cube maps.
+ <LI> glDisableAllTextures()
+ <LI> G3D::setFailureHook
+ <LI> G3D::Shader::fromStrings now accepts optional names for the vertex and pixel shader
+ <LI> G3D::Shader no longer requires values for declared but unused uniform variables
+ <LI> G3D::RenderDevice now stores texture matrix at 32-bit precision (for faster push/popState)
+ <LI> G3D::RenderDevice::setTextureLODBias
+ <LI> G3D::Shader now supports shadow map arguments
+ <LI> G3D::Shader::ArgList checks to see if Texture arguments are null
+ <LI> G3D::RenderDevice::setAlphaWrite now defaults to true if the OSWindow has an alpha channel.
+ <LI> G3D::RenderDevice::screenshotPic now supports alpha
+ <LI> contrib/VideoSerializer
+ <LI> G3D::BinaryOutput::writeBits, G3d::BinaryInput::readBits
+ <LI> G3D::Sky can now be initialized with a NULL renderDevice, provided a non-null one
+ is used with the G3D::Sky::render method.
+ <LI> G3D::pi(), G3D::halfPi(), G3D::twoPi() added to replace defines [Corey]
+ <LI> contrib/Q3Map
+ <LI> Increased G3D::Draw::sphere performance using vertex arrays.
+ <LI> G3D::Array::fastClear
+ <LI> G3D::AABSPTree::insert(Array<T>)
+ <LI> G3D::Texture::sizeOfAllTexturesInMemory
+ <LI> G3D::VARArea::sizeOfAllVARAreasInMemory
+ <LI> G3D::RenderDevice stores cameraToWorldMatrixInverse for faster coordinate system changes.
+ <LI> inlined G3D::Matrix3::operator= for performance
+ <LI> Created installer for Windows install [Corey]
+ <LI> Reorganized the documentation topic index based on abstraction level, added hyperlinks to demo/contrib code
+ <LI> G3D::ReliableConduit and G3D::LightweightConduit now send and receive
+ objects directly; no need to make a G3D::NetMessage. G3D::NetMessage
+ and associated methods are now deprecated.
+ <LI> Win32 GUI G3D::prompt now auto-expands \\n to \\r\\n in prompt string [Corey]
+ <LI> G3D::Draw::frustum
+ <LI> Increased timeout and attempts for G3D::ReliableConduit to handle huge (1 MB) packets
+ <LI> G3D::BinaryOutput::reset (memory writing only; not supported for disk)
+ <LI> Reduced overhead for G3D::ReliableConduit and
+ G3D::LightWeightConduit send routines
+ <LI> Added PPM/PGM/PBM ASCII encode/decode support to G3D::GImage [Corey]
+ <LI> New G3D::PosedModel rendering methods appropriate for shadow casting
+ (with efficient default implementations).
+ <LI> G3D::Lighting
+ <LI> Changed RenderDevice::TEX_INTERPOLATE to mean GL_DECAL and added TEX_BLEND for GL_BLEND
+ <LI> G3D::CoordinateFrame::upVector
+ <LI> G3D::GLight::diffuse
+ <LI> G3D::Rect2D::contains is now const
+ <LI> Rewrote G3D::BinaryOutput to not use G3D::Array
+ <LI> G3D::MD2Model::textureMatrix
+ <LI> G3D::MeshAlg::computeBounds(vertex, index, ...)
+ <LI> G3D::RenderDevice::colorWriteEnabled(), depthWriteEnabled, alphaWriteEnabled
+ <LI> G3D::RenderDevice::setSpecularCoefficient(Color3)
+ <LI> G3D::VAR::maxSize
+ <LI> G3D::RenderDevice::enableTwoSidedLighting
+ <LI> G3D::PosedModel::hasTransparency
+ <LI> G3D::PosedModel::sort
+ <LI> G3D::RenderDevice::renderMode
+ <LI> G3D::MeshAlg::computeNormals(geometry, indexArray);
+ <LI> contrib/ArticulatedModel (beta 3DS support)
+ <LI> G3D::RenderDevice::swapBuffersAutomatically allows caller to suppress page flip.
+ <LI> Added coordinate system documentation.
+ <LI> RenderDevice::enableClip2D, RenderDevice::disableClip2D (scissor region)
+ <LI> contrib/wxGWindow is stable and full featured-- use wxWidgets 2.5.3 with G3D!
+ <LI> G3D::fileIsNewer
+ <LI> G3D::isDirectory
+ <LI> G3D::filenameContainsWildcards
+ <LI> G3D::filenamePath
+ <LI> G3D::Draw::lineSegment now accepts a scale (allowing arrows and axes to thicken appropriately)
+ <LI> G3D::Rect2D::largestCenteredSubRect
+ <LI> G3D::Matrix4::serialize, G3D::Matrix4::deserialize
+ <LI> glTexImage3DEXT
+ <LI> Removed glut.lib and glut.dll from the win32-lib directory.
+ <LI> G3D::writeStringToFile, G3D::TextOutput, and G3D::BinaryOutput now flush by default (safe, not fast).
+ <LI> Shifted push2D by 0.375 pixels as recommended in the OpenGL guide to bias integer coords towards pixel centers
+ <LI> G3D::Draw::rect2DBorder
+ <LI> G3D::Rect2D::border
+ <LI> G3D::RenderDevice now creates a G3D::Win32Window on Windows instead of a G3D::SDLWindow. SDLWindow is now
+ deprecated on Windows.
+ <LI> G3D::VARArea now updates allocation sizes instead of G3D::VAR internally. Added
+ more accessor methods to VARArea to futher remove VAR from VARArea internals. [Corey]
+ <LI> VARSystem.cpp moved to VARArea.cpp - filename change only! [Corey]
+ <LI> Linux build system updated:
+ Builds only static libraries, Does not require libtool/libtoolize anymore,
+ Does not check for or require libraries that normally linked with the .so files,
+ Automatically builds Test project with iCompile during install. [Corey]
+ <LI> G3D::Quat::deserialize, G3D::Quat::serialize
+ <LI> G3D::PhysicsFrame::deserialize, G3D::PhysicsFrame::serialize
+ <LI> G3D::TextInput::Options::singleQuotedStrings (defaults to true, changing the behavior
+ from previous versions).
+ <LI> G3D::Token::extendedType returns information disambiguating characters and strings
+ and floats and ints.
+ <LI> Added data/ah64-body and ah64-rotor
+ <LI> demos/Network_Demo now uses a helicopter model instead of a plane
+ <LI> G3D::VARArea::gl_vertexBufferObject and G3D::VARArea::gl_basePointer for breaking
+ the VARArea abstraction.
+ <LI> GLG3D.h no longer links against SDLMain.lib on Windows if _CONSOLE is defined
+ (since console programs have no WinMain).
+ <LI> SDL's redefinition of main is cleared when not linking sdlmain.lib [Corey]
+ <LI> Moved contrib/Win32Window to G3D::Win32Window
+ <LI> G3D::TextInput::readSymbols
+ <LI> contrib/Image [Morgan]
+ <LI> contrib/wxGWindow [Morgan]
+ <LI> Added support for full-screen antialiasing to contrib/Win32Window
+ <LI> Added joystick support to contrib/Win32Window [Corey]
+ <LI> Win32Window fully-implements OSWindow [Corey]
+ <LI> Texture now supports DDS(2D/CubeMap) and PNG files [Corey]
+ <LI> Added Win32 pbuffer routines (no G3D wrapper, though-- we're waiting for the new ARB API).
+ <LI> G3D::PosedModel::texCoords
+ <LI> G3D::IFSModel now loads IFS 1.1 [Peter]
+ <LI> G3D::IFSModel now loads and saves PLY2 files (plain text IFS format) [Peter]
+ <LI> Automatically switch to glCompressedTexImage2D in G3D::Texture::fromMemory [Corey]
+ <LI> Added G3D::Sky::fromCubeMap for preloaded CubeMap Texture::Ref's [Corey]
+ <LI> Added G3D::Sky::fromFile and deprecated Sky::create [Corey]
+ <LI> Demo and Test projects now build with iCompile, which is included [Corey]
+ <LI> Fix: TextOutput::writeString now escapes special characters
+ <LI> Fix: AABSPTree::serializeStructure
+ <LI> Fix: Properly handle gl_ uniforms on Radeon for Shader
+ <LI> Fix: [ 875467 ] OS X debugBreak (requires default XCode debug menu item 'Break on DebugStr()') [Corey + Derek]
+ <LI> Fix: Can make a G3D::Texture::fromGImage with one channel (defaults to L8 format)
+ <LI> Fix: [ 1149972 ] 6.05: Make Sky render correctly on low-end cards (no Cube mapping) [Corey]
+ <LI> Fix: [ 1032742 ] OS X _DEBUG not defined [Derek]
+ <LI> Fix: 16-bit integer reads in BinaryInput that always reversed endianness. (OSX file reading) [Corey + Derek]
+ <LI> Fix: Matrix4 operator[] was returning a matrix value cast to a pointer [Corey]
+ <LI> Fix: Matrix3 and Matrix4 had missing float* / const float* operators [Corey]
+ <LI> Fix: Rect2D::clip broken for types other than Vector2
+ <LI> Fix: RenderDevice::configureShadowMap result depends on objectToWorldMatrix
+ <LI> Fix: [ 1150650 ] DebugBreak() undefined
+ <LI> Fix: [ 1111534 ] Network Demo crashes starting 2nd server on same machine
+ <LI> Fix: [ 1102091 ] ReliableConduit::receive times out
+ <LI> Fix: Implemented MD2Model::objectSpaceBoundingX methods.
+ <LI> Fix: G3D::Triangle::area is now zero for zero-area triangles (was inf)
+ <LI> Fix: AABSPTree with extent on MSVC 6 no longer enters infinite loop in std::sort
+ <LI> Fix: [ 1105641 ] Does not build with g++ 3.4.x [Corey]
+ <LI> Fix: [ 1103619 ] RenderDevice::countPrimitive is wrong (changed to RenderDevice::countTriangles) [Corey]
+ <LI> Fix: AABSPTree::BoxIntersectionIterator doesn't compile
+ <LI> Fix: [ 1101680 ] copyfile won't overwrite (on Windows now overwrites) [Corey]
+ <LI> Fix: [ 1101646 ] GCamera::frustum incorrect for non-square viewport
+ <LI> Fix: Ultra bright lens flare at sunset [Nicholas Bray]
+ <LI> Fix: IP address strings were reversed by NetAddress(std::string)
+ <LI> Fix: TextInput now returns end of file token for files without trailing whitespace
+ <LI> Fix: [ 1094166 ] 6.05: Release mouse stuck on x-axis [Corey + Morgan]
+ <LI> Fix: Recognize buggy ATI Radeon Mobility cube maps and work around
+ <LI> Fix: Textures now initialize without setting error bit on cards without GL_ARB_shadow
+ <LI> Fix: filenameBaseExt now operates correctly on strings with both \ and / slashes.
+ <LI> Fix: [ 1062659 ] BinaryInput::BinaryInput() memory leak
+ <LI> Fix: Removed RenderDevice::polygonCount, which was never used.
+ <LI> Fix: TextInput::readNumber no longer accepts double preceeding +/- on numbers when Options::signedNumbers is true
+ <LI> Fix: [ 1038733 ] OSWindow cannot set icon properly [Corey]
+ <LI> Fix: [ 939400 ] Linux mouse set position (Wild camera swinging on startup) [Corey]
+ <LI> Fix: [ 1042591 ] Software GL Causes Assertion [Corey]
+ <LI> Fix: [ 1036634 ] debugAssert doesn't work on MSVC 7 [Corey]
+ <LI> Fix: [ 1049024 ] Fix compile warnings from gcc/Linux build [Corey]
+ <LI> Fix: [ 1051272 ] Win32Window doesn't use GWindowSettings properly. [Corey]
+ <LI> Fix: Win32Window clips the proper cursor region during input capture. [Corey]
+ <LI> Fix: GWindows now center and maximize on the primary monitor for Windows.
+ <LI> Fix: [ 1052945 ] TextOutput wordWrap starts on newlines
+ <LI> Fix: [ 1050957 ] TextInput readNumber support for capital 'E' numbers.
+ <LI> Fix: [ 1049674 ] TextInput failes on X. numbers.
+ <LI> Fix: [ 1044028 ] Linux TextOutput Warning
+ <LI> Fix: [ 1032750 ] Grayscale JPG errors [Corey]
+ <LI> Fix: [ 1036225 ] Encode TGA support strips alpha channel [Corey]
+ <LI> Fix: [ 1038631 ] CoordinateFrame::slerp (Quat::slerp has fix) [Corey]
+ <LI> Fix: [ 1033686 ] GImage::GImage(filename) dies on certain (BMP) images [Corey]
+ <LI> Fix: Texture mapping modes for pre-OpenGL 1.3 cards [Dan & Morgan]
+ </UL>
+
+<hr>
+ <P>
+ Changes in 6.04:
+ <UL>
+ <LI> G3D Manual! [ Morgan and Sascha ]
+ <LI> Initial MSVC7 build script. MSVC7 is not an officially supported platform
+ however the release contains MSVC7 precompiled binaries and the build script
+ will automatically build on both 6 and 7.
+ <LI> Improved performance of G3D::writeStringToFile
+ <LI> G3D::ReferenceCountedPointer assignment now allows compile time subtyping
+ <LI> G3D::ReferenceCountedPointer != operator
+ <LI> G3D::ReferenceCountedPointer::notNull
+ <LI> G3D::GLight::directional now normalizes the light vector
+ <LI> G3D::setAssertionHook
+ <LI> [ 1029256 ] G3D::Shader / G3D::VertexAndPixelShader define g3d_ uniforms inside shaders
+ <LI> static G3D::IFSModel::save/load for writing/reading IFS files
+ <LI> G3D::TextInput allows ' inside quoted strings
+ <LI> G3D::TextInput allows \ as a symbol token
+ <LI> G3D::TextInput supports an arbitrary comment character (e.g. '#')
+ <LI> Precompiled binaries for VisualC++ 7 (.NET 2002/2003)
+ <LI> VisualC++ 7 (.NET 2002/2003) supported by build script
+ <LI> Build now MOVEs binaries instead of COPYing them on Windows (allows
+ two compilers to output to the same location)
+ <LI> G3D Guide overview documentation
+ <LI> Changelog and Error FAQ moved under Doxygen
+ <LI> Build scripts and documentation now under the 'doc' .dsp on Windows
+ <LI> Textures now support a DepthReadMode that can be used to perform hardware
+ shadow map comparisions. <B>RenderDevice::configureShadowMap now requires
+ an appropriately configured texture-- in previous releases it would
+ reconfigure the texture for you.</B>
+ <LI> G3D::UserInput::keyReleased, G3D::UserInput::ReleasedKeys
+ <LI> G3D::Array::randomElement
+ <LI> G3D::Array::insert
+ <LI> G3D::RenderDevice::getObjectToWorldMatrix and getCameraToWorldMatrix now return
+ const CoordinateFrame&
+ <LI> Optimized G3D::Array::randomize
+ <LI> G3D::cyclicCatmullRomSpline
+ <LI> G3D::wrap
+ <LI> contrib/AudioDevice
+ <LI> G3D::System::time();
+ <LI> More precise System::sleep
+ <LI> G3D::IFSModel::pose with no arguments
+ <LI> G3D::AABSPTree::serializeStructure, deserializeStructure,
+ <LI> serialize(Vector3::Axis, BinaryOutput), deserialize(Vector3::Axis, BinaryInput),
+ <LI> "glslc" GLSL compiler in the tools directory for getting compile-time errors from shaders
+ <LI> GLCaps::init now takes optional debug log
+ <LI> G3D::VertexAndPixelShader static constructors take optional 'debug' argument
+ <LI> GWindowSettings::visible; Win32Window can now start invisible
+ <LI> [ 991147 ] glBlendEquationEXT, RenderDevice::BlendEq, min, max, subtract, reverse subtract alpha blending
+ <LI> [ 989785 ] Draw::rect2D
+ <LI> GLCaps::numTextureCoords, GLCaps::numTextureUnits, GLCaps::numTextures
+ <LI> GLCaps::G3D_MAX_TEXTURE_UNITS
+ <LI> Rect2D::corner
+ <LI> GCamera::getFrustum, GCamera::frustum, GCamera::Frustum, GCamera::Frustum::Face
+ <LI> Plane constructor that accepts Vector4s (possibly at infinity)
+ <LI> AABox::inf, AABox::zero, AABox::maxFinite
+ <LI> AABox::intersects(Sphere)
+ <LI> Vector3::minFinite, Vector3::maxFinite
+ <LI> Plane::halfSpaceContainsFinite
+ <LI> Plane::halfSpaceContains(Vector4)
+ <LI> AABSPTree::getIntersectingMembers(Array<Plane>)
+ <LI> AABSPTree::getIntersectingMembers(GCamera::Frustum) for view-frustum culling
+ <LI> AABSPTree::getIntersectingMembers(Sphere)
+ <LI> AABox::split
+ <LI> Extended AABox::culledBy, Box::culledBy, and Sphere::culledBy with extra
+ information for bounding volume hierarchies
+ <LI> G3D::computeNormalMap
+ <LI> Matrix3::fuzzyEq(Matrix3)
+ <LI> Removed System::sleep(0.02) from GLG3D demo to give more accurate performance measure
+ <LI> [ 965824 ] changed link library defaults
+ <LI> serialize/deserialize for int, bool, double, float, std::string
+ <LI> G3D::TextOutput
+ <LI> [ 976924 ] Texture::texelWidth
+ <LI> [ 973413 ] VertexAndPixelShader::ArgList::set can be called more than once per variable
+ <LI> OSWindow::setIcon(std::string filename)
+ <LI> Texture::fromMemory that takes a single image (instead of an array of images)
+ <LI> [972604] RenderDevice::setTextureMatrix(uint, Matrix4)
+ <LI> [972747] Rect2D::center
+ <LI> GImage and Texture now load ICO files
+ <LI> GL_SAMPLER_1D_ARB, 2D, 3D, CUBE
+ <LI> Win32Window mouse events
+ <LI> Added normals to AABox collision results
+ <LI> Fix: [ 1026534 ]various cast bugs using Ref types.
+ Removed G3D::ReferenceCountedPointer implicit cast to underlying pointer type
+ This is technically an <B>incompatible change</B>, however we found no occurance
+ in the library or demos using this that was not a bug!
+ <LI> Fix: VAR constructor takes VARAreaRef instead of VARArea* <B>Incompatible change</B>
+ <LI> Fix: ManualCameraController is prevented from looking precisely along the Y-axis, which would cause
+ a singularity.
+ <LI> Fix: Added '?' as a valid symbol Token
+ <LI> Fix: [ 946235 ] GFont::align right w/ fixed_spacing
+ <LI> Fix: [ 1001033 ] RenderDevice with 0 texture units
+ <LI> Fix: GLCaps:: ARB stencil two side -> EXT stencil two side (stencilled shadows were broken)
+ <LI> Fix: [ 993449 ] vsnprintf crashes MSVC 7
+ <LI> Fix: [ 991320 ] Pointer truncation Warnings
+ <LI> Fix: [ 981440 ] AUTO with Texture::fromMemory
+ <LI> Fix: Plane::halfSpaceContains now works for infinite and semi-infinite points
+ <LI> Fix: [ 979032 ] Quat <-> Matrix3 roundtrip inverts
+ <LI> Fix: [ 976743 ] document GLCaps functions
+ <LI> Fix: [ 976746 ] #include GLCaps in g3dall
+ <LI> Fix: [ 973550 ] sampler2DRect now supported in GLSL shaders (NVIDIA only; ATI drivers are broken)
+ <LI> Fix: [ 973490 ] Win32Window width/height off by non-client amount
+ <LI> Fix: [ 961827 ] In debug mode, RenderDevice tries to access
+ GL_MAX_TEXTURE_IMAGE_UNITS_ARB and an assertion fails on cards that
+ don't support it.
+ <LI> Fix: Texture binding for VertexAndPixelShader
+ </UL>
+<hr>
+ <P>
+ Changes in 6.03:
+ <UL>
+ <LI> Matrix4::approxCoordinateFrame
+ <LI> Vector2(const Vector2int16&) [Giulio]
+ <LI> RenderDevice::setObjectShader
+ <LI> RenderDevice::setVertexAndPixelShader
+ <LI> G3D::RenderDevice supports "..._CURRENT" as an option for most settings
+ <LI> inf -> inf(), nan -> nan(), NAN -> NAN()
+ <B>This is an incompatible change-- it was needed to fix a bug with the order
+ of initialization of globals</B>
+ <LI> GImage::sizeInMemory
+ <LI> Defined std::ostream << NetAddress, std::ostream << Vector3
+ <LI> 'build doc' copies the contrib directory to the install directory
+ <LI> LightweightConduit::PacketSizeException
+ <LI> Quat::unitRandom() [Giulio]
+ <LI> Color3::wheelRandom
+ <LI> GImage::save and encode now const [Thanks Arni Mar Jonsson]
+ <LI> LightweightConduit::send that accepts multiple destinations
+ <LI> ReliableConduit::multisend
+ <LI> Moved IFSBuilder from demos to contrib
+ <LI> LightweightConduit and ReliableConduit send/receive can now take references as well as pointers
+ <LI> RenderDevice::clear() that takes no arguments
+ <LI> RenderDevice::setShader
+ <LI> G3D::GApp now catches ShaderGroup::ArgumentError exceptions
+ <LI> System::operatingSystem() now includes a version number on Linux
+ <LI> SDLWindow no longer initializes the audio system; use SDL_InitSubsytem if you need audio.
+ <LI> Extended GLenumToString with GL_SHADER_OBJECTS_ARB types.
+ <LI> NVIDIA p-buffer: GLX_SAMPLE_BUFFERS_ARB, GLX_SAMPLES_ARB, GLX_FLOAT_COMPONENTS_NV,
+ glXDestroyGLXPbufferSGIX, glXChooseFBConfigSGIX, glXCreateGLXPbufferSGIX,
+ glXCreateContextWithConfigSGIX, glXQueryGLXPbufferSGIX
+ <LI> NVIDIA swap lock: glXJoinSwapGroupNV, glXBindSwapBarrierNV, glXQuerySwapGroupNV,
+ glXQueryMaxSwapGroupsNV, glXQueryFrameCountNV, glXResetFrameCountNV
+ <LI> OSWindow::requiresMainLoop, OSWindow::runMainLoop (Beta)
+ <LI> OSWindow::pollEvent, SDLWindow::pollEvent
+ <LI> G3D::GApp accepts an optional OSWindow on construction
+ <LI> G3D::VertexAndPixelShader, G3D::ObjectShader (Beta)
+ <LI> Deprecated GPUProgram, VertexProgram, and PixelProgram (the OpenGL 1.5 shaders
+ follow a different paradigm than the OpenGL 1.3 ones, so the G3D API must change
+ to match it).
+ <LI> Support for GL_ARB_vertex_shader, GL_ARB_fragment_shader, and GL_ARB_shader_objects
+ <LI> G3D::drawFeatureEdges
+ <LI> const Array<Vector3>& G3D::MD2Model::PosedModel::objectSpaceFaceNormals();
+ <LI> G3D::RenderDevice::sendSequentialIndices
+ <LI> Network_Demo
+ <LI> contrib/Win32Window
+ <LI> contrib/pingtest
+ <LI> contrib/GlutWindow [Morgan and Dan Keefe]
+ <LI> contrib/ObjModel [Corey Taylor]
+ <LI> G3D::GLCaps
+ <LI> GAppSettings::logFilename
+ <LI> Deprecated RenderDevice::suportsOpenGLExtension, RenderDevice::supportsImageFormat,
+ other supports shortcuts (use GLCaps instead).
+ <LI> DiscoveryClient::cleanup
+ <LI> Optimized BinaryInput::readUInt32, readUInt16
+ <LI> Extended network documentation
+ <LI> 'fastlib' build target for G3D library developers
+ <LI> glGetVector2, glGetVector3, glGetVector4
+ <LI> float * Quat (double * Quat already existed)
+ <LI> GApp automatically generates g3d-license.txt at runtime ([RFE#856338] CREDIT.TXT)
+ <LI> G3D::license
+ <LI> Removed several large files (tag, ppt, exe) from the source zipfile, bringing it down to 3 MB
+ <LI> Improved CoordinateFrame:pointToObjectSpace() (RFE#715996) [Giulio]
+ <LI> [RFE#945935] Make static constants into functions [Giulio]
+ <LI> Fix: LightweightConduit::send verifies that the packet size is smaller than the UDP limit
+ <LI> Fix: Multitexture on ATI and Wildcat cards
+ <LI> Fix: Incorrect occlusion in GLG3D_Demo (was caused by global constant problem)
+ <LI> Fix: [BUG#949377] Checks for stencil extensions [Giulio]
+ <LI> Fix: [BUG#922725] Non-multitexture implementation for getTextureState() [Giulio]
+ <LI> Fix: Restore ambient light color after RenderDevice::popState
+ <LI> Fix: RenderDevice now initializes OpenGL extensions before testing for multitexture [Erik Cassel, Dan Keefe]
+ <LI> Fix: Bottom clipping plane of GCamera frustum now correct (was slanted incorrectly, making frustum too big)
+ <LI> Fix: GFont::draw2D now returns correct y value (used to be too small)
+ <LI> Fix: NetworkDevice now returns useful hostname on Linux (used to be "localhost")
+ <LI> Fix: The conduit returned from NetworkDevice::createReliableConduit now has ok() == false when connect fails
+ <LI> Fix: Tangent space computation of constant u, v now correct (was missing a factor of 2, leading to slight errors) [Max McGuire]
+ <LI> Fix: [ 925456 ] select broken on Linux (Networking was broken on Linux)
+ <LI> Fix: getDepthBufferValue off by 1 [Andi Fein]
+ </UL>
+
+<hr>
+ <P>
+ Changes in 6.02:
+ <UL>
+ <LI> Default constructor for Line.
+ <LI> Various patches to make G3D work with the CAVE [Dan Keefe]
+ <LI> AABox::set
+ <LI> Made OSWindow::setPosition non-const
+ <LI> VARArea now tests for the presence of all VBO extensions, on the freak chance that
+ a driver has only partial support (due to a bug)
+ <LI> Linux build statically links OpenGL 1.2.1 and loads extensions through OpenGL 1.5
+ to work around Wildcat Linux driver bug (Windows and Mac statically link OpenGL 1.1
+ and load extensions through OpenGL 1.5)
+ <LI> Triangle stores precomputed edge lengths
+ <LI> Ray-triangle with vertex weights
+ <LI> Highly optimized ray-triangle intersection test [Tomas Moller & Ben Trumbore]
+ <LI> Create a texture from 6 different cube-map filenames
+ <LI> Added contrib directory built as part of the 'doc' target
+ <LI> contrib/CoreyGWindow: OSWindow implementations for various platforms
+ <LI> AABSPSet::beginRayIntersection [Pete Hopkins]
+ <LI> AABSPTree::beginBoxIntersection
+ <LI> CollisionDetection::intersectionTimeForMovingPointFixedAABox, Ray::intersectionTime(AABox)
+ [Pierre Terdiman and Andrew Woo]
+ <LI> Triangle::center
+ <LI> Renamed KDTreeSet to AABSPTree, old name is #defined
+ <LI> RenderDevice now works on cards without multitexture
+ <LI> void glTexCoord(const G3D::Vector4& t); [Dan Keefe]
+ <LI> Overloaded float, double, and int * Matrix3
+ <LI> Fix: [ 923944 ] Matrix/Quat ambiguity
+ <LI> Fix: fuzzyEq(inf, inf) is true
+ <LI> Fix: Triangle::randomPoint returns values outside the triangle
+ <LI> Fix: [ 913763 ] tokenTypeToString(Token::END)
+ <LI> Fix: Compute number of texture coordinates before RenderDevice::setVideoMode [Dan Keefe]
+ <LI> Changed the default depth bits to '0' for wider compatibility
+ (Fix: Unable to create OpenGL screen: Couldn't find matching GLX visual)
+ <LI> Fix: [912305] Table, Queue, and Set assignment operators do not free old values
+ <LI> Fix: Separate specular and Multisample on Tablet PC w/ Trident [Dan Keefe]
+ <LI> Fix: Linux debug build now has line numbers
+ <LI> Upgraded to SDL 1.2.7
+ Fix: [ 838030 ] SDL 1.2.6 blocks prompt
+ Fix: FSAA does not work under SDL
+ Fix: Default Win32 refresh rate
+ <LI> Draw::vertexVectors
+ <LI> New meshes from Brown University: hemisphere.ifs, curvy.ifs, head.ifs,
+ closed-low-poly-teapot.ifs, bump.ifs
+ <LI> GLight::specular
+ <LI> SDLWindow::setWindowDimensions and setWindowPosition now work on Win32
+ <LI> GWindowSettings::x, GWindowSettings::y, GWindowSettings::center
+ <LI> System::setEnv
+ <LI> [ 909999 ] OSWindow Joystick interface
+ <LI> double * Quat ([ 909305 ] scalar * {quat, vector, matrix})
+ <LI> Increased the precision of several Vector2 and Vector3 methods
+ <LI> MeshAlg::computeNormals now returns 0 instead of NaN for degenerate normals
+ <LI> Updated main-no-GApp.cpp for 6.02
+ <LI> RenderDevice::screenshotPic can copy from the back buffer
+ <LI> Improved VAR documentation.
+ <LI> If NO_SDL_MAIN is defined, G3D does not attempt to link against sdlmain.lib
+ <LI> UserInput::setPureDeltaMouse
+ <LI> UserInput::mouseXY, mouseX, mouseY
+ <LI> UserInput::mouseDXY
+ <LI> Deprecated UserInput keyMapping constructor argument
+ <LI> RenderDevice::setDrawBuffer [Dan Keefe]
+ <LI> GFont::draw3D [Dan Keefe]
+ <LI> GImage::pixel3(x, y) and GImage::pixel4(x, y)
+ <LI> debugAssert, debugBreak, debugAssertM, etc. all release input grab
+ when an assertion fails (Win32 and Linux) and restore it when the
+ program continues (Win32). This also fixes the DirectInput laggy
+ cursor that occurs after a break.
+ </UL>
+
+<hr>
+ <P>
+ Changes in 6.01:
+ <UL>
+ <LI> Default constructor for G3D::LineSegment
+ <LI> Rect2D::clipPoly (Pete & Morgan)
+ <LI> Draw::poly2D, Draw::poly2DOutline (Pete & Morgan)
+ <LI> Added instructions for rotated text to G3D::GFont::draw2D
+ <LI> Fix: iRandom now compiles correctly under gcc.
+ <LI> Fix: [ 852076 ] Compute better/faster vertex normals in MeshAlg
+ MeshAlg::computeNormals now weighs adjacent faces by their area
+ <LI> Fix: [ 896028 ] Textures broken on Trident TabletPC (Dan Keefe)
+ <LI> Fix: [ 860800 ] ManualCameraController cursor jumps
+ <LI> Fix: G3D::UserInput no longer offsets the mouse position by 1/2 pixel
+ <LI> Fix: Alt-Tab no longer toggles the GApp camera before switching windows
+ <LI> Fix: [ 901248 ] Font bounds y-value incorrect
+ <LI> Fix: G3D::PhysicsFrame::toCoordinateFrame() was rotated by 90 degrees
+ <LI> Fix: [ 895493 ] Radeon 7500 Cube Map
+ <LI> Fix: G3D::MeshAlg::computeWeld produces linker errors on Linux
+ <LI> G3D::TextInput::peekLineNumber(), G3D::TextInput::peekCharacterNumber()
+ <LI> G3D::GAppSettings::dataDir
+ <LI> html/gettingstarted.html
+ <LI> G3D::MeshAlg::debugCheckConsistency
+ <LI> G3D::MD2Model and G3D::IFSModel now weld their adjacency information
+ <LI> Renamed/retyped G3D::PosedModel::adjacentFaces to G3D::PosedModel::vertices
+ (most programs can be fixed by changing the type from Array< Array<int> > to
+ Array<MeshAlg::Vertex> and adjacentVertexArray[v] to vertexArray[v].faceIndex)
+ <LI> Shadow volumes now use the welded adjacency information
+ <LI> G3D::PosedModel now offers both welded and non-welded adjacency information
+ <LI> G3D::contains for C-Arrays
+ <LI> Generate .tag files in the build
+ <LI> G3D::MeshAlg::computeAdjacency does not merge colocated vertices
+ <LI> G3D::MeshAlg::computeAdjacency does not remove degenerate faces and edges
+ <LI> G3D::MeshAlg::Vertex
+ <LI> G3D::Vector3::directionOrZero
+ <LI> G3D::GMaterial
+ <LI> ManualCameraController renamed to G3D::FPCameraController
+ <LI> glGetCurrentContext (beta)
+ <LI> G3D::RenderDevice::supportsImageFormat
+ <LI> G3D::Vector3::magnitude
+ <LI> G3D::Vector3::cross() [returns Matrix3]
+ <LI> G3D::Quat changes (API is still in beta)
+ <LI> G3D::Quat::norm now returns the 2-norm, not the function Dave Eberly uses.
+ <LI> Matrix3 default constructor
+ <LI> Switched UserInput to use SDLWindow internally
+ <LI> Switched RenderDevice to use SDLWindow internally
+ <LI> G3D::Window
+ <LI> G3D::SDLWindow
+ <LI> Renamed G3D::RenderDeviceSettings to G3D::WindowSettings (with a typedef for the old name)
+ <LI> IFSModel now loads models with up to 10 million polygons (like the buddha).
+ <LI> Internal G3D::KDTreeSet state now private.
+ </UL>
+
+<hr>
+ <P>
+ Changes in 6.00:
+ <UL>
+ <LI> FIX: warning: passing `double' for argument 1 of `void G3D::Queue<T>::repackAndRealloc(int)'
+ <LI> Optimized static Matrix3::transpose (36 cycle) and
+ Matrix3::mul (52 cycle) variations.
+ <LI> Changed some lerp arguments from float to double
+ <LI> MeshAlg::computeTangentSpaceBasis
+ <LI> Draw::axes now uses scale to compute axis length
+ <LI> New ParallaxBump demo
+ <LI> Changed several Vector3 return values from float to double
+ <LI> Real-world stars, sun, and moon path (Nick Musurca)
+ <LI> Now compiles under MSVC++ 7.0 (David Baszucki)
+ <LI> Now compiles under g++ OS/X (Ben Landon)
+ <LI> Changed the default RenderDeviceSettings::alphaBits to 0 in the hope that it
+ will work with more graphics cards.
+ <LI> Matrix3::fromX methods became factory methods
+ <LI> G3D::sinc
+ <LI> Multi-platform lib directories
+ <LI> Vector3::average(), Color3::average(), Vector3::sum(), Color3::sum()
+ <LI> Ray::reflect, Ray::refract
+ <LI> Physically correct sky model
+ <LI> FIX: Older graphics cards can now initialize properly
+ <LI> Increased fuzzyEpsilon to 0.000001
+ <LI> Color3::max, Color3::min, Color4::max, Color4::min
+ <LI> Array::sortSubArray
+ <LI> GCamera::getClipPlanes now takes a G3D::Array
+ <LI> G3D::AABox
+ <LI> Box::randomInteriorPoint, Box::randomSurfacePoint
+ <LI> Vector3::cosRandom, Vector3::hemiRandom, Vector3::reflectAbout, Vector3::reflectionDirection, Vector3::refractionDirection
+ <LI> log(Color3)
+ <LI> Upgraded to zlib 1.2.1
+ <LI> VAR::valid (Peter)
+ <LI> System::getLocalTime, System::getTicks
+ <LI> High-performance cycle count and time queries on Linux
+ <LI> UserInput::anyKeyPressed
+ <LI> G3D::Box now provides axes, center, and extent information
+ (serialization is backwards compatible to 5.xx)
+ <LI> TextInput's exceptions now provide file, line, and character numbers
+ as well as preformatted error messages in the style of MSVC++.
+ <LI> G3D::Texture::fromGImage
+ <LI> G3D::TextInput now parses hex numbers of the form 0x#####
+ <LI> G3D::CollisionDetection::penetrationDepthForFixedSphereFixedPlane
+ <LI> G3D::CollisionDetection::penetrationDepthForFixedSphereFixedBox
+ <LI> G3D::beginMarkShadows, G3D::endMarkShadows, G3D::markShadows
+ <LI> GFont::draw2D now returns the string bounds
+ <LI> Sphere::surfaceArea, Sphere::volume, Box::surfaceArea, Box::volume
+ <LI> Two-sided stencil operations
+ <LI> Removed G3D::Real
+ <LI> FIX: [ 855947 ] Fonts are broken on Radeon
+ <LI> Switched vertex arrays to use the new ARB_vertex_buffer_object extension.
+ Compared to 5.xx rendering speed: NVIDIA/Win32 is the same (fast),
+ ATI and Linux rendering are about 10x faster. The API has changed
+ slightly-- most significant, the vertex, normal, color, etc. arrays
+ must all come from the same VARArea now.
+ <LI> Disabled the "conditional is constant" level 4 warning on Windows
+ that is triggered by the for-loop scoping fix.
+ <LI> G3D::SkyParameters::directionalLight
+ <LI> G3D::TextureManager (Peter S. & Morgan)
+ <LI> Flipped skybox X-axis to match OpenGL cube map coordinates
+ <LI> Texture now uses hardware MIP-map generation
+ <LI> Texture::copyFromScreen for cube map faces
+ <LI> RenderDevice::configureReflectionMap
+ <LI> RenderDevice::configureShadowMap
+ <LI> Renamed CFont to GFont
+ <LI> Renamed CImage to GImage
+ <LI> G3D::Matrix3::getRow
+ <LI> Added optional argument drawCelestialBodies to Sky::create.
+ <LI> RenderDevice::getTextureMatrix
+ <LI> Depth Textures
+ <LI> Texture::createEmpty
+ <LI> RenderDevice::setViewport has flipped the y-axis since version 5.00
+ <LI> ReferenceCountedPointer::isLastReference
+ <LI> Support for textures beyond the number of texture units (which occurs on NVIDIA cards)
+ <LI> G3D::PosedModel
+ <LI> G3D::IFSModel
+ <LI> G3D::CoordinateFrame::normalToObjectSpace, G3D::CoordinateFrame::normalToWorldSpace
+ <LI> Simplified arguments on Texture::copyFromScreen
+ <LI> Moved Camera in GLG3D to GCamera in G3D
+ <LI> Moved setProjectionAndCameraMatrix from Camera to RenderDevice
+ <LI> Moved G3D::Rect2D to G3D from GLG3D, changed interface
+ <LI> G3D::setRenderMode
+ <LI> G3D::RenderDevice::setSpecularCoefficient, G3D::RenderDevice::setShininess
+ <LI> G3D::GLight
+ <LI> Renamed G3D::RenderDevice::configureDirectionalLight, configurePointLight to G3D::RenderDevice::setLight
+ <LI> Changed G3D::Rect2D to use doubles
+ <LI> G3D::Camera::setPosition()
+ <LI> G3D::Camera::lookAt()
+ <LI> G3D::ManualCameraController::setPosition()
+ <LI> G3D::System::getTick, G3D::System::getLocalTime
+ <LI> Fixed [ 839618 ] peak var only updated on reset()
+ <LI> G3D::Array::findIndex (thanks to David Baszucki for the suggestion)
+ <LI> Removed RenderDevice::setProjectionMatrix3D and RenderDevice::setProjectionMatrix2D
+ <LI> RenderDevice::project
+ <LI> RenderDevice::push2D() now uses the current viewport instead of full screen by default
+ <LI> RenderDevice::getViewport
+ <LI> G3D::SimTime
+ <LI> Sky::render no longer needs a camera matrix (it gets it from the render device)
+ <LI> SkyRef, Sky::create()
+ <LI> Removed Sky::getName
+ <LI> Removed RenderDevice::setAmbientLightLevel (duplicated RenderDevice::setAmbientLightColor)
+ <LI> G3D::GApp, G3D::GApplet, G3D::GAppSettings
+ <LI> RenderDevice::getCardDescription
+ <LI> GPUProgram interface for setting program constants [Peter, Morgan & Dan]
+ <LI> RenderDevice::getModelViewMatrix
+ <LI> RenderDevice::getModelViewProjectionMatrix
+ <LI> RenderDevice::getProjectionMatrix
+ <LI> Documented some more common compiler errors.
+ <LI> Moved RenderDevice::debugDraw methods to the Draw class, changed rendering from
+ cylinders to lines for wireframe (for performance)
+ <LI> Ray::direction no longer has unit length
+ <LI> Line::point, Line::direction
+ <LI> LineSegment::endPoint
+ <LI> IFSBuilder loads Brown University Sketch Model (sm) format
+ <LI> New IFS models: angel, distributor-cap, dragon2, duck, elephant, hippo, hub, mech-part, rotor, sandal, trumpet, venus-torso, woman
+ <LI> RenderDevices are now optionally resizable
+ <LI> MeshAlg::computeWeld
+ <LI> Array::randomize
+ <LI> Table now refuses to push the load factor above 19/20 and stops rehashing
+ <LI> Table always keeps an odd number of buckets
+ <LI> Sphere::randomInteriorPoint, Sphere::randomSurfacePoint
+ <LI> LineSegment::randomPoint
+ <LI> Hardcoded some common paths into demoFindData
+ <LI> Deprecated old RenderDevice::init method.
+ <LI> Full screen anti-aliasing (FSAA)
+ <LI> G3D::RenderDeviceSettings
+ <LI> All 2, 3, and 4 character swizzles for Vector2, Vector3, Vector4 are defined.
+ <LI> G3D::rsqrt
+ <LI> Most vector methods are also defined as functions now
+ <LI> sign(Vector2), sign(Vector3), sign(Vector4)
+ <LI> G3D::Matrix4
+ <LI> Changed G3D_VER from double to integer
+ <LI> G3D::lerp
+ <LI> Changed G3D::PI, G3D::HALF_PI, and G3D::TWO_PI to #defines
+ <LI> Vector2::clamp, Vector3::clamp, Vector4::clamp
+ <LI> Changed order of arguments to all lerp methods to match DirectX/Cg
+ <LI> Changed order of arguments to G3D::clamp and G3D::iClamp to match DirectX/Cg
+ <LI> G3D::ManualCameraController::ManualCameraController now requires a G3D::UserInput
+ <LI> G3D::UserInput::appHasFocus
+ <LI> G3D::ManualCameraController now stops tracking the mouse when the app loses focus
+ <LI> G3D::ManualCameraController::setActive
+ <LI> G3D::ManualCameraController now manages the mouse cursor instead of G3D::RenderDevice
+ <LI> G3D::UserInput::getMouseXY, G3D::UserInput::getXY
+ <LI> RenderDevice::debugDrawVertexNormals
+ <LI> GPUProgram, VertexProgram, and PixelProgram now recognize the output of the
+ Cg compiler and automatically bind constants.
+ <LI> RenderDevice now loads glActiveStencilFaceEXT
+ <LI> RenderDevice::numTextureCoords
+ <LI> Moved changelog to a separate page
+ <LI> Reformatted overview to be smaller
+ <LI> Added model debugging info to the IFSBuilder display
+ <LI> Welded some broken vertices in the teapot.ifs file
+ <LI> Renamed Font.* to CFont.*
+ <LI> CFont::draw2DString renamed to CFont::draw2D (use a #define to port old code)
+ <LI> MeshAlg
+ <LI> RenderDevice now enables GL_COLOR_MATERIAL by default
+ <LI> msgBox
+ <LI> MD2 model gallery in documentation (Kevin)
+ <LI> MD2Documentor (Kevin)
+ <LI> debugAssertGLOk macro
+ <LI> VertexProgram now supports NVIDIA Vertex Program 2.0
+ <LI> RenderDevice now loads glGenProgramsNV, glDeleteProgramsNV, glBindProgramNV, glLoadProgramNV, glTrackMatrixNV, glProgramParameter4fvNV, glGetProgramParameterfvNV, glGetProgramParameterdvNV extensions
+ <LI> VertexProgram and PixelProgram static factory methods now return reference counted values.
+ <LI> Split the reference value from RenderDevice::setStencilTest into setStencilConstant
+ <LI> RenderDevice::STENCIL_INVERT, RenderDevice::STENCIL_REPLACE, RenderDevice::STENCIL_ZERO
+ <LI> Added brighten argument to Texture::fromFile
+ <LI> Increased CImage JPEG save quality
+ <LI> RenderDevice::screenshot now returns the name of the file that was written
+ <LI> nextPowerOf2 renamed to ceilPow2
+ <LI> System::alignedMalloc, System::alignedFree
+ <LI> Carbon, Crackman, Edenmill, Futurist, Interplanetary,
+ Iomanoid, Starlight, Lesser, and Wild fonts by Ray Larabie.
+ Like all of our fonts, they are free, but please consider a
+ donation to him if you like them. http://www.larabiefonts.com/
+ <LI> MD2Model_Demo
+ <LI> G3D::MD2Model
+ <LI> FIX: Fixed a bug in Array shrinking that could cause memory corruption
+ <LI> FIX: RenderDevice windows with an aspect ratio of less than 1 now allowed.
+ <LI> FIX: TextInput now parses '#', '~', '~=', '&', '&&', '|', '||' correctly
+ <LI> VARArea::reset() now waits for rendering calls using its vertex
+ arrays to complete before wiping the memory.
+ <LI> G3D::filenameBaseExt, G3D::filenameExt
+ <LI> VARArea::finish()
+ <LI> Milestone
+ <LI> TextInput::Options::signedNumbers
+ <LI> RenderDevice now loads glFlushVertexArrayRangeNV
+ <LI> Vector2int16
+ <LI> RenderDevice::freeVARSize()
+ <LI> Array now allocates 16-byte aligned pointers.
+ <LI> Decreased the default camera movement rate by 50% for better resolution.
+ <LI> RenderDevice enables GL_NORMALIZE by default
+ <LI> Improved the performance of Array::append/Array::push/Array::next
+ <LI> Fix: [ 875219 ] Array::sort must use std::sort
+ <LI> Array::next
+ <LI> Array::reverse
+ <LI> PCX file loading
+ <LI> Test images
+ <LI> Color3uint8 as uint8[] addressing
+ <LI> Color4uint8 as uint8[] addressing
+ <LI> Removed const from VAR::pointer
+ <LI> ReferenceCountedPointer::isNull
+ <LI> alwaysAssertM
+ <LI> Log::common, Log::getCommonLogFilename
+ <LI> Switched from static to dynamic linking of zlib
+ <LI> Upgraded to zlib 1.1.3
+ <LI> On Win32 the lib list is automatically updated through pragmas
+ (5.xx programs should revert to linking against default libraries)
+ <LI> Increased default sky quality to 1.00
+ <LI> G3D::CFontRef
+ <LI> RenderDevice now loads all register combiner extensions (NVIDIA only)
+ <LI> Sky::getEnvironmentMap
+ <LI> Sky implementation now uses a cube map (when one is available)
+ <LI> G3D::Sky constructor now takes a render device
+ <LI> Rotated Sky box 90 degrees to match environment maps
+ <LI> G3D::Sky now takes the environment filenames as "sky_*.jpg" instead of "sky_ft.jpg"
+ <LI> Added default filename for Sky constructor
+ <LI> Added caustics textures created with Kjell Andersson's generator http://www.lysator.liu.se/~kand/caustics/
+ <LI> #defined "for" under MSVC so that it obeys C99 scoping rules
+ <LI> System::consoleKeyPressed
+ <LI> System::consoleClearScreen
+ <LI> System::consoleReadKey
+ <LI> NetMessage::type()
+ <LI> Changed the Conduit message protocol to include a message type.
+ The API is backwards compatible to 5.01 even though the protocol is not.
+ <LI> Removed optional argument maxSize from LightweightConduit::receive.
+ <LI> NetAddress::serialize
+ <LI> NetAddress::deserialize
+ <LI> NetAddress == NetAddress
+ <LI> hashCode(NetAddress)
+ <LI> RenderDevice::init now prints ATI or NVIDIA driver version to the log under Windows
+ <LI> readme.html library build instructions now have downloads for required libraries
+ <LI> Library list has changed for Win32 (added version.lib)
+ <LI> System::cpuArchitecture
+ <LI> System::operatingSystem
+ <LI> double-precision Plane::getEquation
+ <LI> Vector2::lerp
+ <LI> Platform specific #defines G3D_WIN32, G3D_LINUX, G3D_OSX
+ <LI> G3D::Array::contains
+ <LI> G3D::Queue::contains
+ <LI> G3D::ImageFormat
+ <LI> G3D::Texture::DIM_CUBE_MAP
+ <LI> G3D::Texture resizes non-power of two textures
+ <LI> G3D::Texture constructors are completely changed from 5.01 (and hopefully easier to use)
+ <LI> G3D::CImage now supports images with alpha
+ <LI> Removed most of the width/height arguments from G3D::Camera methods
+ <LI> BinaryInput::readBytes and BinaryOutput::writeBytes now take void* as an argument to avoid casting
+ <LI> Plane::fromEquation
+ <LI> Removed Plane::getNormal (use Plane::normal instead)
+ <LI> Removed CDTriangle (use G3D::Triangle instead)
+ <LI> Removed Font (use G3D::CFont instead)
+ <LI> FIX: Camera::getClipPlanes now transforms infinite planes correctly.
+ <LI> FIX: The last reference of an RGC pointer assigned to itself no
+ longer tries to collect before re-assigning
+ </UL>
+
+<hr>
+ <P>
+ Changes in 5.01
+ <UL>
+ <LI> G3D::tesselateComplexPolygon
+ <LI> G3D::ConvexPolygon
+ <LI> G3D::ConvexPolyhedron
+ <LI> G3D::iClamp, G3D::clamp
+ <LI> G3D::iWrap
+ <LI> G3D::iRandom, G3D::random
+ <LI> G3D::getFiles
+ <LI> G3D::getDirs
+ <LI> G3D::VAR::pointer
+ <LI> G3D::realWorldLocalTime
+ <LI> G3D::Texture::TRANSPARENT_BORDER
+ <LI> DECLARE_GLFORMATOF
+ <LI> G3D::System::machineEndian
+ <LI> G3D::VertexProgram, G3D::VertexProgramRef, G3D::RenderDevice::setVertexProgram
+ <LI> G3D::PixelProgram, G3D::PixelProgramRef, G3D::RenderDevice::setPixelProgram
+ <LI> G3D::GPUProgram, G3D::GPUProgramRef
+ <LI> G3D::sizeOfGLFormat
+ <LI> G3D::RenderDevice::setVertexAttrib
+ <LI> G3D::Vector2*=Vector2, /= Vector2, * Vector2, / Vector2
+ <LI> glFormatOf
+ <LI> G3D::Color4uint8
+ <LI> G3D::Color3uint8
+ <LI> G3D::Vector3int16
+ <LI> G3D::System::currentProgramFilename
+ <LI> CImage::insertRedAsAlpha
+ <LI> CImage::stripAlpha
+ <LI> Texture::hasAlpha
+ <LI> Added support for TGA with alpha channel
+ <LI> Re-implemented Texture to support a broader range of formats and cleaner implementation.
+ <LI> Fix: Improved Texture::LUMINANCE support
+ <LI> Added == and != overloads for Texture::Ref so that "a != NULL" is now legal and does not require a cast to Texture::Ref.
+ <LI> G3D::CFont is a typedef for G3D::Font to avoid name conflicts with X11 Font under Linux. In future releases, the name Font will be deprecated.
+ <LI> RenderDevice::setPointSize
+ <LI> Added a new teapot (teapot.ifs) that is closed, with a properly fitting top. The classic teapot is now called "utah-teapot.ifs" (Sebastian Schuberth and Simon Winkelbach)
+ <LI> RenderDevice::init now loads glPointParameterfvARB, glPointParameterfARB,
+ glMultiDrawArraysEXT, and glMultiDrawElementsEXT functions.
+ <LI> GLenumToString(4) now returns "GL_TRIANGLES" instead of "GL_LINE_BIT" (both are correct)
+ <LI> Added TextInput::Options to optionally allow C++ comments to
+ be treated as two slashes instead of a comment
+ <LI> Added data/image/meter.jpg, a meter stick texture convenient for testing
+ <LI> Added sansserif, news, and terminal fonts based on Bitstream's <A HREF="http://www.gnome.org/fonts/">free fonts</A>
+ <LI> RenderDevice::numTextureUnits
+ <LI> Added stars to night Sky
+ <LI> Added classic GL dinosaur model as data/ifs/dinosaur.ifs
+ <LI> Documented G3D::glGetProcAddress
+ <LI> Fix: Texture now restored GL_ENABLE bits properly after creation
+ <LI> Fix: Texture::sizeInMemory now accounts for MIP-map levels
+ <LI> Fix: Fonts and skies now adjust their brightness for the screen gamma level
+ <LI> Fix: Strange compilation bug was causing Sky to be black for some programs
+ <LI> resolveFilename
+ <LI> GLProgram_Demo to show how to use vertex programs in G3D
+ <LI> Support for GL_ARB_vertex_program
+ <LI> Modified ManualCameraController so that diagonal movement does not exceed
+ maximum rate.
+ <LI> Added support for non-GL_FLOAT vertex arrays to RenderDevice
+ <LI> Added support for Wavefront OBJ files to IFSBuilder
+ <LI> Removed duplicate copies of SDL.dll from the source tree
+ <LI> Renamed G3D::CDTriangle to G3D::Triangle
+ <LI> Added several G3D::Triangle methods
+ <LI> Moved CollisionDetection::primaryAxis to Vector3::primaryAxis
+ <LI> Fix: Texture::sizeInMemory now returns correct results for RGB8 textures.
+ <LI> Changed texture constructors in ways that slightly break backwards compatibility
+ <LI> Deprecated several arguments to the texture constructors.
+ </UL>
+
+<hr>
+ Changes in 5.00
+ <UL>
+ <LI> Color3::operator*=(const Color3&)
+ <LI> Color3::operator*(const Color3&)
+ <LI> Eliminated duplicate GL headers [James O'Sullivan]
+ <LI> Linux Makefiles [James O'Sullivan, Jordan Parker]
+ <LI> RenderDevice::getProjectionMatrixParams
+ <LI> RenderDevice::debugDrawCylinder
+ <LI> Added an option to not copy input memory for BinaryInput
+ <LI> Added data/ifs/sphere.ifs
+ <LI> Added data/ifs/spikeball.ifs
+ <LI> Added a new (imperfect) demo/tool that converts 3DS and MD2 to IFS.
+ <LI> Added RenderDevice to the Font constructor
+ <LI> Removed RenderDevice from Font::drawString
+ <LI> Included glut32.lib, .dll, and .h (Version 3.7.6) in the distribution.
+ The windows glut port is by Nate Robbins and is from
+ http://www.xmission.com/~nate/glut.html.
+ glut was originally written by Mark Kilgard.
+ <LI> Modified OpenGL headers to work cross platform, with the latest NVIDIA extensions
+ <LI> Changed library name from graphics3D.lib to G3D.lib, same for
+ debug version.
+ <LI> Changed directory structure and added readme.html to explain
+ the new setup.
+ <LI> Changed BinaryInput::readBytes to allow reading onto the stack
+ <LI> Added Vector4::isFinite
+ <LI> G3D::CDTriangle (for 35% faster collision detection)
+ <LI> CollisionDetection::closestPointToRectangle
+ <LI> CollisionDetection::movingSpherePassesThroughFixedBox
+ <LI> CollisionDetection::movingSpherePassesThroughFixedSphere
+ <LI> Changed CollisionDetection::movingXFixedTriangle arguments
+ <LI> CollisionDetection::collisionTimeForMovingSphereFixedSphere
+ <LI> Changed CollisionDetection::distanceToX methods to closestPointToX
+ <LI> Vector3::NAN3
+ <LI> Made Vector3::isUnit fuzzy
+ <LI> Made Vector3::isZero fuzzy
+ <LI> Fix: Texture(std::string, std::string) constructor now works for alpha-only textures.
+ <LI> FIX: Array now calls copy constructor when resizing
+ <LI> FIX: Triangle-sphere and rectangle-sphere collision detection
+ returned an incorrect collision location; now fixed.
+ <LI> FIX: changed VectorX::isFinite to call isFinite (used to give bad result for NaNs)
+ <LI> FIX: Used the normalized edge to compute intersection in
+ CollisionDetection::distanceToTrianglePerimeter
+ <LI> FIX: Changed the order of corners returned from Box::getFaceCorners so the
+ face is ccw, facing out
+ <LI> FIX: ManualCameraController::lookAt now faces along the -z axis.
+ <LI> FIX: data/ifs/icosa.ifs model is now an icosahedron
+ <LI> Made Set::begin() and Set::end() const
+ <LI> Added ifdef _WIN32 all over for typedefing types from Windows to Linux and vice versa.
+ <LI> G3D::isNaN, G3D::isFinite
+ <LI> Added a single triangle triangle.ifs file
+ <LI> G3D::LineSegment
+ <LI> RenderDevice::debugDrawRay
+ <LI> CoordinateFrame::toObjectSpace(Ray&)
+ <LI> CoordinateFrame::toObjectSpace(Box&)
+ <LI> CoordinateFrame::toObjectSpace(Sphere&)
+ <LI> Changed CollisionDetection routines to return the surface normal of the
+ surface at the collision location.
+ <LI> CollisionDetection::collisionTimeForMovingPointFixedCapsule
+ <LI> CollisionDetection::collisionTimeForMovingSphereFixedCapsule
+ <LI> G3D::Capsule class
+ <LI> Removed e-mail addresses from contributor list to protect them from spammers
+ <LI> Linux port [Hari Khalsa & Chris Kern]
+ <LI> Added serialize and deserialize methods, deserializing constructor to
+ Vector2, Vector3, Vector4, Color3, Color4, Matrix3, CoordinateFrame, Box,
+ Sphere, Plane, Ray, Line, Capsule, LineSegment
+ <LI> Moved parts of Plane.h into Plane.cpp
+ <LI> BinaryInput::readBool8 and BinaryOutput::writeBool8
+ <LI> G3D::System [based on Michael Herf, Rob Wyatt, and Benjamin
+ Jurke's work]
+ <LI> Networking infrastructure: G3D::NetworkDevice, G3D::NetAddress,
+ G3D::ReliableConduit, G3D::LightweightConduit, G3D::NetListener
+ <LI> G3D::Camera
+ <LI> Vector2::toString
+ <LI> G3D::createTempFile
+ <LI> G3D::fileLength
+ <LI> UserInput::setKeyMapping
+ <LI> UserInput::keyCodeToString, UserInput::stringToKeyCode
+ <LI> JPEG library uses createTempFile
+ <LI> JPEG library will allocate up to 6MB before resorting to temp
+ files-- faster and more reliable
+ <LI> Moved SDL initialization to RenderDevice constructor from the init
+ method so extension can be used earlier
+ <LI> Support for up to 8 texture units, no longer crashes on machines
+ that have more than 4 units
+ <LI> Made Arrays allocate at least 32 bytes when resized to improve
+ performance of small char stacks
+ <LI> Added UserInput key codes for mouse wheel buttons
+ <LI> UserInput::keyPressed, UserInput::pressedKeys()
+ <LI> UserInput::GKey
+ <LI> Renamed UserInput::poll() to UserInput::endEvents(), added
+ UserInput::beginEvents()
+ <LI> Moved custom UserInput key codes into an enum so they are
+ compile-time constants
+ <LI> Changed all <io.h> to <stdio.h> for cross-platform [Rob & Chris]
+ <LI> Moved LITTLE_ENDIAN and BIG_ENDIAN constants to an enum and renamed
+ them to G3D_LITTLE_ENDIAN and G3D_BIG_ENDIAN for cross-platform
+ [Rob & Chris]
+ <LI> Permanently fixed the precision of Real to be 32-bit float.
+ <LI> RenderDevice now loads the NVIDIA VAR fence extensions.
+ <LI> Renamed RenderDevice::begin to RenderDevice::beginPrimitive, same
+ for end.
+ <LI> Redesigned the vertex array system; see VAR and VARArea.
+ <LI> Changed GLG3D demo to demonstrate the use of the new VAR and
+ VARArea classes
+ <LI> CoordinateFrame(Vector3) constructor.
+ <LI> Improved the performance of zero-radius sphere [aka point]
+ collision detection
+ </UL>
+
+<hr>
+ <P>
+ Changes in 4.01
+ <UL>
+ <LI> trimWhitespace()
+ <LI> Pointwise multiplication and division for Vector3
+ <LI> Array::sort now uses > operator by default; two alternative sort methods allow qsort style sorting
+ <LI> Texture::copyFromScreen
+ <LI> Texture::invertY
+ <LI> BinaryInput/BinaryOutput compression (via zlib)
+ <LI> Alpha-only G3D::Texture mode
+ <LI> G3D::Font and fonts in data/font
+ <LI> Array::fastRemove
+ <LI> TextInput [Morgan & Aaron]
+ <LI> Color4::CLEAR
+ <LI> Table [] operator now returns a non-const reference
+ <LI> RenderDevice::getFrameRate, RenderDevice::getTriangleRate, RenderDevice::getTriangleCount
+ <LI> ManualCameraController::setMoveRate, ManualCameraController::setTurnRate
+ <LI> SkyParameters default constructor
+ <LI> Vector2, Vector3, Vector4 isZero(), isUnit(), isFinite()
+ <LI> Vector4::length(), Vector4::squaredLength()
+ <LI> isValidPointer now returns false for 0xFEEEFEEE
+ <LI> RenderDevice checks for texture compression extensions
+ <LI> Restructured the directories for the CPP sources (only affects people who build G3D)
+ <LI> Included NVIDIA and SGI OpenGL headers in the distribution, changed install notes
+ <LI> Fixed a bug that previously prevented textures from being garbage collected
+ <LI> Fixed Line::distance returning values too small
+ <LI> Fixed Plane(normal, point) constructor to compute point from normalized direction [Kevin]
+ <LI> LED font by Matthew Welch daffy-duck@worldnet.att.net
+ <LI> VenusRising font by Ray Larabie <A HREF="mailto:drowsy@cheerful.com">drowsy@cheerful.com</A>
+ <LI> VideoFreak font by Jakob Fischer pizzadude@pizzadude.dk
+ </UL>
+
+<hr>
+ <P>
+ Changes in 4.00
+ <UL>
+ <LI> Moved texture combine modes from Textures onto RenderDevice texture units
+ <LI> Documented RenderDevice::getHDC() (Windows only)
+ <LI> Renamed RenderDevice::swapBuffers() to RenderDevice::endFrame(), added corresponding RenderDevice::beginFrame()
+ <LI> Moved getNumJoySticks from RenderDevice to UserInput
+ <LI> Added TEX_ADD combine mode
+ <LI> Table::getKeys and Set::getMembers now have overloads that take an Array as input.
+ <LI> BinaryOutput::getCArray
+ <LI> RenderDevice::getObjectToWorldMatrix(), RenderDevice::getCameraToWorldMatrix()
+ <LI> RenderDevice::debugDrawAxes(), RenderDevice::debugDrawBox(), RenderDevice::debugDrawSphere()
+ <LI> Color3::Color3(const Vector3&) and Color4::Color4(const Vector4&)
+ <LI> Moved hashCode(const Vector3&) and hashCode(const Vector4&) to the global namespace [Kevin]
+ <LI> isValidPointer now returns false for 0xCCCCCCCC and 0xDEADBEEF
+ <LI> Fix: RenderDevice::setPolygonOffset now affects polygons rendered in line and point mode
+ <LI> Fix: Sun is now invisible after it goes below the horizon
+ <LI> Fix: BinaryInput now supports endian-ness correctly in memory read mode
+ <LI> Fix: Table.copyFrom and copy constructor now work
+ </UL>
+
+<hr>
+ <P>
+ Changes in 3.02
+ <UL>
+ <LI> Built libraries using "Multithreaded DLL" [Kevin & Darius]
+ <LI> Added depth, color, and stencil bit depth preferences to G3D::RenderDevice
+ <LI> G3D::Sky (plus sky directory in the data distribution)
+ <LI> Sky cube data [Jauhn Dabz, jauhn@yahoo.com, http://nullpoint.fragland.net]
+ <LI> G3D::UserInput
+ <LI> G3D::ManualCameraController
+ <LI> G3D::SkyParameters
+ <LI> G3D::toSeconds, G3D::AMPM, G3D::GameTime, G3D::RealTime
+ <LI> G3D::RenderDevice::project
+ <LI> G3D::linearSpline
+ <LI> G3D::Color3::fromARGB and G3D::Color4::fromARGB
+ <LI> Added non-const G3D::Array::last() [Kevin]
+ <LI> Modified G3D::RenderDevice::configureDirectionalLight to operate in world space
+ <LI> Fix: Flipped the y-axis of G3D::RenderDevice::getDepthBufferValue so it matches the documentation.
+ <LI> Removed brief descriptions from documentation
+ <LI> Removed sqrt, sin, cos, etc. that conflict with standard library names
+ <LI> Removed TWO_PI constant
+ <LI> Removed G3D::Matrix3 virtual destructor
+ <LI> Removed G3D::Quat virtual destructor [Kevin]
+ </UL>
+
+ <hr>
+ Changes in 3.01
+ <UL>
+ <LI> Changed an assert() to debugAssert() in Queue.h
+ <LI> G3D::Table doesn't grow the number of buckets under bad hash codes [Morgan & Darius]
+ <LI> G3D::Table allocates only 10 initial buckets
+ <LI> G3D::Table::debugGetLoad()
+ <LI> G3D::CollisionDetection::collisionTimeForMovingPointFixedRectangle
+ <LI> G3D::CollisionDetection::collisionTimeForMovingPointFixedBox
+ <LI> G3D::Ray::intersectionTime, G3D::Ray::unit()
+ <LI> G3D::Log [Morgan & Aaron]
+ <LI> G3D::RenderDevice (OpenGL state abstraction. VertexBuffer support is beta only)
+ <LI> G3D::Texture (includes texture compression, image loading, and texture rectangle)
+ <LI> Added a comment to the vector classes noting that they can't be sublcassed [Kevin Egan]
+ </UL>
+<hr>
+ Changes in 3.00
+ <UL>
+ <LI> G3D::NEWLINE
+ <LI> writeStringToFile
+ <LI> Fixed empty stringJoin bug
+ <LI> Fixed parseFilename with no path bug
+ <LI> Vector3::INF3, Vector3::ZERO3
+ <LI> G3D::PhysicsFrame (beta-- this interface is going to change in 4.00)
+ <LI> G3D::Vector4
+ <LI> G3D::Queue
+ <LI> Default constructor for G3D::CImage
+ <LI> G3D::isValidHeapPointer, G3D::isValidPointer
+ <LI> G3D::Ray
+ <LI> CImage copy constructor, CImage::load
+ <LI> Removed \#pragma once for gcc compatibility
+ <LI> Renamed several hashcode methods to hashCode
+ <LI> Fixed fuzzy math to work with infinite numbers
+ <LI> Fixed Table::remove(), Set::remove() bug [Darius Jazayeri]
+ <LI> G3D::CoordinateFrame.toObjectSpace(Vector4), G3D::CoordinateFrame.toWorldSpace(Vector4)
+ <LI> Added the data directory
+ <LI> G3D::CollisionDetection
+ <LI> G3D::Sphere::culledBy()
+ <LI> Added the GLG3D library [Morgan McGuire & Seth Block]
+ <LI> Changed SDL_GL_Demo to use GLG3D, rotate triangle, and use color blending
+ <LI> Fixed debugPrintf to handle long strings on Win32
+ <LI> Wrapped the MMX headers with \#ifdefs [Nate Miller]
+ <LI> Moved OpenGL code out of CoordinateFrame.h/cpp
+ <LI> Fixed BinaryInput readVector*, readColor* to read in correct order [Nate Miller]
+ <LI> BinaryInput::readVector4, BinaryInput::readColor4, BinaryOutput::writeVector4, BinaryOutput::writeColor4
+ <LI> IFS_Demo for loading IFS files, dealing with models in OpenGL [Nate Miller]
+ </UL>
+
+<hr>
+ <P>
+ Changes in 2.00
+ <UL>
+ <LI> Vector2 members renamed to x,y from s,t
+ <LI> Added SDL_GL_Demo and Win32_Demo
+ <LI> Removed Group
+ </UL>
+
+<hr>
+ <P>
+ Changes in 1.10
+ <UL>
+ <LI> CImage, color conversion routines [Morgan McGuire, John Chisholm, and Edward Resnick]
+ <LI> Array dereference for BinaryInput
+ <LI> BinaryInput from memory
+ <LI> BinaryOutput to memory
+ <LI> toUpper(std::string), toLower(std::string)
+ <LI> Group::clear()
+ <LI> inf, nan as global constants (double precision)
+ <LI> Can iterate over const Tables
+ <LI> Table::deleteValues()
+ <LI> Fixed an off-by-one bug in BinaryInput::readString()
+ <LI> beginsWith() and wordWrap() string utilities
+ <LI> prompt dialogs have fixed width font [Kurt Miller]
+ <LI> iMax(), iMin()
+ <LI> Array::sort()
+ <LI> stringCompare(), stringPtrCompare()
+ <LI> readFileAsString()
+ <LI> Fixed textPrompt() to wait for input
+ <LI> BinaryInput.getFilename(), BinaryOutput.getFilename()
+ <LI> ReferenceCount [Justin Miller]
+ <LI> endsWith()
+ <LI> stringSplit(), stringJoin()
+ <LI> Renamed format.* to stringutils.*
+ <LI> fileExists(), parseFilename(), createDirectory(), copyFile()
+ <LI> highestBit() [Jukka Liimatta]
+ <LI> flipRGBVertical()
+ <LI> Changed all header guards to use G3D_ prefix
+ <LI> ConvexPolyhedron
+ <LI> Virtual destructors on almost all objects.
+ <LI> RGBtoBGR()
+ <LI> Color4
+ <LI> Array::pop(bool shrinkArray=true)
+ <LI> Vector2::isFinite, Vector2::fuzzyEq, Vector::fuzzyNe
+ </UL>
+ <P>
+
+<hr>
+ Changes in 1.09
+ <UL>
+ <LI> Removed pointer hash [Aaron Orenstein]
+ <LI> Changed some includes from quotes to pointy brackets [Aaron Orenstein]
+ <LI> Sphere::toString()
+ <LI> Plane::toString()
+ <LI> Added a change log
+ </UL>
+
+ */
diff --git a/externals/g3dlite/doc-files/contributors.dox b/externals/g3dlite/doc-files/contributors.dox
new file mode 100644
index 00000000000..3cd946a0bf1
--- /dev/null
+++ b/externals/g3dlite/doc-files/contributors.dox
@@ -0,0 +1,85 @@
+/**
+@page contributors G3D Developer Credits
+
+ <b>Team Members</b>
+ <br><i>7.01 Release Team</i>
+ <br>Morgan McGuire [Williams College] - Project Manager
+ <br>Corey Taylor [EA] - Assistant Project Manager and Linux Lead
+ <br>Casey O'Donnell [RPI] - OS X Lead
+ <br>Dan Keefe [Brown University] - Developer
+ <br>Kyle Whitson [Williams College] - Developer
+ <br>Danny Yuxing Huang [Williams College] - Developer
+
+ <b>Previous Contributors and Cited Sources</b> <br>This library
+ contains code and resources contributed by the following people, or
+ based open code and articles by them. Starred (*) developers are
+ previous members of the %G3D team.
+
+ <p>David Baszucki
+ <BR>Seth Block
+ <BR>Nicholas Bray
+ <BR>Nick Capens
+ <BR>Erik Cassel
+ <BR>John Chisholm*
+ <BR>Jauhn Dabz
+ <BR>Erik de Castro Lopo
+ <br>Rich Deeson*
+ <BR>Chris Demetriou
+ <BR>L. Peter Deutsch
+ <br>Dmitri*
+ <BR>Dave Eberly
+ <BR>Kevin Egan*
+ <BR>Cass Everitt
+ <BR>Dan Fast*
+ <BR>Andi Fein
+ <BR>Jakob Fischer
+ <BR>Dan Keefe*
+ <BR>Harishabd Khalsa
+ <BR>Nicolai Haehnle
+ <BR>Michael Herf
+ <br>Daniel Hilferty*
+ <BR>Pete Hopkins
+ <br>Danny Yuxing Huang
+ <BR>Peter Hunt
+ <BR>Robert Hunter
+ <BR>Ed Johnson
+ <BR>Benjamin Jurke
+ <BR>Chris Kern
+ <BR>Independent JPEG Group
+ <BR>Darius Jazayeri
+ <BR>Ben Landon*
+ <BR>Thomas G. Lane
+ <BR>Ray Larabie
+ <BR>Jukka Liimatta
+ <BR>Giulio Mainardi
+ <BR>Jeff Marsceill
+ <BR>Max McGuire
+ <BR>Morgan McGuire
+ <BR>Justin Miller
+ <BR>Kurt Miller
+ <BR>Nate Miller
+ <BR>Tomas Moller
+ <BR>Eric Muller*
+ <BR>Nick Musurca
+ <BR>Akita Noek
+ <BR>James O'Sullivan*
+ <BR>Aaron Orenstein
+ <BR>Jordan Parker
+ <BR>Edward Resnick
+ <BR>Alex Rice
+ <BR>Jack Ritter
+ <BR>Nate Robbins
+ <BR>Joshua Schpok*
+ <BR>Sebastian Schubert
+ <BR>SGI
+ <BR>Ben Shine*
+ <BR>Peter Sibley*
+ <br>Gabe Taubman*
+ <BR>Corey Taylor*
+ <BR>Pierre Terdiman
+ <BR>Ben Trumbore
+ <BR>Matthew Welch
+ <BR>Simon Winkelbach
+ <BR>Laura Wollstadt
+ <BR>Andrew Woo
+*/
diff --git a/externals/g3dlite/doc-files/license.dox b/externals/g3dlite/doc-files/license.dox
new file mode 100644
index 00000000000..b4302651fbf
--- /dev/null
+++ b/externals/g3dlite/doc-files/license.dox
@@ -0,0 +1,120 @@
+/** @page license License
+
+\htmlonly
+<TABLE BORDER=0 WIDTH=80%><TR><TD><I><FONT FACE="Arial">
+<A HREF="guideintro.html"><IMG SRC="backarrow.gif" BORDER=0 ALIGN=MIDDLE>
+Introduction</A></I></FONT></TD><TD ALIGN=RIGHT><FONT FACE="Arial"><I>
+<A HREF="guideinstall.html">
+Installation <IMG SRC="forwardarrow.gif" BORDER=0 ALIGN=MIDDLE></A></I></FONT></TD></TR></TABLE>
+\endhtmlonly
+
+@section intent Intent of License
+ (This section is informal and not legally binding.)
+
+ <BR> This library is free code-- you can use it without charge and
+ it is minimally legally encumbered. Unlike some other free libraries,
+ we <B>do not</B> require you to release
+ your source code or make your own program open source.
+
+ <P> I intend the license (below) to protect me and the other
+ contributors from liability and allow you to use the source however
+ you want. You can make your own closed or open-source programs,
+ sell them, give them away, whatever.
+
+ <P>
+ You have an obligation to say "this software is based in part on
+ the work of the Independent JPEG Group" in your documentation or
+ application help if you use the G3D::GImage class because it is based on
+ the IJG library. The OpenGL headers and ZLib headers included may
+ be freely distributed provided their copyright notices remain
+ intact.
+
+ <P>
+ For convenience, G3D::license is a function that returns the license
+ string you must put in your documentation. G3D::GApp will automatically
+ write a file (g3d-license.txt) to disk with the contents of this
+ license unless you tell it not to.
+
+ <P>
+ Most of the data resources have either entered the public domain and have
+ been in several published papers or are data that I have explicitly
+ received permission to distribute with G3D. The G3D fonts are actually font
+ images, not TrueType font descriptions and may be freely
+ distributed. As a rule of thumb, you can freely use and distribute
+ anything you find in the data directory but may need permission to use
+ it in a commercial product. Check the various copyright.txt files
+ in the data directories for specific information.
+
+ <P>
+
+ You are required by the BSD license to acknowledge G3D in your
+ documentation. This can be as minimal as a note buried in the
+ fine print at the end of a manual or a text file accompanying
+ your program. I appreciate it if you acknowledged the library
+ more publicly but you aren't required to.
+
+ <P>
+
+ Likewise, you are encouraged but not required to submit patches to
+ improve the library for the benefit of all. E-mail me with bugs,
+ patches, and questions. <P>
+
+ -Morgan McGuire
+ &lt;<I><A HREF="mailto:matrix@graphics3d.com">matrix@graphics3d.com</A></I>&gt;
+
+ <HR>
+
+ @section reallicense License
+
+ <I>G3D is licensed under the <A HREF="http://www.opensource.org/licenses/bsd-license.php">BSD license</A>,
+ with portions controlled by the <A HREF="IJG-README.TXT">IJG license</A>,
+ <A HREF="libpng-LICENSE.txt">PNG Reference Library license</A> and
+ <A HREF="http://www.gnu.org/copyleft/lesser.html">GNU Lesser General Public License (LGPL)</A>.</I>
+
+ <CODE>
+ <IMG SRC="http://opensource.org/trademarks/osi-certified/web/osi-certified-120x100.gif">
+ <DT>This product uses software from the G3D project (http://g3d-cpp.sf.net)
+ <DT>Copyright &copy; 2000-2008, Morgan McGuire
+ <DT>All rights reserved.
+ <P>
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions
+ are met:
+ <P>
+ Redistributions of source code must retain the above copyright notice,
+ this list of conditions and the following disclaimer.
+ <P>
+ Redistributions in binary form must reproduce the above copyright notice,
+ this list of conditions and the following disclaimer in the documentation
+ and/or other materials provided with the distribution.
+ <P>
+ Neither the name of Morgan McGuire, Williams College, Brown University, nor the names of
+ its contributors may be used to endorse or promote products derived from
+ this software without specific prior written permission.
+ <P>
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ THE POSSIBILITY OF SUCH DAMAGE.
+ </CODE>
+
+ <P>
+ You must also agree to be bound by the terms of the Independent JPEG
+ Group license for the portions of this library that are based
+ on the work of the Independent JPEG Group, <B>if you use those
+ portions</B>. Note: if you do not use the G3D::GImage class,
+ this clause does not apply to you because the linker will
+ strip that code from your project. The <A
+ HREF="IJG-README.TXT">IJG-README.TXT</A> file contains the
+ Independent JPEG Group license.
+
+ </OL>
+
+ */ \ No newline at end of file
diff --git a/externals/g3dlite/win/VC90/g3dlite.vcproj b/externals/g3dlite/win/VC90/g3dlite.vcproj
new file mode 100644
index 00000000000..e76d610ab69
--- /dev/null
+++ b/externals/g3dlite/win/VC90/g3dlite.vcproj
@@ -0,0 +1,463 @@
+<?xml version="1.0" encoding="Windows-1252"?>
+<VisualStudioProject
+ ProjectType="Visual C++"
+ Version="9,00"
+ Name="g3dlite"
+ ProjectGUID="{8072769E-CF10-48BF-B9E1-12752A5DAC6E}"
+ RootNamespace="sockets"
+ Keyword="Win32Proj"
+ TargetFrameworkVersion="196613"
+ >
+ <Platforms>
+ <Platform
+ Name="Win32"
+ />
+ <Platform
+ Name="x64"
+ />
+ </Platforms>
+ <ToolFiles>
+ </ToolFiles>
+ <Configurations>
+ <Configuration
+ Name="Debug|Win32"
+ OutputDirectory="..\..\bin\$(PlatformName)_$(ConfigurationName)"
+ IntermediateDirectory=".\g3dlite__$(PlatformName)_$(ConfigurationName)"
+ ConfigurationType="4"
+ CharacterSet="2"
+ >
+ <Tool
+ Name="VCPreBuildEventTool"
+ />
+ <Tool
+ Name="VCCustomBuildTool"
+ />
+ <Tool
+ Name="VCXMLDataGeneratorTool"
+ />
+ <Tool
+ Name="VCWebServiceProxyGeneratorTool"
+ />
+ <Tool
+ Name="VCMIDLTool"
+ />
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalOptions="/MP"
+ Optimization="0"
+ AdditionalIncludeDirectories="..\..\zip.lib\include;..\..\G3D.lib\include;..\..\..\zlib;"
+ AdditionalUsingDirectories=""
+ PreprocessorDefinitions="WIN32;_DEBUG;_CONSOLE"
+ MinimalRebuild="false"
+ BasicRuntimeChecks="3"
+ RuntimeLibrary="3"
+ BufferSecurityCheck="true"
+ EnableFunctionLevelLinking="true"
+ UsePrecompiledHeader="0"
+ AssemblerListingLocation=".\g3dlite__$(PlatformName)_$(ConfigurationName)\"
+ ObjectFile=".\g3dlite__$(PlatformName)_$(ConfigurationName)\"
+ ProgramDataBaseFileName=".\g3dlite__$(PlatformName)_$(ConfigurationName)\"
+ WarningLevel="3"
+ Detect64BitPortabilityProblems="false"
+ DebugInformationFormat="3"
+ CallingConvention="0"
+ />
+ <Tool
+ Name="VCManagedResourceCompilerTool"
+ />
+ <Tool
+ Name="VCResourceCompilerTool"
+ />
+ <Tool
+ Name="VCPreLinkEventTool"
+ />
+ <Tool
+ Name="VCLibrarianTool"
+ OutputFile="$(OutDir)/g3dlite.lib"
+ IgnoreAllDefaultLibraries="true"
+ />
+ <Tool
+ Name="VCALinkTool"
+ />
+ <Tool
+ Name="VCXDCMakeTool"
+ />
+ <Tool
+ Name="VCBscMakeTool"
+ />
+ <Tool
+ Name="VCFxCopTool"
+ />
+ <Tool
+ Name="VCPostBuildEventTool"
+ />
+ </Configuration>
+ <Configuration
+ Name="Debug|x64"
+ OutputDirectory="..\..\bin\$(PlatformName)_$(ConfigurationName)"
+ IntermediateDirectory=".\g3dlite__$(PlatformName)_$(ConfigurationName)"
+ ConfigurationType="4"
+ CharacterSet="2"
+ >
+ <Tool
+ Name="VCPreBuildEventTool"
+ />
+ <Tool
+ Name="VCCustomBuildTool"
+ />
+ <Tool
+ Name="VCXMLDataGeneratorTool"
+ />
+ <Tool
+ Name="VCWebServiceProxyGeneratorTool"
+ />
+ <Tool
+ Name="VCMIDLTool"
+ TargetEnvironment="3"
+ />
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalOptions="/MP"
+ Optimization="0"
+ AdditionalIncludeDirectories="..\..\zip.lib\include;..\..\G3D.lib\include;..\..\..\zlib;"
+ AdditionalUsingDirectories=""
+ PreprocessorDefinitions="WIN32;_DEBUG;_CONSOLE"
+ MinimalRebuild="false"
+ BasicRuntimeChecks="3"
+ RuntimeLibrary="3"
+ BufferSecurityCheck="true"
+ EnableFunctionLevelLinking="true"
+ UsePrecompiledHeader="0"
+ AssemblerListingLocation=".\g3dlite__$(PlatformName)_$(ConfigurationName)\"
+ ObjectFile=".\g3dlite__$(PlatformName)_$(ConfigurationName)\"
+ ProgramDataBaseFileName=".\g3dlite__$(PlatformName)_$(ConfigurationName)\"
+ WarningLevel="3"
+ Detect64BitPortabilityProblems="false"
+ DebugInformationFormat="3"
+ CallingConvention="0"
+ />
+ <Tool
+ Name="VCManagedResourceCompilerTool"
+ />
+ <Tool
+ Name="VCResourceCompilerTool"
+ />
+ <Tool
+ Name="VCPreLinkEventTool"
+ />
+ <Tool
+ Name="VCLibrarianTool"
+ OutputFile="$(OutDir)/g3dlite.lib"
+ IgnoreAllDefaultLibraries="true"
+ />
+ <Tool
+ Name="VCALinkTool"
+ />
+ <Tool
+ Name="VCXDCMakeTool"
+ />
+ <Tool
+ Name="VCBscMakeTool"
+ />
+ <Tool
+ Name="VCFxCopTool"
+ />
+ <Tool
+ Name="VCPostBuildEventTool"
+ />
+ </Configuration>
+ <Configuration
+ Name="Release|Win32"
+ OutputDirectory="..\..\bin\$(PlatformName)_$(ConfigurationName)"
+ IntermediateDirectory=".\g3dlite__$(PlatformName)_$(ConfigurationName)"
+ ConfigurationType="4"
+ CharacterSet="2"
+ >
+ <Tool
+ Name="VCPreBuildEventTool"
+ />
+ <Tool
+ Name="VCCustomBuildTool"
+ />
+ <Tool
+ Name="VCXMLDataGeneratorTool"
+ />
+ <Tool
+ Name="VCWebServiceProxyGeneratorTool"
+ />
+ <Tool
+ Name="VCMIDLTool"
+ />
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalOptions="/MP"
+ AdditionalIncludeDirectories="..\..\zip.lib\include;..\..\G3D.lib\include;..\..\..\zlib;"
+ AdditionalUsingDirectories=""
+ PreprocessorDefinitions="WIN32;NDEBUG;_CONSOLE;_SECURE_SCL=0"
+ RuntimeLibrary="2"
+ EnableEnhancedInstructionSet="1"
+ UsePrecompiledHeader="0"
+ AssemblerListingLocation=".\g3dlite__$(PlatformName)_$(ConfigurationName)\"
+ ObjectFile=".\g3dlite__$(PlatformName)_$(ConfigurationName)\"
+ ProgramDataBaseFileName=".\g3dlite__$(PlatformName)_$(ConfigurationName)\"
+ WarningLevel="3"
+ Detect64BitPortabilityProblems="false"
+ DebugInformationFormat="3"
+ CallingConvention="0"
+ />
+ <Tool
+ Name="VCManagedResourceCompilerTool"
+ />
+ <Tool
+ Name="VCResourceCompilerTool"
+ />
+ <Tool
+ Name="VCPreLinkEventTool"
+ />
+ <Tool
+ Name="VCLibrarianTool"
+ OutputFile="$(OutDir)/g3dlite.lib"
+ IgnoreAllDefaultLibraries="true"
+ />
+ <Tool
+ Name="VCALinkTool"
+ />
+ <Tool
+ Name="VCXDCMakeTool"
+ />
+ <Tool
+ Name="VCBscMakeTool"
+ />
+ <Tool
+ Name="VCFxCopTool"
+ />
+ <Tool
+ Name="VCPostBuildEventTool"
+ />
+ </Configuration>
+ <Configuration
+ Name="Release|x64"
+ OutputDirectory="..\..\bin\$(PlatformName)_$(ConfigurationName)"
+ IntermediateDirectory=".\g3dlite__$(PlatformName)_$(ConfigurationName)"
+ ConfigurationType="4"
+ CharacterSet="2"
+ >
+ <Tool
+ Name="VCPreBuildEventTool"
+ />
+ <Tool
+ Name="VCCustomBuildTool"
+ />
+ <Tool
+ Name="VCXMLDataGeneratorTool"
+ />
+ <Tool
+ Name="VCWebServiceProxyGeneratorTool"
+ />
+ <Tool
+ Name="VCMIDLTool"
+ TargetEnvironment="3"
+ />
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalOptions="/MP"
+ AdditionalIncludeDirectories="..\..\zip.lib\include;..\..\G3D.lib\include;..\..\..\zlib;"
+ AdditionalUsingDirectories=""
+ PreprocessorDefinitions="WIN32;NDEBUG;_CONSOLE;_SECURE_SCL=0"
+ RuntimeLibrary="2"
+ EnableEnhancedInstructionSet="0"
+ UsePrecompiledHeader="0"
+ AssemblerListingLocation=".\g3dlite__$(PlatformName)_$(ConfigurationName)\"
+ ObjectFile=".\g3dlite__$(PlatformName)_$(ConfigurationName)\"
+ ProgramDataBaseFileName=".\g3dlite__$(PlatformName)_$(ConfigurationName)\"
+ WarningLevel="3"
+ Detect64BitPortabilityProblems="false"
+ DebugInformationFormat="3"
+ CallingConvention="0"
+ />
+ <Tool
+ Name="VCManagedResourceCompilerTool"
+ />
+ <Tool
+ Name="VCResourceCompilerTool"
+ />
+ <Tool
+ Name="VCPreLinkEventTool"
+ />
+ <Tool
+ Name="VCLibrarianTool"
+ OutputFile="$(OutDir)/g3dlite.lib"
+ IgnoreAllDefaultLibraries="true"
+ />
+ <Tool
+ Name="VCALinkTool"
+ />
+ <Tool
+ Name="VCXDCMakeTool"
+ />
+ <Tool
+ Name="VCBscMakeTool"
+ />
+ <Tool
+ Name="VCFxCopTool"
+ />
+ <Tool
+ Name="VCPostBuildEventTool"
+ />
+ </Configuration>
+ </Configurations>
+ <References>
+ </References>
+ <Files>
+ <Filter
+ Name="Header Files"
+ >
+ </Filter>
+ <Filter
+ Name="Source Files"
+ >
+ <File
+ RelativePath="..\..\G3D.lib\source\AABox.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\..\G3D.lib\source\AnyVal.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\..\G3D.lib\source\BinaryFormat.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\..\G3D.lib\source\BinaryInput.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\..\G3D.lib\source\BinaryOutput.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\..\G3D.lib\source\Box.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\..\G3D.lib\source\Capsule.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\..\G3D.lib\source\CollisionDetection.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\..\G3D.lib\source\CoordinateFrame.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\..\G3D.lib\source\Crypto.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\..\G3D.lib\source\Cylinder.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\..\G3D.lib\source\debugAssert.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\..\G3D.lib\source\fileutils.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\..\G3D.lib\source\format.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\..\G3D.lib\source\g3dmath.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\..\G3D.lib\source\Line.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\..\G3D.lib\source\LineSegment.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\..\G3D.lib\source\Log.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\..\G3D.lib\source\Matrix3.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\..\G3D.lib\source\Matrix4.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\..\G3D.lib\source\Plane.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\..\G3D.lib\source\prompt.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\..\G3D.lib\source\Quat.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\..\G3D.lib\source\Ray.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\..\G3D.lib\source\RegistryUtil.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\..\G3D.lib\source\Sphere.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\..\G3D.lib\source\stringutils.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\..\G3D.lib\source\System.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\..\G3D.lib\source\TextInput.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\..\G3D.lib\source\TextOutput.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\..\G3D.lib\source\Triangle.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\..\G3D.lib\source\UprightFrame.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\..\G3D.lib\source\Vector2.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\..\G3D.lib\source\Vector3.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\..\G3D.lib\source\Vector4.cpp"
+ >
+ </File>
+ </Filter>
+ </Files>
+ <Globals>
+ </Globals>
+</VisualStudioProject>
diff --git a/externals/g3dlite/win/delme b/externals/g3dlite/win/delme
deleted file mode 100644
index e69de29bb2d..00000000000
--- a/externals/g3dlite/win/delme
+++ /dev/null
diff --git a/externals/g3dlite/win/g3dlite.sln b/externals/g3dlite/win/g3dlite.sln
new file mode 100644
index 00000000000..875374e3944
--- /dev/null
+++ b/externals/g3dlite/win/g3dlite.sln
@@ -0,0 +1,25 @@
+Microsoft Visual Studio Solution File, Format Version 10.00
+# Visual Studio 2008
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "g3dlite", "VC90\g3dlite.vcproj", "{8072769E-CF10-48BF-B9E1-12752A5DAC6E}"
+EndProject
+Global
+ GlobalSection(SolutionConfigurationPlatforms) = preSolution
+ Debug|Win32 = Debug|Win32
+ Debug|x64 = Debug|x64
+ Release|Win32 = Release|Win32
+ Release|x64 = Release|x64
+ EndGlobalSection
+ GlobalSection(ProjectConfigurationPlatforms) = postSolution
+ {8072769E-CF10-48BF-B9E1-12752A5DAC6E}.Debug|Win32.ActiveCfg = Debug|Win32
+ {8072769E-CF10-48BF-B9E1-12752A5DAC6E}.Debug|Win32.Build.0 = Debug|Win32
+ {8072769E-CF10-48BF-B9E1-12752A5DAC6E}.Debug|x64.ActiveCfg = Debug|x64
+ {8072769E-CF10-48BF-B9E1-12752A5DAC6E}.Debug|x64.Build.0 = Debug|x64
+ {8072769E-CF10-48BF-B9E1-12752A5DAC6E}.Release|Win32.ActiveCfg = Release|Win32
+ {8072769E-CF10-48BF-B9E1-12752A5DAC6E}.Release|Win32.Build.0 = Release|Win32
+ {8072769E-CF10-48BF-B9E1-12752A5DAC6E}.Release|x64.ActiveCfg = Release|x64
+ {8072769E-CF10-48BF-B9E1-12752A5DAC6E}.Release|x64.Build.0 = Release|x64
+ EndGlobalSection
+ GlobalSection(SolutionProperties) = preSolution
+ HideSolutionNode = FALSE
+ EndGlobalSection
+EndGlobal
diff --git a/externals/g3dlite/zip.lib/include/zip/ioapi.h b/externals/g3dlite/zip.lib/include/zip/ioapi.h
new file mode 100644
index 00000000000..7d457baab34
--- /dev/null
+++ b/externals/g3dlite/zip.lib/include/zip/ioapi.h
@@ -0,0 +1,75 @@
+/* ioapi.h -- IO base function header for compress/uncompress .zip
+ files using zlib + zip or unzip API
+
+ Version 1.01e, February 12th, 2005
+
+ Copyright (C) 1998-2005 Gilles Vollant
+*/
+
+#ifndef _ZLIBIOAPI_H
+#define _ZLIBIOAPI_H
+
+
+#define ZLIB_FILEFUNC_SEEK_CUR (1)
+#define ZLIB_FILEFUNC_SEEK_END (2)
+#define ZLIB_FILEFUNC_SEEK_SET (0)
+
+#define ZLIB_FILEFUNC_MODE_READ (1)
+#define ZLIB_FILEFUNC_MODE_WRITE (2)
+#define ZLIB_FILEFUNC_MODE_READWRITEFILTER (3)
+
+#define ZLIB_FILEFUNC_MODE_EXISTING (4)
+#define ZLIB_FILEFUNC_MODE_CREATE (8)
+
+
+#ifndef ZCALLBACK
+
+#if (defined(WIN32) || defined (WINDOWS) || defined (_WINDOWS)) && defined(CALLBACK) && defined (USEWINDOWS_CALLBACK)
+#define ZCALLBACK CALLBACK
+#else
+#define ZCALLBACK
+#endif
+#endif
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef voidpf (ZCALLBACK *open_file_func) OF((voidpf opaque, const char* filename, int mode));
+typedef uLong (ZCALLBACK *read_file_func) OF((voidpf opaque, voidpf stream, void* buf, uLong size));
+typedef uLong (ZCALLBACK *write_file_func) OF((voidpf opaque, voidpf stream, const void* buf, uLong size));
+typedef long (ZCALLBACK *tell_file_func) OF((voidpf opaque, voidpf stream));
+typedef long (ZCALLBACK *seek_file_func) OF((voidpf opaque, voidpf stream, uLong offset, int origin));
+typedef int (ZCALLBACK *close_file_func) OF((voidpf opaque, voidpf stream));
+typedef int (ZCALLBACK *testerror_file_func) OF((voidpf opaque, voidpf stream));
+
+typedef struct zlib_filefunc_def_s
+{
+ open_file_func zopen_file;
+ read_file_func zread_file;
+ write_file_func zwrite_file;
+ tell_file_func ztell_file;
+ seek_file_func zseek_file;
+ close_file_func zclose_file;
+ testerror_file_func zerror_file;
+ voidpf opaque;
+} zlib_filefunc_def;
+
+
+
+void fill_fopen_filefunc OF((zlib_filefunc_def* pzlib_filefunc_def));
+
+#define ZREAD(filefunc,filestream,buf,size) ((*((filefunc).zread_file))((filefunc).opaque,filestream,buf,size))
+#define ZWRITE(filefunc,filestream,buf,size) ((*((filefunc).zwrite_file))((filefunc).opaque,filestream,buf,size))
+#define ZTELL(filefunc,filestream) ((*((filefunc).ztell_file))((filefunc).opaque,filestream))
+#define ZSEEK(filefunc,filestream,pos,mode) ((*((filefunc).zseek_file))((filefunc).opaque,filestream,pos,mode))
+#define ZCLOSE(filefunc,filestream) ((*((filefunc).zclose_file))((filefunc).opaque,filestream))
+#define ZERROR(filefunc,filestream) ((*((filefunc).zerror_file))((filefunc).opaque,filestream))
+
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
+
diff --git a/externals/g3dlite/zip.lib/include/zip/unzip.h b/externals/g3dlite/zip.lib/include/zip/unzip.h
new file mode 100644
index 00000000000..b247937c807
--- /dev/null
+++ b/externals/g3dlite/zip.lib/include/zip/unzip.h
@@ -0,0 +1,354 @@
+/* unzip.h -- IO for uncompress .zip files using zlib
+ Version 1.01e, February 12th, 2005
+
+ Copyright (C) 1998-2005 Gilles Vollant
+
+ This unzip package allow extract file from .ZIP file, compatible with PKZip 2.04g
+ WinZip, InfoZip tools and compatible.
+
+ Multi volume ZipFile (span) are not supported.
+ Encryption compatible with pkzip 2.04g only supported
+ Old compressions used by old PKZip 1.x are not supported
+
+
+ I WAIT FEEDBACK at mail info@winimage.com
+ Visit also http://www.winimage.com/zLibDll/unzip.htm for evolution
+
+ Condition of use and distribution are the same than zlib :
+
+ This software is provided 'as-is', without any express or implied
+ warranty. In no event will the authors be held liable for any damages
+ arising from the use of this software.
+
+ Permission is granted to anyone to use this software for any purpose,
+ including commercial applications, and to alter it and redistribute it
+ freely, subject to the following restrictions:
+
+ 1. The origin of this software must not be misrepresented; you must not
+ claim that you wrote the original software. If you use this software
+ in a product, an acknowledgment in the product documentation would be
+ appreciated but is not required.
+ 2. Altered source versions must be plainly marked as such, and must not be
+ misrepresented as being the original software.
+ 3. This notice may not be removed or altered from any source distribution.
+
+
+*/
+
+/* for more info about .ZIP format, see
+ http://www.info-zip.org/pub/infozip/doc/appnote-981119-iz.zip
+ http://www.info-zip.org/pub/infozip/doc/
+ PkWare has also a specification at :
+ ftp://ftp.pkware.com/probdesc.zip
+*/
+
+#ifndef _unz_H
+#define _unz_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#ifndef _ZLIB_H
+#include "zlib.h"
+#endif
+
+#ifndef _ZLIBIOAPI_H
+#include "ioapi.h"
+#endif
+
+#if defined(STRICTUNZIP) || defined(STRICTZIPUNZIP)
+/* like the STRICT of WIN32, we define a pointer that cannot be converted
+ from (void*) without cast */
+typedef struct TagunzFile__ { int unused; } unzFile__;
+typedef unzFile__ *unzFile;
+#else
+typedef voidp unzFile;
+#endif
+
+
+#define UNZ_OK (0)
+#define UNZ_END_OF_LIST_OF_FILE (-100)
+#define UNZ_ERRNO (Z_ERRNO)
+#define UNZ_EOF (0)
+#define UNZ_PARAMERROR (-102)
+#define UNZ_BADZIPFILE (-103)
+#define UNZ_INTERNALERROR (-104)
+#define UNZ_CRCERROR (-105)
+
+/* tm_unz contain date/time info */
+typedef struct tm_unz_s
+{
+ uInt tm_sec; /* seconds after the minute - [0,59] */
+ uInt tm_min; /* minutes after the hour - [0,59] */
+ uInt tm_hour; /* hours since midnight - [0,23] */
+ uInt tm_mday; /* day of the month - [1,31] */
+ uInt tm_mon; /* months since January - [0,11] */
+ uInt tm_year; /* years - [1980..2044] */
+} tm_unz;
+
+/* unz_global_info structure contain global data about the ZIPfile
+ These data comes from the end of central dir */
+typedef struct unz_global_info_s
+{
+ uLong number_entry; /* total number of entries in
+ the central dir on this disk */
+ uLong size_comment; /* size of the global comment of the zipfile */
+} unz_global_info;
+
+
+/* unz_file_info contain information about a file in the zipfile */
+typedef struct unz_file_info_s
+{
+ uLong version; /* version made by 2 bytes */
+ uLong version_needed; /* version needed to extract 2 bytes */
+ uLong flag; /* general purpose bit flag 2 bytes */
+ uLong compression_method; /* compression method 2 bytes */
+ uLong dosDate; /* last mod file date in Dos fmt 4 bytes */
+ uLong crc; /* crc-32 4 bytes */
+ uLong compressed_size; /* compressed size 4 bytes */
+ uLong uncompressed_size; /* uncompressed size 4 bytes */
+ uLong size_filename; /* filename length 2 bytes */
+ uLong size_file_extra; /* extra field length 2 bytes */
+ uLong size_file_comment; /* file comment length 2 bytes */
+
+ uLong disk_num_start; /* disk number start 2 bytes */
+ uLong internal_fa; /* internal file attributes 2 bytes */
+ uLong external_fa; /* external file attributes 4 bytes */
+
+ tm_unz tmu_date;
+} unz_file_info;
+
+extern int ZEXPORT unzStringFileNameCompare OF ((const char* fileName1,
+ const char* fileName2,
+ int iCaseSensitivity));
+/*
+ Compare two filename (fileName1,fileName2).
+ If iCaseSenisivity = 1, comparision is case sensitivity (like strcmp)
+ If iCaseSenisivity = 2, comparision is not case sensitivity (like strcmpi
+ or strcasecmp)
+ If iCaseSenisivity = 0, case sensitivity is defaut of your operating system
+ (like 1 on Unix, 2 on Windows)
+*/
+
+
+extern unzFile ZEXPORT unzOpen OF((const char *path));
+/*
+ Open a Zip file. path contain the full pathname (by example,
+ on a Windows XP computer "c:\\zlib\\zlib113.zip" or on an Unix computer
+ "zlib/zlib113.zip".
+ If the zipfile cannot be opened (file don't exist or in not valid), the
+ return value is NULL.
+ Else, the return value is a unzFile Handle, usable with other function
+ of this unzip package.
+*/
+
+extern unzFile ZEXPORT unzOpen2 OF((const char *path,
+ zlib_filefunc_def* pzlib_filefunc_def));
+/*
+ Open a Zip file, like unzOpen, but provide a set of file low level API
+ for read/write the zip file (see ioapi.h)
+*/
+
+extern int ZEXPORT unzClose OF((unzFile file));
+/*
+ Close a ZipFile opened with unzipOpen.
+ If there is files inside the .Zip opened with unzOpenCurrentFile (see later),
+ these files MUST be closed with unzipCloseCurrentFile before call unzipClose.
+ return UNZ_OK if there is no problem. */
+
+extern int ZEXPORT unzGetGlobalInfo OF((unzFile file,
+ unz_global_info *pglobal_info));
+/*
+ Write info about the ZipFile in the *pglobal_info structure.
+ No preparation of the structure is needed
+ return UNZ_OK if there is no problem. */
+
+
+extern int ZEXPORT unzGetGlobalComment OF((unzFile file,
+ char *szComment,
+ uLong uSizeBuf));
+/*
+ Get the global comment string of the ZipFile, in the szComment buffer.
+ uSizeBuf is the size of the szComment buffer.
+ return the number of byte copied or an error code <0
+*/
+
+
+/***************************************************************************/
+/* Unzip package allow you browse the directory of the zipfile */
+
+extern int ZEXPORT unzGoToFirstFile OF((unzFile file));
+/*
+ Set the current file of the zipfile to the first file.
+ return UNZ_OK if there is no problem
+*/
+
+extern int ZEXPORT unzGoToNextFile OF((unzFile file));
+/*
+ Set the current file of the zipfile to the next file.
+ return UNZ_OK if there is no problem
+ return UNZ_END_OF_LIST_OF_FILE if the actual file was the latest.
+*/
+
+extern int ZEXPORT unzLocateFile OF((unzFile file,
+ const char *szFileName,
+ int iCaseSensitivity));
+/*
+ Try locate the file szFileName in the zipfile.
+ For the iCaseSensitivity signification, see unzStringFileNameCompare
+
+ return value :
+ UNZ_OK if the file is found. It becomes the current file.
+ UNZ_END_OF_LIST_OF_FILE if the file is not found
+*/
+
+
+/* ****************************************** */
+/* Ryan supplied functions */
+/* unz_file_info contain information about a file in the zipfile */
+typedef struct unz_file_pos_s
+{
+ uLong pos_in_zip_directory; /* offset in zip file directory */
+ uLong num_of_file; /* # of file */
+} unz_file_pos;
+
+extern int ZEXPORT unzGetFilePos(
+ unzFile file,
+ unz_file_pos* file_pos);
+
+extern int ZEXPORT unzGoToFilePos(
+ unzFile file,
+ unz_file_pos* file_pos);
+
+/* ****************************************** */
+
+extern int ZEXPORT unzGetCurrentFileInfo OF((unzFile file,
+ unz_file_info *pfile_info,
+ char *szFileName,
+ uLong fileNameBufferSize,
+ void *extraField,
+ uLong extraFieldBufferSize,
+ char *szComment,
+ uLong commentBufferSize));
+/*
+ Get Info about the current file
+ if pfile_info!=NULL, the *pfile_info structure will contain somes info about
+ the current file
+ if szFileName!=NULL, the filemane string will be copied in szFileName
+ (fileNameBufferSize is the size of the buffer)
+ if extraField!=NULL, the extra field information will be copied in extraField
+ (extraFieldBufferSize is the size of the buffer).
+ This is the Central-header version of the extra field
+ if szComment!=NULL, the comment string of the file will be copied in szComment
+ (commentBufferSize is the size of the buffer)
+*/
+
+/***************************************************************************/
+/* for reading the content of the current zipfile, you can open it, read data
+ from it, and close it (you can close it before reading all the file)
+ */
+
+extern int ZEXPORT unzOpenCurrentFile OF((unzFile file));
+/*
+ Open for reading data the current file in the zipfile.
+ If there is no error, the return value is UNZ_OK.
+*/
+
+extern int ZEXPORT unzOpenCurrentFilePassword OF((unzFile file,
+ const char* password));
+/*
+ Open for reading data the current file in the zipfile.
+ password is a crypting password
+ If there is no error, the return value is UNZ_OK.
+*/
+
+extern int ZEXPORT unzOpenCurrentFile2 OF((unzFile file,
+ int* method,
+ int* level,
+ int raw));
+/*
+ Same than unzOpenCurrentFile, but open for read raw the file (not uncompress)
+ if raw==1
+ *method will receive method of compression, *level will receive level of
+ compression
+ note : you can set level parameter as NULL (if you did not want known level,
+ but you CANNOT set method parameter as NULL
+*/
+
+extern int ZEXPORT unzOpenCurrentFile3 OF((unzFile file,
+ int* method,
+ int* level,
+ int raw,
+ const char* password));
+/*
+ Same than unzOpenCurrentFile, but open for read raw the file (not uncompress)
+ if raw==1
+ *method will receive method of compression, *level will receive level of
+ compression
+ note : you can set level parameter as NULL (if you did not want known level,
+ but you CANNOT set method parameter as NULL
+*/
+
+
+extern int ZEXPORT unzCloseCurrentFile OF((unzFile file));
+/*
+ Close the file in zip opened with unzOpenCurrentFile
+ Return UNZ_CRCERROR if all the file was read but the CRC is not good
+*/
+
+extern int ZEXPORT unzReadCurrentFile OF((unzFile file,
+ voidp buf,
+ unsigned len));
+/*
+ Read bytes from the current file (opened by unzOpenCurrentFile)
+ buf contain buffer where data must be copied
+ len the size of buf.
+
+ return the number of byte copied if somes bytes are copied
+ return 0 if the end of file was reached
+ return <0 with error code if there is an error
+ (UNZ_ERRNO for IO error, or zLib error for uncompress error)
+*/
+
+extern z_off_t ZEXPORT unztell OF((unzFile file));
+/*
+ Give the current position in uncompressed data
+*/
+
+extern int ZEXPORT unzeof OF((unzFile file));
+/*
+ return 1 if the end of file was reached, 0 elsewhere
+*/
+
+extern int ZEXPORT unzGetLocalExtrafield OF((unzFile file,
+ voidp buf,
+ unsigned len));
+/*
+ Read extra field from the current file (opened by unzOpenCurrentFile)
+ This is the local-header version of the extra field (sometimes, there is
+ more info in the local-header version than in the central-header)
+
+ if buf==NULL, it return the size of the local extra field
+
+ if buf!=NULL, len is the size of the buffer, the extra header is copied in
+ buf.
+ the return value is the number of bytes copied in buf, or (if <0)
+ the error code
+*/
+
+/***************************************************************************/
+
+/* Get the current file offset */
+extern uLong ZEXPORT unzGetOffset (unzFile file);
+
+/* Set the current file offset */
+extern int ZEXPORT unzSetOffset (unzFile file, uLong pos);
+
+
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _unz_H */
diff --git a/externals/g3dlite/zip.lib/include/zip/zip.h b/externals/g3dlite/zip.lib/include/zip/zip.h
new file mode 100644
index 00000000000..acacce83b9b
--- /dev/null
+++ b/externals/g3dlite/zip.lib/include/zip/zip.h
@@ -0,0 +1,235 @@
+/* zip.h -- IO for compress .zip files using zlib
+ Version 1.01e, February 12th, 2005
+
+ Copyright (C) 1998-2005 Gilles Vollant
+
+ This unzip package allow creates .ZIP file, compatible with PKZip 2.04g
+ WinZip, InfoZip tools and compatible.
+ Multi volume ZipFile (span) are not supported.
+ Encryption compatible with pkzip 2.04g only supported
+ Old compressions used by old PKZip 1.x are not supported
+
+ For uncompress .zip file, look at unzip.h
+
+
+ I WAIT FEEDBACK at mail info@winimage.com
+ Visit also http://www.winimage.com/zLibDll/unzip.html for evolution
+
+ Condition of use and distribution are the same than zlib :
+
+ This software is provided 'as-is', without any express or implied
+ warranty. In no event will the authors be held liable for any damages
+ arising from the use of this software.
+
+ Permission is granted to anyone to use this software for any purpose,
+ including commercial applications, and to alter it and redistribute it
+ freely, subject to the following restrictions:
+
+ 1. The origin of this software must not be misrepresented; you must not
+ claim that you wrote the original software. If you use this software
+ in a product, an acknowledgment in the product documentation would be
+ appreciated but is not required.
+ 2. Altered source versions must be plainly marked as such, and must not be
+ misrepresented as being the original software.
+ 3. This notice may not be removed or altered from any source distribution.
+
+
+*/
+
+/* for more info about .ZIP format, see
+ http://www.info-zip.org/pub/infozip/doc/appnote-981119-iz.zip
+ http://www.info-zip.org/pub/infozip/doc/
+ PkWare has also a specification at :
+ ftp://ftp.pkware.com/probdesc.zip
+*/
+
+#ifndef _zip_H
+#define _zip_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#ifndef _ZLIB_H
+#include "zlib.h"
+#endif
+
+#ifndef _ZLIBIOAPI_H
+#include "ioapi.h"
+#endif
+
+#if defined(STRICTZIP) || defined(STRICTZIPUNZIP)
+/* like the STRICT of WIN32, we define a pointer that cannot be converted
+ from (void*) without cast */
+typedef struct TagzipFile__ { int unused; } zipFile__;
+typedef zipFile__ *zipFile;
+#else
+typedef voidp zipFile;
+#endif
+
+#define ZIP_OK (0)
+#define ZIP_EOF (0)
+#define ZIP_ERRNO (Z_ERRNO)
+#define ZIP_PARAMERROR (-102)
+#define ZIP_BADZIPFILE (-103)
+#define ZIP_INTERNALERROR (-104)
+
+#ifndef DEF_MEM_LEVEL
+# if MAX_MEM_LEVEL >= 8
+# define DEF_MEM_LEVEL 8
+# else
+# define DEF_MEM_LEVEL MAX_MEM_LEVEL
+# endif
+#endif
+/* default memLevel */
+
+/* tm_zip contain date/time info */
+typedef struct tm_zip_s
+{
+ uInt tm_sec; /* seconds after the minute - [0,59] */
+ uInt tm_min; /* minutes after the hour - [0,59] */
+ uInt tm_hour; /* hours since midnight - [0,23] */
+ uInt tm_mday; /* day of the month - [1,31] */
+ uInt tm_mon; /* months since January - [0,11] */
+ uInt tm_year; /* years - [1980..2044] */
+} tm_zip;
+
+typedef struct
+{
+ tm_zip tmz_date; /* date in understandable format */
+ uLong dosDate; /* if dos_date == 0, tmu_date is used */
+/* uLong flag; */ /* general purpose bit flag 2 bytes */
+
+ uLong internal_fa; /* internal file attributes 2 bytes */
+ uLong external_fa; /* external file attributes 4 bytes */
+} zip_fileinfo;
+
+typedef const char* zipcharpc;
+
+
+#define APPEND_STATUS_CREATE (0)
+#define APPEND_STATUS_CREATEAFTER (1)
+#define APPEND_STATUS_ADDINZIP (2)
+
+extern zipFile ZEXPORT zipOpen OF((const char *pathname, int append));
+/*
+ Create a zipfile.
+ pathname contain on Windows XP a filename like "c:\\zlib\\zlib113.zip" or on
+ an Unix computer "zlib/zlib113.zip".
+ if the file pathname exist and append==APPEND_STATUS_CREATEAFTER, the zip
+ will be created at the end of the file.
+ (useful if the file contain a self extractor code)
+ if the file pathname exist and append==APPEND_STATUS_ADDINZIP, we will
+ add files in existing zip (be sure you don't add file that doesn't exist)
+ If the zipfile cannot be opened, the return value is NULL.
+ Else, the return value is a zipFile Handle, usable with other function
+ of this zip package.
+*/
+
+/* Note : there is no delete function into a zipfile.
+ If you want delete file into a zipfile, you must open a zipfile, and create another
+ Of couse, you can use RAW reading and writing to copy the file you did not want delte
+*/
+
+extern zipFile ZEXPORT zipOpen2 OF((const char *pathname,
+ int append,
+ zipcharpc* globalcomment,
+ zlib_filefunc_def* pzlib_filefunc_def));
+
+extern int ZEXPORT zipOpenNewFileInZip OF((zipFile file,
+ const char* filename,
+ const zip_fileinfo* zipfi,
+ const void* extrafield_local,
+ uInt size_extrafield_local,
+ const void* extrafield_global,
+ uInt size_extrafield_global,
+ const char* comment,
+ int method,
+ int level));
+/*
+ Open a file in the ZIP for writing.
+ filename : the filename in zip (if NULL, '-' without quote will be used
+ *zipfi contain supplemental information
+ if extrafield_local!=NULL and size_extrafield_local>0, extrafield_local
+ contains the extrafield data the the local header
+ if extrafield_global!=NULL and size_extrafield_global>0, extrafield_global
+ contains the extrafield data the the local header
+ if comment != NULL, comment contain the comment string
+ method contain the compression method (0 for store, Z_DEFLATED for deflate)
+ level contain the level of compression (can be Z_DEFAULT_COMPRESSION)
+*/
+
+
+extern int ZEXPORT zipOpenNewFileInZip2 OF((zipFile file,
+ const char* filename,
+ const zip_fileinfo* zipfi,
+ const void* extrafield_local,
+ uInt size_extrafield_local,
+ const void* extrafield_global,
+ uInt size_extrafield_global,
+ const char* comment,
+ int method,
+ int level,
+ int raw));
+
+/*
+ Same than zipOpenNewFileInZip, except if raw=1, we write raw file
+ */
+
+extern int ZEXPORT zipOpenNewFileInZip3 OF((zipFile file,
+ const char* filename,
+ const zip_fileinfo* zipfi,
+ const void* extrafield_local,
+ uInt size_extrafield_local,
+ const void* extrafield_global,
+ uInt size_extrafield_global,
+ const char* comment,
+ int method,
+ int level,
+ int raw,
+ int windowBits,
+ int memLevel,
+ int strategy,
+ const char* password,
+ uLong crcForCtypting));
+
+/*
+ Same than zipOpenNewFileInZip2, except
+ windowBits,memLevel,,strategy : see parameter strategy in deflateInit2
+ password : crypting password (NULL for no crypting)
+ crcForCtypting : crc of file to compress (needed for crypting)
+ */
+
+
+extern int ZEXPORT zipWriteInFileInZip OF((zipFile file,
+ const void* buf,
+ unsigned len));
+/*
+ Write data in the zipfile
+*/
+
+extern int ZEXPORT zipCloseFileInZip OF((zipFile file));
+/*
+ Close the current file in the zipfile
+*/
+
+extern int ZEXPORT zipCloseFileInZipRaw OF((zipFile file,
+ uLong uncompressed_size,
+ uLong crc32));
+/*
+ Close the current file in the zipfile, for fiel opened with
+ parameter raw=1 in zipOpenNewFileInZip2
+ uncompressed_size and crc32 are value for the uncompressed size
+*/
+
+extern int ZEXPORT zipClose OF((zipFile file,
+ const char* global_comment));
+/*
+ Close the zipfile
+*/
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _zip_H */
diff --git a/externals/g3dlite/zip.lib/source/crypt.h b/externals/g3dlite/zip.lib/source/crypt.h
new file mode 100644
index 00000000000..622f4bc2ec4
--- /dev/null
+++ b/externals/g3dlite/zip.lib/source/crypt.h
@@ -0,0 +1,132 @@
+/* crypt.h -- base code for crypt/uncrypt ZIPfile
+
+
+ Version 1.01e, February 12th, 2005
+
+ Copyright (C) 1998-2005 Gilles Vollant
+
+ This code is a modified version of crypting code in Infozip distribution
+
+ The encryption/decryption parts of this source code (as opposed to the
+ non-echoing password parts) were originally written in Europe. The
+ whole source package can be freely distributed, including from the USA.
+ (Prior to January 2000, re-export from the US was a violation of US law.)
+
+ This encryption code is a direct transcription of the algorithm from
+ Roger Schlafly, described by Phil Katz in the file appnote.txt. This
+ file (appnote.txt) is distributed with the PKZIP program (even in the
+ version without encryption capabilities).
+
+ If you don't need crypting in your application, just define symbols
+ NOCRYPT and NOUNCRYPT.
+
+ This code support the "Traditional PKWARE Encryption".
+
+ The new AES encryption added on Zip format by Winzip (see the page
+ http://www.winzip.com/aes_info.htm ) and PKWare PKZip 5.x Strong
+ Encryption is not supported.
+*/
+
+#define CRC32(c, b) ((*(pcrc_32_tab+(((int)(c) ^ (b)) & 0xff))) ^ ((c) >> 8))
+
+/***********************************************************************
+ * Return the next byte in the pseudo-random sequence
+ */
+static int decrypt_byte(unsigned long* pkeys, const unsigned long* pcrc_32_tab)
+{
+ unsigned temp; /* POTENTIAL BUG: temp*(temp^1) may overflow in an
+ * unpredictable manner on 16-bit systems; not a problem
+ * with any known compiler so far, though */
+
+ temp = ((unsigned)(*(pkeys+2)) & 0xffff) | 2;
+ return (int)(((temp * (temp ^ 1)) >> 8) & 0xff);
+}
+
+/***********************************************************************
+ * Update the encryption keys with the next byte of plain text
+ */
+static int update_keys(unsigned long* pkeys,const unsigned long* pcrc_32_tab,int c)
+{
+ (*(pkeys+0)) = CRC32((*(pkeys+0)), c);
+ (*(pkeys+1)) += (*(pkeys+0)) & 0xff;
+ (*(pkeys+1)) = (*(pkeys+1)) * 134775813L + 1;
+ {
+ register int keyshift = (int)((*(pkeys+1)) >> 24);
+ (*(pkeys+2)) = CRC32((*(pkeys+2)), keyshift);
+ }
+ return c;
+}
+
+
+/***********************************************************************
+ * Initialize the encryption keys and the random header according to
+ * the given password.
+ */
+static void init_keys(const char* passwd,unsigned long* pkeys,const unsigned long* pcrc_32_tab)
+{
+ *(pkeys+0) = 305419896L;
+ *(pkeys+1) = 591751049L;
+ *(pkeys+2) = 878082192L;
+ while (*passwd != '\0') {
+ update_keys(pkeys,pcrc_32_tab,(int)*passwd);
+ passwd++;
+ }
+}
+
+#define zdecode(pkeys,pcrc_32_tab,c) \
+ (update_keys(pkeys,pcrc_32_tab,c ^= decrypt_byte(pkeys,pcrc_32_tab)))
+
+#define zencode(pkeys,pcrc_32_tab,c,t) \
+ (t=decrypt_byte(pkeys,pcrc_32_tab), update_keys(pkeys,pcrc_32_tab,c), t^(c))
+
+#ifdef INCLUDECRYPTINGCODE_IFCRYPTALLOWED
+
+#define RAND_HEAD_LEN 12
+ /* "last resort" source for second part of crypt seed pattern */
+# ifndef ZCR_SEED2
+# define ZCR_SEED2 3141592654UL /* use PI as default pattern */
+# endif
+
+static int crypthead(passwd, buf, bufSize, pkeys, pcrc_32_tab, crcForCrypting)
+ const char *passwd; /* password string */
+ unsigned char *buf; /* where to write header */
+ int bufSize;
+ unsigned long* pkeys;
+ const unsigned long* pcrc_32_tab;
+ unsigned long crcForCrypting;
+{
+ int n; /* index in random header */
+ int t; /* temporary */
+ int c; /* random byte */
+ unsigned char header[RAND_HEAD_LEN-2]; /* random header */
+ static unsigned calls = 0; /* ensure different random header each time */
+
+ if (bufSize<RAND_HEAD_LEN)
+ return 0;
+
+ /* First generate RAND_HEAD_LEN-2 random bytes. We encrypt the
+ * output of rand() to get less predictability, since rand() is
+ * often poorly implemented.
+ */
+ if (++calls == 1)
+ {
+ srand((unsigned)(time(NULL) ^ ZCR_SEED2));
+ }
+ init_keys(passwd, pkeys, pcrc_32_tab);
+ for (n = 0; n < RAND_HEAD_LEN-2; n++)
+ {
+ c = (rand() >> 7) & 0xff;
+ header[n] = (unsigned char)zencode(pkeys, pcrc_32_tab, c, t);
+ }
+ /* Encrypt random header (last two bytes is high word of crc) */
+ init_keys(passwd, pkeys, pcrc_32_tab);
+ for (n = 0; n < RAND_HEAD_LEN-2; n++)
+ {
+ buf[n] = (unsigned char)zencode(pkeys, pcrc_32_tab, header[n], t);
+ }
+ buf[n++] = zencode(pkeys, pcrc_32_tab, (int)(crcForCrypting >> 16) & 0xff, t);
+ buf[n++] = zencode(pkeys, pcrc_32_tab, (int)(crcForCrypting >> 24) & 0xff, t);
+ return n;
+}
+
+#endif
diff --git a/externals/g3dlite/zip.lib/source/ioapi.c b/externals/g3dlite/zip.lib/source/ioapi.c
new file mode 100644
index 00000000000..f1bee23e64b
--- /dev/null
+++ b/externals/g3dlite/zip.lib/source/ioapi.c
@@ -0,0 +1,177 @@
+/* ioapi.c -- IO base function header for compress/uncompress .zip
+ files using zlib + zip or unzip API
+
+ Version 1.01e, February 12th, 2005
+
+ Copyright (C) 1998-2005 Gilles Vollant
+*/
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "zlib.h"
+#include "ioapi.h"
+
+
+
+/* I've found an old Unix (a SunOS 4.1.3_U1) without all SEEK_* defined.... */
+
+#ifndef SEEK_CUR
+#define SEEK_CUR 1
+#endif
+
+#ifndef SEEK_END
+#define SEEK_END 2
+#endif
+
+#ifndef SEEK_SET
+#define SEEK_SET 0
+#endif
+
+voidpf ZCALLBACK fopen_file_func OF((
+ voidpf opaque,
+ const char* filename,
+ int mode));
+
+uLong ZCALLBACK fread_file_func OF((
+ voidpf opaque,
+ voidpf stream,
+ void* buf,
+ uLong size));
+
+uLong ZCALLBACK fwrite_file_func OF((
+ voidpf opaque,
+ voidpf stream,
+ const void* buf,
+ uLong size));
+
+long ZCALLBACK ftell_file_func OF((
+ voidpf opaque,
+ voidpf stream));
+
+long ZCALLBACK fseek_file_func OF((
+ voidpf opaque,
+ voidpf stream,
+ uLong offset,
+ int origin));
+
+int ZCALLBACK fclose_file_func OF((
+ voidpf opaque,
+ voidpf stream));
+
+int ZCALLBACK ferror_file_func OF((
+ voidpf opaque,
+ voidpf stream));
+
+
+voidpf ZCALLBACK fopen_file_func (opaque, filename, mode)
+ voidpf opaque;
+ const char* filename;
+ int mode;
+{
+ FILE* file = NULL;
+ const char* mode_fopen = NULL;
+ if ((mode & ZLIB_FILEFUNC_MODE_READWRITEFILTER)==ZLIB_FILEFUNC_MODE_READ)
+ mode_fopen = "rb";
+ else
+ if (mode & ZLIB_FILEFUNC_MODE_EXISTING)
+ mode_fopen = "r+b";
+ else
+ if (mode & ZLIB_FILEFUNC_MODE_CREATE)
+ mode_fopen = "wb";
+
+ if ((filename!=NULL) && (mode_fopen != NULL))
+ file = fopen(filename, mode_fopen);
+ return file;
+}
+
+
+uLong ZCALLBACK fread_file_func (opaque, stream, buf, size)
+ voidpf opaque;
+ voidpf stream;
+ void* buf;
+ uLong size;
+{
+ uLong ret;
+ ret = (uLong)fread(buf, 1, (size_t)size, (FILE *)stream);
+ return ret;
+}
+
+
+uLong ZCALLBACK fwrite_file_func (opaque, stream, buf, size)
+ voidpf opaque;
+ voidpf stream;
+ const void* buf;
+ uLong size;
+{
+ uLong ret;
+ ret = (uLong)fwrite(buf, 1, (size_t)size, (FILE *)stream);
+ return ret;
+}
+
+long ZCALLBACK ftell_file_func (opaque, stream)
+ voidpf opaque;
+ voidpf stream;
+{
+ long ret;
+ ret = ftell((FILE *)stream);
+ return ret;
+}
+
+long ZCALLBACK fseek_file_func (opaque, stream, offset, origin)
+ voidpf opaque;
+ voidpf stream;
+ uLong offset;
+ int origin;
+{
+ int fseek_origin=0;
+ long ret;
+ switch (origin)
+ {
+ case ZLIB_FILEFUNC_SEEK_CUR :
+ fseek_origin = SEEK_CUR;
+ break;
+ case ZLIB_FILEFUNC_SEEK_END :
+ fseek_origin = SEEK_END;
+ break;
+ case ZLIB_FILEFUNC_SEEK_SET :
+ fseek_origin = SEEK_SET;
+ break;
+ default: return -1;
+ }
+ ret = 0;
+ fseek((FILE *)stream, offset, fseek_origin);
+ return ret;
+}
+
+int ZCALLBACK fclose_file_func (opaque, stream)
+ voidpf opaque;
+ voidpf stream;
+{
+ int ret;
+ ret = fclose((FILE *)stream);
+ return ret;
+}
+
+int ZCALLBACK ferror_file_func (opaque, stream)
+ voidpf opaque;
+ voidpf stream;
+{
+ int ret;
+ ret = ferror((FILE *)stream);
+ return ret;
+}
+
+void fill_fopen_filefunc (pzlib_filefunc_def)
+ zlib_filefunc_def* pzlib_filefunc_def;
+{
+ pzlib_filefunc_def->zopen_file = fopen_file_func;
+ pzlib_filefunc_def->zread_file = fread_file_func;
+ pzlib_filefunc_def->zwrite_file = fwrite_file_func;
+ pzlib_filefunc_def->ztell_file = ftell_file_func;
+ pzlib_filefunc_def->zseek_file = fseek_file_func;
+ pzlib_filefunc_def->zclose_file = fclose_file_func;
+ pzlib_filefunc_def->zerror_file = ferror_file_func;
+ pzlib_filefunc_def->opaque = NULL;
+}
diff --git a/externals/g3dlite/zip.lib/source/iowin32.c b/externals/g3dlite/zip.lib/source/iowin32.c
new file mode 100644
index 00000000000..ce911e3636e
--- /dev/null
+++ b/externals/g3dlite/zip.lib/source/iowin32.c
@@ -0,0 +1,272 @@
+#ifdef _MSC_VER
+/* iowin32.c -- IO base function header for compress/uncompress .zip
+ files using zlib + zip or unzip API
+ This IO API version uses the Win32 API (for Microsoft Windows)
+
+ Version 1.01e, February 12th, 2005
+
+ Copyright (C) 1998-2005 Gilles Vollant
+*/
+
+#include <stdlib.h>
+
+#include "zlib.h"
+#include "ioapi.h"
+#include "iowin32.h"
+
+#ifndef INVALID_HANDLE_VALUE
+#define INVALID_HANDLE_VALUE (0xFFFFFFFF)
+#endif
+
+#ifndef INVALID_SET_FILE_POINTER
+#define INVALID_SET_FILE_POINTER ((DWORD)-1)
+#endif
+
+voidpf ZCALLBACK win32_open_file_func OF((
+ voidpf opaque,
+ const char* filename,
+ int mode));
+
+uLong ZCALLBACK win32_read_file_func OF((
+ voidpf opaque,
+ voidpf stream,
+ void* buf,
+ uLong size));
+
+uLong ZCALLBACK win32_write_file_func OF((
+ voidpf opaque,
+ voidpf stream,
+ const void* buf,
+ uLong size));
+
+long ZCALLBACK win32_tell_file_func OF((
+ voidpf opaque,
+ voidpf stream));
+
+long ZCALLBACK win32_seek_file_func OF((
+ voidpf opaque,
+ voidpf stream,
+ uLong offset,
+ int origin));
+
+int ZCALLBACK win32_close_file_func OF((
+ voidpf opaque,
+ voidpf stream));
+
+int ZCALLBACK win32_error_file_func OF((
+ voidpf opaque,
+ voidpf stream));
+
+typedef struct
+{
+ HANDLE hf;
+ int error;
+} WIN32FILE_IOWIN;
+
+voidpf ZCALLBACK win32_open_file_func (opaque, filename, mode)
+ voidpf opaque;
+ const char* filename;
+ int mode;
+{
+ const char* mode_fopen = NULL;
+ DWORD dwDesiredAccess,dwCreationDisposition,dwShareMode,dwFlagsAndAttributes ;
+ HANDLE hFile = 0;
+ voidpf ret=NULL;
+
+ dwDesiredAccess = dwShareMode = dwFlagsAndAttributes = 0;
+
+ if ((mode & ZLIB_FILEFUNC_MODE_READWRITEFILTER)==ZLIB_FILEFUNC_MODE_READ)
+ {
+ dwDesiredAccess = GENERIC_READ;
+ dwCreationDisposition = OPEN_EXISTING;
+ dwShareMode = FILE_SHARE_READ;
+ }
+ else
+ if (mode & ZLIB_FILEFUNC_MODE_EXISTING)
+ {
+ dwDesiredAccess = GENERIC_WRITE | GENERIC_READ;
+ dwCreationDisposition = OPEN_EXISTING;
+ }
+ else
+ if (mode & ZLIB_FILEFUNC_MODE_CREATE)
+ {
+ dwDesiredAccess = GENERIC_WRITE | GENERIC_READ;
+ dwCreationDisposition = CREATE_ALWAYS;
+ }
+
+ if ((filename!=NULL) && (dwDesiredAccess != 0))
+ hFile = CreateFile((LPCTSTR)filename, dwDesiredAccess, dwShareMode, NULL,
+ dwCreationDisposition, dwFlagsAndAttributes, NULL);
+
+ if (hFile == INVALID_HANDLE_VALUE)
+ hFile = NULL;
+
+ if (hFile != NULL)
+ {
+ WIN32FILE_IOWIN w32fiow;
+ w32fiow.hf = hFile;
+ w32fiow.error = 0;
+ ret = malloc(sizeof(WIN32FILE_IOWIN));
+ if (ret==NULL)
+ CloseHandle(hFile);
+ else *((WIN32FILE_IOWIN*)ret) = w32fiow;
+ }
+ return ret;
+}
+
+
+uLong ZCALLBACK win32_read_file_func (opaque, stream, buf, size)
+ voidpf opaque;
+ voidpf stream;
+ void* buf;
+ uLong size;
+{
+ uLong ret=0;
+ HANDLE hFile = NULL;
+ if (stream!=NULL)
+ hFile = ((WIN32FILE_IOWIN*)stream) -> hf;
+ if (hFile != NULL)
+ if (!ReadFile(hFile, buf, size, &ret, NULL))
+ {
+ DWORD dwErr = GetLastError();
+ if (dwErr == ERROR_HANDLE_EOF)
+ dwErr = 0;
+ ((WIN32FILE_IOWIN*)stream) -> error=(int)dwErr;
+ }
+
+ return ret;
+}
+
+
+uLong ZCALLBACK win32_write_file_func (opaque, stream, buf, size)
+ voidpf opaque;
+ voidpf stream;
+ const void* buf;
+ uLong size;
+{
+ uLong ret=0;
+ HANDLE hFile = NULL;
+ if (stream!=NULL)
+ hFile = ((WIN32FILE_IOWIN*)stream) -> hf;
+
+ if (hFile !=NULL)
+ if (!WriteFile(hFile, buf, size, &ret, NULL))
+ {
+ DWORD dwErr = GetLastError();
+ if (dwErr == ERROR_HANDLE_EOF)
+ dwErr = 0;
+ ((WIN32FILE_IOWIN*)stream) -> error=(int)dwErr;
+ }
+
+ return ret;
+}
+
+long ZCALLBACK win32_tell_file_func (opaque, stream)
+ voidpf opaque;
+ voidpf stream;
+{
+ long ret=-1;
+ HANDLE hFile = NULL;
+ if (stream!=NULL)
+ hFile = ((WIN32FILE_IOWIN*)stream) -> hf;
+ if (hFile != NULL)
+ {
+ DWORD dwSet = SetFilePointer(hFile, 0, NULL, FILE_CURRENT);
+ if (dwSet == INVALID_SET_FILE_POINTER)
+ {
+ DWORD dwErr = GetLastError();
+ ((WIN32FILE_IOWIN*)stream) -> error=(int)dwErr;
+ ret = -1;
+ }
+ else
+ ret=(long)dwSet;
+ }
+ return ret;
+}
+
+long ZCALLBACK win32_seek_file_func (opaque, stream, offset, origin)
+ voidpf opaque;
+ voidpf stream;
+ uLong offset;
+ int origin;
+{
+ DWORD dwMoveMethod=0xFFFFFFFF;
+ HANDLE hFile = NULL;
+
+ long ret=-1;
+ if (stream!=NULL)
+ hFile = ((WIN32FILE_IOWIN*)stream) -> hf;
+ switch (origin)
+ {
+ case ZLIB_FILEFUNC_SEEK_CUR :
+ dwMoveMethod = FILE_CURRENT;
+ break;
+ case ZLIB_FILEFUNC_SEEK_END :
+ dwMoveMethod = FILE_END;
+ break;
+ case ZLIB_FILEFUNC_SEEK_SET :
+ dwMoveMethod = FILE_BEGIN;
+ break;
+ default: return -1;
+ }
+
+ if (hFile != NULL)
+ {
+ DWORD dwSet = SetFilePointer(hFile, offset, NULL, dwMoveMethod);
+ if (dwSet == INVALID_SET_FILE_POINTER)
+ {
+ DWORD dwErr = GetLastError();
+ ((WIN32FILE_IOWIN*)stream) -> error=(int)dwErr;
+ ret = -1;
+ }
+ else
+ ret=0;
+ }
+ return ret;
+}
+
+int ZCALLBACK win32_close_file_func (opaque, stream)
+ voidpf opaque;
+ voidpf stream;
+{
+ int ret=-1;
+
+ if (stream!=NULL)
+ {
+ HANDLE hFile;
+ hFile = ((WIN32FILE_IOWIN*)stream) -> hf;
+ if (hFile != NULL)
+ {
+ CloseHandle(hFile);
+ ret=0;
+ }
+ free(stream);
+ }
+ return ret;
+}
+
+int ZCALLBACK win32_error_file_func (opaque, stream)
+ voidpf opaque;
+ voidpf stream;
+{
+ int ret=-1;
+ if (stream!=NULL)
+ {
+ ret = ((WIN32FILE_IOWIN*)stream) -> error;
+ }
+ return ret;
+}
+
+void fill_win32_filefunc (pzlib_filefunc_def)
+ zlib_filefunc_def* pzlib_filefunc_def;
+{
+ pzlib_filefunc_def->zopen_file = win32_open_file_func;
+ pzlib_filefunc_def->zread_file = win32_read_file_func;
+ pzlib_filefunc_def->zwrite_file = win32_write_file_func;
+ pzlib_filefunc_def->ztell_file = win32_tell_file_func;
+ pzlib_filefunc_def->zseek_file = win32_seek_file_func;
+ pzlib_filefunc_def->zclose_file = win32_close_file_func;
+ pzlib_filefunc_def->zerror_file = win32_error_file_func;
+ pzlib_filefunc_def->opaque=NULL;
+}
+#endif
diff --git a/externals/g3dlite/zip.lib/source/iowin32.h b/externals/g3dlite/zip.lib/source/iowin32.h
new file mode 100644
index 00000000000..1978f6152dd
--- /dev/null
+++ b/externals/g3dlite/zip.lib/source/iowin32.h
@@ -0,0 +1,23 @@
+#ifdef _MSC_VER
+/* iowin32.h -- IO base function header for compress/uncompress .zip
+ files using zlib + zip or unzip API
+ This IO API version uses the Win32 API (for Microsoft Windows)
+
+ Version 1.01e, February 12th, 2005
+
+ Copyright (C) 1998-2005 Gilles Vollant
+*/
+
+#include <windows.h>
+
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+void fill_win32_filefunc OF((zlib_filefunc_def* pzlib_filefunc_def));
+
+#ifdef __cplusplus
+}
+#endif
+#endif
diff --git a/externals/g3dlite/zip.lib/source/unzip.c b/externals/g3dlite/zip.lib/source/unzip.c
new file mode 100644
index 00000000000..e80bc5bde7c
--- /dev/null
+++ b/externals/g3dlite/zip.lib/source/unzip.c
@@ -0,0 +1,1604 @@
+/* unzip.c -- IO for uncompress .zip files using zlib
+ Version 1.01e, February 12th, 2005
+
+ Copyright (C) 1998-2005 Gilles Vollant
+
+ Read unzip.h for more info
+*/
+
+/* Decryption code comes from crypt.c by Info-ZIP but has been greatly reduced in terms of
+compatibility with older software. The following is from the original crypt.c. Code
+woven in by Terry Thorsen 1/2003.
+*/
+/*
+ Copyright (c) 1990-2000 Info-ZIP. All rights reserved.
+
+ See the accompanying file LICENSE, version 2000-Apr-09 or later
+ (the contents of which are also included in zip.h) for terms of use.
+ If, for some reason, all these files are missing, the Info-ZIP license
+ also may be found at: ftp://ftp.info-zip.org/pub/infozip/license.html
+*/
+/*
+ crypt.c (full version) by Info-ZIP. Last revised: [see crypt.h]
+
+ The encryption/decryption parts of this source code (as opposed to the
+ non-echoing password parts) were originally written in Europe. The
+ whole source package can be freely distributed, including from the USA.
+ (Prior to January 2000, re-export from the US was a violation of US law.)
+ */
+
+/*
+ This encryption code is a direct transcription of the algorithm from
+ Roger Schlafly, described by Phil Katz in the file appnote.txt. This
+ file (appnote.txt) is distributed with the PKZIP program (even in the
+ version without encryption capabilities).
+ */
+
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include "zlib.h"
+#include "unzip.h"
+
+#ifdef STDC
+# include <stddef.h>
+# include <string.h>
+# include <stdlib.h>
+#endif
+#ifdef NO_ERRNO_H
+ extern int errno;
+#else
+# include <errno.h>
+#endif
+
+
+#ifndef local
+# define local static
+#endif
+/* compile with -Dlocal if your debugger can't find static symbols */
+
+
+#ifndef CASESENSITIVITYDEFAULT_NO
+# if !defined(unix) && !defined(CASESENSITIVITYDEFAULT_YES)
+# define CASESENSITIVITYDEFAULT_NO
+# endif
+#endif
+
+
+#ifndef UNZ_BUFSIZE
+#define UNZ_BUFSIZE (16384)
+#endif
+
+#ifndef UNZ_MAXFILENAMEINZIP
+#define UNZ_MAXFILENAMEINZIP (256)
+#endif
+
+#ifndef ALLOC
+# define ALLOC(size) (malloc(size))
+#endif
+#ifndef TRYFREE
+# define TRYFREE(p) {if (p) free(p);}
+#endif
+
+#define SIZECENTRALDIRITEM (0x2e)
+#define SIZEZIPLOCALHEADER (0x1e)
+
+
+
+
+const char unz_copyright[] =
+ " unzip 1.01 Copyright 1998-2004 Gilles Vollant - http://www.winimage.com/zLibDll";
+
+/* unz_file_info_interntal contain internal info about a file in zipfile*/
+typedef struct unz_file_info_internal_s
+{
+ uLong offset_curfile;/* relative offset of local header 4 bytes */
+} unz_file_info_internal;
+
+
+/* file_in_zip_read_info_s contain internal information about a file in zipfile,
+ when reading and decompress it */
+typedef struct
+{
+ char *read_buffer; /* internal buffer for compressed data */
+ z_stream stream; /* zLib stream structure for inflate */
+
+ uLong pos_in_zipfile; /* position in byte on the zipfile, for fseek*/
+ uLong stream_initialised; /* flag set if stream structure is initialised*/
+
+ uLong offset_local_extrafield;/* offset of the local extra field */
+ uInt size_local_extrafield;/* size of the local extra field */
+ uLong pos_local_extrafield; /* position in the local extra field in read*/
+
+ uLong crc32; /* crc32 of all data uncompressed */
+ uLong crc32_wait; /* crc32 we must obtain after decompress all */
+ uLong rest_read_compressed; /* number of byte to be decompressed */
+ uLong rest_read_uncompressed;/*number of byte to be obtained after decomp*/
+ zlib_filefunc_def z_filefunc;
+ voidpf filestream; /* io structore of the zipfile */
+ uLong compression_method; /* compression method (0==store) */
+ uLong byte_before_the_zipfile;/* byte before the zipfile, (>0 for sfx)*/
+ int raw;
+} file_in_zip_read_info_s;
+
+
+/* unz_s contain internal information about the zipfile
+*/
+typedef struct
+{
+ zlib_filefunc_def z_filefunc;
+ voidpf filestream; /* io structore of the zipfile */
+ unz_global_info gi; /* public global information */
+ uLong byte_before_the_zipfile;/* byte before the zipfile, (>0 for sfx)*/
+ uLong num_file; /* number of the current file in the zipfile*/
+ uLong pos_in_central_dir; /* pos of the current file in the central dir*/
+ uLong current_file_ok; /* flag about the usability of the current file*/
+ uLong central_pos; /* position of the beginning of the central dir*/
+
+ uLong size_central_dir; /* size of the central directory */
+ uLong offset_central_dir; /* offset of start of central directory with
+ respect to the starting disk number */
+
+ unz_file_info cur_file_info; /* public info about the current file in zip*/
+ unz_file_info_internal cur_file_info_internal; /* private info about it*/
+ file_in_zip_read_info_s* pfile_in_zip_read; /* structure about the current
+ file if we are decompressing it */
+ int encrypted;
+# ifndef NOUNCRYPT
+ unsigned long keys[3]; /* keys defining the pseudo-random sequence */
+ const unsigned long* pcrc_32_tab;
+# endif
+} unz_s;
+
+
+#ifndef NOUNCRYPT
+#include "crypt.h"
+#endif
+
+/* ===========================================================================
+ Read a byte from a gz_stream; update next_in and avail_in. Return EOF
+ for end of file.
+ IN assertion: the stream s has been sucessfully opened for reading.
+*/
+
+
+local int unzlocal_getByte OF((
+ const zlib_filefunc_def* pzlib_filefunc_def,
+ voidpf filestream,
+ int *pi));
+
+local int unzlocal_getByte(pzlib_filefunc_def,filestream,pi)
+ const zlib_filefunc_def* pzlib_filefunc_def;
+ voidpf filestream;
+ int *pi;
+{
+ unsigned char c;
+ int err = (int)ZREAD(*pzlib_filefunc_def,filestream,&c,1);
+ if (err==1)
+ {
+ *pi = (int)c;
+ return UNZ_OK;
+ }
+ else
+ {
+ if (ZERROR(*pzlib_filefunc_def,filestream))
+ return UNZ_ERRNO;
+ else
+ return UNZ_EOF;
+ }
+}
+
+
+/* ===========================================================================
+ Reads a long in LSB order from the given gz_stream. Sets
+*/
+local int unzlocal_getShort OF((
+ const zlib_filefunc_def* pzlib_filefunc_def,
+ voidpf filestream,
+ uLong *pX));
+
+local int unzlocal_getShort (pzlib_filefunc_def,filestream,pX)
+ const zlib_filefunc_def* pzlib_filefunc_def;
+ voidpf filestream;
+ uLong *pX;
+{
+ uLong x = 0;
+ int i = 0;
+ int err = 0;
+
+ err = unzlocal_getByte(pzlib_filefunc_def,filestream,&i);
+ x = (uLong)i;
+
+ if (err==UNZ_OK)
+ err = unzlocal_getByte(pzlib_filefunc_def,filestream,&i);
+ x += ((uLong)i)<<8;
+
+ if (err==UNZ_OK)
+ *pX = x;
+ else
+ *pX = 0;
+ return err;
+}
+
+local int unzlocal_getLong OF((
+ const zlib_filefunc_def* pzlib_filefunc_def,
+ voidpf filestream,
+ uLong *pX));
+
+local int unzlocal_getLong (pzlib_filefunc_def,filestream,pX)
+ const zlib_filefunc_def* pzlib_filefunc_def;
+ voidpf filestream;
+ uLong *pX;
+{
+ uLong x = 0;
+ int i = 0;
+ int err = 0;
+
+ err = unzlocal_getByte(pzlib_filefunc_def,filestream,&i);
+ x = (uLong)i;
+
+ if (err==UNZ_OK)
+ err = unzlocal_getByte(pzlib_filefunc_def,filestream,&i);
+ x += ((uLong)i)<<8;
+
+ if (err==UNZ_OK)
+ err = unzlocal_getByte(pzlib_filefunc_def,filestream,&i);
+ x += ((uLong)i)<<16;
+
+ if (err==UNZ_OK)
+ err = unzlocal_getByte(pzlib_filefunc_def,filestream,&i);
+ x += ((uLong)i)<<24;
+
+ if (err==UNZ_OK)
+ *pX = x;
+ else
+ *pX = 0;
+ return err;
+}
+
+
+/* My own strcmpi / strcasecmp */
+local int strcmpcasenosensitive_internal (fileName1,fileName2)
+ const char* fileName1;
+ const char* fileName2;
+{
+ for (;;)
+ {
+ char c1=*(fileName1++);
+ char c2=*(fileName2++);
+ if ((c1>='a') && (c1<='z'))
+ c1 -= 0x20;
+ if ((c2>='a') && (c2<='z'))
+ c2 -= 0x20;
+ if (c1=='\0')
+ return ((c2=='\0') ? 0 : -1);
+ if (c2=='\0')
+ return 1;
+ if (c1<c2)
+ return -1;
+ if (c1>c2)
+ return 1;
+ }
+}
+
+
+#ifdef CASESENSITIVITYDEFAULT_NO
+#define CASESENSITIVITYDEFAULTVALUE 2
+#else
+#define CASESENSITIVITYDEFAULTVALUE 1
+#endif
+
+#ifndef STRCMPCASENOSENTIVEFUNCTION
+#define STRCMPCASENOSENTIVEFUNCTION strcmpcasenosensitive_internal
+#endif
+
+/*
+ Compare two filename (fileName1,fileName2).
+ If iCaseSenisivity = 1, comparision is case sensitivity (like strcmp)
+ If iCaseSenisivity = 2, comparision is not case sensitivity (like strcmpi
+ or strcasecmp)
+ If iCaseSenisivity = 0, case sensitivity is defaut of your operating system
+ (like 1 on Unix, 2 on Windows)
+
+*/
+extern int ZEXPORT unzStringFileNameCompare (fileName1,fileName2,iCaseSensitivity)
+ const char* fileName1;
+ const char* fileName2;
+ int iCaseSensitivity;
+{
+ if (iCaseSensitivity==0)
+ iCaseSensitivity=CASESENSITIVITYDEFAULTVALUE;
+
+ if (iCaseSensitivity==1)
+ return strcmp(fileName1,fileName2);
+
+ return STRCMPCASENOSENTIVEFUNCTION(fileName1,fileName2);
+}
+
+#ifndef BUFREADCOMMENT
+#define BUFREADCOMMENT (0x400)
+#endif
+
+/*
+ Locate the Central directory of a zipfile (at the end, just before
+ the global comment)
+*/
+local uLong unzlocal_SearchCentralDir OF((
+ const zlib_filefunc_def* pzlib_filefunc_def,
+ voidpf filestream));
+
+local uLong unzlocal_SearchCentralDir(pzlib_filefunc_def,filestream)
+ const zlib_filefunc_def* pzlib_filefunc_def;
+ voidpf filestream;
+{
+ unsigned char* buf;
+ uLong uSizeFile;
+ uLong uBackRead;
+ uLong uMaxBack=0xffff; /* maximum size of global comment */
+ uLong uPosFound=0;
+
+ if (ZSEEK(*pzlib_filefunc_def,filestream,0,ZLIB_FILEFUNC_SEEK_END) != 0)
+ return 0;
+
+
+ uSizeFile = ZTELL(*pzlib_filefunc_def,filestream);
+
+ if (uMaxBack>uSizeFile)
+ uMaxBack = uSizeFile;
+
+ buf = (unsigned char*)ALLOC(BUFREADCOMMENT+4);
+ if (buf==NULL)
+ return 0;
+
+ uBackRead = 4;
+ while (uBackRead<uMaxBack)
+ {
+ uLong uReadSize,uReadPos ;
+ int i;
+ if (uBackRead+BUFREADCOMMENT>uMaxBack)
+ uBackRead = uMaxBack;
+ else
+ uBackRead+=BUFREADCOMMENT;
+ uReadPos = uSizeFile-uBackRead ;
+
+ uReadSize = ((BUFREADCOMMENT+4) < (uSizeFile-uReadPos)) ?
+ (BUFREADCOMMENT+4) : (uSizeFile-uReadPos);
+ if (ZSEEK(*pzlib_filefunc_def,filestream,uReadPos,ZLIB_FILEFUNC_SEEK_SET)!=0)
+ break;
+
+ if (ZREAD(*pzlib_filefunc_def,filestream,buf,uReadSize)!=uReadSize)
+ break;
+
+ for (i=(int)uReadSize-3; (i--)>0;)
+ if (((*(buf+i))==0x50) && ((*(buf+i+1))==0x4b) &&
+ ((*(buf+i+2))==0x05) && ((*(buf+i+3))==0x06))
+ {
+ uPosFound = uReadPos+i;
+ break;
+ }
+
+ if (uPosFound!=0)
+ break;
+ }
+ TRYFREE(buf);
+ return uPosFound;
+}
+
+/*
+ Open a Zip file. path contain the full pathname (by example,
+ on a Windows NT computer "c:\\test\\zlib114.zip" or on an Unix computer
+ "zlib/zlib114.zip".
+ If the zipfile cannot be opened (file doesn't exist or in not valid), the
+ return value is NULL.
+ Else, the return value is a unzFile Handle, usable with other function
+ of this unzip package.
+*/
+extern unzFile ZEXPORT unzOpen2 (path, pzlib_filefunc_def)
+ const char *path;
+ zlib_filefunc_def* pzlib_filefunc_def;
+{
+ unz_s us;
+ unz_s *s;
+ uLong central_pos,uL;
+
+ uLong number_disk; /* number of the current dist, used for
+ spaning ZIP, unsupported, always 0*/
+ uLong number_disk_with_CD; /* number the the disk with central dir, used
+ for spaning ZIP, unsupported, always 0*/
+ uLong number_entry_CD; /* total number of entries in
+ the central dir
+ (same than number_entry on nospan) */
+
+ int err=UNZ_OK;
+
+ if (unz_copyright[0]!=' ')
+ return NULL;
+
+ if (pzlib_filefunc_def==NULL)
+ fill_fopen_filefunc(&us.z_filefunc);
+ else
+ us.z_filefunc = *pzlib_filefunc_def;
+
+ us.filestream= (*(us.z_filefunc.zopen_file))(us.z_filefunc.opaque,
+ path,
+ ZLIB_FILEFUNC_MODE_READ |
+ ZLIB_FILEFUNC_MODE_EXISTING);
+ if (us.filestream==NULL)
+ return NULL;
+
+ central_pos = unzlocal_SearchCentralDir(&us.z_filefunc,us.filestream);
+ if (central_pos==0)
+ err=UNZ_ERRNO;
+
+ if (ZSEEK(us.z_filefunc, us.filestream,
+ central_pos,ZLIB_FILEFUNC_SEEK_SET)!=0)
+ err=UNZ_ERRNO;
+
+ /* the signature, already checked */
+ if (unzlocal_getLong(&us.z_filefunc, us.filestream,&uL)!=UNZ_OK)
+ err=UNZ_ERRNO;
+
+ /* number of this disk */
+ if (unzlocal_getShort(&us.z_filefunc, us.filestream,&number_disk)!=UNZ_OK)
+ err=UNZ_ERRNO;
+
+ /* number of the disk with the start of the central directory */
+ if (unzlocal_getShort(&us.z_filefunc, us.filestream,&number_disk_with_CD)!=UNZ_OK)
+ err=UNZ_ERRNO;
+
+ /* total number of entries in the central dir on this disk */
+ if (unzlocal_getShort(&us.z_filefunc, us.filestream,&us.gi.number_entry)!=UNZ_OK)
+ err=UNZ_ERRNO;
+
+ /* total number of entries in the central dir */
+ if (unzlocal_getShort(&us.z_filefunc, us.filestream,&number_entry_CD)!=UNZ_OK)
+ err=UNZ_ERRNO;
+
+ if ((number_entry_CD!=us.gi.number_entry) ||
+ (number_disk_with_CD!=0) ||
+ (number_disk!=0))
+ err=UNZ_BADZIPFILE;
+
+ /* size of the central directory */
+ if (unzlocal_getLong(&us.z_filefunc, us.filestream,&us.size_central_dir)!=UNZ_OK)
+ err=UNZ_ERRNO;
+
+ /* offset of start of central directory with respect to the
+ starting disk number */
+ if (unzlocal_getLong(&us.z_filefunc, us.filestream,&us.offset_central_dir)!=UNZ_OK)
+ err=UNZ_ERRNO;
+
+ /* zipfile comment length */
+ if (unzlocal_getShort(&us.z_filefunc, us.filestream,&us.gi.size_comment)!=UNZ_OK)
+ err=UNZ_ERRNO;
+
+ if ((central_pos<us.offset_central_dir+us.size_central_dir) &&
+ (err==UNZ_OK))
+ err=UNZ_BADZIPFILE;
+
+ if (err!=UNZ_OK)
+ {
+ ZCLOSE(us.z_filefunc, us.filestream);
+ return NULL;
+ }
+
+ us.byte_before_the_zipfile = central_pos -
+ (us.offset_central_dir+us.size_central_dir);
+ us.central_pos = central_pos;
+ us.pfile_in_zip_read = NULL;
+ us.encrypted = 0;
+
+
+ s=(unz_s*)ALLOC(sizeof(unz_s));
+ *s=us;
+ unzGoToFirstFile((unzFile)s);
+ return (unzFile)s;
+}
+
+
+extern unzFile ZEXPORT unzOpen (path)
+ const char *path;
+{
+ return unzOpen2(path, NULL);
+}
+
+/*
+ Close a ZipFile opened with unzipOpen.
+ If there is files inside the .Zip opened with unzipOpenCurrentFile (see later),
+ these files MUST be closed with unzipCloseCurrentFile before call unzipClose.
+ return UNZ_OK if there is no problem. */
+extern int ZEXPORT unzClose (file)
+ unzFile file;
+{
+ unz_s* s;
+ if (file==NULL)
+ return UNZ_PARAMERROR;
+ s=(unz_s*)file;
+
+ if (s->pfile_in_zip_read!=NULL)
+ unzCloseCurrentFile(file);
+
+ ZCLOSE(s->z_filefunc, s->filestream);
+ TRYFREE(s);
+ return UNZ_OK;
+}
+
+
+/*
+ Write info about the ZipFile in the *pglobal_info structure.
+ No preparation of the structure is needed
+ return UNZ_OK if there is no problem. */
+extern int ZEXPORT unzGetGlobalInfo (file,pglobal_info)
+ unzFile file;
+ unz_global_info *pglobal_info;
+{
+ unz_s* s;
+ if (file==NULL)
+ return UNZ_PARAMERROR;
+ s=(unz_s*)file;
+ *pglobal_info=s->gi;
+ return UNZ_OK;
+}
+
+
+/*
+ Translate date/time from Dos format to tm_unz (readable more easilty)
+*/
+local void unzlocal_DosDateToTmuDate (ulDosDate, ptm)
+ uLong ulDosDate;
+ tm_unz* ptm;
+{
+ uLong uDate;
+ uDate = (uLong)(ulDosDate>>16);
+ ptm->tm_mday = (uInt)(uDate&0x1f) ;
+ ptm->tm_mon = (uInt)((((uDate)&0x1E0)/0x20)-1) ;
+ ptm->tm_year = (uInt)(((uDate&0x0FE00)/0x0200)+1980) ;
+
+ ptm->tm_hour = (uInt) ((ulDosDate &0xF800)/0x800);
+ ptm->tm_min = (uInt) ((ulDosDate&0x7E0)/0x20) ;
+ ptm->tm_sec = (uInt) (2*(ulDosDate&0x1f)) ;
+}
+
+/*
+ Get Info about the current file in the zipfile, with internal only info
+*/
+local int unzlocal_GetCurrentFileInfoInternal OF((unzFile file,
+ unz_file_info *pfile_info,
+ unz_file_info_internal
+ *pfile_info_internal,
+ char *szFileName,
+ uLong fileNameBufferSize,
+ void *extraField,
+ uLong extraFieldBufferSize,
+ char *szComment,
+ uLong commentBufferSize));
+
+local int unzlocal_GetCurrentFileInfoInternal (file,
+ pfile_info,
+ pfile_info_internal,
+ szFileName, fileNameBufferSize,
+ extraField, extraFieldBufferSize,
+ szComment, commentBufferSize)
+ unzFile file;
+ unz_file_info *pfile_info;
+ unz_file_info_internal *pfile_info_internal;
+ char *szFileName;
+ uLong fileNameBufferSize;
+ void *extraField;
+ uLong extraFieldBufferSize;
+ char *szComment;
+ uLong commentBufferSize;
+{
+ unz_s* s;
+ unz_file_info file_info;
+ unz_file_info_internal file_info_internal;
+ int err=UNZ_OK;
+ uLong uMagic;
+ long lSeek=0;
+
+ if (file==NULL)
+ return UNZ_PARAMERROR;
+ s=(unz_s*)file;
+ if (ZSEEK(s->z_filefunc, s->filestream,
+ s->pos_in_central_dir+s->byte_before_the_zipfile,
+ ZLIB_FILEFUNC_SEEK_SET)!=0)
+ err=UNZ_ERRNO;
+
+
+ /* we check the magic */
+ if (err==UNZ_OK)
+ {
+ if (unzlocal_getLong(&s->z_filefunc, s->filestream,&uMagic) != UNZ_OK)
+ err=UNZ_ERRNO;
+ else if (uMagic!=0x02014b50)
+ err=UNZ_BADZIPFILE;
+ }
+
+ if (unzlocal_getShort(&s->z_filefunc, s->filestream,&file_info.version) != UNZ_OK)
+ err=UNZ_ERRNO;
+
+ if (unzlocal_getShort(&s->z_filefunc, s->filestream,&file_info.version_needed) != UNZ_OK)
+ err=UNZ_ERRNO;
+
+ if (unzlocal_getShort(&s->z_filefunc, s->filestream,&file_info.flag) != UNZ_OK)
+ err=UNZ_ERRNO;
+
+ if (unzlocal_getShort(&s->z_filefunc, s->filestream,&file_info.compression_method) != UNZ_OK)
+ err=UNZ_ERRNO;
+
+ if (unzlocal_getLong(&s->z_filefunc, s->filestream,&file_info.dosDate) != UNZ_OK)
+ err=UNZ_ERRNO;
+
+ unzlocal_DosDateToTmuDate(file_info.dosDate,&file_info.tmu_date);
+
+ if (unzlocal_getLong(&s->z_filefunc, s->filestream,&file_info.crc) != UNZ_OK)
+ err=UNZ_ERRNO;
+
+ if (unzlocal_getLong(&s->z_filefunc, s->filestream,&file_info.compressed_size) != UNZ_OK)
+ err=UNZ_ERRNO;
+
+ if (unzlocal_getLong(&s->z_filefunc, s->filestream,&file_info.uncompressed_size) != UNZ_OK)
+ err=UNZ_ERRNO;
+
+ if (unzlocal_getShort(&s->z_filefunc, s->filestream,&file_info.size_filename) != UNZ_OK)
+ err=UNZ_ERRNO;
+
+ if (unzlocal_getShort(&s->z_filefunc, s->filestream,&file_info.size_file_extra) != UNZ_OK)
+ err=UNZ_ERRNO;
+
+ if (unzlocal_getShort(&s->z_filefunc, s->filestream,&file_info.size_file_comment) != UNZ_OK)
+ err=UNZ_ERRNO;
+
+ if (unzlocal_getShort(&s->z_filefunc, s->filestream,&file_info.disk_num_start) != UNZ_OK)
+ err=UNZ_ERRNO;
+
+ if (unzlocal_getShort(&s->z_filefunc, s->filestream,&file_info.internal_fa) != UNZ_OK)
+ err=UNZ_ERRNO;
+
+ if (unzlocal_getLong(&s->z_filefunc, s->filestream,&file_info.external_fa) != UNZ_OK)
+ err=UNZ_ERRNO;
+
+ if (unzlocal_getLong(&s->z_filefunc, s->filestream,&file_info_internal.offset_curfile) != UNZ_OK)
+ err=UNZ_ERRNO;
+
+ lSeek+=file_info.size_filename;
+ if ((err==UNZ_OK) && (szFileName!=NULL))
+ {
+ uLong uSizeRead ;
+ if (file_info.size_filename<fileNameBufferSize)
+ {
+ *(szFileName+file_info.size_filename)='\0';
+ uSizeRead = file_info.size_filename;
+ }
+ else
+ uSizeRead = fileNameBufferSize;
+
+ if ((file_info.size_filename>0) && (fileNameBufferSize>0))
+ if (ZREAD(s->z_filefunc, s->filestream,szFileName,uSizeRead)!=uSizeRead)
+ err=UNZ_ERRNO;
+ lSeek -= uSizeRead;
+ }
+
+
+ if ((err==UNZ_OK) && (extraField!=NULL))
+ {
+ uLong uSizeRead ;
+ if (file_info.size_file_extra<extraFieldBufferSize)
+ uSizeRead = file_info.size_file_extra;
+ else
+ uSizeRead = extraFieldBufferSize;
+
+ if (lSeek!=0)
+ {
+ if (ZSEEK(s->z_filefunc, s->filestream,lSeek,ZLIB_FILEFUNC_SEEK_CUR)==0)
+ lSeek=0;
+ else
+ err=UNZ_ERRNO;
+ }
+ if ((file_info.size_file_extra>0) && (extraFieldBufferSize>0))
+ if (ZREAD(s->z_filefunc, s->filestream,extraField,uSizeRead)!=uSizeRead)
+ err=UNZ_ERRNO;
+ lSeek += file_info.size_file_extra - uSizeRead;
+ }
+ else
+ lSeek+=file_info.size_file_extra;
+
+
+ if ((err==UNZ_OK) && (szComment!=NULL))
+ {
+ uLong uSizeRead ;
+ if (file_info.size_file_comment<commentBufferSize)
+ {
+ *(szComment+file_info.size_file_comment)='\0';
+ uSizeRead = file_info.size_file_comment;
+ }
+ else
+ uSizeRead = commentBufferSize;
+
+ if (lSeek!=0)
+ {
+ if (ZSEEK(s->z_filefunc, s->filestream,lSeek,ZLIB_FILEFUNC_SEEK_CUR)==0)
+ lSeek=0;
+ else
+ err=UNZ_ERRNO;
+ }
+ if ((file_info.size_file_comment>0) && (commentBufferSize>0))
+ if (ZREAD(s->z_filefunc, s->filestream,szComment,uSizeRead)!=uSizeRead)
+ err=UNZ_ERRNO;
+ lSeek+=file_info.size_file_comment - uSizeRead;
+ }
+ else
+ lSeek+=file_info.size_file_comment;
+
+ if ((err==UNZ_OK) && (pfile_info!=NULL))
+ *pfile_info=file_info;
+
+ if ((err==UNZ_OK) && (pfile_info_internal!=NULL))
+ *pfile_info_internal=file_info_internal;
+
+ return err;
+}
+
+
+
+/*
+ Write info about the ZipFile in the *pglobal_info structure.
+ No preparation of the structure is needed
+ return UNZ_OK if there is no problem.
+*/
+extern int ZEXPORT unzGetCurrentFileInfo (file,
+ pfile_info,
+ szFileName, fileNameBufferSize,
+ extraField, extraFieldBufferSize,
+ szComment, commentBufferSize)
+ unzFile file;
+ unz_file_info *pfile_info;
+ char *szFileName;
+ uLong fileNameBufferSize;
+ void *extraField;
+ uLong extraFieldBufferSize;
+ char *szComment;
+ uLong commentBufferSize;
+{
+ return unzlocal_GetCurrentFileInfoInternal(file,pfile_info,NULL,
+ szFileName,fileNameBufferSize,
+ extraField,extraFieldBufferSize,
+ szComment,commentBufferSize);
+}
+
+/*
+ Set the current file of the zipfile to the first file.
+ return UNZ_OK if there is no problem
+*/
+extern int ZEXPORT unzGoToFirstFile (file)
+ unzFile file;
+{
+ int err=UNZ_OK;
+ unz_s* s;
+ if (file==NULL)
+ return UNZ_PARAMERROR;
+ s=(unz_s*)file;
+ s->pos_in_central_dir=s->offset_central_dir;
+ s->num_file=0;
+ err=unzlocal_GetCurrentFileInfoInternal(file,&s->cur_file_info,
+ &s->cur_file_info_internal,
+ NULL,0,NULL,0,NULL,0);
+ s->current_file_ok = (err == UNZ_OK);
+ return err;
+}
+
+/*
+ Set the current file of the zipfile to the next file.
+ return UNZ_OK if there is no problem
+ return UNZ_END_OF_LIST_OF_FILE if the actual file was the latest.
+*/
+extern int ZEXPORT unzGoToNextFile (file)
+ unzFile file;
+{
+ unz_s* s;
+ int err;
+
+ if (file==NULL)
+ return UNZ_PARAMERROR;
+ s=(unz_s*)file;
+ if (!s->current_file_ok)
+ return UNZ_END_OF_LIST_OF_FILE;
+ if (s->gi.number_entry != 0xffff) /* 2^16 files overflow hack */
+ if (s->num_file+1==s->gi.number_entry)
+ return UNZ_END_OF_LIST_OF_FILE;
+
+ s->pos_in_central_dir += SIZECENTRALDIRITEM + s->cur_file_info.size_filename +
+ s->cur_file_info.size_file_extra + s->cur_file_info.size_file_comment ;
+ s->num_file++;
+ err = unzlocal_GetCurrentFileInfoInternal(file,&s->cur_file_info,
+ &s->cur_file_info_internal,
+ NULL,0,NULL,0,NULL,0);
+ s->current_file_ok = (err == UNZ_OK);
+ return err;
+}
+
+
+/*
+ Try locate the file szFileName in the zipfile.
+ For the iCaseSensitivity signification, see unzipStringFileNameCompare
+
+ return value :
+ UNZ_OK if the file is found. It becomes the current file.
+ UNZ_END_OF_LIST_OF_FILE if the file is not found
+*/
+extern int ZEXPORT unzLocateFile (file, szFileName, iCaseSensitivity)
+ unzFile file;
+ const char *szFileName;
+ int iCaseSensitivity;
+{
+ unz_s* s;
+ int err;
+
+ /* We remember the 'current' position in the file so that we can jump
+ * back there if we fail.
+ */
+ unz_file_info cur_file_infoSaved;
+ unz_file_info_internal cur_file_info_internalSaved;
+ uLong num_fileSaved;
+ uLong pos_in_central_dirSaved;
+
+
+ if (file==NULL)
+ return UNZ_PARAMERROR;
+
+ if (strlen(szFileName)>=UNZ_MAXFILENAMEINZIP)
+ return UNZ_PARAMERROR;
+
+ s=(unz_s*)file;
+ if (!s->current_file_ok)
+ return UNZ_END_OF_LIST_OF_FILE;
+
+ /* Save the current state */
+ num_fileSaved = s->num_file;
+ pos_in_central_dirSaved = s->pos_in_central_dir;
+ cur_file_infoSaved = s->cur_file_info;
+ cur_file_info_internalSaved = s->cur_file_info_internal;
+
+ err = unzGoToFirstFile(file);
+
+ while (err == UNZ_OK)
+ {
+ char szCurrentFileName[UNZ_MAXFILENAMEINZIP+1];
+ err = unzGetCurrentFileInfo(file,NULL,
+ szCurrentFileName,sizeof(szCurrentFileName)-1,
+ NULL,0,NULL,0);
+ if (err == UNZ_OK)
+ {
+ if (unzStringFileNameCompare(szCurrentFileName,
+ szFileName,iCaseSensitivity)==0)
+ return UNZ_OK;
+ err = unzGoToNextFile(file);
+ }
+ }
+
+ /* We failed, so restore the state of the 'current file' to where we
+ * were.
+ */
+ s->num_file = num_fileSaved ;
+ s->pos_in_central_dir = pos_in_central_dirSaved ;
+ s->cur_file_info = cur_file_infoSaved;
+ s->cur_file_info_internal = cur_file_info_internalSaved;
+ return err;
+}
+
+
+/*
+///////////////////////////////////////////
+// Contributed by Ryan Haksi (mailto://cryogen@infoserve.net)
+// I need random access
+//
+// Further optimization could be realized by adding an ability
+// to cache the directory in memory. The goal being a single
+// comprehensive file read to put the file I need in a memory.
+*/
+
+/*
+typedef struct unz_file_pos_s
+{
+ uLong pos_in_zip_directory; // offset in file
+ uLong num_of_file; // # of file
+} unz_file_pos;
+*/
+
+extern int ZEXPORT unzGetFilePos(file, file_pos)
+ unzFile file;
+ unz_file_pos* file_pos;
+{
+ unz_s* s;
+
+ if (file==NULL || file_pos==NULL)
+ return UNZ_PARAMERROR;
+ s=(unz_s*)file;
+ if (!s->current_file_ok)
+ return UNZ_END_OF_LIST_OF_FILE;
+
+ file_pos->pos_in_zip_directory = s->pos_in_central_dir;
+ file_pos->num_of_file = s->num_file;
+
+ return UNZ_OK;
+}
+
+extern int ZEXPORT unzGoToFilePos(file, file_pos)
+ unzFile file;
+ unz_file_pos* file_pos;
+{
+ unz_s* s;
+ int err;
+
+ if (file==NULL || file_pos==NULL)
+ return UNZ_PARAMERROR;
+ s=(unz_s*)file;
+
+ /* jump to the right spot */
+ s->pos_in_central_dir = file_pos->pos_in_zip_directory;
+ s->num_file = file_pos->num_of_file;
+
+ /* set the current file */
+ err = unzlocal_GetCurrentFileInfoInternal(file,&s->cur_file_info,
+ &s->cur_file_info_internal,
+ NULL,0,NULL,0,NULL,0);
+ /* return results */
+ s->current_file_ok = (err == UNZ_OK);
+ return err;
+}
+
+/*
+// Unzip Helper Functions - should be here?
+///////////////////////////////////////////
+*/
+
+/*
+ Read the local header of the current zipfile
+ Check the coherency of the local header and info in the end of central
+ directory about this file
+ store in *piSizeVar the size of extra info in local header
+ (filename and size of extra field data)
+*/
+local int unzlocal_CheckCurrentFileCoherencyHeader (s,piSizeVar,
+ poffset_local_extrafield,
+ psize_local_extrafield)
+ unz_s* s;
+ uInt* piSizeVar;
+ uLong *poffset_local_extrafield;
+ uInt *psize_local_extrafield;
+{
+ uLong uMagic,uData,uFlags;
+ uLong size_filename;
+ uLong size_extra_field;
+ int err=UNZ_OK;
+
+ *piSizeVar = 0;
+ *poffset_local_extrafield = 0;
+ *psize_local_extrafield = 0;
+
+ if (ZSEEK(s->z_filefunc, s->filestream,s->cur_file_info_internal.offset_curfile +
+ s->byte_before_the_zipfile,ZLIB_FILEFUNC_SEEK_SET)!=0)
+ return UNZ_ERRNO;
+
+
+ if (err==UNZ_OK)
+ {
+ if (unzlocal_getLong(&s->z_filefunc, s->filestream,&uMagic) != UNZ_OK)
+ err=UNZ_ERRNO;
+ else if (uMagic!=0x04034b50)
+ err=UNZ_BADZIPFILE;
+ }
+ if (unzlocal_getShort(&s->z_filefunc, s->filestream,&uData) != UNZ_OK)
+ err=UNZ_ERRNO;
+/*
+ else if ((err==UNZ_OK) && (uData!=s->cur_file_info.wVersion))
+ err=UNZ_BADZIPFILE;
+*/
+ if (unzlocal_getShort(&s->z_filefunc, s->filestream,&uFlags) != UNZ_OK)
+ err=UNZ_ERRNO;
+
+ if (unzlocal_getShort(&s->z_filefunc, s->filestream,&uData) != UNZ_OK)
+ err=UNZ_ERRNO;
+ else if ((err==UNZ_OK) && (uData!=s->cur_file_info.compression_method))
+ err=UNZ_BADZIPFILE;
+
+ if ((err==UNZ_OK) && (s->cur_file_info.compression_method!=0) &&
+ (s->cur_file_info.compression_method!=Z_DEFLATED))
+ err=UNZ_BADZIPFILE;
+
+ if (unzlocal_getLong(&s->z_filefunc, s->filestream,&uData) != UNZ_OK) /* date/time */
+ err=UNZ_ERRNO;
+
+ if (unzlocal_getLong(&s->z_filefunc, s->filestream,&uData) != UNZ_OK) /* crc */
+ err=UNZ_ERRNO;
+ else if ((err==UNZ_OK) && (uData!=s->cur_file_info.crc) &&
+ ((uFlags & 8)==0))
+ err=UNZ_BADZIPFILE;
+
+ if (unzlocal_getLong(&s->z_filefunc, s->filestream,&uData) != UNZ_OK) /* size compr */
+ err=UNZ_ERRNO;
+ else if ((err==UNZ_OK) && (uData!=s->cur_file_info.compressed_size) &&
+ ((uFlags & 8)==0))
+ err=UNZ_BADZIPFILE;
+
+ if (unzlocal_getLong(&s->z_filefunc, s->filestream,&uData) != UNZ_OK) /* size uncompr */
+ err=UNZ_ERRNO;
+ else if ((err==UNZ_OK) && (uData!=s->cur_file_info.uncompressed_size) &&
+ ((uFlags & 8)==0))
+ err=UNZ_BADZIPFILE;
+
+
+ if (unzlocal_getShort(&s->z_filefunc, s->filestream,&size_filename) != UNZ_OK)
+ err=UNZ_ERRNO;
+ else if ((err==UNZ_OK) && (size_filename!=s->cur_file_info.size_filename))
+ err=UNZ_BADZIPFILE;
+
+ *piSizeVar += (uInt)size_filename;
+
+ if (unzlocal_getShort(&s->z_filefunc, s->filestream,&size_extra_field) != UNZ_OK)
+ err=UNZ_ERRNO;
+ *poffset_local_extrafield= s->cur_file_info_internal.offset_curfile +
+ SIZEZIPLOCALHEADER + size_filename;
+ *psize_local_extrafield = (uInt)size_extra_field;
+
+ *piSizeVar += (uInt)size_extra_field;
+
+ return err;
+}
+
+/*
+ Open for reading data the current file in the zipfile.
+ If there is no error and the file is opened, the return value is UNZ_OK.
+*/
+extern int ZEXPORT unzOpenCurrentFile3 (file, method, level, raw, password)
+ unzFile file;
+ int* method;
+ int* level;
+ int raw;
+ const char* password;
+{
+ int err=UNZ_OK;
+ uInt iSizeVar;
+ unz_s* s;
+ file_in_zip_read_info_s* pfile_in_zip_read_info;
+ uLong offset_local_extrafield; /* offset of the local extra field */
+ uInt size_local_extrafield; /* size of the local extra field */
+# ifndef NOUNCRYPT
+ char source[12];
+# else
+ if (password != NULL)
+ return UNZ_PARAMERROR;
+# endif
+
+ if (file==NULL)
+ return UNZ_PARAMERROR;
+ s=(unz_s*)file;
+ if (!s->current_file_ok)
+ return UNZ_PARAMERROR;
+
+ if (s->pfile_in_zip_read != NULL)
+ unzCloseCurrentFile(file);
+
+ if (unzlocal_CheckCurrentFileCoherencyHeader(s,&iSizeVar,
+ &offset_local_extrafield,&size_local_extrafield)!=UNZ_OK)
+ return UNZ_BADZIPFILE;
+
+ pfile_in_zip_read_info = (file_in_zip_read_info_s*)
+ ALLOC(sizeof(file_in_zip_read_info_s));
+ if (pfile_in_zip_read_info==NULL)
+ return UNZ_INTERNALERROR;
+
+ pfile_in_zip_read_info->read_buffer=(char*)ALLOC(UNZ_BUFSIZE);
+ pfile_in_zip_read_info->offset_local_extrafield = offset_local_extrafield;
+ pfile_in_zip_read_info->size_local_extrafield = size_local_extrafield;
+ pfile_in_zip_read_info->pos_local_extrafield=0;
+ pfile_in_zip_read_info->raw=raw;
+
+ if (pfile_in_zip_read_info->read_buffer==NULL)
+ {
+ TRYFREE(pfile_in_zip_read_info);
+ return UNZ_INTERNALERROR;
+ }
+
+ pfile_in_zip_read_info->stream_initialised=0;
+
+ if (method!=NULL)
+ *method = (int)s->cur_file_info.compression_method;
+
+ if (level!=NULL)
+ {
+ *level = 6;
+ switch (s->cur_file_info.flag & 0x06)
+ {
+ case 6 : *level = 1; break;
+ case 4 : *level = 2; break;
+ case 2 : *level = 9; break;
+ }
+ }
+
+ if ((s->cur_file_info.compression_method!=0) &&
+ (s->cur_file_info.compression_method!=Z_DEFLATED))
+ err=UNZ_BADZIPFILE;
+
+ pfile_in_zip_read_info->crc32_wait=s->cur_file_info.crc;
+ pfile_in_zip_read_info->crc32=0;
+ pfile_in_zip_read_info->compression_method =
+ s->cur_file_info.compression_method;
+ pfile_in_zip_read_info->filestream=s->filestream;
+ pfile_in_zip_read_info->z_filefunc=s->z_filefunc;
+ pfile_in_zip_read_info->byte_before_the_zipfile=s->byte_before_the_zipfile;
+
+ pfile_in_zip_read_info->stream.total_out = 0;
+
+ if ((s->cur_file_info.compression_method==Z_DEFLATED) &&
+ (!raw))
+ {
+ pfile_in_zip_read_info->stream.zalloc = (alloc_func)0;
+ pfile_in_zip_read_info->stream.zfree = (free_func)0;
+ pfile_in_zip_read_info->stream.opaque = (voidpf)0;
+ pfile_in_zip_read_info->stream.next_in = (voidpf)0;
+ pfile_in_zip_read_info->stream.avail_in = 0;
+
+ err=inflateInit2(&pfile_in_zip_read_info->stream, -MAX_WBITS);
+ if (err == Z_OK)
+ pfile_in_zip_read_info->stream_initialised=1;
+ else
+ {
+ TRYFREE(pfile_in_zip_read_info);
+ return err;
+ }
+ /* windowBits is passed < 0 to tell that there is no zlib header.
+ * Note that in this case inflate *requires* an extra "dummy" byte
+ * after the compressed stream in order to complete decompression and
+ * return Z_STREAM_END.
+ * In unzip, i don't wait absolutely Z_STREAM_END because I known the
+ * size of both compressed and uncompressed data
+ */
+ }
+ pfile_in_zip_read_info->rest_read_compressed =
+ s->cur_file_info.compressed_size ;
+ pfile_in_zip_read_info->rest_read_uncompressed =
+ s->cur_file_info.uncompressed_size ;
+
+
+ pfile_in_zip_read_info->pos_in_zipfile =
+ s->cur_file_info_internal.offset_curfile + SIZEZIPLOCALHEADER +
+ iSizeVar;
+
+ pfile_in_zip_read_info->stream.avail_in = (uInt)0;
+
+ s->pfile_in_zip_read = pfile_in_zip_read_info;
+
+# ifndef NOUNCRYPT
+ if (password != NULL)
+ {
+ int i;
+ s->pcrc_32_tab = get_crc_table();
+ init_keys(password,s->keys,s->pcrc_32_tab);
+ if (ZSEEK(s->z_filefunc, s->filestream,
+ s->pfile_in_zip_read->pos_in_zipfile +
+ s->pfile_in_zip_read->byte_before_the_zipfile,
+ SEEK_SET)!=0)
+ return UNZ_INTERNALERROR;
+ if(ZREAD(s->z_filefunc, s->filestream,source, 12)<12)
+ return UNZ_INTERNALERROR;
+
+ for (i = 0; i<12; i++)
+ zdecode(s->keys,s->pcrc_32_tab,source[i]);
+
+ s->pfile_in_zip_read->pos_in_zipfile+=12;
+ s->encrypted=1;
+ }
+# endif
+
+
+ return UNZ_OK;
+}
+
+extern int ZEXPORT unzOpenCurrentFile (file)
+ unzFile file;
+{
+ return unzOpenCurrentFile3(file, NULL, NULL, 0, NULL);
+}
+
+extern int ZEXPORT unzOpenCurrentFilePassword (file, password)
+ unzFile file;
+ const char* password;
+{
+ return unzOpenCurrentFile3(file, NULL, NULL, 0, password);
+}
+
+extern int ZEXPORT unzOpenCurrentFile2 (file,method,level,raw)
+ unzFile file;
+ int* method;
+ int* level;
+ int raw;
+{
+ return unzOpenCurrentFile3(file, method, level, raw, NULL);
+}
+
+/*
+ Read bytes from the current file.
+ buf contain buffer where data must be copied
+ len the size of buf.
+
+ return the number of byte copied if somes bytes are copied
+ return 0 if the end of file was reached
+ return <0 with error code if there is an error
+ (UNZ_ERRNO for IO error, or zLib error for uncompress error)
+*/
+extern int ZEXPORT unzReadCurrentFile (file, buf, len)
+ unzFile file;
+ voidp buf;
+ unsigned len;
+{
+ int err=UNZ_OK;
+ uInt iRead = 0;
+ unz_s* s;
+ file_in_zip_read_info_s* pfile_in_zip_read_info;
+ if (file==NULL)
+ return UNZ_PARAMERROR;
+ s=(unz_s*)file;
+ pfile_in_zip_read_info=s->pfile_in_zip_read;
+
+ if (pfile_in_zip_read_info==NULL)
+ return UNZ_PARAMERROR;
+
+
+ if ((pfile_in_zip_read_info->read_buffer == NULL))
+ return UNZ_END_OF_LIST_OF_FILE;
+ if (len==0)
+ return 0;
+
+ pfile_in_zip_read_info->stream.next_out = (Bytef*)buf;
+
+ pfile_in_zip_read_info->stream.avail_out = (uInt)len;
+
+ if ((len>pfile_in_zip_read_info->rest_read_uncompressed) &&
+ (!(pfile_in_zip_read_info->raw)))
+ pfile_in_zip_read_info->stream.avail_out =
+ (uInt)pfile_in_zip_read_info->rest_read_uncompressed;
+
+ if ((len>pfile_in_zip_read_info->rest_read_compressed+
+ pfile_in_zip_read_info->stream.avail_in) &&
+ (pfile_in_zip_read_info->raw))
+ pfile_in_zip_read_info->stream.avail_out =
+ (uInt)pfile_in_zip_read_info->rest_read_compressed+
+ pfile_in_zip_read_info->stream.avail_in;
+
+ while (pfile_in_zip_read_info->stream.avail_out>0)
+ {
+ if ((pfile_in_zip_read_info->stream.avail_in==0) &&
+ (pfile_in_zip_read_info->rest_read_compressed>0))
+ {
+ uInt uReadThis = UNZ_BUFSIZE;
+ if (pfile_in_zip_read_info->rest_read_compressed<uReadThis)
+ uReadThis = (uInt)pfile_in_zip_read_info->rest_read_compressed;
+ if (uReadThis == 0)
+ return UNZ_EOF;
+ if (ZSEEK(pfile_in_zip_read_info->z_filefunc,
+ pfile_in_zip_read_info->filestream,
+ pfile_in_zip_read_info->pos_in_zipfile +
+ pfile_in_zip_read_info->byte_before_the_zipfile,
+ ZLIB_FILEFUNC_SEEK_SET)!=0)
+ return UNZ_ERRNO;
+ if (ZREAD(pfile_in_zip_read_info->z_filefunc,
+ pfile_in_zip_read_info->filestream,
+ pfile_in_zip_read_info->read_buffer,
+ uReadThis)!=uReadThis)
+ return UNZ_ERRNO;
+
+
+# ifndef NOUNCRYPT
+ if(s->encrypted)
+ {
+ uInt i;
+ for(i=0;i<uReadThis;i++)
+ pfile_in_zip_read_info->read_buffer[i] =
+ zdecode(s->keys,s->pcrc_32_tab,
+ pfile_in_zip_read_info->read_buffer[i]);
+ }
+# endif
+
+
+ pfile_in_zip_read_info->pos_in_zipfile += uReadThis;
+
+ pfile_in_zip_read_info->rest_read_compressed-=uReadThis;
+
+ pfile_in_zip_read_info->stream.next_in =
+ (Bytef*)pfile_in_zip_read_info->read_buffer;
+ pfile_in_zip_read_info->stream.avail_in = (uInt)uReadThis;
+ }
+
+ if ((pfile_in_zip_read_info->compression_method==0) || (pfile_in_zip_read_info->raw))
+ {
+ uInt uDoCopy,i ;
+
+ if ((pfile_in_zip_read_info->stream.avail_in == 0) &&
+ (pfile_in_zip_read_info->rest_read_compressed == 0))
+ return (iRead==0) ? UNZ_EOF : iRead;
+
+ if (pfile_in_zip_read_info->stream.avail_out <
+ pfile_in_zip_read_info->stream.avail_in)
+ uDoCopy = pfile_in_zip_read_info->stream.avail_out ;
+ else
+ uDoCopy = pfile_in_zip_read_info->stream.avail_in ;
+
+ for (i=0;i<uDoCopy;i++)
+ *(pfile_in_zip_read_info->stream.next_out+i) =
+ *(pfile_in_zip_read_info->stream.next_in+i);
+
+ pfile_in_zip_read_info->crc32 = crc32(pfile_in_zip_read_info->crc32,
+ pfile_in_zip_read_info->stream.next_out,
+ uDoCopy);
+ pfile_in_zip_read_info->rest_read_uncompressed-=uDoCopy;
+ pfile_in_zip_read_info->stream.avail_in -= uDoCopy;
+ pfile_in_zip_read_info->stream.avail_out -= uDoCopy;
+ pfile_in_zip_read_info->stream.next_out += uDoCopy;
+ pfile_in_zip_read_info->stream.next_in += uDoCopy;
+ pfile_in_zip_read_info->stream.total_out += uDoCopy;
+ iRead += uDoCopy;
+ }
+ else
+ {
+ uLong uTotalOutBefore,uTotalOutAfter;
+ const Bytef *bufBefore;
+ uLong uOutThis;
+ int flush=Z_SYNC_FLUSH;
+
+ uTotalOutBefore = pfile_in_zip_read_info->stream.total_out;
+ bufBefore = pfile_in_zip_read_info->stream.next_out;
+
+ /*
+ if ((pfile_in_zip_read_info->rest_read_uncompressed ==
+ pfile_in_zip_read_info->stream.avail_out) &&
+ (pfile_in_zip_read_info->rest_read_compressed == 0))
+ flush = Z_FINISH;
+ */
+ err=inflate(&pfile_in_zip_read_info->stream,flush);
+
+ if ((err>=0) && (pfile_in_zip_read_info->stream.msg!=NULL))
+ err = Z_DATA_ERROR;
+
+ uTotalOutAfter = pfile_in_zip_read_info->stream.total_out;
+ uOutThis = uTotalOutAfter-uTotalOutBefore;
+
+ pfile_in_zip_read_info->crc32 =
+ crc32(pfile_in_zip_read_info->crc32,bufBefore,
+ (uInt)(uOutThis));
+
+ pfile_in_zip_read_info->rest_read_uncompressed -=
+ uOutThis;
+
+ iRead += (uInt)(uTotalOutAfter - uTotalOutBefore);
+
+ if (err==Z_STREAM_END)
+ return (iRead==0) ? UNZ_EOF : iRead;
+ if (err!=Z_OK)
+ break;
+ }
+ }
+
+ if (err==Z_OK)
+ return iRead;
+ return err;
+}
+
+
+/*
+ Give the current position in uncompressed data
+*/
+extern z_off_t ZEXPORT unztell (file)
+ unzFile file;
+{
+ unz_s* s;
+ file_in_zip_read_info_s* pfile_in_zip_read_info;
+ if (file==NULL)
+ return UNZ_PARAMERROR;
+ s=(unz_s*)file;
+ pfile_in_zip_read_info=s->pfile_in_zip_read;
+
+ if (pfile_in_zip_read_info==NULL)
+ return UNZ_PARAMERROR;
+
+ return (z_off_t)pfile_in_zip_read_info->stream.total_out;
+}
+
+
+/*
+ return 1 if the end of file was reached, 0 elsewhere
+*/
+extern int ZEXPORT unzeof (file)
+ unzFile file;
+{
+ unz_s* s;
+ file_in_zip_read_info_s* pfile_in_zip_read_info;
+ if (file==NULL)
+ return UNZ_PARAMERROR;
+ s=(unz_s*)file;
+ pfile_in_zip_read_info=s->pfile_in_zip_read;
+
+ if (pfile_in_zip_read_info==NULL)
+ return UNZ_PARAMERROR;
+
+ if (pfile_in_zip_read_info->rest_read_uncompressed == 0)
+ return 1;
+ else
+ return 0;
+}
+
+
+
+/*
+ Read extra field from the current file (opened by unzOpenCurrentFile)
+ This is the local-header version of the extra field (sometimes, there is
+ more info in the local-header version than in the central-header)
+
+ if buf==NULL, it return the size of the local extra field that can be read
+
+ if buf!=NULL, len is the size of the buffer, the extra header is copied in
+ buf.
+ the return value is the number of bytes copied in buf, or (if <0)
+ the error code
+*/
+extern int ZEXPORT unzGetLocalExtrafield (file,buf,len)
+ unzFile file;
+ voidp buf;
+ unsigned len;
+{
+ unz_s* s;
+ file_in_zip_read_info_s* pfile_in_zip_read_info;
+ uInt read_now;
+ uLong size_to_read;
+
+ if (file==NULL)
+ return UNZ_PARAMERROR;
+ s=(unz_s*)file;
+ pfile_in_zip_read_info=s->pfile_in_zip_read;
+
+ if (pfile_in_zip_read_info==NULL)
+ return UNZ_PARAMERROR;
+
+ size_to_read = (pfile_in_zip_read_info->size_local_extrafield -
+ pfile_in_zip_read_info->pos_local_extrafield);
+
+ if (buf==NULL)
+ return (int)size_to_read;
+
+ if (len>size_to_read)
+ read_now = (uInt)size_to_read;
+ else
+ read_now = (uInt)len ;
+
+ if (read_now==0)
+ return 0;
+
+ if (ZSEEK(pfile_in_zip_read_info->z_filefunc,
+ pfile_in_zip_read_info->filestream,
+ pfile_in_zip_read_info->offset_local_extrafield +
+ pfile_in_zip_read_info->pos_local_extrafield,
+ ZLIB_FILEFUNC_SEEK_SET)!=0)
+ return UNZ_ERRNO;
+
+ if (ZREAD(pfile_in_zip_read_info->z_filefunc,
+ pfile_in_zip_read_info->filestream,
+ buf,read_now)!=read_now)
+ return UNZ_ERRNO;
+
+ return (int)read_now;
+}
+
+/*
+ Close the file in zip opened with unzipOpenCurrentFile
+ Return UNZ_CRCERROR if all the file was read but the CRC is not good
+*/
+extern int ZEXPORT unzCloseCurrentFile (file)
+ unzFile file;
+{
+ int err=UNZ_OK;
+
+ unz_s* s;
+ file_in_zip_read_info_s* pfile_in_zip_read_info;
+ if (file==NULL)
+ return UNZ_PARAMERROR;
+ s=(unz_s*)file;
+ pfile_in_zip_read_info=s->pfile_in_zip_read;
+
+ if (pfile_in_zip_read_info==NULL)
+ return UNZ_PARAMERROR;
+
+
+ if ((pfile_in_zip_read_info->rest_read_uncompressed == 0) &&
+ (!pfile_in_zip_read_info->raw))
+ {
+ if (pfile_in_zip_read_info->crc32 != pfile_in_zip_read_info->crc32_wait)
+ err=UNZ_CRCERROR;
+ }
+
+
+ TRYFREE(pfile_in_zip_read_info->read_buffer);
+ pfile_in_zip_read_info->read_buffer = NULL;
+ if (pfile_in_zip_read_info->stream_initialised)
+ inflateEnd(&pfile_in_zip_read_info->stream);
+
+ pfile_in_zip_read_info->stream_initialised = 0;
+ TRYFREE(pfile_in_zip_read_info);
+
+ s->pfile_in_zip_read=NULL;
+
+ return err;
+}
+
+
+/*
+ Get the global comment string of the ZipFile, in the szComment buffer.
+ uSizeBuf is the size of the szComment buffer.
+ return the number of byte copied or an error code <0
+*/
+extern int ZEXPORT unzGetGlobalComment (file, szComment, uSizeBuf)
+ unzFile file;
+ char *szComment;
+ uLong uSizeBuf;
+{
+ unz_s* s;
+ uLong uReadThis ;
+ if (file==NULL)
+ return UNZ_PARAMERROR;
+ s=(unz_s*)file;
+
+ uReadThis = uSizeBuf;
+ if (uReadThis>s->gi.size_comment)
+ uReadThis = s->gi.size_comment;
+
+ if (ZSEEK(s->z_filefunc,s->filestream,s->central_pos+22,ZLIB_FILEFUNC_SEEK_SET)!=0)
+ return UNZ_ERRNO;
+
+ if (uReadThis>0)
+ {
+ *szComment='\0';
+ if (ZREAD(s->z_filefunc,s->filestream,szComment,uReadThis)!=uReadThis)
+ return UNZ_ERRNO;
+ }
+
+ if ((szComment != NULL) && (uSizeBuf > s->gi.size_comment))
+ *(szComment+s->gi.size_comment)='\0';
+ return (int)uReadThis;
+}
+
+/* Additions by RX '2004 */
+extern uLong ZEXPORT unzGetOffset (file)
+ unzFile file;
+{
+ unz_s* s;
+
+ if (file==NULL)
+ return UNZ_PARAMERROR;
+ s=(unz_s*)file;
+ if (!s->current_file_ok)
+ return 0;
+ if (s->gi.number_entry != 0 && s->gi.number_entry != 0xffff)
+ if (s->num_file==s->gi.number_entry)
+ return 0;
+ return s->pos_in_central_dir;
+}
+
+extern int ZEXPORT unzSetOffset (file, pos)
+ unzFile file;
+ uLong pos;
+{
+ unz_s* s;
+ int err;
+
+ if (file==NULL)
+ return UNZ_PARAMERROR;
+ s=(unz_s*)file;
+
+ s->pos_in_central_dir = pos;
+ s->num_file = s->gi.number_entry; /* hack */
+ err = unzlocal_GetCurrentFileInfoInternal(file,&s->cur_file_info,
+ &s->cur_file_info_internal,
+ NULL,0,NULL,0,NULL,0);
+ s->current_file_ok = (err == UNZ_OK);
+ return err;
+}
diff --git a/externals/g3dlite/zip.lib/source/zip.c b/externals/g3dlite/zip.lib/source/zip.c
new file mode 100644
index 00000000000..d5f9fe53d26
--- /dev/null
+++ b/externals/g3dlite/zip.lib/source/zip.c
@@ -0,0 +1,1221 @@
+/* zip.c -- IO on .zip files using zlib
+ Version 1.01e, February 12th, 2005
+
+ 27 Dec 2004 Rolf Kalbermatter
+ Modification to zipOpen2 to support globalComment retrieval.
+
+ Copyright (C) 1998-2005 Gilles Vollant
+
+ Read zip.h for more info
+*/
+
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+#include "zlib.h"
+#include "zip.h"
+
+#ifdef STDC
+# include <stddef.h>
+# include <string.h>
+# include <stdlib.h>
+#endif
+#ifdef NO_ERRNO_H
+ extern int errno;
+#else
+# include <errno.h>
+#endif
+
+
+#ifndef local
+# define local static
+#endif
+/* compile with -Dlocal if your debugger can't find static symbols */
+
+#ifndef VERSIONMADEBY
+# define VERSIONMADEBY (0x0) /* platform depedent */
+#endif
+
+#ifndef Z_BUFSIZE
+#define Z_BUFSIZE (16384)
+#endif
+
+#ifndef Z_MAXFILENAMEINZIP
+#define Z_MAXFILENAMEINZIP (256)
+#endif
+
+#ifndef ALLOC
+# define ALLOC(size) (malloc(size))
+#endif
+#ifndef TRYFREE
+# define TRYFREE(p) {if (p) free(p);}
+#endif
+
+/*
+#define SIZECENTRALDIRITEM (0x2e)
+#define SIZEZIPLOCALHEADER (0x1e)
+*/
+
+/* I've found an old Unix (a SunOS 4.1.3_U1) without all SEEK_* defined.... */
+
+#ifndef SEEK_CUR
+#define SEEK_CUR 1
+#endif
+
+#ifndef SEEK_END
+#define SEEK_END 2
+#endif
+
+#ifndef SEEK_SET
+#define SEEK_SET 0
+#endif
+
+#ifndef DEF_MEM_LEVEL
+#if MAX_MEM_LEVEL >= 8
+# define DEF_MEM_LEVEL 8
+#else
+# define DEF_MEM_LEVEL MAX_MEM_LEVEL
+#endif
+#endif
+const char zip_copyright[] =
+ " zip 1.01 Copyright 1998-2004 Gilles Vollant - http://www.winimage.com/zLibDll";
+
+
+#define SIZEDATA_INDATABLOCK (4096-(4*4))
+
+#define LOCALHEADERMAGIC (0x04034b50)
+#define CENTRALHEADERMAGIC (0x02014b50)
+#define ENDHEADERMAGIC (0x06054b50)
+
+#define FLAG_LOCALHEADER_OFFSET (0x06)
+#define CRC_LOCALHEADER_OFFSET (0x0e)
+
+#define SIZECENTRALHEADER (0x2e) /* 46 */
+
+typedef struct linkedlist_datablock_internal_s
+{
+ struct linkedlist_datablock_internal_s* next_datablock;
+ uLong avail_in_this_block;
+ uLong filled_in_this_block;
+ uLong unused; /* for future use and alignement */
+ unsigned char data[SIZEDATA_INDATABLOCK];
+} linkedlist_datablock_internal;
+
+typedef struct linkedlist_data_s
+{
+ linkedlist_datablock_internal* first_block;
+ linkedlist_datablock_internal* last_block;
+} linkedlist_data;
+
+
+typedef struct
+{
+ z_stream stream; /* zLib stream structure for inflate */
+ int stream_initialised; /* 1 is stream is initialised */
+ uInt pos_in_buffered_data; /* last written byte in buffered_data */
+
+ uLong pos_local_header; /* offset of the local header of the file
+ currenty writing */
+ char* central_header; /* central header data for the current file */
+ uLong size_centralheader; /* size of the central header for cur file */
+ uLong flag; /* flag of the file currently writing */
+
+ int method; /* compression method of file currenty wr.*/
+ int raw; /* 1 for directly writing raw data */
+ Byte buffered_data[Z_BUFSIZE];/* buffer contain compressed data to be writ*/
+ uLong dosDate;
+ uLong crc32;
+ int encrypt;
+#ifndef NOCRYPT
+ unsigned long keys[3]; /* keys defining the pseudo-random sequence */
+ const unsigned long* pcrc_32_tab;
+ int crypt_header_size;
+#endif
+} curfile_info;
+
+typedef struct
+{
+ zlib_filefunc_def z_filefunc;
+ voidpf filestream; /* io structore of the zipfile */
+ linkedlist_data central_dir;/* datablock with central dir in construction*/
+ int in_opened_file_inzip; /* 1 if a file in the zip is currently writ.*/
+ curfile_info ci; /* info on the file curretly writing */
+
+ uLong begin_pos; /* position of the beginning of the zipfile */
+ uLong add_position_when_writting_offset;
+ uLong number_entry;
+#ifndef NO_ADDFILEINEXISTINGZIP
+ char *globalcomment;
+#endif
+} zip_internal;
+
+
+
+#ifndef NOCRYPT
+#define INCLUDECRYPTINGCODE_IFCRYPTALLOWED
+#include "crypt.h"
+#endif
+
+local linkedlist_datablock_internal* allocate_new_datablock()
+{
+ linkedlist_datablock_internal* ldi;
+ ldi = (linkedlist_datablock_internal*)
+ ALLOC(sizeof(linkedlist_datablock_internal));
+ if (ldi!=NULL)
+ {
+ ldi->next_datablock = NULL ;
+ ldi->filled_in_this_block = 0 ;
+ ldi->avail_in_this_block = SIZEDATA_INDATABLOCK ;
+ }
+ return ldi;
+}
+
+local void free_datablock(ldi)
+ linkedlist_datablock_internal* ldi;
+{
+ while (ldi!=NULL)
+ {
+ linkedlist_datablock_internal* ldinext = ldi->next_datablock;
+ TRYFREE(ldi);
+ ldi = ldinext;
+ }
+}
+
+local void init_linkedlist(ll)
+ linkedlist_data* ll;
+{
+ ll->first_block = ll->last_block = NULL;
+}
+
+/*
+// Never used!
+local void free_linkedlist(ll)
+ linkedlist_data* ll;
+{
+ free_datablock(ll->first_block);
+ ll->first_block = ll->last_block = NULL;
+}
+*/
+
+local int add_data_in_datablock(ll,buf,len)
+ linkedlist_data* ll;
+ const void* buf;
+ uLong len;
+{
+ linkedlist_datablock_internal* ldi;
+ const unsigned char* from_copy;
+
+ if (ll==NULL)
+ return ZIP_INTERNALERROR;
+
+ if (ll->last_block == NULL)
+ {
+ ll->first_block = ll->last_block = allocate_new_datablock();
+ if (ll->first_block == NULL)
+ return ZIP_INTERNALERROR;
+ }
+
+ ldi = ll->last_block;
+ from_copy = (unsigned char*)buf;
+
+ while (len>0)
+ {
+ uInt copy_this;
+ uInt i;
+ unsigned char* to_copy;
+
+ if (ldi->avail_in_this_block==0)
+ {
+ ldi->next_datablock = allocate_new_datablock();
+ if (ldi->next_datablock == NULL)
+ return ZIP_INTERNALERROR;
+ ldi = ldi->next_datablock ;
+ ll->last_block = ldi;
+ }
+
+ if (ldi->avail_in_this_block < len)
+ copy_this = (uInt)ldi->avail_in_this_block;
+ else
+ copy_this = (uInt)len;
+
+ to_copy = &(ldi->data[ldi->filled_in_this_block]);
+
+ for (i=0;i<copy_this;i++)
+ *(to_copy+i)=*(from_copy+i);
+
+ ldi->filled_in_this_block += copy_this;
+ ldi->avail_in_this_block -= copy_this;
+ from_copy += copy_this ;
+ len -= copy_this;
+ }
+ return ZIP_OK;
+}
+
+
+
+/****************************************************************************/
+
+#ifndef NO_ADDFILEINEXISTINGZIP
+/* ===========================================================================
+ Inputs a long in LSB order to the given file
+ nbByte == 1, 2 or 4 (byte, short or long)
+*/
+
+local int ziplocal_putValue OF((const zlib_filefunc_def* pzlib_filefunc_def,
+ voidpf filestream, uLong x, int nbByte));
+local int ziplocal_putValue (pzlib_filefunc_def, filestream, x, nbByte)
+ const zlib_filefunc_def* pzlib_filefunc_def;
+ voidpf filestream;
+ uLong x;
+ int nbByte;
+{
+ unsigned char buf[4];
+ int n;
+ for (n = 0; n < nbByte; n++)
+ {
+ buf[n] = (unsigned char)(x & 0xff);
+ x >>= 8;
+ }
+ if (x != 0)
+ { /* data overflow - hack for ZIP64 (X Roche) */
+ for (n = 0; n < nbByte; n++)
+ {
+ buf[n] = 0xff;
+ }
+ }
+
+ if (ZWRITE(*pzlib_filefunc_def,filestream,buf,nbByte)!=(uLong)nbByte)
+ return ZIP_ERRNO;
+ else
+ return ZIP_OK;
+}
+
+local void ziplocal_putValue_inmemory OF((void* dest, uLong x, int nbByte));
+local void ziplocal_putValue_inmemory (dest, x, nbByte)
+ void* dest;
+ uLong x;
+ int nbByte;
+{
+ unsigned char* buf=(unsigned char*)dest;
+ int n;
+ for (n = 0; n < nbByte; n++) {
+ buf[n] = (unsigned char)(x & 0xff);
+ x >>= 8;
+ }
+
+ if (x != 0)
+ { /* data overflow - hack for ZIP64 */
+ for (n = 0; n < nbByte; n++)
+ {
+ buf[n] = 0xff;
+ }
+ }
+}
+
+/****************************************************************************/
+
+
+local uLong ziplocal_TmzDateToDosDate(ptm,dosDate)
+ const tm_zip* ptm;
+ uLong dosDate;
+{
+ uLong year = (uLong)ptm->tm_year;
+ if (year>1980)
+ year-=1980;
+ else if (year>80)
+ year-=80;
+ return
+ (uLong) (((ptm->tm_mday) + (32 * (ptm->tm_mon+1)) + (512 * year)) << 16) |
+ ((ptm->tm_sec/2) + (32* ptm->tm_min) + (2048 * (uLong)ptm->tm_hour));
+}
+
+
+/****************************************************************************/
+
+local int ziplocal_getByte OF((
+ const zlib_filefunc_def* pzlib_filefunc_def,
+ voidpf filestream,
+ int *pi));
+
+local int ziplocal_getByte(pzlib_filefunc_def,filestream,pi)
+ const zlib_filefunc_def* pzlib_filefunc_def;
+ voidpf filestream;
+ int *pi;
+{
+ unsigned char c;
+ int err = (int)ZREAD(*pzlib_filefunc_def,filestream,&c,1);
+ if (err==1)
+ {
+ *pi = (int)c;
+ return ZIP_OK;
+ }
+ else
+ {
+ if (ZERROR(*pzlib_filefunc_def,filestream))
+ return ZIP_ERRNO;
+ else
+ return ZIP_EOF;
+ }
+}
+
+
+/* ===========================================================================
+ Reads a long in LSB order from the given gz_stream. Sets
+*/
+local int ziplocal_getShort OF((
+ const zlib_filefunc_def* pzlib_filefunc_def,
+ voidpf filestream,
+ uLong *pX));
+
+local int ziplocal_getShort (pzlib_filefunc_def,filestream,pX)
+ const zlib_filefunc_def* pzlib_filefunc_def;
+ voidpf filestream;
+ uLong *pX;
+{
+ uLong x = 0;
+ int i = 0;
+ int err = 0;
+
+ err = ziplocal_getByte(pzlib_filefunc_def,filestream,&i);
+ x = (uLong)i;
+
+ if (err==ZIP_OK)
+ err = ziplocal_getByte(pzlib_filefunc_def,filestream,&i);
+ x += ((uLong)i)<<8;
+
+ if (err==ZIP_OK)
+ *pX = x;
+ else
+ *pX = 0;
+ return err;
+}
+
+local int ziplocal_getLong OF((
+ const zlib_filefunc_def* pzlib_filefunc_def,
+ voidpf filestream,
+ uLong *pX));
+
+local int ziplocal_getLong (pzlib_filefunc_def,filestream,pX)
+ const zlib_filefunc_def* pzlib_filefunc_def;
+ voidpf filestream;
+ uLong *pX;
+{
+ uLong x = 0;
+ int i = 0;
+ int err = 0;
+
+ err = ziplocal_getByte(pzlib_filefunc_def,filestream,&i);
+ x = (uLong)i;
+
+ if (err==ZIP_OK)
+ err = ziplocal_getByte(pzlib_filefunc_def,filestream,&i);
+ x += ((uLong)i)<<8;
+
+ if (err==ZIP_OK)
+ err = ziplocal_getByte(pzlib_filefunc_def,filestream,&i);
+ x += ((uLong)i)<<16;
+
+ if (err==ZIP_OK)
+ err = ziplocal_getByte(pzlib_filefunc_def,filestream,&i);
+ x += ((uLong)i)<<24;
+
+ if (err==ZIP_OK)
+ *pX = x;
+ else
+ *pX = 0;
+ return err;
+}
+
+#ifndef BUFREADCOMMENT
+#define BUFREADCOMMENT (0x400)
+#endif
+/*
+ Locate the Central directory of a zipfile (at the end, just before
+ the global comment)
+*/
+local uLong ziplocal_SearchCentralDir OF((
+ const zlib_filefunc_def* pzlib_filefunc_def,
+ voidpf filestream));
+
+local uLong ziplocal_SearchCentralDir(pzlib_filefunc_def,filestream)
+ const zlib_filefunc_def* pzlib_filefunc_def;
+ voidpf filestream;
+{
+ unsigned char* buf;
+ uLong uSizeFile;
+ uLong uBackRead;
+ uLong uMaxBack=0xffff; /* maximum size of global comment */
+ uLong uPosFound=0;
+
+ if (ZSEEK(*pzlib_filefunc_def,filestream,0,ZLIB_FILEFUNC_SEEK_END) != 0)
+ return 0;
+
+
+ uSizeFile = ZTELL(*pzlib_filefunc_def,filestream);
+
+ if (uMaxBack>uSizeFile)
+ uMaxBack = uSizeFile;
+
+ buf = (unsigned char*)ALLOC(BUFREADCOMMENT+4);
+ if (buf==NULL)
+ return 0;
+
+ uBackRead = 4;
+ while (uBackRead<uMaxBack)
+ {
+ uLong uReadSize,uReadPos ;
+ int i;
+ if (uBackRead+BUFREADCOMMENT>uMaxBack)
+ uBackRead = uMaxBack;
+ else
+ uBackRead+=BUFREADCOMMENT;
+ uReadPos = uSizeFile-uBackRead ;
+
+ uReadSize = ((BUFREADCOMMENT+4) < (uSizeFile-uReadPos)) ?
+ (BUFREADCOMMENT+4) : (uSizeFile-uReadPos);
+ if (ZSEEK(*pzlib_filefunc_def,filestream,uReadPos,ZLIB_FILEFUNC_SEEK_SET)!=0)
+ break;
+
+ if (ZREAD(*pzlib_filefunc_def,filestream,buf,uReadSize)!=uReadSize)
+ break;
+
+ for (i=(int)uReadSize-3; (i--)>0;)
+ if (((*(buf+i))==0x50) && ((*(buf+i+1))==0x4b) &&
+ ((*(buf+i+2))==0x05) && ((*(buf+i+3))==0x06))
+ {
+ uPosFound = uReadPos+i;
+ break;
+ }
+
+ if (uPosFound!=0)
+ break;
+ }
+ TRYFREE(buf);
+ return uPosFound;
+}
+#endif /* !NO_ADDFILEINEXISTINGZIP*/
+
+/************************************************************/
+extern zipFile ZEXPORT zipOpen2 (pathname, append, globalcomment, pzlib_filefunc_def)
+ const char *pathname;
+ int append;
+ zipcharpc* globalcomment;
+ zlib_filefunc_def* pzlib_filefunc_def;
+{
+ zip_internal ziinit;
+ zip_internal* zi;
+ int err=ZIP_OK;
+
+
+ if (pzlib_filefunc_def==NULL)
+ fill_fopen_filefunc(&ziinit.z_filefunc);
+ else
+ ziinit.z_filefunc = *pzlib_filefunc_def;
+
+ ziinit.filestream = (*(ziinit.z_filefunc.zopen_file))
+ (ziinit.z_filefunc.opaque,
+ pathname,
+ (append == APPEND_STATUS_CREATE) ?
+ (ZLIB_FILEFUNC_MODE_READ | ZLIB_FILEFUNC_MODE_WRITE | ZLIB_FILEFUNC_MODE_CREATE) :
+ (ZLIB_FILEFUNC_MODE_READ | ZLIB_FILEFUNC_MODE_WRITE | ZLIB_FILEFUNC_MODE_EXISTING));
+
+ if (ziinit.filestream == NULL)
+ return NULL;
+ ziinit.begin_pos = ZTELL(ziinit.z_filefunc,ziinit.filestream);
+ ziinit.in_opened_file_inzip = 0;
+ ziinit.ci.stream_initialised = 0;
+ ziinit.number_entry = 0;
+ ziinit.add_position_when_writting_offset = 0;
+ init_linkedlist(&(ziinit.central_dir));
+
+
+ zi = (zip_internal*)ALLOC(sizeof(zip_internal));
+ if (zi==NULL)
+ {
+ ZCLOSE(ziinit.z_filefunc,ziinit.filestream);
+ return NULL;
+ }
+
+ /* now we add file in a zipfile */
+# ifndef NO_ADDFILEINEXISTINGZIP
+ ziinit.globalcomment = NULL;
+ if (append == APPEND_STATUS_ADDINZIP)
+ {
+ uLong byte_before_the_zipfile;/* byte before the zipfile, (>0 for sfx)*/
+
+ uLong size_central_dir; /* size of the central directory */
+ uLong offset_central_dir; /* offset of start of central directory */
+ uLong central_pos,uL;
+
+ uLong number_disk; /* number of the current dist, used for
+ spaning ZIP, unsupported, always 0*/
+ uLong number_disk_with_CD; /* number the the disk with central dir, used
+ for spaning ZIP, unsupported, always 0*/
+ uLong number_entry;
+ uLong number_entry_CD; /* total number of entries in
+ the central dir
+ (same than number_entry on nospan) */
+ uLong size_comment;
+
+ central_pos = ziplocal_SearchCentralDir(&ziinit.z_filefunc,ziinit.filestream);
+ if (central_pos==0)
+ err=ZIP_ERRNO;
+
+ if (ZSEEK(ziinit.z_filefunc, ziinit.filestream,
+ central_pos,ZLIB_FILEFUNC_SEEK_SET)!=0)
+ err=ZIP_ERRNO;
+
+ /* the signature, already checked */
+ if (ziplocal_getLong(&ziinit.z_filefunc, ziinit.filestream,&uL)!=ZIP_OK)
+ err=ZIP_ERRNO;
+
+ /* number of this disk */
+ if (ziplocal_getShort(&ziinit.z_filefunc, ziinit.filestream,&number_disk)!=ZIP_OK)
+ err=ZIP_ERRNO;
+
+ /* number of the disk with the start of the central directory */
+ if (ziplocal_getShort(&ziinit.z_filefunc, ziinit.filestream,&number_disk_with_CD)!=ZIP_OK)
+ err=ZIP_ERRNO;
+
+ /* total number of entries in the central dir on this disk */
+ if (ziplocal_getShort(&ziinit.z_filefunc, ziinit.filestream,&number_entry)!=ZIP_OK)
+ err=ZIP_ERRNO;
+
+ /* total number of entries in the central dir */
+ if (ziplocal_getShort(&ziinit.z_filefunc, ziinit.filestream,&number_entry_CD)!=ZIP_OK)
+ err=ZIP_ERRNO;
+
+ if ((number_entry_CD!=number_entry) ||
+ (number_disk_with_CD!=0) ||
+ (number_disk!=0))
+ err=ZIP_BADZIPFILE;
+
+ /* size of the central directory */
+ if (ziplocal_getLong(&ziinit.z_filefunc, ziinit.filestream,&size_central_dir)!=ZIP_OK)
+ err=ZIP_ERRNO;
+
+ /* offset of start of central directory with respect to the
+ starting disk number */
+ if (ziplocal_getLong(&ziinit.z_filefunc, ziinit.filestream,&offset_central_dir)!=ZIP_OK)
+ err=ZIP_ERRNO;
+
+ /* zipfile global comment length */
+ if (ziplocal_getShort(&ziinit.z_filefunc, ziinit.filestream,&size_comment)!=ZIP_OK)
+ err=ZIP_ERRNO;
+
+ if ((central_pos<offset_central_dir+size_central_dir) &&
+ (err==ZIP_OK))
+ err=ZIP_BADZIPFILE;
+
+ if (err!=ZIP_OK)
+ {
+ ZCLOSE(ziinit.z_filefunc, ziinit.filestream);
+ return NULL;
+ }
+
+ if (size_comment>0)
+ {
+ ziinit.globalcomment = ALLOC(size_comment+1);
+ if (ziinit.globalcomment)
+ {
+ size_comment = ZREAD(ziinit.z_filefunc, ziinit.filestream,ziinit.globalcomment,size_comment);
+ ziinit.globalcomment[size_comment]=0;
+ }
+ }
+
+ byte_before_the_zipfile = central_pos -
+ (offset_central_dir+size_central_dir);
+ ziinit.add_position_when_writting_offset = byte_before_the_zipfile;
+
+ {
+ uLong size_central_dir_to_read = size_central_dir;
+ size_t buf_size = SIZEDATA_INDATABLOCK;
+ void* buf_read = (void*)ALLOC(buf_size);
+ if (ZSEEK(ziinit.z_filefunc, ziinit.filestream,
+ offset_central_dir + byte_before_the_zipfile,
+ ZLIB_FILEFUNC_SEEK_SET) != 0)
+ err=ZIP_ERRNO;
+
+ while ((size_central_dir_to_read>0) && (err==ZIP_OK))
+ {
+ uLong read_this = SIZEDATA_INDATABLOCK;
+ if (read_this > size_central_dir_to_read)
+ read_this = size_central_dir_to_read;
+ if (ZREAD(ziinit.z_filefunc, ziinit.filestream,buf_read,read_this) != read_this)
+ err=ZIP_ERRNO;
+
+ if (err==ZIP_OK)
+ err = add_data_in_datablock(&ziinit.central_dir,buf_read,
+ (uLong)read_this);
+ size_central_dir_to_read-=read_this;
+ }
+ TRYFREE(buf_read);
+ }
+ ziinit.begin_pos = byte_before_the_zipfile;
+ ziinit.number_entry = number_entry_CD;
+
+ if (ZSEEK(ziinit.z_filefunc, ziinit.filestream,
+ offset_central_dir+byte_before_the_zipfile,ZLIB_FILEFUNC_SEEK_SET)!=0)
+ err=ZIP_ERRNO;
+ }
+
+ if (globalcomment)
+ {
+ *globalcomment = ziinit.globalcomment;
+ }
+# endif /* !NO_ADDFILEINEXISTINGZIP*/
+
+ if (err != ZIP_OK)
+ {
+# ifndef NO_ADDFILEINEXISTINGZIP
+ TRYFREE(ziinit.globalcomment);
+# endif /* !NO_ADDFILEINEXISTINGZIP*/
+ TRYFREE(zi);
+ return NULL;
+ }
+ else
+ {
+ *zi = ziinit;
+ return (zipFile)zi;
+ }
+}
+
+extern zipFile ZEXPORT zipOpen (pathname, append)
+ const char *pathname;
+ int append;
+{
+ return zipOpen2(pathname,append,NULL,NULL);
+}
+
+extern int ZEXPORT zipOpenNewFileInZip3 (file, filename, zipfi,
+ extrafield_local, size_extrafield_local,
+ extrafield_global, size_extrafield_global,
+ comment, method, level, raw,
+ windowBits, memLevel, strategy,
+ password, crcForCrypting)
+ zipFile file;
+ const char* filename;
+ const zip_fileinfo* zipfi;
+ const void* extrafield_local;
+ uInt size_extrafield_local;
+ const void* extrafield_global;
+ uInt size_extrafield_global;
+ const char* comment;
+ int method;
+ int level;
+ int raw;
+ int windowBits;
+ int memLevel;
+ int strategy;
+ const char* password;
+ uLong crcForCrypting;
+{
+ zip_internal* zi;
+ uInt size_filename;
+ uInt size_comment;
+ uInt i;
+ int err = ZIP_OK;
+
+# ifdef NOCRYPT
+ if (password != NULL)
+ return ZIP_PARAMERROR;
+# endif
+
+ if (file == NULL)
+ return ZIP_PARAMERROR;
+ if ((method!=0) && (method!=Z_DEFLATED))
+ return ZIP_PARAMERROR;
+
+ zi = (zip_internal*)file;
+
+ if (zi->in_opened_file_inzip == 1)
+ {
+ err = zipCloseFileInZip (file);
+ if (err != ZIP_OK)
+ return err;
+ }
+
+
+ if (filename==NULL)
+ filename="-";
+
+ if (comment==NULL)
+ size_comment = 0;
+ else
+ size_comment = (uInt)strlen(comment);
+
+ size_filename = (uInt)strlen(filename);
+
+ if (zipfi == NULL)
+ zi->ci.dosDate = 0;
+ else
+ {
+ if (zipfi->dosDate != 0)
+ zi->ci.dosDate = zipfi->dosDate;
+ else zi->ci.dosDate = ziplocal_TmzDateToDosDate(&zipfi->tmz_date,zipfi->dosDate);
+ }
+
+ zi->ci.flag = 0;
+ if ((level==8) || (level==9))
+ zi->ci.flag |= 2;
+ if ((level==2))
+ zi->ci.flag |= 4;
+ if ((level==1))
+ zi->ci.flag |= 6;
+ if (password != NULL)
+ zi->ci.flag |= 1;
+
+ zi->ci.crc32 = 0;
+ zi->ci.method = method;
+ zi->ci.encrypt = 0;
+ zi->ci.stream_initialised = 0;
+ zi->ci.pos_in_buffered_data = 0;
+ zi->ci.raw = raw;
+ zi->ci.pos_local_header = ZTELL(zi->z_filefunc,zi->filestream) ;
+ zi->ci.size_centralheader = SIZECENTRALHEADER + size_filename +
+ size_extrafield_global + size_comment;
+ zi->ci.central_header = (char*)ALLOC((uInt)zi->ci.size_centralheader);
+
+ ziplocal_putValue_inmemory(zi->ci.central_header,(uLong)CENTRALHEADERMAGIC,4);
+ /* version info */
+ ziplocal_putValue_inmemory(zi->ci.central_header+4,(uLong)VERSIONMADEBY,2);
+ ziplocal_putValue_inmemory(zi->ci.central_header+6,(uLong)20,2);
+ ziplocal_putValue_inmemory(zi->ci.central_header+8,(uLong)zi->ci.flag,2);
+ ziplocal_putValue_inmemory(zi->ci.central_header+10,(uLong)zi->ci.method,2);
+ ziplocal_putValue_inmemory(zi->ci.central_header+12,(uLong)zi->ci.dosDate,4);
+ ziplocal_putValue_inmemory(zi->ci.central_header+16,(uLong)0,4); /*crc*/
+ ziplocal_putValue_inmemory(zi->ci.central_header+20,(uLong)0,4); /*compr size*/
+ ziplocal_putValue_inmemory(zi->ci.central_header+24,(uLong)0,4); /*uncompr size*/
+ ziplocal_putValue_inmemory(zi->ci.central_header+28,(uLong)size_filename,2);
+ ziplocal_putValue_inmemory(zi->ci.central_header+30,(uLong)size_extrafield_global,2);
+ ziplocal_putValue_inmemory(zi->ci.central_header+32,(uLong)size_comment,2);
+ ziplocal_putValue_inmemory(zi->ci.central_header+34,(uLong)0,2); /*disk nm start*/
+
+ if (zipfi==NULL)
+ ziplocal_putValue_inmemory(zi->ci.central_header+36,(uLong)0,2);
+ else
+ ziplocal_putValue_inmemory(zi->ci.central_header+36,(uLong)zipfi->internal_fa,2);
+
+ if (zipfi==NULL)
+ ziplocal_putValue_inmemory(zi->ci.central_header+38,(uLong)0,4);
+ else
+ ziplocal_putValue_inmemory(zi->ci.central_header+38,(uLong)zipfi->external_fa,4);
+
+ ziplocal_putValue_inmemory(zi->ci.central_header+42,(uLong)zi->ci.pos_local_header- zi->add_position_when_writting_offset,4);
+
+ for (i=0;i<size_filename;i++)
+ *(zi->ci.central_header+SIZECENTRALHEADER+i) = *(filename+i);
+
+ for (i=0;i<size_extrafield_global;i++)
+ *(zi->ci.central_header+SIZECENTRALHEADER+size_filename+i) =
+ *(((const char*)extrafield_global)+i);
+
+ for (i=0;i<size_comment;i++)
+ *(zi->ci.central_header+SIZECENTRALHEADER+size_filename+
+ size_extrafield_global+i) = *(comment+i);
+ if (zi->ci.central_header == NULL)
+ return ZIP_INTERNALERROR;
+
+ /* write the local header */
+ err = ziplocal_putValue(&zi->z_filefunc,zi->filestream,(uLong)LOCALHEADERMAGIC,4);
+
+ if (err==ZIP_OK)
+ err = ziplocal_putValue(&zi->z_filefunc,zi->filestream,(uLong)20,2);/* version needed to extract */
+ if (err==ZIP_OK)
+ err = ziplocal_putValue(&zi->z_filefunc,zi->filestream,(uLong)zi->ci.flag,2);
+
+ if (err==ZIP_OK)
+ err = ziplocal_putValue(&zi->z_filefunc,zi->filestream,(uLong)zi->ci.method,2);
+
+ if (err==ZIP_OK)
+ err = ziplocal_putValue(&zi->z_filefunc,zi->filestream,(uLong)zi->ci.dosDate,4);
+
+ if (err==ZIP_OK)
+ err = ziplocal_putValue(&zi->z_filefunc,zi->filestream,(uLong)0,4); /* crc 32, unknown */
+ if (err==ZIP_OK)
+ err = ziplocal_putValue(&zi->z_filefunc,zi->filestream,(uLong)0,4); /* compressed size, unknown */
+ if (err==ZIP_OK)
+ err = ziplocal_putValue(&zi->z_filefunc,zi->filestream,(uLong)0,4); /* uncompressed size, unknown */
+
+ if (err==ZIP_OK)
+ err = ziplocal_putValue(&zi->z_filefunc,zi->filestream,(uLong)size_filename,2);
+
+ if (err==ZIP_OK)
+ err = ziplocal_putValue(&zi->z_filefunc,zi->filestream,(uLong)size_extrafield_local,2);
+
+ if ((err==ZIP_OK) && (size_filename>0))
+ if (ZWRITE(zi->z_filefunc,zi->filestream,filename,size_filename)!=size_filename)
+ err = ZIP_ERRNO;
+
+ if ((err==ZIP_OK) && (size_extrafield_local>0))
+ if (ZWRITE(zi->z_filefunc,zi->filestream,extrafield_local,size_extrafield_local)
+ !=size_extrafield_local)
+ err = ZIP_ERRNO;
+
+ zi->ci.stream.avail_in = (uInt)0;
+ zi->ci.stream.avail_out = (uInt)Z_BUFSIZE;
+ zi->ci.stream.next_out = zi->ci.buffered_data;
+ zi->ci.stream.total_in = 0;
+ zi->ci.stream.total_out = 0;
+
+ if ((err==ZIP_OK) && (zi->ci.method == Z_DEFLATED) && (!zi->ci.raw))
+ {
+ zi->ci.stream.zalloc = (alloc_func)0;
+ zi->ci.stream.zfree = (free_func)0;
+ zi->ci.stream.opaque = (voidpf)0;
+
+ if (windowBits>0)
+ windowBits = -windowBits;
+
+ err = deflateInit2(&zi->ci.stream, level,
+ Z_DEFLATED, windowBits, memLevel, strategy);
+
+ if (err==Z_OK)
+ zi->ci.stream_initialised = 1;
+ }
+# ifndef NOCRYPT
+ zi->ci.crypt_header_size = 0;
+ if ((err==Z_OK) && (password != NULL))
+ {
+ unsigned char bufHead[RAND_HEAD_LEN];
+ unsigned int sizeHead;
+ zi->ci.encrypt = 1;
+ zi->ci.pcrc_32_tab = get_crc_table();
+ /*init_keys(password,zi->ci.keys,zi->ci.pcrc_32_tab);*/
+
+ sizeHead=crypthead(password,bufHead,RAND_HEAD_LEN,zi->ci.keys,zi->ci.pcrc_32_tab,crcForCrypting);
+ zi->ci.crypt_header_size = sizeHead;
+
+ if (ZWRITE(zi->z_filefunc,zi->filestream,bufHead,sizeHead) != sizeHead)
+ err = ZIP_ERRNO;
+ }
+# endif
+
+ if (err==Z_OK)
+ zi->in_opened_file_inzip = 1;
+ return err;
+}
+
+extern int ZEXPORT zipOpenNewFileInZip2(file, filename, zipfi,
+ extrafield_local, size_extrafield_local,
+ extrafield_global, size_extrafield_global,
+ comment, method, level, raw)
+ zipFile file;
+ const char* filename;
+ const zip_fileinfo* zipfi;
+ const void* extrafield_local;
+ uInt size_extrafield_local;
+ const void* extrafield_global;
+ uInt size_extrafield_global;
+ const char* comment;
+ int method;
+ int level;
+ int raw;
+{
+ return zipOpenNewFileInZip3 (file, filename, zipfi,
+ extrafield_local, size_extrafield_local,
+ extrafield_global, size_extrafield_global,
+ comment, method, level, raw,
+ -MAX_WBITS, DEF_MEM_LEVEL, Z_DEFAULT_STRATEGY,
+ NULL, 0);
+}
+
+extern int ZEXPORT zipOpenNewFileInZip (file, filename, zipfi,
+ extrafield_local, size_extrafield_local,
+ extrafield_global, size_extrafield_global,
+ comment, method, level)
+ zipFile file;
+ const char* filename;
+ const zip_fileinfo* zipfi;
+ const void* extrafield_local;
+ uInt size_extrafield_local;
+ const void* extrafield_global;
+ uInt size_extrafield_global;
+ const char* comment;
+ int method;
+ int level;
+{
+ return zipOpenNewFileInZip2 (file, filename, zipfi,
+ extrafield_local, size_extrafield_local,
+ extrafield_global, size_extrafield_global,
+ comment, method, level, 0);
+}
+
+local int zipFlushWriteBuffer(zi)
+ zip_internal* zi;
+{
+ int err=ZIP_OK;
+
+ if (zi->ci.encrypt != 0)
+ {
+#ifndef NOCRYPT
+ uInt i;
+ int t;
+ for (i=0;i<zi->ci.pos_in_buffered_data;i++)
+ zi->ci.buffered_data[i] = zencode(zi->ci.keys, zi->ci.pcrc_32_tab,
+ zi->ci.buffered_data[i],t);
+#endif
+ }
+ if (ZWRITE(zi->z_filefunc,zi->filestream,zi->ci.buffered_data,zi->ci.pos_in_buffered_data)
+ !=zi->ci.pos_in_buffered_data)
+ err = ZIP_ERRNO;
+ zi->ci.pos_in_buffered_data = 0;
+ return err;
+}
+
+extern int ZEXPORT zipWriteInFileInZip (file, buf, len)
+ zipFile file;
+ const void* buf;
+ unsigned len;
+{
+ zip_internal* zi;
+ int err=ZIP_OK;
+
+ if (file == NULL)
+ return ZIP_PARAMERROR;
+ zi = (zip_internal*)file;
+
+ if (zi->in_opened_file_inzip == 0)
+ return ZIP_PARAMERROR;
+
+ zi->ci.stream.next_in = (void*)buf;
+ zi->ci.stream.avail_in = len;
+ zi->ci.crc32 = crc32(zi->ci.crc32,buf,len);
+
+ while ((err==ZIP_OK) && (zi->ci.stream.avail_in>0))
+ {
+ if (zi->ci.stream.avail_out == 0)
+ {
+ if (zipFlushWriteBuffer(zi) == ZIP_ERRNO)
+ err = ZIP_ERRNO;
+ zi->ci.stream.avail_out = (uInt)Z_BUFSIZE;
+ zi->ci.stream.next_out = zi->ci.buffered_data;
+ }
+
+
+ if(err != ZIP_OK)
+ break;
+
+ if ((zi->ci.method == Z_DEFLATED) && (!zi->ci.raw))
+ {
+ uLong uTotalOutBefore = zi->ci.stream.total_out;
+ err=deflate(&zi->ci.stream, Z_NO_FLUSH);
+ zi->ci.pos_in_buffered_data += (uInt)(zi->ci.stream.total_out - uTotalOutBefore) ;
+
+ }
+ else
+ {
+ uInt copy_this,i;
+ if (zi->ci.stream.avail_in < zi->ci.stream.avail_out)
+ copy_this = zi->ci.stream.avail_in;
+ else
+ copy_this = zi->ci.stream.avail_out;
+ for (i=0;i<copy_this;i++)
+ *(((char*)zi->ci.stream.next_out)+i) =
+ *(((const char*)zi->ci.stream.next_in)+i);
+ {
+ zi->ci.stream.avail_in -= copy_this;
+ zi->ci.stream.avail_out-= copy_this;
+ zi->ci.stream.next_in+= copy_this;
+ zi->ci.stream.next_out+= copy_this;
+ zi->ci.stream.total_in+= copy_this;
+ zi->ci.stream.total_out+= copy_this;
+ zi->ci.pos_in_buffered_data += copy_this;
+ }
+ }
+ }
+
+ return err;
+}
+
+extern int ZEXPORT zipCloseFileInZipRaw (file, uncompressed_size, crc32)
+ zipFile file;
+ uLong uncompressed_size;
+ uLong crc32;
+{
+ zip_internal* zi;
+ uLong compressed_size;
+ int err=ZIP_OK;
+
+ if (file == NULL)
+ return ZIP_PARAMERROR;
+ zi = (zip_internal*)file;
+
+ if (zi->in_opened_file_inzip == 0)
+ return ZIP_PARAMERROR;
+ zi->ci.stream.avail_in = 0;
+
+ if ((zi->ci.method == Z_DEFLATED) && (!zi->ci.raw))
+ while (err==ZIP_OK)
+ {
+ uLong uTotalOutBefore;
+ if (zi->ci.stream.avail_out == 0)
+ {
+ if (zipFlushWriteBuffer(zi) == ZIP_ERRNO)
+ err = ZIP_ERRNO;
+ zi->ci.stream.avail_out = (uInt)Z_BUFSIZE;
+ zi->ci.stream.next_out = zi->ci.buffered_data;
+ }
+ uTotalOutBefore = zi->ci.stream.total_out;
+ err=deflate(&zi->ci.stream, Z_FINISH);
+ zi->ci.pos_in_buffered_data += (uInt)(zi->ci.stream.total_out - uTotalOutBefore) ;
+ }
+
+ if (err==Z_STREAM_END)
+ err=ZIP_OK; /* this is normal */
+
+ if ((zi->ci.pos_in_buffered_data>0) && (err==ZIP_OK))
+ if (zipFlushWriteBuffer(zi)==ZIP_ERRNO)
+ err = ZIP_ERRNO;
+
+ if ((zi->ci.method == Z_DEFLATED) && (!zi->ci.raw))
+ {
+ err=deflateEnd(&zi->ci.stream);
+ zi->ci.stream_initialised = 0;
+ }
+
+ if (!zi->ci.raw)
+ {
+ crc32 = (uLong)zi->ci.crc32;
+ uncompressed_size = (uLong)zi->ci.stream.total_in;
+ }
+ compressed_size = (uLong)zi->ci.stream.total_out;
+# ifndef NOCRYPT
+ compressed_size += zi->ci.crypt_header_size;
+# endif
+
+ ziplocal_putValue_inmemory(zi->ci.central_header+16,crc32,4); /*crc*/
+ ziplocal_putValue_inmemory(zi->ci.central_header+20,
+ compressed_size,4); /*compr size*/
+ if (zi->ci.stream.data_type == Z_ASCII)
+ ziplocal_putValue_inmemory(zi->ci.central_header+36,(uLong)Z_ASCII,2);
+ ziplocal_putValue_inmemory(zi->ci.central_header+24,
+ uncompressed_size,4); /*uncompr size*/
+
+ if (err==ZIP_OK)
+ err = add_data_in_datablock(&zi->central_dir,zi->ci.central_header,
+ (uLong)zi->ci.size_centralheader);
+ free(zi->ci.central_header);
+
+ if (err==ZIP_OK)
+ {
+ long cur_pos_inzip = ZTELL(zi->z_filefunc,zi->filestream);
+ if (ZSEEK(zi->z_filefunc,zi->filestream,
+ zi->ci.pos_local_header + 14,ZLIB_FILEFUNC_SEEK_SET)!=0)
+ err = ZIP_ERRNO;
+
+ if (err==ZIP_OK)
+ err = ziplocal_putValue(&zi->z_filefunc,zi->filestream,crc32,4); /* crc 32, unknown */
+
+ if (err==ZIP_OK) /* compressed size, unknown */
+ err = ziplocal_putValue(&zi->z_filefunc,zi->filestream,compressed_size,4);
+
+ if (err==ZIP_OK) /* uncompressed size, unknown */
+ err = ziplocal_putValue(&zi->z_filefunc,zi->filestream,uncompressed_size,4);
+
+ if (ZSEEK(zi->z_filefunc,zi->filestream,
+ cur_pos_inzip,ZLIB_FILEFUNC_SEEK_SET)!=0)
+ err = ZIP_ERRNO;
+ }
+
+ zi->number_entry ++;
+ zi->in_opened_file_inzip = 0;
+
+ return err;
+}
+
+extern int ZEXPORT zipCloseFileInZip (file)
+ zipFile file;
+{
+ return zipCloseFileInZipRaw (file,0,0);
+}
+
+extern int ZEXPORT zipClose (file, global_comment)
+ zipFile file;
+ const char* global_comment;
+{
+ zip_internal* zi;
+ int err = 0;
+ uLong size_centraldir = 0;
+ uLong centraldir_pos_inzip;
+ uInt size_global_comment;
+ if (file == NULL)
+ return ZIP_PARAMERROR;
+ zi = (zip_internal*)file;
+
+ if (zi->in_opened_file_inzip == 1)
+ {
+ err = zipCloseFileInZip (file);
+ }
+
+#ifndef NO_ADDFILEINEXISTINGZIP
+ if (global_comment==NULL)
+ global_comment = zi->globalcomment;
+#endif
+ if (global_comment==NULL)
+ size_global_comment = 0;
+ else
+ size_global_comment = (uInt)strlen(global_comment);
+
+ centraldir_pos_inzip = ZTELL(zi->z_filefunc,zi->filestream);
+ if (err==ZIP_OK)
+ {
+ linkedlist_datablock_internal* ldi = zi->central_dir.first_block ;
+ while (ldi!=NULL)
+ {
+ if ((err==ZIP_OK) && (ldi->filled_in_this_block>0))
+ if (ZWRITE(zi->z_filefunc,zi->filestream,
+ ldi->data,ldi->filled_in_this_block)
+ !=ldi->filled_in_this_block )
+ err = ZIP_ERRNO;
+
+ size_centraldir += ldi->filled_in_this_block;
+ ldi = ldi->next_datablock;
+ }
+ }
+ free_datablock(zi->central_dir.first_block);
+
+ if (err==ZIP_OK) /* Magic End */
+ err = ziplocal_putValue(&zi->z_filefunc,zi->filestream,(uLong)ENDHEADERMAGIC,4);
+
+ if (err==ZIP_OK) /* number of this disk */
+ err = ziplocal_putValue(&zi->z_filefunc,zi->filestream,(uLong)0,2);
+
+ if (err==ZIP_OK) /* number of the disk with the start of the central directory */
+ err = ziplocal_putValue(&zi->z_filefunc,zi->filestream,(uLong)0,2);
+
+ if (err==ZIP_OK) /* total number of entries in the central dir on this disk */
+ err = ziplocal_putValue(&zi->z_filefunc,zi->filestream,(uLong)zi->number_entry,2);
+
+ if (err==ZIP_OK) /* total number of entries in the central dir */
+ err = ziplocal_putValue(&zi->z_filefunc,zi->filestream,(uLong)zi->number_entry,2);
+
+ if (err==ZIP_OK) /* size of the central directory */
+ err = ziplocal_putValue(&zi->z_filefunc,zi->filestream,(uLong)size_centraldir,4);
+
+ if (err==ZIP_OK) /* offset of start of central directory with respect to the
+ starting disk number */
+ err = ziplocal_putValue(&zi->z_filefunc,zi->filestream,
+ (uLong)(centraldir_pos_inzip - zi->add_position_when_writting_offset),4);
+
+ if (err==ZIP_OK) /* zipfile comment length */
+ err = ziplocal_putValue(&zi->z_filefunc,zi->filestream,(uLong)size_global_comment,2);
+
+ if ((err==ZIP_OK) && (size_global_comment>0))
+ if (ZWRITE(zi->z_filefunc,zi->filestream,
+ global_comment,size_global_comment) != size_global_comment)
+ err = ZIP_ERRNO;
+
+ if (ZCLOSE(zi->z_filefunc,zi->filestream) != 0)
+ if (err == ZIP_OK)
+ err = ZIP_ERRNO;
+
+#ifndef NO_ADDFILEINEXISTINGZIP
+ TRYFREE(zi->globalcomment);
+#endif
+ TRYFREE(zi);
+
+ return err;
+}