aboutsummaryrefslogtreecommitdiff
path: root/dep
diff options
context:
space:
mode:
Diffstat (limited to 'dep')
-rw-r--r--dep/include/bzip2/bzlib.h282
-rw-r--r--dep/include/g3dlite/G3D/AABox.h174
-rw-r--r--dep/include/g3dlite/G3D/Any.h570
-rw-r--r--dep/include/g3dlite/G3D/AnyVal.h512
-rw-r--r--dep/include/g3dlite/G3D/AreaMemoryManager.h93
-rw-r--r--dep/include/g3dlite/G3D/Array.h395
-rw-r--r--dep/include/g3dlite/G3D/AtomicInt32.h164
-rw-r--r--dep/include/g3dlite/G3D/BinaryFormat.h140
-rw-r--r--dep/include/g3dlite/G3D/BinaryInput.h441
-rw-r--r--dep/include/g3dlite/G3D/BinaryOutput.h421
-rw-r--r--dep/include/g3dlite/G3D/BoundsTrait.h20
-rw-r--r--dep/include/g3dlite/G3D/Box.h104
-rw-r--r--dep/include/g3dlite/G3D/Box2D.h121
-rw-r--r--dep/include/g3dlite/G3D/BumpMapPreprocess.h61
-rw-r--r--dep/include/g3dlite/G3D/Capsule.h90
-rw-r--r--dep/include/g3dlite/G3D/CollisionDetection.h1507
-rw-r--r--dep/include/g3dlite/G3D/Color1.h144
-rw-r--r--dep/include/g3dlite/G3D/Color1uint8.h91
-rw-r--r--dep/include/g3dlite/G3D/Color3.h432
-rw-r--r--dep/include/g3dlite/G3D/Color3uint8.h110
-rw-r--r--dep/include/g3dlite/G3D/Color4.h338
-rw-r--r--dep/include/g3dlite/G3D/Color4uint8.h115
-rw-r--r--dep/include/g3dlite/G3D/Cone.h68
-rw-r--r--dep/include/g3dlite/G3D/ConvexPolyhedron.h180
-rw-r--r--dep/include/g3dlite/G3D/CoordinateFrame.h146
-rw-r--r--dep/include/g3dlite/G3D/Crypto.h58
-rw-r--r--dep/include/g3dlite/G3D/Cylinder.h92
-rw-r--r--dep/include/g3dlite/G3D/EqualsTrait.h26
-rw-r--r--dep/include/g3dlite/G3D/G3D.h162
-rw-r--r--dep/include/g3dlite/G3D/G3DAll.h26
-rw-r--r--dep/include/g3dlite/G3D/G3DGameUnits.h42
-rw-r--r--dep/include/g3dlite/G3D/GCamera.h319
-rw-r--r--dep/include/g3dlite/G3D/GImage.h607
-rw-r--r--dep/include/g3dlite/G3D/GLight.h106
-rw-r--r--dep/include/g3dlite/G3D/GMutex.h123
-rw-r--r--dep/include/g3dlite/G3D/GThread.h121
-rw-r--r--dep/include/g3dlite/G3D/GUniqueID.h69
-rw-r--r--dep/include/g3dlite/G3D/HashTrait.h92
-rw-r--r--dep/include/g3dlite/G3D/Image1.h81
-rw-r--r--dep/include/g3dlite/G3D/Image1uint8.h80
-rw-r--r--dep/include/g3dlite/G3D/Image3.h81
-rw-r--r--dep/include/g3dlite/G3D/Image3uint8.h85
-rw-r--r--dep/include/g3dlite/G3D/Image4.h86
-rw-r--r--dep/include/g3dlite/G3D/Image4uint8.h85
-rw-r--r--dep/include/g3dlite/G3D/ImageFormat.h419
-rw-r--r--dep/include/g3dlite/G3D/Intersect.h55
-rw-r--r--dep/include/g3dlite/G3D/KDTree.h1667
-rw-r--r--dep/include/g3dlite/G3D/Line.h30
-rw-r--r--dep/include/g3dlite/G3D/LineSegment.h115
-rw-r--r--dep/include/g3dlite/G3D/Log.h109
-rw-r--r--dep/include/g3dlite/G3D/Map2D.h667
-rw-r--r--dep/include/g3dlite/G3D/Matrix.h634
-rw-r--r--dep/include/g3dlite/G3D/Matrix2.h69
-rw-r--r--dep/include/g3dlite/G3D/Matrix3.h138
-rw-r--r--dep/include/g3dlite/G3D/Matrix4.h249
-rw-r--r--dep/include/g3dlite/G3D/MemoryManager.h93
-rw-r--r--dep/include/g3dlite/G3D/MeshAlg.h683
-rw-r--r--dep/include/g3dlite/G3D/MeshBuilder.h82
-rw-r--r--dep/include/g3dlite/G3D/NetAddress.h132
-rw-r--r--dep/include/g3dlite/G3D/NetworkDevice.h738
-rw-r--r--dep/include/g3dlite/G3D/ParseError.h59
-rw-r--r--dep/include/g3dlite/G3D/PhysicsFrame.h74
-rw-r--r--dep/include/g3dlite/G3D/Plane.h24
-rw-r--r--dep/include/g3dlite/G3D/PointHashGrid.h917
-rw-r--r--dep/include/g3dlite/G3D/PointKDTree.h1185
-rw-r--r--dep/include/g3dlite/G3D/Pointer.h292
-rw-r--r--dep/include/g3dlite/G3D/PositionTrait.h7
-rw-r--r--dep/include/g3dlite/G3D/PrecomputedRandom.h110
-rw-r--r--dep/include/g3dlite/G3D/Quat.h124
-rw-r--r--dep/include/g3dlite/G3D/Queue.h364
-rw-r--r--dep/include/g3dlite/G3D/Random.h139
-rw-r--r--dep/include/g3dlite/G3D/Ray.h172
-rw-r--r--dep/include/g3dlite/G3D/Rect2D.h417
-rw-r--r--dep/include/g3dlite/G3D/ReferenceCount.h570
-rw-r--r--dep/include/g3dlite/G3D/RegistryUtil.h48
-rw-r--r--dep/include/g3dlite/G3D/Set.h186
-rw-r--r--dep/include/g3dlite/G3D/SmallArray.h155
-rw-r--r--dep/include/g3dlite/G3D/Sphere.h97
-rw-r--r--dep/include/g3dlite/G3D/Spline.h367
-rw-r--r--dep/include/g3dlite/G3D/Stopwatch.h144
-rw-r--r--dep/include/g3dlite/G3D/System.h474
-rw-r--r--dep/include/g3dlite/G3D/Table.h789
-rw-r--r--dep/include/g3dlite/G3D/TextInput.h801
-rw-r--r--dep/include/g3dlite/G3D/TextOutput.h249
-rw-r--r--dep/include/g3dlite/G3D/ThreadSet.h87
-rw-r--r--dep/include/g3dlite/G3D/Triangle.h79
-rw-r--r--dep/include/g3dlite/G3D/UprightFrame.h83
-rw-r--r--dep/include/g3dlite/G3D/Vector2.h206
-rw-r--r--dep/include/g3dlite/G3D/Vector2int16.h82
-rw-r--r--dep/include/g3dlite/G3D/Vector3.h541
-rw-r--r--dep/include/g3dlite/G3D/Vector3int16.h101
-rw-r--r--dep/include/g3dlite/G3D/Vector3int32.h128
-rw-r--r--dep/include/g3dlite/G3D/Vector4.h231
-rw-r--r--dep/include/g3dlite/G3D/Vector4int8.h113
-rw-r--r--dep/include/g3dlite/G3D/WeakCache.h122
-rw-r--r--dep/include/g3dlite/G3D/Welder.h82
-rw-r--r--dep/include/g3dlite/G3D/WrapMode.h93
-rw-r--r--dep/include/g3dlite/G3D/constants.h129
-rw-r--r--dep/include/g3dlite/G3D/debug.h66
-rw-r--r--dep/include/g3dlite/G3D/debugAssert.h233
-rw-r--r--dep/include/g3dlite/G3D/debugPrintf.h62
-rw-r--r--dep/include/g3dlite/G3D/enumclass.h147
-rw-r--r--dep/include/g3dlite/G3D/fileutils.h254
-rw-r--r--dep/include/g3dlite/G3D/filter.h29
-rw-r--r--dep/include/g3dlite/G3D/format.h25
-rw-r--r--dep/include/g3dlite/G3D/g3dfnmatch.h83
-rw-r--r--dep/include/g3dlite/G3D/g3dmath.h633
-rw-r--r--dep/include/g3dlite/G3D/platform.h344
-rw-r--r--dep/include/g3dlite/G3D/prompt.h67
-rw-r--r--dep/include/g3dlite/G3D/serialize.h30
-rw-r--r--dep/include/g3dlite/G3D/splinefunc.h118
-rw-r--r--dep/include/g3dlite/G3D/stringutils.h33
-rw-r--r--dep/include/g3dlite/G3D/uint128.h51
-rw-r--r--dep/include/g3dlite/G3D/units.h126
-rw-r--r--dep/include/g3dlite/G3D/vectorMath.h235
-rw-r--r--dep/src/bzip2/blocksort.c1094
-rw-r--r--dep/src/bzip2/bzlib.c1572
-rw-r--r--dep/src/bzip2/bzlib.h282
-rw-r--r--dep/src/bzip2/bzlib_private.h509
-rw-r--r--dep/src/bzip2/compress.c672
-rw-r--r--dep/src/bzip2/crctable.c104
-rw-r--r--dep/src/bzip2/decompress.c626
-rw-r--r--dep/src/bzip2/huffman.c205
-rw-r--r--dep/src/bzip2/randtable.c84
-rw-r--r--dep/src/g3dlite/AABox.cpp308
-rw-r--r--dep/src/g3dlite/Any.cpp1237
-rw-r--r--dep/src/g3dlite/AnyVal.cpp1379
-rw-r--r--dep/src/g3dlite/AreaMemoryManager.cpp87
-rw-r--r--dep/src/g3dlite/BinaryFormat.cpp81
-rw-r--r--dep/src/g3dlite/BinaryInput.cpp568
-rw-r--r--dep/src/g3dlite/BinaryOutput.cpp522
-rw-r--r--dep/src/g3dlite/Box.cpp213
-rw-r--r--dep/src/g3dlite/Box2D.cpp113
-rw-r--r--dep/src/g3dlite/BumpMapPreprocess.cpp43
-rw-r--r--dep/src/g3dlite/CMakeLists.txt49
-rw-r--r--dep/src/g3dlite/Capsule.cpp179
-rw-r--r--dep/src/g3dlite/CollisionDetection.cpp2455
-rw-r--r--dep/src/g3dlite/Color1.cpp58
-rw-r--r--dep/src/g3dlite/Color1uint8.cpp38
-rw-r--r--dep/src/g3dlite/Color3.cpp384
-rw-r--r--dep/src/g3dlite/Color3uint8.cpp45
-rw-r--r--dep/src/g3dlite/Color4.cpp192
-rw-r--r--dep/src/g3dlite/Color4uint8.cpp47
-rw-r--r--dep/src/g3dlite/Cone.cpp79
-rw-r--r--dep/src/g3dlite/ConvexPolyhedron.cpp457
-rw-r--r--dep/src/g3dlite/CoordinateFrame.cpp436
-rw-r--r--dep/src/g3dlite/Crypto.cpp144
-rw-r--r--dep/src/g3dlite/Crypto_md5.cpp471
-rw-r--r--dep/src/g3dlite/Cylinder.cpp176
-rw-r--r--dep/src/g3dlite/GCamera.cpp502
-rw-r--r--dep/src/g3dlite/GImage.cpp1166
-rw-r--r--dep/src/g3dlite/GImage_bayer.cpp298
-rw-r--r--dep/src/g3dlite/GImage_bmp.cpp717
-rw-r--r--dep/src/g3dlite/GImage_jpeg.cpp446
-rw-r--r--dep/src/g3dlite/GImage_png.cpp266
-rw-r--r--dep/src/g3dlite/GImage_ppm.cpp217
-rw-r--r--dep/src/g3dlite/GImage_tga.cpp193
-rw-r--r--dep/src/g3dlite/GLight.cpp267
-rw-r--r--dep/src/g3dlite/GThread.cpp229
-rw-r--r--dep/src/g3dlite/GUniqueID.cpp78
-rw-r--r--dep/src/g3dlite/Image1.cpp224
-rw-r--r--dep/src/g3dlite/Image1uint8.cpp212
-rw-r--r--dep/src/g3dlite/Image3.cpp225
-rw-r--r--dep/src/g3dlite/Image3uint8.cpp225
-rw-r--r--dep/src/g3dlite/Image4.cpp226
-rw-r--r--dep/src/g3dlite/Image4uint8.cpp222
-rw-r--r--dep/src/g3dlite/ImageFormat.cpp567
-rw-r--r--dep/src/g3dlite/ImageFormat_convert.cpp1307
-rw-r--r--dep/src/g3dlite/Intersect.cpp844
-rw-r--r--dep/src/g3dlite/Line.cpp89
-rw-r--r--dep/src/g3dlite/LineSegment.cpp236
-rw-r--r--dep/src/g3dlite/Log.cpp146
-rw-r--r--dep/src/g3dlite/Makefile.am69
-rw-r--r--dep/src/g3dlite/Matrix.cpp1802
-rw-r--r--dep/src/g3dlite/Matrix3.cpp353
-rw-r--r--dep/src/g3dlite/Matrix4.cpp523
-rw-r--r--dep/src/g3dlite/MemoryManager.cpp91
-rw-r--r--dep/src/g3dlite/MeshAlg.cpp637
-rw-r--r--dep/src/g3dlite/MeshAlgAdjacency.cpp739
-rw-r--r--dep/src/g3dlite/MeshAlgWeld.cpp213
-rw-r--r--dep/src/g3dlite/MeshBuilder.cpp113
-rw-r--r--dep/src/g3dlite/NetAddress.cpp164
-rw-r--r--dep/src/g3dlite/NetworkDevice.cpp1362
-rw-r--r--dep/src/g3dlite/PhysicsFrame.cpp77
-rw-r--r--dep/src/g3dlite/Plane.cpp42
-rw-r--r--dep/src/g3dlite/PrecomputedRandom.cpp125
-rw-r--r--dep/src/g3dlite/Quat.cpp583
-rw-r--r--dep/src/g3dlite/Random.cpp212
-rw-r--r--dep/src/g3dlite/Ray.cpp218
-rw-r--r--dep/src/g3dlite/Rect2D.cpp41
-rw-r--r--dep/src/g3dlite/ReferenceCount.cpp61
-rw-r--r--dep/src/g3dlite/RegistryUtil.cpp290
-rw-r--r--dep/src/g3dlite/Sphere.cpp223
-rw-r--r--dep/src/g3dlite/SplineBase.cpp162
-rw-r--r--dep/src/g3dlite/Stopwatch.cpp119
-rw-r--r--dep/src/g3dlite/System.cpp1329
-rw-r--r--dep/src/g3dlite/TextInput.cpp1136
-rw-r--r--dep/src/g3dlite/TextOutput.cpp452
-rw-r--r--dep/src/g3dlite/ThreadSet.cpp166
-rw-r--r--dep/src/g3dlite/Triangle.cpp110
-rw-r--r--dep/src/g3dlite/UprightFrame.cpp132
-rw-r--r--dep/src/g3dlite/Vector2.cpp224
-rw-r--r--dep/src/g3dlite/Vector2int16.cpp47
-rw-r--r--dep/src/g3dlite/Vector3.cpp238
-rw-r--r--dep/src/g3dlite/Vector3int16.cpp49
-rw-r--r--dep/src/g3dlite/Vector3int32.cpp57
-rw-r--r--dep/src/g3dlite/Vector4.cpp111
-rw-r--r--dep/src/g3dlite/Vector4int8.cpp58
-rw-r--r--dep/src/g3dlite/Welder.cpp416
-rw-r--r--dep/src/g3dlite/WinMain.cpp159
-rw-r--r--dep/src/g3dlite/constants.cpp82
-rw-r--r--dep/src/g3dlite/debugAssert.cpp389
-rw-r--r--dep/src/g3dlite/fileutils.cpp1165
-rw-r--r--dep/src/g3dlite/filter.cpp32
-rw-r--r--dep/src/g3dlite/format.cpp58
-rw-r--r--dep/src/g3dlite/g3dfnmatch.cpp204
-rw-r--r--dep/src/g3dlite/g3dmath.cpp108
-rw-r--r--dep/src/g3dlite/license.cpp73
-rw-r--r--dep/src/g3dlite/license.html172
-rw-r--r--dep/src/g3dlite/prompt.cpp729
-rw-r--r--dep/src/g3dlite/stringutils.cpp275
-rw-r--r--dep/src/g3dlite/uint128.cpp155
222 files changed, 64437 insertions, 3036 deletions
diff --git a/dep/include/bzip2/bzlib.h b/dep/include/bzip2/bzlib.h
new file mode 100644
index 00000000000..c5b75d6d8ff
--- /dev/null
+++ b/dep/include/bzip2/bzlib.h
@@ -0,0 +1,282 @@
+
+/*-------------------------------------------------------------*/
+/*--- Public header file for the library. ---*/
+/*--- bzlib.h ---*/
+/*-------------------------------------------------------------*/
+
+/* ------------------------------------------------------------------
+ This file is part of bzip2/libbzip2, a program and library for
+ lossless, block-sorting data compression.
+
+ bzip2/libbzip2 version 1.0.5 of 10 December 2007
+ Copyright (C) 1996-2007 Julian Seward <jseward@bzip.org>
+
+ Please read the WARNING, DISCLAIMER and PATENTS sections in the
+ README file.
+
+ This program is released under the terms of the license contained
+ in the file LICENSE.
+ ------------------------------------------------------------------ */
+
+
+#ifndef _BZLIB_H
+#define _BZLIB_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define BZ_RUN 0
+#define BZ_FLUSH 1
+#define BZ_FINISH 2
+
+#define BZ_OK 0
+#define BZ_RUN_OK 1
+#define BZ_FLUSH_OK 2
+#define BZ_FINISH_OK 3
+#define BZ_STREAM_END 4
+#define BZ_SEQUENCE_ERROR (-1)
+#define BZ_PARAM_ERROR (-2)
+#define BZ_MEM_ERROR (-3)
+#define BZ_DATA_ERROR (-4)
+#define BZ_DATA_ERROR_MAGIC (-5)
+#define BZ_IO_ERROR (-6)
+#define BZ_UNEXPECTED_EOF (-7)
+#define BZ_OUTBUFF_FULL (-8)
+#define BZ_CONFIG_ERROR (-9)
+
+typedef
+ struct {
+ char *next_in;
+ unsigned int avail_in;
+ unsigned int total_in_lo32;
+ unsigned int total_in_hi32;
+
+ char *next_out;
+ unsigned int avail_out;
+ unsigned int total_out_lo32;
+ unsigned int total_out_hi32;
+
+ void *state;
+
+ void *(*bzalloc)(void *,int,int);
+ void (*bzfree)(void *,void *);
+ void *opaque;
+ }
+ bz_stream;
+
+
+#ifndef BZ_IMPORT
+#define BZ_EXPORT
+#endif
+
+#ifndef BZ_NO_STDIO
+/* Need a definitition for FILE */
+#include <stdio.h>
+#endif
+
+#ifdef _WIN32
+# include <windows.h>
+# ifdef small
+ /* windows.h define small to char */
+# undef small
+# endif
+# ifdef BZ_EXPORT
+# define BZ_API(func) WINAPI func
+# define BZ_EXTERN extern
+# else
+ /* import windows dll dynamically */
+# define BZ_API(func) (WINAPI * func)
+# define BZ_EXTERN
+# endif
+#else
+# define BZ_API(func) func
+# define BZ_EXTERN extern
+#endif
+
+
+/*-- Core (low-level) library functions --*/
+
+BZ_EXTERN int BZ_API(BZ2_bzCompressInit) (
+ bz_stream* strm,
+ int blockSize100k,
+ int verbosity,
+ int workFactor
+ );
+
+BZ_EXTERN int BZ_API(BZ2_bzCompress) (
+ bz_stream* strm,
+ int action
+ );
+
+BZ_EXTERN int BZ_API(BZ2_bzCompressEnd) (
+ bz_stream* strm
+ );
+
+BZ_EXTERN int BZ_API(BZ2_bzDecompressInit) (
+ bz_stream *strm,
+ int verbosity,
+ int small
+ );
+
+BZ_EXTERN int BZ_API(BZ2_bzDecompress) (
+ bz_stream* strm
+ );
+
+BZ_EXTERN int BZ_API(BZ2_bzDecompressEnd) (
+ bz_stream *strm
+ );
+
+
+
+/*-- High(er) level library functions --*/
+
+#ifndef BZ_NO_STDIO
+#define BZ_MAX_UNUSED 5000
+
+typedef void BZFILE;
+
+BZ_EXTERN BZFILE* BZ_API(BZ2_bzReadOpen) (
+ int* bzerror,
+ FILE* f,
+ int verbosity,
+ int small,
+ void* unused,
+ int nUnused
+ );
+
+BZ_EXTERN void BZ_API(BZ2_bzReadClose) (
+ int* bzerror,
+ BZFILE* b
+ );
+
+BZ_EXTERN void BZ_API(BZ2_bzReadGetUnused) (
+ int* bzerror,
+ BZFILE* b,
+ void** unused,
+ int* nUnused
+ );
+
+BZ_EXTERN int BZ_API(BZ2_bzRead) (
+ int* bzerror,
+ BZFILE* b,
+ void* buf,
+ int len
+ );
+
+BZ_EXTERN BZFILE* BZ_API(BZ2_bzWriteOpen) (
+ int* bzerror,
+ FILE* f,
+ int blockSize100k,
+ int verbosity,
+ int workFactor
+ );
+
+BZ_EXTERN void BZ_API(BZ2_bzWrite) (
+ int* bzerror,
+ BZFILE* b,
+ void* buf,
+ int len
+ );
+
+BZ_EXTERN void BZ_API(BZ2_bzWriteClose) (
+ int* bzerror,
+ BZFILE* b,
+ int abandon,
+ unsigned int* nbytes_in,
+ unsigned int* nbytes_out
+ );
+
+BZ_EXTERN void BZ_API(BZ2_bzWriteClose64) (
+ int* bzerror,
+ BZFILE* b,
+ int abandon,
+ unsigned int* nbytes_in_lo32,
+ unsigned int* nbytes_in_hi32,
+ unsigned int* nbytes_out_lo32,
+ unsigned int* nbytes_out_hi32
+ );
+#endif
+
+
+/*-- Utility functions --*/
+
+BZ_EXTERN int BZ_API(BZ2_bzBuffToBuffCompress) (
+ char* dest,
+ unsigned int* destLen,
+ char* source,
+ unsigned int sourceLen,
+ int blockSize100k,
+ int verbosity,
+ int workFactor
+ );
+
+BZ_EXTERN int BZ_API(BZ2_bzBuffToBuffDecompress) (
+ char* dest,
+ unsigned int* destLen,
+ char* source,
+ unsigned int sourceLen,
+ int small,
+ int verbosity
+ );
+
+
+/*--
+ Code contributed by Yoshioka Tsuneo (tsuneo@rr.iij4u.or.jp)
+ to support better zlib compatibility.
+ This code is not _officially_ part of libbzip2 (yet);
+ I haven't tested it, documented it, or considered the
+ threading-safeness of it.
+ If this code breaks, please contact both Yoshioka and me.
+--*/
+
+BZ_EXTERN const char * BZ_API(BZ2_bzlibVersion) (
+ void
+ );
+
+#ifndef BZ_NO_STDIO
+BZ_EXTERN BZFILE * BZ_API(BZ2_bzopen) (
+ const char *path,
+ const char *mode
+ );
+
+BZ_EXTERN BZFILE * BZ_API(BZ2_bzdopen) (
+ int fd,
+ const char *mode
+ );
+
+BZ_EXTERN int BZ_API(BZ2_bzread) (
+ BZFILE* b,
+ void* buf,
+ int len
+ );
+
+BZ_EXTERN int BZ_API(BZ2_bzwrite) (
+ BZFILE* b,
+ void* buf,
+ int len
+ );
+
+BZ_EXTERN int BZ_API(BZ2_bzflush) (
+ BZFILE* b
+ );
+
+BZ_EXTERN void BZ_API(BZ2_bzclose) (
+ BZFILE* b
+ );
+
+BZ_EXTERN const char * BZ_API(BZ2_bzerror) (
+ BZFILE *b,
+ int *errnum
+ );
+#endif
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
+
+/*-------------------------------------------------------------*/
+/*--- end bzlib.h ---*/
+/*-------------------------------------------------------------*/
diff --git a/dep/include/g3dlite/G3D/AABox.h b/dep/include/g3dlite/G3D/AABox.h
index b1b1477fe9f..2e8da1f6098 100644
--- a/dep/include/g3dlite/G3D/AABox.h
+++ b/dep/include/g3dlite/G3D/AABox.h
@@ -1,14 +1,14 @@
/**
@file AABox.h
-
+
Axis-aligned box class
-
- @maintainer Morgan McGuire, matrix@graphics3d.com
-
+
+ @maintainer Morgan McGuire, http://graphics.cs.williams.edu
+
@created 2004-01-10
- @edited 2006-02-10
+ @edited 2009-02-10
- Copyright 2000-2006, Morgan McGuire.
+ Copyright 2000-2009, Morgan McGuire.
All rights reserved.
*/
@@ -19,6 +19,7 @@
#include "G3D/Vector3.h"
#include "G3D/debug.h"
#include "G3D/Array.h"
+#include "G3D/Plane.h"
namespace G3D {
@@ -27,6 +28,7 @@ namespace G3D {
*/
class AABox {
private:
+ friend class Intersect;
/** Optional argument placeholder */
static int dummy;
@@ -42,7 +44,7 @@ public:
/**
Constructs a zero-area AABox at v.
*/
- inline AABox(const Vector3& v) {
+ inline explicit AABox(const Vector3& v) {
lo = hi = v;
}
@@ -65,6 +67,27 @@ public:
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 bool isFinite() const {
+ return lo.isFinite() && hi.isFinite();
+ }
+
inline const Vector3& low() const {
return lo;
}
@@ -76,20 +99,15 @@ public:
/**
The largest possible finite box.
*/
- static inline const AABox& maxFinite() {
- static const AABox b = AABox(Vector3::minFinite(), Vector3::maxFinite());
- return b;
- }
+ static const AABox& maxFinite();
- static inline const AABox& inf() {
- static const AABox b = AABox(-Vector3::inf(), Vector3::inf());
- return b;
- }
+ /** A large finite box. This is smaller than FLT_MAX
+ because it leaves room to add boxes together. */
+ static const AABox& large();
- static inline const AABox& zero() {
- static const AABox b = AABox(Vector3::zero(), Vector3::zero());
- return b;
- }
+ static const AABox& inf();
+
+ static const AABox& zero();
/**
Returns the centroid of the box.
@@ -98,36 +116,21 @@ public:
return (lo + hi) * 0.5;
}
+ Vector3 corner(int index) const;
+
/**
Distance from corner(0) to the next corner along axis a.
*/
- inline double extent(int a) const {
+ inline float extent(int a) const {
debugAssert(a < 3);
return hi[a] - lo[a];
}
+
inline Vector3 extent() const {
return hi - lo;
}
- /**
- @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 = 0xFFFFFF) const;
/**
Splits the box into two AABoxes along the specified axis. low contains
@@ -136,46 +139,57 @@ public:
*/
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
+ /**
+ 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.
+ <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 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
+ @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,
+
+ */
+ 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 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 {
@@ -188,16 +202,11 @@ public:
(point.z <= hi.z);
}
- /** @deprecated */
- inline float surfaceArea() const {
+ 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 area() const {
- return surfaceArea();
- }
-
inline float volume() const {
Vector3 diag = hi - lo;
return diag.x * diag.y * diag.z;
@@ -207,9 +216,6 @@ public:
Vector3 randomSurfacePoint() const;
- /** @deprecated use Box constructor */
- class Box toBox() const;
-
/** Returns true if there is any overlap */
bool intersects(const AABox& other) const;
@@ -224,7 +230,7 @@ public:
return AABox(L, H);
}
- inline unsigned int hashCode() const {
+ inline size_t hashCode() const {
return lo.hashCode() + hi.hashCode();
}
@@ -236,6 +242,20 @@ public:
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;
}
@@ -243,12 +263,10 @@ public:
}
-/**
- Hashing function for use with Table.
- */
-inline unsigned int hashCode(const G3D::AABox& b) {
- return b.hashCode();
-}
+template <> struct HashTrait<G3D::AABox> {
+ static size_t hashCode(const G3D::AABox& key) { return key.hashCode(); }
+};
-#endif
+
+#endif
diff --git a/dep/include/g3dlite/G3D/Any.h b/dep/include/g3dlite/G3D/Any.h
new file mode 100644
index 00000000000..49701202ca9
--- /dev/null
+++ b/dep/include/g3dlite/G3D/Any.h
@@ -0,0 +1,570 @@
+/**
+ @file Any.h
+
+ @author Morgan McGuire, Shawn Yarbrough, and Corey Taylor
+ @maintainer Morgan McGuire
+
+ @created 2006-06-11
+ @edited 2009-12-16
+
+ Copyright 2000-2010, Morgan McGuire.
+ All rights reserved.
+ */
+
+#ifndef G3D_Any_h
+#define G3D_Any_h
+
+#include "G3D/platform.h"
+#include "G3D/Table.h"
+#include "G3D/Array.h"
+#include "G3D/AtomicInt32.h"
+#include <string>
+
+// needed for Token
+#include "G3D/TextInput.h"
+
+#ifdef verify
+#undef verify
+#endif
+
+namespace G3D {
+
+class TextOutput;
+
+/**
+\brief Easy loading and saving of human-readable configuration files.
+
+Encodes typed, structured data and can serialize it to a human
+readable format that is very similar to the Python language's data
+syntax. Well-suited for quickly creating human-readable file formats,
+especially since deserialization and serialization preserve comments and
+an Any can tell you what file and line it came from.
+
+The class is designed so that copying Anys generally is fast, even if
+it is a large array or table. This is because data is shared between
+copies until it is mutated, at which point an actual copy occurs.
+
+\section Example
+Sample File:
+<pre>
+{
+ shape = "round",
+
+ # in meters
+ radius = 3.7,
+
+ position = Vector3(1.0, -1.0, 0.0),
+ texture = { format = "RGB8", size = (320, 200)}
+}
+</pre>
+
+Sample code using:
+<pre>
+Any x;
+x.load("ball.txt");
+if (x["shape"].string() == "round") {
+ x["density"] = 3;
+}
+x.save("ball.txt");
+</pre>
+
+The custom serialization format was chosen to be terse, easy for
+humans to read, and easy for machines to parse. It was specifically
+chosen over formats like XML, YAML, JSON, S-expressions, and Protocol
+Buffers, although there is no reason you could not write readers and
+writers for G3D::Any that support those.
+
+G3D::Any assumes that structures do not contain cycles; it is an
+error to create a structure like:
+
+<pre>
+Any x(Any::ARRAY);
+x.array().append(x); // don't do this!
+</pre>
+
+although no exception will be thrown at runtime during that append.
+
+
+\section Parsing
+
+The primary use of Any is to create your own text file formats.
+The Vector3 constructor is a good example of how to use the Any::verify
+methods to provide good error checking while parsing such formats:
+
+<pre>
+Vector3::Vector3(const Any& any) {
+ any.verifyName("Vector3");
+ any.verifyType(Any::TABLE, Any::ARRAY);
+ any.verifySize(3);
+
+ if (any.type() == Any::ARRAY) {
+ x = any[0];
+ y = any[1];
+ z = any[2];
+ } else {
+ // Table
+ x = any["x"];
+ y = any["y"];
+ z = any["z"];
+ }
+}
+</pre>
+
+\section BNF
+Serialized format BNF:
+
+<pre>
+identifier ::= (letter | "_") (letter | digit | "_")*
+identifier-op ::= "::" | "->" | "."
+
+identifier-exp ::= [identifier-op] identifier (identifier-op identifier)*
+
+comment ::= "#" <any characters> "\n"
+separator ::= "," | ";"
+
+number ::= <legal C printf number format>
+string ::= <legal C double-quoted string; backslashes must be escaped>
+boolean ::= "True" | "False"
+none ::= "None"
+array ::= "(" [value ("," value)*] ")"
+pair ::= (identifier | string) "=" value
+table ::= "{" [pair (separator pair)*] "}"
+named-array ::= identifier-exp tuple
+named-table ::= identifier-exp dict
+
+value ::= [comment] (none | number | boolean | string | array | table | named-array | named-table)
+</pre>
+
+Except for single-line comments, whitespace is not significant.
+All parsing is case-insensitive.
+
+The deserializer allows the substitution of [] for () when writing
+tuples and ";" for ",".
+
+The serializer indents four spaces for each level of nesting.
+Tables are written with the keys in alphabetic order.
+*/
+class Any {
+public:
+
+ enum Type {NONE, BOOLEAN, NUMBER, STRING, ARRAY, TABLE};
+
+ static std::string toString(Type t);
+
+ /** Where an Any came from in a file. Useful for throwing parsing errors */
+ class Source {
+ public:
+ std::string filename;
+ int line;
+ int character;
+
+ Source() : line(0), character(0) {}
+
+ void set(const TextInput& ti, const Token& t) {
+ filename = ti.filename();
+ line = t.line();
+ character = t.character();
+ }
+ };
+
+ typedef Array<Any> AnyArray;
+ typedef Table<std::string, Any> AnyTable;
+
+private:
+
+ /** Called from deserialize() */
+ static void deserializeComment(TextInput& ti, Token& token, std::string& comment);
+
+ /** NONE, BOOLEAN, and NUMBER are stored directly in the Any */
+ union SimpleValue {
+ bool b;
+ double n;
+
+ inline SimpleValue() : n(0.0) {}
+ inline SimpleValue(bool x) : b(x) {}
+ inline SimpleValue(double x) : n(x) {}
+ };
+
+ class Data {
+ public:
+ /** ARRAY, TABLE, or STRING value only. NULL otherwise. */
+ union Value {
+ std::string* s;
+ Array<Any>* a;
+ AnyTable* t;
+ inline Value() : s(NULL) {}
+ };
+
+ // Needed so that the destructor knows what is in Value
+ // and can call its destructor.
+ Type type;
+
+ /** Always points to memory that is allocated with the Data, so
+ the destructor does not delete this. */
+ Value value;
+
+ std::string comment;
+
+ std::string name;
+
+ /** For STRING, ARRAY and TABLE 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.
+ */
+ AtomicInt32 referenceCount;
+
+ Source source;
+
+ private:
+
+ /** Called by create() */
+ inline Data(Type t) : type(t), referenceCount(1) {}
+
+ /** Called by destroy */
+ ~Data();
+
+ public:
+
+ /** Clones the argument */
+ static Data* create(const Data* d);
+ static Data* create(Type t);
+
+ /** Free d, invoking its destructor and freeing the memory for
+ the value. */
+ static void destroy(Data* d);
+
+ };
+
+ /** If not empty, this Any was created from operator[] on a table
+ and perhaps was not intended to exist. The name is needed to
+ format the error message if it is read from before it is
+ written to.
+
+ The source of a placeholder object is that of the parent
+ object until it is written.
+ */
+ std::string m_placeholderName;
+
+ Type m_type;
+ SimpleValue m_simpleValue;
+ mutable Data* m_data;
+
+ /** Called before every read operation to ensure that this object
+ is not a placeholder. */
+ void beforeRead() const;
+
+ /** Called before every write operation to wipe the placeholder
+ status. */
+ void beforeWrite();
+
+ /** Decrements the reference count (if there is one). If the
+ reference count is zero after decrement, calls delete on @a m_data
+ and sets it to NULL.
+ */
+ void dropReference();
+
+ /** Allocate the Data object if it does not exist */
+ void ensureData();
+
+ /** If m_data is not NULL, ensure that it has a unique reference
+ and contains a valid m_data. This has a race condition if two
+ threads are both trying to modify the same Any
+ simultaneously.*/
+ void ensureMutable();
+
+ /** Read an unnamed a TABLE or ARRAY. Token should be the open
+ paren token; it is the next token after the close on
+ return. Called from deserialize().*/
+ void deserializeBody(TextInput& ti, Token& token);
+
+ void deserialize(TextInput& ti, Token& token);
+
+ /** Read the name of a named Array or Table. */
+ static void deserializeName(TextInput& ti, Token& token, std::string& name);
+
+ /** Read until a comma is consumed or a close paren is hit, and
+ return that token. Considers the passed in token to be the first
+ value read. */
+ static void readUntilCommaOrClose(TextInput& ti, Token& token);
+
+ /** Construct an Any that is a proxy for a table fetch from \a data.
+ This proxy can be copied exactly once on return from operator[].*/
+ Any(const std::string& key, Data* data);
+
+ inline bool isPlaceholder() const {
+ return ! m_placeholderName.empty();
+ }
+
+public:
+
+ /** Base class for all Any exceptions.*/
+ class Exception {
+ public:
+ virtual ~Exception() {}
+ };
+
+ /** Thrown by operator[] when a key is not present in a const table. */
+ class KeyNotFound : public ParseError {
+ public:
+ std::string key;
+ };
+
+ /** Thrown by operator[] when an array index is not present. */
+ class IndexOutOfBounds : public Exception {
+ public:
+ int index;
+ int size;
+ inline IndexOutOfBounds() : index(0), size(0) {}
+ inline IndexOutOfBounds(int i, int s) : index(i), size(s) {}
+ };
+
+ /** NONE constructor */
+ Any();
+
+ /** Deserialize */
+ explicit Any(TextInput& t);
+
+ Any(const Any& x);
+
+ /** NUMBER constructor */
+ Any(double x);
+
+#ifdef G3D_32BIT
+ /** NUMBER constructor */
+ Any(int64 x);
+#endif // G3D_32BIT
+
+#if 0
+ /** NUMBER constructor */
+ Any(int32 x);
+#endif // 0
+
+ /** NUMBER constructor */
+ Any(long x);
+
+ /** NUMBER constructor */
+ Any(int x);
+
+ /** NUMBER constructor */
+ Any(short x);
+
+ /** BOOLEAN constructor */
+ Any(bool x);
+
+ /** STRING constructor */
+ Any(const std::string& x);
+
+ /** STRING constructor */
+ Any(const char* x);
+
+ /** \a t must be ARRAY or TABLE */
+ Any(Type t, const std::string& name = "");
+
+ ~Any();
+
+ /** Removes the comment and name */
+ Any& operator=(const Any& x);
+
+ /** Removes the comment and name */
+ Any& operator=(double x);
+
+ /** Removes the comment and name */
+ Any& operator=(int x);
+
+ /** Removes the comment and name */
+ Any& operator=(bool x);
+
+ /** Removes the comment and name */
+ Any& operator=(const std::string& x);
+
+ /** Removes the comment and name */
+ Any& operator=(const char* x);
+
+ /** \a t must be ARRAY, TABLE, or NONE. Removes the comment and name */
+ Any& operator=(Type t);
+
+ Type type() const;
+
+ /** Same as deserialize or load, but operates on a string instead
+ of a stream or file.
+
+ \sa deserialize, load
+ */
+ void parse(const std::string& src);
+
+ std::string unparse() const;
+
+ /** Comments appear before values when they are in serialized form.*/
+ const std::string& comment() const;
+ void setComment(const std::string& c);
+
+ /** True if this is the NONE value */
+ bool isNone() const;
+
+ /** Throws a ParseError exception if this is not a number */
+ double number() const;
+ const std::string& string() const;
+ bool boolean() const;
+
+ /** If this is named ARRAY or TABLE, returns the name. */
+ const std::string& name() const;
+
+ /** \brief Set the name used when serializing an ARRAY or TABLE.
+
+ Only legal for ARRAY or TABLE. The \a name must begin with a letter
+ and contain only letters, numbers, underscores and scope operators.
+
+ <pre>
+ a2z
+ hello
+ Foo::bar
+ color.red
+ this->that
+ __x
+ </pre>
+
+
+ The scope operators "::", "->", and
+ ".", may have spaces around them. The name may not
+ contain parentheses.
+ */
+ void setName(const std::string& name);
+
+ /** Number of elements if this is an ARRAY or TABLE */
+ int size() const;
+ int length() const;
+
+ /** For an array, returns the ith element */
+ const Any& operator[](int i) const;
+ Any& operator[](int i);
+
+ /** Directly exposes the underlying data structure for an ARRAY. */
+ const Array<Any>& array() const;
+ void append(const Any& v0);
+ void append(const Any& v0, const Any& v1);
+ void append(const Any& v0, const Any& v1, const Any& v2);
+ void append(const Any& v0, const Any& v1, const Any& v2, const Any& v3);
+
+ /** Directly exposes the underlying data structure for table.*/
+ const Table<std::string, Any>& table() const;
+
+ /** For a table, returns the element for \a key. Throws KeyNotFound
+ exception if the element does not exist.
+ */
+ const Any& operator[](const std::string& key) const;
+
+ // Needed to prevent the operator[](int) overload from catching
+ // string literals
+ inline const Any& operator[](const char* key) const {
+ return operator[](std::string(key));
+ }
+
+ /**
+ Fetch an element from a table. This can be used as:
+
+ <pre>
+ a["key"] = value; (create the key if it did not exist)
+ </pre>
+
+ or
+
+ <pre>
+ value = a["key"]; (throw an error if the key did not exist)
+ </pre>
+
+ <b>Note:</b>
+ In order to cause elements to be correctly created in the
+ first case while still providing "key not found" errors in the
+ second case, the Any returned is a special object that delays
+ the actual fetch until the following assignment or method
+ call. This means that in the event of an error, the exception
+ may be thrown from a line other than the actual fetch. Use
+ the Any::get() or the const Any::operator[]() methods to avoid
+ this behavior and ensure error-checking at fetch time.
+ */
+ Any& operator[](const std::string& key);
+
+ /** \copydoc Any::operator[](const std::string&) */
+ inline Any& operator[](const char* key) {
+ return operator[](std::string(key));
+ }
+
+ /** For a table, returns the element for key \a x and \a
+ defaultVal if it does not exist. */
+ const Any& get(const std::string& key, const Any& defaultVal) const;
+
+ /** Returns true if this key is in the TABLE. Illegal to call on an object that is not a TABLE. */
+ bool containsKey(const std::string& key) const;
+
+ /** For a table, assigns the element for key k. */
+ void set(const std::string& key, const Any& val);
+
+ /** for an ARRAY, resizes and returns the last element */
+ Any& next();
+
+
+ /** True if the Anys are exactly equal, ignoring comments. Applies deeply on arrays and tables. */
+ bool operator==(const Any& x) const;
+ bool operator!=(const Any& x) const;
+
+ operator int() const;
+ operator float() const;
+ operator double() const;
+ operator bool() const;
+ operator std::string() const;
+
+ /** Resize to \a n elements, where new elements are NIL
+ It is an error to call this method if this is not an Any::ARRAY */
+ void resize(int n);
+
+ /**
+ Clears all entries.
+ This must be a TABLE or ARRAY */
+ void clear();
+
+ /** Parse from a file.
+ \sa deserialize, parse */
+ void load(const std::string& filename);
+
+ /** Uses the serialize method. */
+ void save(const std::string& filename) const;
+
+ void serialize(TextOutput& to) const;
+ /** Parse from a stream.
+ \sa load, parse */
+ void deserialize(TextInput& ti);
+
+ const Source& source() const;
+
+ /** Throws a ParseError if \a value is false. Useful for quickly
+ creating parse rules in classes that deserialize from Any.
+ */
+ void verify(bool value, const std::string& message = "") const;
+
+ /** Verifies that the name begins with identifier \a n. It may contain
+ identifier operators after this */
+ void verifyName(const std::string& n) const;
+
+ /** Verifies that the type is \a t. */
+ void verifyType(Type t) const;
+
+ /** Throws an exception if the type is not \a t0 or \a t1. */
+ void verifyType(Type t0, Type t1) const;
+
+ /** Verifies that the size is between \a low and \a high, inclusive */
+ void verifySize(int low, int high) const;
+
+ /** Verifies that the size is exactly \a s */
+ void verifySize(int s) const;
+
+private:
+
+ void deserializeTable(TextInput& ti);
+ void deserializeArray(TextInput& ti,const std::string& term);
+
+}; // class Any
+
+} // namespace G3D
+
+#endif
diff --git a/dep/include/g3dlite/G3D/AnyVal.h b/dep/include/g3dlite/G3D/AnyVal.h
new file mode 100644
index 00000000000..8c1bc72f206
--- /dev/null
+++ b/dep/include/g3dlite/G3D/AnyVal.h
@@ -0,0 +1,512 @@
+/**
+ @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;
+
+/**
+ \deprecated
+ <b>Use the G3D::Any class instead. This is only provided for
+ backwards compatibility to G3D 7.xx.</b>
+
+ 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>
+
+\deprecated
+ */
+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/dep/include/g3dlite/G3D/AreaMemoryManager.h b/dep/include/g3dlite/G3D/AreaMemoryManager.h
new file mode 100644
index 00000000000..d8d8f710359
--- /dev/null
+++ b/dep/include/g3dlite/G3D/AreaMemoryManager.h
@@ -0,0 +1,93 @@
+/**
+ @file AreaMemoryManager.h
+
+ @maintainer Morgan McGuire, http://graphics.cs.williams.edu
+
+ @created 2009-01-20
+ @edited 2009-05-29
+
+ Copyright 2000-2009, Morgan McGuire.
+ All rights reserved.
+ */
+
+
+#ifndef G3D_AreaMemoryManager_h
+#define G3D_AreaMemoryManager_h
+
+#include "G3D/platform.h"
+#include "G3D/g3dmath.h"
+#include "G3D/Array.h"
+#include "G3D/MemoryManager.h"
+
+namespace G3D {
+
+/**
+ \brief Allocates memory in large blocks and then frees it as an area.
+
+ Useful for ensuring cache coherence and for reducing the time cost of
+ multiple allocations and deallocations.
+
+ <b>Not threadsafe</b>
+ */
+class AreaMemoryManager : public MemoryManager {
+private:
+
+ class Buffer {
+ private:
+ uint8* m_first;
+ size_t m_size;
+ size_t m_used;
+
+ public:
+
+ Buffer(size_t size);
+
+ ~Buffer();
+
+ /** Returns NULL if out of space */
+ void* alloc(size_t s);
+ };
+
+ size_t m_sizeHint;
+
+ /** The underlying array is stored in regular MemoryManager heap memory */
+ Array<Buffer*> m_bufferArray;
+
+ AreaMemoryManager(size_t sizeHint);
+
+public:
+
+ typedef ReferenceCountedPointer<AreaMemoryManager> Ref;
+
+ /**
+ \param sizeHint Total amount of memory expected to be allocated.
+ The allocator will allocate memory from the system in increments
+ of this size.
+ */
+ static AreaMemoryManager::Ref create(size_t sizeHint = 10 * 1024 * 1024);
+
+ /** Invokes deallocateAll. */
+ ~AreaMemoryManager();
+
+ size_t bytesAllocated() const;
+
+ /** Allocates memory out of the buffer pool.
+ @param s must be no larger than sizeHint */
+ virtual void* alloc(size_t s);
+
+ /** Ignored. */
+ virtual void free(void* x);
+
+ virtual bool isThreadsafe() const;
+
+ /** Deletes all previously allocated memory. Because delete is not
+ invoked on objects in this memory, it is not safe to simply
+ free memory containing C++ objects that expect their destructors
+ to be called. */
+ void deallocateAll();
+};
+
+typedef AreaMemoryManager CoherentAllocator;
+}
+
+#endif
diff --git a/dep/include/g3dlite/G3D/Array.h b/dep/include/g3dlite/G3D/Array.h
index 34401af40c4..cc9e1d9dd01 100644
--- a/dep/include/g3dlite/G3D/Array.h
+++ b/dep/include/g3dlite/G3D/Array.h
@@ -1,22 +1,23 @@
-/**
+/**
@file Array.h
-
+
@maintainer Morgan McGuire, graphics3d.com
@cite Portions written by Aaron Orenstein, a@orenstein.name
-
+
@created 2001-03-11
- @edited 2007-05-12
+ @edited 2009-05-29
- Copyright 2000-2007, Morgan McGuire.
+ Copyright 2000-2009, Morgan McGuire, http://graphics.cs.williams.edu
All rights reserved.
*/
-#ifndef G3D_ARRAY_H
-#define G3D_ARRAY_H
+#ifndef G3D_Array_h
+#define G3D_Array_h
#include "G3D/platform.h"
#include "G3D/debug.h"
#include "G3D/System.h"
+#include "G3D/MemoryManager.h"
#ifdef G3D_DEBUG
// For formatting error messages
# include "G3D/format.h"
@@ -24,15 +25,16 @@
#include <vector>
#include <algorithm>
-#ifdef G3D_WIN32
+#ifdef _MSC_VER
# include <new>
-
+
# pragma warning (push)
// debug information too long
# pragma warning( disable : 4312)
# pragma warning( disable : 4786)
#endif
+
namespace G3D {
/**
@@ -46,7 +48,7 @@ const int SORT_INCREASING = 1;
const int SORT_DECREASING = -1;
/**
- Dynamic 1D array.
+ \brief Dynamic 1D array tuned for performance.
Objects must have a default constructor (constructor that
takes no arguments) in order to be used with this template.
@@ -56,19 +58,15 @@ const int SORT_DECREASING = -1;
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 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
+ 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
+ to append to small arrays.
+
+ Then 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
@@ -79,24 +77,38 @@ const int SORT_DECREASING = -1;
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.
+
+ \sa G3D::SmallArray
*/
-template <class T>
+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;
+ T* data;
+
+ int num;
+ int numAllocated;
- int num;
- int numAllocated;
+ MemoryManager::Ref m_memoryManager;
- void init(int n, int a) {
- debugAssert(n <= a);
+ /** \param n Number of elements
+ */
+ void init(int n, const MemoryManager::Ref& m) {
+ m_memoryManager = m;
debugAssert(n >= 0);
this->num = 0;
this->numAllocated = 0;
data = NULL;
- if (a > 0) {
+ if (n > 0) {
resize(n);
} else {
data = NULL;
@@ -104,7 +116,7 @@ private:
}
void _copy(const Array &other) {
- init(other.num, other.num);
+ init(other.num, MemoryManager::create());
for (int i = 0; i < num; i++) {
data[i] = other.data[i];
}
@@ -118,28 +130,31 @@ private:
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)
+ 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
+
+ // 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
+ // elements are actually revealed to the application. They
// will be constructed in the resize() method.
- data = (T*)System::alignedMalloc(sizeof(T) * numAllocated, 16);
+ data = (T*)m_memoryManager->alloc(sizeof(T) * numAllocated);
+ alwaysAssertM(data, "Memory manager returned NULL: out of memory?");
// Call the copy constructors
- {const int N = iMin(oldNum, numAllocated);
+ {const int N = G3D::min(oldNum, numAllocated);
const T* end = data + N;
T* oldPtr = oldData;
for (T* ptr = data; ptr < end; ++ptr, ++oldPtr) {
@@ -149,7 +164,7 @@ private:
const T* constructed = new (ptr) T(*oldPtr);
(void)constructed;
- debugAssertM(constructed == ptr,
+ debugAssertM(constructed == ptr,
"new returned a different address than the one provided by Array.");
}}
@@ -159,19 +174,31 @@ private:
ptr->~T();
}}
- System::alignedFree(oldData);
+ m_memoryManager->free(oldData);
}
public:
/**
- C++ STL style iterator variable. Call begin() to get
+ 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.
@@ -196,43 +223,80 @@ public:
}
/**
- The array returned is only valid until the next append() or resize call, or
- the Array is deallocated.
+ 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.
+ 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 a zero length array (no heap allocation occurs until resize). */
+ Array() : num(0) {
+ init(0, MemoryManager::create());
+ debugAssert(num >= 0);
+ }
+
+
+ /** Creates an array containing v0. */
+ Array(const T& v0) {
+ init(1, MemoryManager::create());
+ (*this)[0] = v0;
+ }
+
+ /** Creates an array containing v0 and v1. */
+ Array(const T& v0, const T& v1) {
+ init(2, MemoryManager::create());
+ (*this)[0] = v0;
+ (*this)[1] = v1;
+ }
+
+ /** Creates an array containing v0...v2. */
+ Array(const T& v0, const T& v1, const T& v2) {
+ init(3, MemoryManager::create());
+ (*this)[0] = v0;
+ (*this)[1] = v1;
+ (*this)[2] = v2;
+ }
+
+ /** Creates an array containing v0...v3. */
+ Array(const T& v0, const T& v1, const T& v2, const T& v3) {
+ init(4, MemoryManager::create());
+ (*this)[0] = v0;
+ (*this)[1] = v1;
+ (*this)[2] = v2;
+ (*this)[3] = v3;
+ }
+
+ /** Creates an array containing v0...v4. */
+ Array(const T& v0, const T& v1, const T& v2, const T& v3, const T& v4) {
+ init(5, MemoryManager::create());
+ (*this)[0] = v0;
+ (*this)[1] = v1;
+ (*this)[2] = v2;
+ (*this)[3] = v3;
+ (*this)[4] = v4;
+ }
- /**
- Creates an array of size.
- */
- Array(int size) {
- init(size, size);
- }
/**
Copy constructor
*/
- Array(const Array& other) {
+ Array(const Array& other) : num(0) {
_copy(other);
+ debugAssert(num >= 0);
}
/**
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
+ (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).
@@ -242,36 +306,44 @@ public:
for (int i = 0; i < num; i++) {
(data + i)->~T();
}
-
- System::alignedFree(data);
+
+ m_memoryManager->free(data);
// Set to 0 in case this Array is global and gets referenced during app exit
data = NULL;
- num = 0;
+ num = 0;
numAllocated = 0;
}
/**
- Removes all elements. Use resize(0, false) or fastClear if you want to
+ 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() {
- resize(0);
+ void clear(bool shrink = true) {
+ resize(0, shrink);
}
- /** resize(0, false) */
+ void clearAndSetMemoryManager(const MemoryManager::Ref& m) {
+ clear();
+ debugAssert(data == NULL);
+ m_memoryManager = m;
+ }
+
+ /** resize(0, false)
+ @deprecated*/
void fastClear() {
- resize(0, false);
+ clear(false);
}
/**
Assignment operator.
*/
Array& operator=(const Array& other) {
- resize(other.num);
- for (int i = 0; i < num; ++i) {
+ debugAssert(num >= 0);
+ resize(other.num); for (int i = 0; i < num; ++i) {
data[i] = other[i];
}
+ debugAssert(num >= 0);
return *this;
}
@@ -283,6 +355,10 @@ public:
return *this;
}
+ inline MemoryManager::Ref memoryManager() const {
+ return m_memoryManager;
+ }
+
/**
Number of elements in the array.
*/
@@ -302,26 +378,13 @@ public:
Swaps element index with the last element in the array then
shrinks the array by one.
*/
- void fastRemove(int index) {
+ void fastRemove(int index, bool shrinkIfNecessary = false) {
debugAssert(index >= 0);
debugAssert(index < num);
data[index] = data[num - 1];
- resize(size() - 1);
- }
-
- /**
- Resizes, calling the default constructor for
- newly created objects and shrinking the underlying
- array as needed (and calling destructors as needed).
- */
- void resize(int n) {
- resize(n, true);
+ resize(size() - 1, shrinkIfNecessary);
}
- /** Resizes without shrinking the underlying array */
- void fastResize(int n) {
- resize(n, false);
- }
/**
Inserts at the specified index and shifts all other elements up by one.
@@ -338,20 +401,34 @@ public:
/** @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) {
- 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();
- }
+ faster but can waste memory.
+ */
+ void resize(int n, bool shrinkIfNecessary = true) {
+ debugAssert(n >= 0);
+ if (num == n) {
+ return;
+ }
- // Once allocated, always maintain 10 elements or 32 bytes, whichever is higher.
- static const int minSize = iMax(10, 32 / sizeof(T));
+ int oldNum = num;
+ num = n;
- if (num > numAllocated) {
+ // 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.
+ const int minSize = std::max(MIN_ELEMENTS, (int)(MIN_BYTES / sizeof(T)));
+
+ if ((MIN_ELEMENTS == 0) && (MIN_BYTES == 0) && (n == 0) && shrinkIfNecessary) {
+ // Deallocate the array completely
+ numAllocated = 0;
+ m_memoryManager->free(data);
+ data = NULL;
+ return;
+ }
+
+ if (num > numAllocated) {
// Grow the underlying array
if (numAllocated == 0) {
@@ -360,7 +437,7 @@ public:
debugAssert(oldNum == 0);
realloc(oldNum);
} else {
-
+
if (num < minSize) {
// Grow to at least the minimum size
numAllocated = minSize;
@@ -375,7 +452,7 @@ public:
float growFactor = 3.0;
- size_t oldSizeBytes = numAllocated * sizeof(T);
+ int oldSizeBytes = numAllocated * sizeof(T);
if (oldSizeBytes > 400000) {
// Avoid bloat
growFactor = 1.5;
@@ -417,7 +494,7 @@ public:
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.
@@ -437,8 +514,11 @@ public:
}
}
+
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);
@@ -449,12 +529,14 @@ public:
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;
@@ -476,6 +558,7 @@ public:
}
}
+
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;
@@ -560,33 +643,51 @@ public:
pop();
}
- /**
+ /**
"The member function returns the storage currently allocated to hold the controlled
- sequence, a value at least as large as size()"
+ 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."
+ /**
+ "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."
+ /**
+ "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];
}
+ /**
+ "The member function returns a reference to the last element of the controlled sequence,
+ which must be non-empty."
+ For compatibility with std::vector.
+ */
+ T& back() {
+ return (*this)[size()-1];
+ }
+
+ /**
+ "The member function returns a reference to the last element of the controlled sequence,
+ which must be non-empty."
+ For compatibility with std::vector.
+ */
+ const T& back() const {
+ return (*this)[size()-1];
+ }
+
/**
Removes the last element and returns it. By default, shrinks the underlying array.
*/
@@ -604,6 +705,7 @@ public:
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.
@@ -616,6 +718,7 @@ public:
*this = temp;
}
+
/**
Performs bounds checks in debug mode
*/
@@ -626,7 +729,7 @@ public:
}
inline T& operator[](unsigned int n) {
- debugAssertM(((int)n < num), format("Array index out of bounds. n = %d, size() = %d", n, num));
+ debugAssertM(n < (unsigned int)num, format("Array index out of bounds. n = %d, size() = %d", n, num));
return data[n];
}
@@ -705,13 +808,13 @@ public:
/** Returns element middleIndex() */
inline const T& middle() const {
debugAssertM(num > 0, "Array is empty");
- return data[num >> 1];
+ return data[num >> 1];
}
/** Returns element middleIndex() */
inline T& middle() {
debugAssertM(num > 0, "Array is empty");
- return data[num >> 1];
+ return data[num >> 1];
}
/**
@@ -727,6 +830,19 @@ public:
/**
Returns the index of (the first occurance of) an index or -1 if
+ not found. Searches from the right.
+ */
+ int rfindIndex(const T& value) const {
+ for (int i = num -1 ; i >= 0; --i) {
+ if (data[i] == value) {
+ return i;
+ }
+ }
+ return -1;
+ }
+
+ /**
+ Returns the index of (the first occurance of) an index or -1 if
not found.
*/
int findIndex(const T& value) const {
@@ -773,14 +889,14 @@ public:
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);
}
@@ -789,7 +905,7 @@ public:
*/
void reverse() {
T temp;
-
+
int n2 = num / 2;
for (int i = 0; i < n2; ++i) {
temp = data[num - 1 - i];
@@ -807,7 +923,7 @@ public:
}
</PRE>
- Note that for pointer arrays, the <CODE>const</CODE> must come
+ 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>
@@ -815,13 +931,28 @@ public:
return elem1->x < elem2->x;
}
</PRE>
+
+ or a functor, e.g.,
+ <pre>
+bool
+less_than_functor::operator()( const double& lhs, const double& rhs ) const
+{
+return( lhs < rhs? true : false );
+}
+</pre>
*/
- void sort(bool (__cdecl *lessThan)(const T& elem1, const T& elem2)) {
+ // void sort(bool (__cdecl *lessThan)(const T& elem1, const T& elem2)) {
+ // std::sort(data, data + num, lessThan);
+ //}
+ template<class LessThan>
+ void sort(const LessThan& lessThan) {
+ // Using std::sort, which according to http://www.open-std.org/JTC1/SC22/WG21/docs/D_4.cpp
+ // was 2x faster than qsort for arrays around size 2000 on intel core2 with gcc
std::sort(data, data + num, lessThan);
}
/**
- Sorts the array in increasing order using the > or < operator. To
+ 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>
@@ -880,8 +1011,8 @@ public:
};
/** 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.
-
+ 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:
@@ -900,7 +1031,7 @@ public:
*/
template<typename Comparator>
void partition(
- const T& partitionElement,
+ const T& partitionElement,
Array<T>& ltArray,
Array<T>& eqArray,
Array<T>& gtArray,
@@ -935,7 +1066,7 @@ public:
Uses < and == on elements to perform a partition. See partition().
*/
void partition(
- const T& partitionElement,
+ const T& partitionElement,
Array<T>& ltArray,
Array<T>& eqArray,
Array<T>& gtArray) const {
@@ -943,7 +1074,7 @@ public:
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
@@ -953,8 +1084,8 @@ public:
@param comparator see parition() for a discussion.*/
template<typename Comparator>
void medianPartition(
- Array<T>& ltMedian,
- Array<T>& eqMedian,
+ Array<T>& ltMedian,
+ Array<T>& eqMedian,
Array<T>& gtMedian,
Array<T>& tempArray,
const Comparator& comparator) const {
@@ -978,7 +1109,7 @@ public:
{
// Two element array; median is the smaller
int c = comparator(first(), last());
-
+
switch (c) {
case -1:
// first was bigger
@@ -1003,14 +1134,14 @@ public:
// All other cases use a recursive randomized median
- // Number of values less than all in the current arrays
+ // Number of values less than all in the current arrays
int ltBoost = 0;
- // Number of values greater than all in the current arrays
+ // 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:
+ // lt array:
// [1 2 3] size = 3, choose half = (s + 1) /2
//
int lowerHalfSize, upperHalfSize;
@@ -1049,7 +1180,7 @@ public:
if ((L >= lowerHalfSize) &&
(U >= upperHalfSize)) {
- // x must be the partition median
+ // x must be the partition median
break;
} else if (L < lowerHalfSize) {
@@ -1058,10 +1189,10 @@ public:
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
+ // 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;
@@ -1072,10 +1203,10 @@ public:
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
+ // 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;
@@ -1092,19 +1223,20 @@ public:
}
/**
- Computes a median partition using the default comparator and a dynamically allocated temporary
+ 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>& 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() {
@@ -1119,8 +1251,10 @@ public:
}
}
+
};
+
/** 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) {
@@ -1133,9 +1267,8 @@ template<class T> bool contains(const T* array, int len, const T& e) {
} // namespace
+#ifdef _MSC_VER
+# pragma warning (pop)
#endif
-#ifdef G3D_WIN32
-# pragma warning (push)
#endif
-
diff --git a/dep/include/g3dlite/G3D/AtomicInt32.h b/dep/include/g3dlite/G3D/AtomicInt32.h
new file mode 100644
index 00000000000..2d63f998355
--- /dev/null
+++ b/dep/include/g3dlite/G3D/AtomicInt32.h
@@ -0,0 +1,164 @@
+/**
+ @file AtomicInt32.h
+
+ @maintainer Morgan McGuire, http://graphics.cs.williams.edu
+
+ @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) || defined(G3D_OSX)
+ // Based on Apache Portable Runtime
+ // http://koders.com/c/fid3B6631EE94542CDBAA03E822CA780CBA1B024822.aspx
+ int32 ret;
+ asm volatile ("lock; cmpxchgl %1, %2"
+ : "=a" (ret)
+ : "r" (exchange), "m" (m_value), "0"(comperand)
+ : "memory", "cc");
+ return ret;
+
+ // Note that OSAtomicCompareAndSwap32 does not return a useful value for us
+ // so it can't satisfy the cmpxchgl contract.
+# endif
+ }
+
+};
+
+} // namespace
+
+#endif
diff --git a/dep/include/g3dlite/G3D/BinaryFormat.h b/dep/include/g3dlite/G3D/BinaryFormat.h
new file mode 100644
index 00000000000..f6719a1c540
--- /dev/null
+++ b/dep/include/g3dlite/G3D/BinaryFormat.h
@@ -0,0 +1,140 @@
+/**
+ @file BinaryFormat.h
+ @maintainer Morgan McGuire, http://graphics.cs.williams.edu
+
+ @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/dep/include/g3dlite/G3D/BinaryInput.h b/dep/include/g3dlite/G3D/BinaryInput.h
new file mode 100644
index 00000000000..1dac93ea55e
--- /dev/null
+++ b/dep/include/g3dlite/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-2009, 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/dep/include/g3dlite/G3D/BinaryOutput.h b/dep/include/g3dlite/G3D/BinaryOutput.h
new file mode 100644
index 00000000000..d81ec56a67b
--- /dev/null
+++ b/dep/include/g3dlite/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/dep/include/g3dlite/G3D/BoundsTrait.h b/dep/include/g3dlite/G3D/BoundsTrait.h
new file mode 100644
index 00000000000..15e1418010c
--- /dev/null
+++ b/dep/include/g3dlite/G3D/BoundsTrait.h
@@ -0,0 +1,20 @@
+/**
+ @file BoundsTrait.h
+
+ @maintainer Morgan McGuire, http://graphics.cs.williams.edu
+ @created 2008-10-01
+ @edited 2008-10-01
+ Copyright 2000-2009, 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/dep/include/g3dlite/G3D/Box.h b/dep/include/g3dlite/G3D/Box.h
index 3ac3c61d0f6..82af9125b05 100644
--- a/dep/include/g3dlite/G3D/Box.h
+++ b/dep/include/g3dlite/G3D/Box.h
@@ -1,13 +1,13 @@
/**
@file Box.h
-
+
Box class
-
- @maintainer Morgan McGuire, matrix@graphics3d.com
-
+
+ @maintainer Morgan McGuire, http://graphics.cs.williams.edu
+
@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-01-05
+ @edited 2007-06-05
Copyright 2000-2006, Morgan McGuire.
All rights reserved.
@@ -26,7 +26,8 @@ namespace G3D {
class CoordinateFrame;
/**
- An arbitrary 3D box, useful as a bounding box.
+ An arbitrary 3D box, useful as a bounding box.
+
To construct a box from a coordinate frame, center and extent, use the idiom:
@@ -42,7 +43,7 @@ private:
/**
<PRE>
3 2 7 6
-
+
0 1 4 5
front back (seen through front)
@@ -54,7 +55,7 @@ private:
Unit axes.
*/
Vector3 _axis[3];
-
+
Vector3 _center;
/**
@@ -83,10 +84,17 @@ public:
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
+ 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
@@ -103,18 +111,6 @@ public:
return _center;
}
- inline Vector3 getCenter() const {
- return center();
- }
-
- /**
- Returns a corner (0 <= i < 8)
- @deprecated
- */
- inline Vector3 getCorner(int i) const {
- debugAssert(i < 8);
- return _corner[i];
- }
inline Vector3 corner(int i) const {
debugAssert(i < 8);
@@ -153,65 +149,34 @@ public:
Vector3& v2,
Vector3& v3) 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 = -1) const;
- /**
+ /**
See AABox::culledBy
- */
- bool culledBy(
- const Array<Plane>& plane,
- int32& cullingPlaneIndex,
- const uint32 testMask,
- uint32& childMask) const;
+ */
+ 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 culledBy
+ (
+ const Array<Plane>& plane,
+ int32& cullingPlaneIndex = dummy,
+ const uint32 testMask = -1) const;
bool contains(
const Vector3& point) const;
- /** @deprecated */
- float surfaceArea() const;
-
- inline float area() const {
- return surfaceArea();
- }
+ float area() const;
float volume() const;
- void getRandomSurfacePoint(Vector3& P, Vector3& N = Vector3::dummy) const;
-
- /**
- @deprecated
- Uniformly distributed on the surface.
- */
- inline Vector3 randomSurfacePoint() const {
- Vector3 V;
- getRandomSurfacePoint(V);
- return V;
- }
+ void getRandomSurfacePoint(Vector3& P, Vector3& N = Vector3::ignore()) const;
/**
Uniformly distributed on the interior (includes surface)
@@ -219,9 +184,12 @@ public:
Vector3 randomInteriorPoint() const;
void getBounds(class AABox&) const;
+
+ bool isFinite() const {
+ return G3D::isFinite(_volume);
+ }
};
}
#endif
-
diff --git a/dep/include/g3dlite/G3D/Box2D.h b/dep/include/g3dlite/G3D/Box2D.h
new file mode 100644
index 00000000000..80accad89dd
--- /dev/null
+++ b/dep/include/g3dlite/G3D/Box2D.h
@@ -0,0 +1,121 @@
+/**
+ @file Box2D.h
+
+ @maintainer Morgan McGuire, http://graphics.cs.williams.edu
+
+ @created 2001-06-02
+ @edited 2008-12-27
+
+ Copyright 2000-2009, Morgan McGuire.
+ All rights reserved.
+ */
+
+#ifndef G3D_Box2D_h
+#define G3D_Box2D_h
+
+#include "G3D/platform.h"
+#include "G3D/Vector2.h"
+
+namespace G3D {
+
+class CoordinateFrame;
+typedef class CoordinateFrame CFrame;
+class Rect2D;
+typedef class Rect2D AABox2D;
+
+/**
+ 2D oriented box
+ @cite http://www.flipcode.com/archives/2D_OBB_Intersection.shtml
+ */
+class Box2D {
+private:
+ /** Corners of the box, where 0 is the lower left. */
+ Vector2 m_corner[4];
+
+ /** Two edges of the box extended away from corner[0], with length
+ = 1 / extentSquared */
+ Vector2 m_axisin[2];
+
+ /** Two edges of the box extended away from corner[0], with unit length */
+ Vector2 m_axis[2];
+
+ /** Centroid of the box */
+ Vector2 m_center;
+
+ /** origin[a] = m_corner[0].dot(m_axisin[a]); */
+ float origin[2];
+
+ /** Surface area */
+ float m_area;
+
+ Vector2 m_extent;
+
+ /** Returns true if other overlaps one dimension of this. */
+ bool overlaps1Way(const Box2D& other) const;
+
+
+ /** Updates the axes after the m_corners move. Assumes the
+ m_corners actually form a rectangle. */
+ void computeAxes();
+
+public:
+
+ /**
+ @param center World-space center
+ @param w Width along object-space x-axis
+ @param h Height along object-space y-axis
+ @param angle Counter-clockwise angle from object-space x-axis in radians
+ */
+ Box2D(const Vector2& center = Vector2(0, 0), float w = 0, float h = 0, float angle = 0);
+
+ Box2D(const AABox2D& b);
+
+ Box2D(const Vector2& min, const Vector2& max);
+
+ /** Transform @a b by @a frame, discarding the Z components, and
+ compute the new box.*/
+ Box2D(const CFrame& frame, Box2D& b);
+
+ inline bool contains(const Vector2& v) const {
+ // Take to object space:
+ const Vector2& p = v - m_center;
+ float x = p.dot(m_axisin[0]);
+ float y = p.dot(m_axisin[1]);
+
+ // Must be within extent/2 on both axes in object space
+ return (abs(x) <= 0.5f) && (abs(y) <= 0.5f);
+ }
+
+ /** @brief Distance from corner(0) to the next corner along the box's local axis a. */
+ inline const Vector2& extent() const {
+ return m_extent;
+ }
+
+ /** @brief Unit length vector along axis @a a */
+ inline const Vector2& axis(int a) const {
+ debugAssert(a == 0 || a == 1);
+ return m_axis[a];
+ }
+
+ /** @brief Surface area */
+ inline float area() const {
+ return m_area;
+ }
+
+ inline const Vector2& corner(int i) const {
+ debugAssert(i >=0 && i <= 3);
+ return m_corner[i];
+ }
+
+ inline const Vector2& center() const {
+ return m_center;
+ }
+
+ /** Returns true if the intersection of the boxes is non-empty. */
+ inline bool overlaps(const Box2D& other) const {
+ return overlaps1Way(other) && other.overlaps1Way(*this);
+ }
+};
+
+} // G3D
+#endif
diff --git a/dep/include/g3dlite/G3D/BumpMapPreprocess.h b/dep/include/g3dlite/G3D/BumpMapPreprocess.h
new file mode 100644
index 00000000000..955f99e61b2
--- /dev/null
+++ b/dep/include/g3dlite/G3D/BumpMapPreprocess.h
@@ -0,0 +1,61 @@
+/**
+ \file BumpMapPreprocess.h
+
+ \maintainer Morgan McGuire, http://graphics.cs.williams.edu
+
+ \created 2010-01-28
+ \edited 2010-01-28
+
+ Copyright 2000-2010, Morgan McGuire.
+ All rights reserved.
+ */
+
+#ifndef G3D_BumpMapPreprocess_h
+#define G3D_BumpMapPreprocess_h
+
+#include "G3D/platform.h"
+
+namespace G3D {
+class Any;
+
+/**
+Not in the BumpMap class to avoid a circular dependency between Texture and BumpMap.
+G3D::GImage::computeNormalMap().
+*/
+class BumpMapPreprocess {
+public:
+
+ /** If true, the elevations are box filtered after computing normals
+ and before uploading, which produces better results for parallax offset mapping
+ Defaults to false. */
+ bool lowPassFilter;
+
+ /** Height of the maximum ("white") value, in pixels, for the purpose of computing normals.
+ 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 negative value means to set zExtentPixels to -zExtentPixels * max(width, height).
+ The default is -0.02.
+ */
+ float zExtentPixels;
+
+ /** After computing normals, scale the height by |N.z|, a trick that reduces texture swim in steep areas for parallax offset
+ mapping. Defaults to false.*/
+ bool scaleZByNz;
+
+ BumpMapPreprocess() : lowPassFilter(false), zExtentPixels(-0.02f), scaleZByNz(false) {}
+
+ BumpMapPreprocess(const Any& any);
+
+ operator Any() const;
+
+ bool operator==(const BumpMapPreprocess& other) const {
+ return
+ (lowPassFilter == other.lowPassFilter) &&
+ (zExtentPixels == other.zExtentPixels) &&
+ (scaleZByNz == other.scaleZByNz);
+ }
+};
+
+}
+
+#endif
diff --git a/dep/include/g3dlite/G3D/Capsule.h b/dep/include/g3dlite/G3D/Capsule.h
new file mode 100644
index 00000000000..baeea3aa82b
--- /dev/null
+++ b/dep/include/g3dlite/G3D/Capsule.h
@@ -0,0 +1,90 @@
+/**
+ @file Capsule.h
+
+ @maintainer Morgan McGuire, http://graphics.cs.williams.edu
+
+ @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/dep/include/g3dlite/G3D/CollisionDetection.h b/dep/include/g3dlite/G3D/CollisionDetection.h
index 5e95498ffed..c8fcf5534c2 100644
--- a/dep/include/g3dlite/G3D/CollisionDetection.h
+++ b/dep/include/g3dlite/G3D/CollisionDetection.h
@@ -1,19 +1,21 @@
/**
@file CollisionDetection.h
+
Moving collision detection for simple primitives.
- @author Morgan McGuire, matrix@graphics3d.com
+ @author Morgan McGuire, http://graphics.cs.williams.edu
@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.
+ Box-Triangle by Tomas Akenine-Moller
@created 2001-11-19
- @edited 2006-01-10
+ @edited 2008-12-19
- Copyright 2000-2006, Morgan McGuire.
+ Copyright 2000-2009, Morgan McGuire.
All rights reserved.
*/
@@ -31,26 +33,27 @@
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
+ 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
+ <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
+ 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
+ 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
@@ -66,17 +69,17 @@ namespace G3D {
<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>Vector3</b></td><td>\link Vector3::operator== V3::==\endlink \link Vector3::fuzzyEq V3::fuzzy \endlink \link G3D::distance distance \endlink</td><td bgcolor=#C0C0C0 colspan=10 ></td></tr>
+ <tr><td><b>LineSegment</b></td><td>\link LineSegment::closestPoint LS::closestPoint\endlink \link LineSegment::distance LS::distance\endlink \link CollisionDetection::closestPointOnLineSegment CD\endlink</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>Line</b></td><td>Line::closestPoint Line::distance</td><td></td><td>\link CollisionDetection::closestPointsBetweenLineAndLine CD\endlink</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>Sphere</b></td><td>Sphere::contains</td><td></td><td>\link CollisionDetection::collisionTimeForMovingPointFixedSphere CD \endlink, \link Ray::intersectionTime R::time\endlink</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>
+ <tr><td><b>AABox</b></td><td>AABox::contains</td><td></td><td></td><td></td><td></td><td>\link CollisionDetection::fixedSolidBoxIntersectsFixedTriangle CD\endlink</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>\link CollisionDetection::collisionTimeForMovingPointFixedBox CD\endlink</td><td>(treat as Ray)</td><td>\link CollisionDetection::penetrationDepthForFixedBoxFixedPlane CD \endlink</td><td>\link CollisionDetection::penetrationDepthForFixedBoxFixedPlane CD\endlink</td><td>\link CollisionDetection::penetrationDepthForFixedSphereFixedBox CD\endlink</td><td>None (use OPCODE)</td><td>\link CollisionDetection::movingSpherePassesThroughFixedBox CD \endlink</td><td>\link CollisionDetection::penetrationDepthForFixedBoxFixedBox CD\endlink</td><td>\link CollisionDetection::penetrationDepthForFixedBoxFixedBox CD\endlink</td></tr>
</table>
<p>
@@ -84,28 +87,31 @@ namespace G3D {
<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>
+
+ @deprecated Routines moving to the G3D::Intersect class in G3D 8.0
*/
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 Vector3 ignore;
- /**
- Default parameter if value passed to a function as reference is
- not to be calculated. Must be explicitly supported by function.
- */
+ /**
+ 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.
- */
+ /**
+ 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() {}
@@ -117,32 +123,32 @@ public:
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.
+ @param separatingAxisIndex Separating axis.
+ @param box1 Box 1.
+ @param box2 Box 2.
- @return Axis that separates the two boxes.
- */
+ @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.
- */
+ 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,
@@ -157,15 +163,15 @@ public:
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.
+ @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.
@@ -180,41 +186,42 @@ public:
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
+
+ /**
+ 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.
- */
+ @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,
@@ -226,70 +233,70 @@ public:
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)
- */
+ /**
+ 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.
+ /**
+ 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
+ @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)
+ @param box1 Box 1.
+ @param box2 Box 2.
+ @param lastSeparatingAxis Last separating axis.
+ (optimization - see note)
- @return true - Intersection.
- @return false - otherwise.
- */
+ @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.
- */
+ 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,
@@ -297,7 +304,7 @@ public:
Vector3 & closest2);
/**
- Calculates the depth of penetration between two fixed boxes.
+ 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
@@ -308,26 +315,26 @@ public:
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.
+ @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
+ @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)
+ @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.
- */
+ @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,
@@ -336,21 +343,21 @@ public:
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
+ 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]
+ @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.
- */
+ @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,
@@ -358,127 +365,125 @@ public:
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.
+ 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]
+ @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.
- */
+ @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.
- */
+ /**
+ 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 sphereA Fixed Sphere.
+ @param planeB 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.
- */
+ /**
+ 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().
- */
+ /**
+ 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,
+ 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().
- */
+ /**
+ 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
+
+ @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,
@@ -488,25 +493,25 @@ public:
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().
- */
+ /**
+ 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,
@@ -515,30 +520,30 @@ public:
const Vector3& v2,
Vector3& location) {
float t = collisionTimeForMovingPointFixedTriangle(orig, dir, v0, v1, v2);
- if (t < inf()) {
+ if (t < finf()) {
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().
- */
+ /**
+ 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,
@@ -548,34 +553,34 @@ public:
float t = collisionTimeForMovingPointFixedTriangle(
orig, dir, tri.vertex(0), tri.vertex(1), tri.vertex(2));
-
- if ((t < inf()) && (&location != &ignore)) {
+
+ if ((t < finf()) && (&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().
- */
+ /**
+ 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,
@@ -585,570 +590,616 @@ public:
Vector3& location,
Vector3& normal) {
float t = collisionTimeForMovingPointFixedTriangle(orig, dir, v0, v1, v2);
- if (t < inf()) {
+ if (t < finf()) {
location = orig + dir * t;
- normal = (v2 - v0).cross(v1 - v0).direction();
+ normal = (v1 - v0).cross(v2 - 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 Optimized code by Pierre Terdiman, 2000 (~20-30% faster on 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 Vector3& point,
+ const Vector3& velocity,
const class AABox& box,
- Vector3& outLocation,
+ Vector3& outLocation,
bool& inside = ignoreBool,
Vector3& outNormal = ignore);
/**
- Calculates time between the intersection of a moving point and a fixed
- Axis-Aligned Box (AABox).
+ Calculates time between the intersection of a moving point and a fixed
+ Axis-Aligned Box (AABox).
- @note Avoids the sqrt from collisionTimeForMovingPointFixedAABox.
+ @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]
+ @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().
- */
+ @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 Vector3& point,
+ const Vector3& velocity,
const class AABox& box,
- Vector3& outLocation,
+ 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.
+ /**
+ @brief Calculates intersection of a ray and a static
+ Axis-Aligned Box (AABox).
- @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]
+ @note Avoids the sqrt from collisionTimeForMovingPointFixedAABox;
+ early-out branches and operations optimized for Intel Core2 architecture.
+
+ @param invDir 1/dir
+ @param location Location of collision. [Post Condition]
+ @param inside Does the ray originate inside the box? [Post Condition]
- @return Time til collision. If there is no collision then the return
- value will be inf().
+ @return True if the ray hits the box
*/
+ static bool __fastcall rayAABox(
+ const Ray& ray,
+ const Vector3& invDir,
+ const AABox& box,
+ const Vector3& boxCenter,
+ float boundingRadiusSquared,
+ Vector3& location,
+ bool& inside);
+
+ /**
+ 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 outLocation Location of collision. [Post Condition]
+ @param outNormal Sphere's surface normal to collision [Post Condition]
+ \param solid If true, rays inside the sphere immediately intersect (good for collision detection). If false, they hit the opposite side of the sphere (good for ray tracing).
+
+ @return Time until 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);
+ const Vector3& point,
+ const Vector3& velocity,
+ const class Sphere& sphere,
+ Vector3& outLocation,
+ Vector3& outNormal = ignore,
+ bool solid = false);
/**
- Calculates time between the intersection of a moving point and a fixed
- box.
+ 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.
+ @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]
+ @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().
- */
+ @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,
+ 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().
- */
+ /**
+ 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,
+ 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.
+ /**
+ 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 outLocation 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);
- @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]
+ /**
+ Calculates time between the intersection of a moving sphere and a fixed
+ triangle.
- @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);
+ @param sphere Moving sphere.
+ @param velocity Sphere's velocity.
+ @param plane Fixed Plane.
+ @param outLocation Location of collision -- not center position of sphere
+ at the collision time. [Post Condition]
+ @param outNormal Box's surface normal to collision [Post Condition]
- /**
- 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().
- */
+ @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,
+ 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.
- @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().
- */
+ /**
+ 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,
- Vector3& outNormal = 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().
- */
+ 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 outLocation 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,
+ 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.
+ /**
+ 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.
+ @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]
+ @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().
- */
+ @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,
+ 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().
+ /** Calculates time between the intersection of a moving sphere
+ and a fixed sphere.
+
+ If they are already interpenetrating, returns 0 and @a
+ location is the closest point on the surface of the fixed sphere
+ to the center of the moving sphere.
+
+ @param sphere Moving sphere.
+ @param velocity Sphere's velocity.
+ @param fixedSphere Fixed Sphere.
+ @param outLocation Location of collision -- not center position of sphere
+ at the collision time. [Post Condition]
+ @param outNormal Moving sphere's surface normal to collision [Post Condition]
+
+ @return Time until 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);
+ const Sphere& sphere,
+ const Vector3& velocity,
+ const 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,
+ 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.
+ 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.
+ @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.
+ @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.
- */
+ @return Direction of bounce.
+ */
static Vector3 bounceDirection(
- const class Sphere& sphere,
- const Vector3& velocity,
- const float collisionTime,
- const Vector3& collisionLocation,
+ 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.
+ 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.
+ @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.
+ @param sphere Moving sphere.
+ @param velocity Sphere's velocity.
+ @param collisionTime Time of collision
+ @param collisionLocation Collision location.
- @return Direction of slide.
- */
+ @return Direction of slide.
+ */
static Vector3 slideDirection(
- const class Sphere& sphere,
- const Vector3& velocity,
- const float collisionTime,
- const Vector3& collisionLocation);
+ 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.
+ /**
+ 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.
+ @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);
+ @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.
+ 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.
+ @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 1.
- @param v1 line vertex 2.
+ @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.
- */
+ @return Closests point to <code>point</code> on the line segment.
+ */
static Vector3 closestPointOnLineSegment(
- const Vector3& v0,
- const Vector3& v1,
+ 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 1.
- @param v1 Triangle vertex 2.
- @param v2 Triangle vertex 3.
- @param point External point.
-
- @return Closests point to <code>point</code> on the perimeter of the
- triangle.
- */
- static Vector3 closestPointToTrianglePerimeter(
- const Vector3& v0,
- const Vector3& v1,
- const Vector3& v2,
- const Vector3& point);
+ float edgeLength,
+ 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 1.
- @param v1 Triangle vertex 2.
- @param v2 Triangle vertex 3.
- @param point External point.
-
- @return Closests point to <code>point</code> on the perimeter of the
- triangle.
- */
- static Vector3 closestPointToTrianglePerimeter(
+ 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 v Triangle vertices.
+ @param point External point.
+ @param edgeIndex The point lies on the edge between v[edgeIndex] and v[(edgeIndex + 1) % 3]
+
+ @return Closest point to <code>point</code> on the perimeter of the
+ triangle.
+ */
+ static Vector3 closestPointOnTrianglePerimeter(
const Vector3 v[3],
const Vector3 edgeDirection[3],
- const double edgeLength[3],
- const Vector3& point);
+ const float edgeLength[3],
+ const Vector3& point,
+ int& edgeIndex);
/**
- Tests whether a point is contained within the triangle defined by
- v0, v1, & v2 and its plane's normal.
-
- @param v0 Triangle vertex 1.
- @param v1 Triangle vertex 2.
- @param v2 Triangle vertex 3.
- @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.
-
- @return true - if point is inside the triangle.
- @return false - otherwise
- */
+ 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& v0,
+ const Vector3& v1,
+ const Vector3& v2,
+ const Vector3& normal,
const Vector3& point,
- Vector3::Axis primaryAxis = Vector3::DETECT_AXIS);
+ 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.
+ 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.
+ @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.
+ @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.
- */
+ @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.
+ /**
+ 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.
+ @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.
+ @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.
- */
+ @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.
+ /**
+ Tests for the intersection of two fixed spheres.
- @param sphere1 Fixed sphere 1.
- @param sphere2 Fixed sphere 2.
+ @param sphere1 Fixed sphere 1.
+ @param sphere2 Fixed sphere 2.
- @return true - if the two spheres touch.
- @return false - if there is no intersection.
- */
+ @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.
+ /**
+ Tests for the intersection of a fixed sphere and a fixed box.
- @param sphere Fixed sphere.
- @param box Fixed box.
+ @param sphere Fixed sphere.
+ @param box Fixed box.
- @return true - if the two objects touch.
- @return false - if there is no intersection.
- */
+ @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);
+
+ static bool fixedSolidBoxIntersectsFixedTriangle(
+ const AABox& box,
+ 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
- */
+ 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);
+ 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.
- */
+ 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);
+ 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.
+ 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.
+ @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);
+ */
+ 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/dep/include/g3dlite/G3D/Color1.h b/dep/include/g3dlite/G3D/Color1.h
new file mode 100644
index 00000000000..0f68c84b363
--- /dev/null
+++ b/dep/include/g3dlite/G3D/Color1.h
@@ -0,0 +1,144 @@
+/**
+ @file Color1.h
+
+ Monochrome Color class
+
+ @maintainer Morgan McGuire, http://graphics.cs.williams.edu
+ @created 2007-01-31
+ @edited 2009-03-20
+
+ Copyright 2000-2009, 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) {
+ }
+
+ inline bool isZero() const {
+ return value == 0.0f;
+ }
+
+ inline bool isOne() const {
+ return value == 1.0f;
+ }
+
+ static const Color1& one();
+
+ static const Color1& zero();
+
+ /** Returns the value three times */
+ class Color3 rgb() const;
+
+ 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/dep/include/g3dlite/G3D/Color1uint8.h b/dep/include/g3dlite/G3D/Color1uint8.h
new file mode 100644
index 00000000000..092099d0d17
--- /dev/null
+++ b/dep/include/g3dlite/G3D/Color1uint8.h
@@ -0,0 +1,91 @@
+/**
+ @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 {
+
+/**
+ 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.
+ */
+G3D_BEGIN_PACKED_CLASS(1)
+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;
+ }
+
+}
+G3D_END_PACKED_CLASS(1)
+}
+#endif
diff --git a/dep/include/g3dlite/G3D/Color3.h b/dep/include/g3dlite/G3D/Color3.h
new file mode 100644
index 00000000000..bffe434fc27
--- /dev/null
+++ b/dep/include/g3dlite/G3D/Color3.h
@@ -0,0 +1,432 @@
+/**
+ @file Color3.h
+
+ Color class
+
+ @maintainer Morgan McGuire, http://graphics.cs.williams.edu
+ @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 2009-04-28
+
+ Copyright 2000-2009, 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 {
+class Any;
+
+/**
+ 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();
+
+ /** \param any Must be in one of the following forms:
+ - Color3(#, #, #)
+ - Color3::fromARGB(#)
+ - Color3{r = #, g = #, b = #)
+ - Color3::one()
+ - Color3::zero()
+ */
+ Color3(const Any& any);
+
+ /** Converts the Color3 to an Any. */
+ operator Any() const;
+
+ 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]);
+
+ /** Returns this color */
+ const Color3& rgb() const {
+ return *this;
+ }
+
+ /**
+ Initialize from another color.
+ */
+ Color3 (const Color3& other);
+
+ Color3 (const class Color3uint8& other);
+
+ inline bool isZero() const {
+ return (r == 0.0f) && (g == 0.0f) && (b == 0.0f);
+ }
+
+ inline bool isOne() const {
+ return (r == 1.0f) && (g == 1.0f) && (b == 1.0f);
+ }
+
+ bool isFinite() const;
+
+ /**
+ 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();
+
+ /** Generate colors according to the ANSI color set, mod 16.
+ \sa pastelMap */
+ static Color3 ansiMap(uint32 i);
+
+ /**
+ Generate colors using a hash such that adjacent values
+ are unlikely to have similar colors.
+
+ Useful for rendering with
+ stable but arbitrary colors, e.g., when debugging a mesh
+ algorithm.
+
+ \sa ansiMap
+ */
+ static Color3 pastelMap(uint32 i);
+
+ /**
+ * 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;
+ inline Color3 operator* (float s) const {
+ return Color3(r * s, g * s, b * s);
+ }
+ Color3 operator* (const Color3& rkVector) const;
+ inline Color3 operator/ (float fScalar) const {
+ return (*this) * (1.0f / fScalar);
+ }
+ 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;
+
+ // 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));
+ }
+
+ 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));
+ }
+
+ /** Smallest element */
+ inline float min() const {
+ return G3D::min(G3D::min(r, g), b);
+ }
+
+ /** Largest element */
+ inline float max() const {
+ return G3D::max(G3D::max(r, g), 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* (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/dep/include/g3dlite/G3D/Color3uint8.h b/dep/include/g3dlite/G3D/Color3uint8.h
new file mode 100644
index 00000000000..bd4b00d7fd6
--- /dev/null
+++ b/dep/include/g3dlite/G3D/Color3uint8.h
@@ -0,0 +1,110 @@
+/**
+ @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).
+ */
+
+G3D_BEGIN_PACKED_CLASS(1)
+
+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
+ uint8& operator[] (int i) const {
+ debugAssert((unsigned int)i < 3);
+ return ((uint8*)this)[i];
+ }
+
+ operator uint8* () {
+ return (G3D::uint8*)this;
+ }
+
+ operator const uint8* () const {
+ return (uint8*)this;
+ }
+
+ 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);
+ }
+}
+G3D_END_PACKED_CLASS(1)
+
+} // namespace G3D
+
+#endif
diff --git a/dep/include/g3dlite/G3D/Color4.h b/dep/include/g3dlite/G3D/Color4.h
new file mode 100644
index 00000000000..d8858abbce2
--- /dev/null
+++ b/dep/include/g3dlite/G3D/Color4.h
@@ -0,0 +1,338 @@
+/**
+ @file Color4.h
+
+ Color class
+
+ @maintainer Morgan McGuire, http://graphics.cs.williams.edu
+ @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 2009-11-15
+
+ Copyright 2000-2009, 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 {
+
+class Any;
+
+/**
+ 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:
+
+ /** \param any Must be in one of the following forms:
+ - Color4(#, #, #, #)
+ - Color4::fromARGB(#)
+ - Color4{r = #, g = #, b = #, a = #)
+ */
+ Color4(const Any& any);
+
+ /** Converts the Color4 to an Any. */
+ operator Any() const;
+
+ /**
+ * 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);
+
+ Color4(float r, float g, float b, float a = 1.0);
+
+ static const Color4& one();
+
+ Color4(float value[4]);
+
+ /**
+ * Initialize from another color.
+ */
+ Color4(const Color4& other);
+
+
+ inline bool isZero() const {
+ return (r == 0.0f) && (g == 0.0f) && (b == 0.0f) && (a == 0.0f);
+ }
+
+ inline bool isOne() const {
+ return (r == 1.0f) && (g == 1.0f) && (b == 1.0f) && (a == 1.0f);
+ }
+
+ 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;
+
+ // 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;
+ inline Color4 operator* (const Color4& k) const {
+ return Color4(r*k.r, g*k.g, b*k.b, a * k.a);
+ }
+ 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();
+ static const Color4& nan();
+
+ inline bool isFinite() const {
+ return G3D::isFinite(r) && G3D::isFinite(g) && G3D::isFinite(b) && G3D::isFinite(a);
+ }
+
+ 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 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/dep/include/g3dlite/G3D/Color4uint8.h b/dep/include/g3dlite/G3D/Color4uint8.h
new file mode 100644
index 00000000000..ab8c0729276
--- /dev/null
+++ b/dep/include/g3dlite/G3D/Color4uint8.h
@@ -0,0 +1,115 @@
+/**
+ @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).
+
+ */
+G3D_BEGIN_PACKED_CLASS(1)
+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
+ uint8& operator[] (int i) const {
+ return ((uint8*)this)[i];
+ }
+
+ operator uint8* () {
+ return (uint8*)this;
+ }
+
+ operator const uint8* () const {
+ return (uint8*)this;
+ }
+
+
+ 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);
+ }
+
+}
+G3D_END_PACKED_CLASS(1)
+
+} // namespace G3D
+
+#endif
diff --git a/dep/include/g3dlite/G3D/Cone.h b/dep/include/g3dlite/G3D/Cone.h
new file mode 100644
index 00000000000..d801a9b348f
--- /dev/null
+++ b/dep/include/g3dlite/G3D/Cone.h
@@ -0,0 +1,68 @@
+/**
+ @file Cone.h
+
+ Cone class
+
+ @maintainer Morgan McGuire, http://graphics.cs.williams.edu
+ @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/dep/include/g3dlite/G3D/ConvexPolyhedron.h b/dep/include/g3dlite/G3D/ConvexPolyhedron.h
new file mode 100644
index 00000000000..a6fdd62cf90
--- /dev/null
+++ b/dep/include/g3dlite/G3D/ConvexPolyhedron.h
@@ -0,0 +1,180 @@
+/**
+ @file ConvexPolyhedron.h
+
+ @maintainer Morgan McGuire, http://graphics.cs.williams.edu
+
+ @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 Vector3& v0, const Vector3& v1, const Vector3& v2);
+ 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/dep/include/g3dlite/G3D/CoordinateFrame.h b/dep/include/g3dlite/G3D/CoordinateFrame.h
index 62cbbd47639..7ed4d0acc65 100644
--- a/dep/include/g3dlite/G3D/CoordinateFrame.h
+++ b/dep/include/g3dlite/G3D/CoordinateFrame.h
@@ -1,17 +1,17 @@
/**
@file CoordinateFrame.h
- @maintainer Morgan McGuire, matrix@graphics3d.com
-
+ @maintainer Morgan McGuire, http://graphics.cs.williams.edu
+
@created 2001-03-04
- @edited 2006-04-07
+ @edited 2009-04-29
- Copyright 2000-2006, Morgan McGuire.
+ Copyright 2000-2009, Morgan McGuire.
All rights reserved.
*/
-#ifndef G3D_COORDINATEFRAME_H
-#define G3D_COORDINATEFRAME_H
+#ifndef G3D_CFrame_h
+#define G3D_CFrame_h
#include "G3D/platform.h"
#include "G3D/Vector3.h"
@@ -24,47 +24,55 @@
#include <cstdarg>
#include <assert.h>
+#ifdef _MSC_VER
+// Turn off "conditional expression is constant" warning; MSVC generates this
+// for debug assertions in inlined methods.
+# pragma warning (disable : 4127)
+#endif
+
+
namespace G3D {
+class Any;
/**
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.
+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
+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
+from a Matrix4 using Matrix4::approxCoordinateFrame, however, because a Matrix4 is more
general than a CoordinateFrame, some information may be lost.
-See also: G3D::Matrix4, G3D::Quat
+@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. */
+ Matrix3 rotation;
- /**
- Takes object space points to world space.
- */
- Vector3 translation;
+ /** Takes object space points to world space. */
+ Vector3 translation;
- /**
- The direction an object "looks" relative to its own axes.
- @deprecated This is always -1 and will be fixed at that value in future releases.
- */
- static const float zLookDirection;
+ /** \param any Must be in one of the following forms:
+ - CFrame((matrix3 expr), (vector3 expr))
+ - CFrame::fromXYZYPRDegrees(#, #, #, #, #, #)
+ - CFrame { rotation = (matrix3 expr), translation = (vector3 expr) }
+ */
+ CoordinateFrame(const Any& any);
+
+ /** Converts the CFrame to an Any. */
+ operator Any() const;
inline bool operator==(const CoordinateFrame& other) const {
return (translation == other.translation) && (rotation == other.rotation);
@@ -83,14 +91,12 @@ public:
/**
Initializes to the identity coordinate frame.
*/
- inline CoordinateFrame() :
- rotation(Matrix3::identity()), translation(Vector3::zero()) {
- }
+ CoordinateFrame();
CoordinateFrame(const Vector3& _translation) :
rotation(Matrix3::identity()), translation(_translation) {
}
-
+
CoordinateFrame(const Matrix3 &rotation, const Vector3 &translation) :
rotation(rotation), translation(translation) {
}
@@ -99,6 +105,23 @@ public:
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) {}
@@ -117,21 +140,26 @@ public:
/** 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)
+ 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);
+ Vector3 look = rotation.column(2);
float angle = -(float) atan2(-look.x, look.z);
return angle;
}
@@ -157,23 +185,24 @@ public:
*/
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]);
+ 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.
+ 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]);
+ 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]);
}
/**
@@ -235,7 +264,7 @@ public:
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;
@@ -260,47 +289,29 @@ public:
const Vector3& target,
Vector3 up);
- /** @deprecated See lookVector */
- inline Vector3 getLookVector() const {
- return rotation.getColumn(2) * zLookDirection;
- }
-
/** The direction this camera is looking (its negative z axis)*/
- inline Vector3 lookVector() const {
- return rotation.getColumn(2) * zLookDirection;
- }
+ inline Vector3 lookVector() const {
+ return -rotation.column(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);
- }
-
- /**
- If a viewer looks along the look vector, this is the viewer's "left"
- @deprecated leftVector
- */
- inline Vector3 getLeftVector() const {
- return -rotation.getColumn(0);
+ return rotation.column(1);
}
- /** @deprecated See rightVector */
- inline Vector3 getRightVector() const {
- return rotation.getColumn(0);
- }
+ inline Vector3 rightVector() const {
+ return rotation.column(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);
- }
-
- inline Vector3 rightVector() const {
- return rotation.getColumn(0);
+ return -rotation.column(0);
}
/**
@@ -313,7 +324,8 @@ public:
};
+typedef CoordinateFrame CFrame;
+
} // namespace
#endif
-
diff --git a/dep/include/g3dlite/G3D/Crypto.h b/dep/include/g3dlite/G3D/Crypto.h
index 0bd5b75efca..56c816a4977 100644
--- a/dep/include/g3dlite/G3D/Crypto.h
+++ b/dep/include/g3dlite/G3D/Crypto.h
@@ -1,7 +1,8 @@
-/**
+/**
@file Crypto.h
-
- @maintainer Morgan McGuire, matrix@graphics3d.com
+
+ @maintainer Morgan McGuire, http://graphics.cs.williams.edu
+
@created 2006-03-29
@edited 2006-04-06
@@ -16,6 +17,48 @@
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:
@@ -31,6 +74,14 @@ public:
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.
*/
@@ -43,4 +94,3 @@ public:
}
#endif
-
diff --git a/dep/include/g3dlite/G3D/Cylinder.h b/dep/include/g3dlite/G3D/Cylinder.h
new file mode 100644
index 00000000000..85eba77b794
--- /dev/null
+++ b/dep/include/g3dlite/G3D/Cylinder.h
@@ -0,0 +1,92 @@
+/**
+ @file Cylinder.h
+
+ @maintainer Morgan McGuire, http://graphics.cs.williams.edu
+
+ @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/dep/include/g3dlite/G3D/EqualsTrait.h b/dep/include/g3dlite/G3D/EqualsTrait.h
new file mode 100644
index 00000000000..349cb5088fb
--- /dev/null
+++ b/dep/include/g3dlite/G3D/EqualsTrait.h
@@ -0,0 +1,26 @@
+/**
+ @file EqualsTrait.h
+
+ @maintainer Morgan McGuire, http://graphics.cs.williams.edu
+ @created 2008-10-01
+ @edited 2008-10-01
+ Copyright 2000-2009, 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/dep/include/g3dlite/G3D/G3D.h b/dep/include/g3dlite/G3D/G3D.h
new file mode 100644
index 00000000000..5b56b9c71dc
--- /dev/null
+++ b/dep/include/g3dlite/G3D/G3D.h
@@ -0,0 +1,162 @@
+/**
+ @file G3D.h
+
+ This header includes all of the G3D libraries in
+ appropriate namespaces.
+
+ @maintainer Morgan McGuire, http://graphics.cs.williams.edu
+
+ @created 2001-08-25
+ @edited 2010-01-30
+
+ Copyright 2000-2010, Morgan McGuire.
+ All rights reserved.
+*/
+
+#ifndef G3D_G3D_h
+#define G3D_G3D_h
+
+#define NOMINMAX 1
+#ifdef min
+ #undef min
+#endif
+#ifdef max
+ #undef max
+#endif
+
+#include "G3D/platform.h"
+#include "G3D/units.h"
+#include "G3D/ParseError.h"
+#include "G3D/Random.h"
+#include "G3D/Array.h"
+#include "G3D/SmallArray.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/Box2D.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/g3dfnmatch.h"
+#include "G3D/G3DGameUnits.h"
+#include "G3D/g3dmath.h"
+#include "G3D/uint128.h"
+#include "G3D/fileutils.h"
+#include "G3D/ReferenceCount.h"
+#include "G3D/Welder.h"
+#include "G3D/GMutex.h"
+#include "G3D/PrecomputedRandom.h"
+#include "G3D/MemoryManager.h"
+#include "G3D/AreaMemoryManager.h"
+#include "G3D/BumpMapPreprocess.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/Intersect.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/MeshAlg.h"
+#include "G3D/vectorMath.h"
+#include "G3D/Rect2D.h"
+#include "G3D/GCamera.h"
+#include "G3D/GLight.h"
+#include "G3D/KDTree.h"
+#include "G3D/PointKDTree.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/Any.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/dep/include/g3dlite/G3D/G3DAll.h b/dep/include/g3dlite/G3D/G3DAll.h
new file mode 100644
index 00000000000..1176fe742e7
--- /dev/null
+++ b/dep/include/g3dlite/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, http://graphics.cs.williams.edu
+
+ @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/dep/include/g3dlite/G3D/G3DGameUnits.h b/dep/include/g3dlite/G3D/G3DGameUnits.h
new file mode 100644
index 00000000000..e2bc2c811e8
--- /dev/null
+++ b/dep/include/g3dlite/G3D/G3DGameUnits.h
@@ -0,0 +1,42 @@
+/**
+ @file G3DGameUnits.h
+
+ @maintainer Morgan McGuire, http://graphics.cs.williams.edu
+ @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};
+
+/** \deprecated */
+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};
+
+/**
+ 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/dep/include/g3dlite/G3D/GCamera.h b/dep/include/g3dlite/G3D/GCamera.h
index 7bf8e26e562..018fbc85d59 100644
--- a/dep/include/g3dlite/G3D/GCamera.h
+++ b/dep/include/g3dlite/G3D/GCamera.h
@@ -1,59 +1,88 @@
/**
@file GCamera.h
- @maintainer Morgan McGuire, matrix@graphics3d.com
+ @maintainer Morgan McGuire, http://graphics.cs.williams.edu
- @created 2001-06-02
- @edited 2006-02-11
+ @created 2005-07-20
+ @edited 2009-04-20
*/
-#ifndef G3D_GCAMERA_H
-#define G3D_GCAMERA_H
+#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;
+class Any;
+
/**
- There is a viewport of width x height size in world space that corresponds to
- a screenWidth x screenHeight pixel grid on a
- renderDevice->getWidth() x renderDevice->getHeight()
- window.
+ 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::getViewport().
+ RenderDevice::viewport().
*/
class GCamera {
-private:
+public:
/**
- Vertical field of view (in radians)
+ Stores the direction of the field of view
*/
- float fieldOfView;
+ enum FOVDirection {HORIZONTAL, VERTICAL};
- /**
- The image plane depth corresponding to a vertical field of
- view, where the film size is 1x1.
- */
- float imagePlaneDepth;
+private:
+
+ /** Full field of view (in radians) */
+ float m_fieldOfView;
- /**
- Clipping plane, *not* imaging plane. Positive numbers.
- */
- float nearPlane;
+ /** Clipping plane, *not* imaging plane. Negative numbers. */
+ float m_nearPlaneZ;
- /**
- Positive
- */
- float farPlane;
+ /** Negative */
+ float m_farPlaneZ;
+
+ /** Stores the camera's location and orientation */
+ CoordinateFrame m_cframe;
- CoordinateFrame cframe;
+ /** Horizontal or Vertical */
+ FOVDirection m_direction;
public:
+ /** Must be of the format produced by the Any cast, e.g.,
+
+ <pre>
+ GCamera {
+ coordinateFrame = CFrame::fromXYZYPRDegrees(-13.3f, 8.0f, -1.9f, 246.6f, -3),
+ nearPlaneZ = -0.5,
+ farPlaneZ = -50,
+ fovDirection = "HORIZONTAL",
+ fovAngleDegrees = 90
+ }</pre>
+
+ Missing fields are filled from the default GCamera constructor.
+ */
+ GCamera(const Any& any);
+
+ operator Any() const;
+
class Frustum {
public:
class Face {
@@ -64,7 +93,7 @@ public:
/** The plane containing the face. */
Plane plane;
};
-
+
/** The vertices, in homogeneous space. If w == 0,
a vertex is at infinity. */
Array<Vector4> vertexPos;
@@ -79,50 +108,95 @@ public:
GCamera();
+ GCamera(const Matrix4& proj, const CFrame& frame);
+
virtual ~GCamera();
- CoordinateFrame getCoordinateFrame() const;
+ /** 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;
- void setCoordinateFrame(const CoordinateFrame& c);
- /**
- Sets the horizontal field of view, in radians. The
- initial angle is toRadians(55).
- <UL>
- <LI> toRadians(50) - Telephoto
- <LI> toRadians(110) - Normal
- <LI> toRadians(140) - Wide angle
- </UL>
+ /** Sets a new coordinate frame for the camera */
+ void setCoordinateFrame(const CoordinateFrame& c);
+
+ /** Sets \a P equal to the camera's projection matrix. This is the
+ matrix that maps points to the homogeneous clip cube that
+ varies from -1 to 1 on all axes. The projection matrix does
+ not include the camera transform.
+
+ This is the matrix that a RenderDevice (or OpenGL) uses as the projection matrix.
+ @sa RenderDevice::setProjectionAndCameraMatrix, RenderDevice::setProjectionMatrix, Matrix4::perspectiveProjection
*/
- void setFieldOfView(float angle);
+ void getProjectUnitMatrix(const Rect2D& viewport, Matrix4& P) const;
+
+ /** Sets \a P equal to the matrix that transforms points to pixel
+ coordinates on the given viewport. A point correspoinding to
+ the top-left corner of the viewport in camera space will
+ transform to viewport.x0y0() and the bottom-right to viewport.x1y1(). */
+ void getProjectPixelMatrix(const Rect2D& viewport, Matrix4& P) const;
+
+ /** Converts projected points from OpenGL standards
+ (-1, 1) to normal 3D coordinate standards (0, 1)
+
+ \deprecated
+ */ // TODO: Remove
+ Vector3 convertFromUnitToNormal(const Vector3& in, const Rect2D& viewport) const;
/**
- Sets the field of view based on a desired image plane depth
- (<I>s'</I>) and film dimensions in world space. Depth must be positive. Width,
- depth, and height are measured in the same units (meters are
- recommended). The field of view will span the diagonal to the
- image.<P> <I>Note</I>: to simulate a 35mm GCamera, set width =
- 0.36 mm and height = 0.24 mm. The width and height used are
- generally not the pixel dimensions of the image.
+ Sets the field of view, in radians. The
+ initial angle is toRadians(55). Must specify
+ the direction of the angle.
+
+ This is the full angle, i.e., from the left side of the
+ viewport to the right side.
*/
- void setImagePlaneDepth(
- float depth,
- const class Rect2D& viewport);
+ void setFieldOfView(float angle, FOVDirection direction);
- inline double getFieldOfView() const {
- return fieldOfView;
+ /** Returns the current full field of view angle (from the left side of the
+ viewport to the right side) 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 <I>rhw</I>.
+ 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.
*/
- G3D::Vector3 project(
- const G3D::Vector3& point,
- const class Rect2D& viewport) const;
+ 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
@@ -133,23 +207,29 @@ public:
/**
Returns the world space 3D viewport corners. These
are at the near clipping plane. The corners are constructed
- from the nearPlaneZ, getViewportWidth, and getViewportHeight.
+ from the nearPlaneZ, viewportWidth, and viewportHeight.
"left" and "right" are from the GCamera's perspective.
*/
- void get3DViewportCorners(
- const class Rect2D& viewport,
- Vector3& outUR,
- Vector3& outUL,
- Vector3& outLL,
- Vector3& outLR) const;
+ void getNearViewportCorners(const class Rect2D& viewport,
+ Vector3& outUR, Vector3& outUL,
+ Vector3& outLL, Vector3& outLR) const;
/**
- Returns the image plane depth, <I>s'</I>, given the current field
- of view for film of dimensions width x height. See
- setImagePlaneDepth for a discussion of worldspace values width and height.
- */
- float getImagePlaneDepth(
- const class Rect2D& viewport) 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
@@ -157,10 +237,13 @@ public:
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.
- */
+ through pixel centers, add 0.5 to x and y.
+ */
Ray worldRay(
float x,
float y,
@@ -169,80 +252,86 @@ public:
/**
Returns a negative z-value.
*/
- inline float getNearPlaneZ() const {
- return -nearPlane;
+ inline float nearPlaneZ() const {
+ return m_nearPlaneZ;
}
/**
Returns a negative z-value.
*/
- inline float getFarPlaneZ() const {
- return -farPlane;
+ 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);
- farPlane = -z;
+ m_farPlaneZ = z;
}
-
+
+ /**
+ Sets a new value for the near clipping plane
+ Expects a negative value
+ */
inline void setNearPlaneZ(float z) {
debugAssert(z < 0);
- nearPlane = -z;
+ m_nearPlaneZ = z;
}
/**
- Returns the GCamera space width of the viewport.
- */
- float getViewportWidth(
- const class Rect2D& viewport) const;
-
- /**
- Returns the GCamera space height of the viewport.
+ Returns the camera space width of the viewport at the near plane.
*/
- float getViewportHeight(
- const class Rect2D& viewport) const;
+ float viewportWidth(const class Rect2D& viewport) const;
/**
- Read back a GCamera space z-value at pixel (x, y) from the depth buffer.
- double getZValue(
- double x,
- double y,
- const class Rect2D& viewport,
- double polygonOffset = 0) 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.
+ /**
+ 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;
+ 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.
+ /**
+ Returns the world space view frustum, which is a truncated pyramid describing
+ the volume of space seen by this camera.
*/
- void getFrustum(const Rect2D& viewport, GCamera::Frustum& f) const;
-
- GCamera::Frustum frustum(const Rect2D& viewport) const;
-
+ 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/dep/include/g3dlite/G3D/GImage.h b/dep/include/g3dlite/G3D/GImage.h
new file mode 100644
index 00000000000..8ae11134fc9
--- /dev/null
+++ b/dep/include/g3dlite/G3D/GImage.h
@@ -0,0 +1,607 @@
+/**
+ \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, http://graphics.cs.williams.edu
+
+ \created 2002-05-27
+ \edited 2010-01-04
+
+ Copyright 2000-2010, 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"
+#include "G3D/MemoryManager.h"
+#include "G3D/BumpMapPreprocess.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), which includes PPM, PGM,
+ and PBM. 8-bit paletted PCX, 24-bit PCX, and ICO are supported for
+ decoding only.
+
+ Sample usage:
+
+ \verbatim
+ // Loading from disk:
+ G3D::GImage im1("test.jpg");
+
+ // Loading from memory:
+ G3D::GImage im2(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(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");
+ \endverbatim
+
+ 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:
+
+ /** Used exclusively for allocating m_byte; this may be an
+ implementation that allocates directly on a GPU.*/
+ MemoryManager::Ref m_memMan;
+ uint8* m_byte;
+
+ int m_channels;
+ int m_width;
+ int m_height;
+
+public:
+
+ class Error {
+ public:
+ Error(
+ const std::string& reason,
+ const std::string& filename = "") :
+ reason(reason), filename(filename) {}
+
+ std::string reason;
+ std::string filename;
+ };
+
+ /** PGM, PPM, and PBM all come in two versions and are classified as PPM_* files */
+ enum Format {JPEG, BMP, TGA, PCX, ICO, PNG,
+ PPM_BINARY, PGM_BINARY = PPM_BINARY,
+ PPM_ASCII, PGM_ASCII = PPM_ASCII,
+ AUTODETECT, UNKNOWN};
+
+
+ /**
+ The number of channels; either 3 (RGB) or 4 (RGBA)
+ */
+ inline int channels() const {
+ return m_channels;
+ }
+
+ inline int width() const {
+ return m_width;
+ }
+
+ inline int height() const {
+ return m_height;
+ }
+
+ inline const uint8* byte() const {
+ return m_byte;
+ }
+
+ /** Returns a pointer to the underlying data, which is stored
+ in row-major order without row padding.
+ e.g., <code>uint8* ptr = image.rawData<uint8>();
+ */
+ template<typename Type>
+ inline const Type* rawData() const {
+ return (Type*)m_byte;
+ }
+
+ /** \copybrief GImage::rawData() const */
+ template<typename Type>
+ inline Type* rawData() {
+ return (Type*)m_byte;
+ }
+
+ inline const Color1uint8* pixel1() const {
+ debugAssertM(m_channels == 1, format("Tried to call GImage::pixel1 on an image with %d channels", m_channels));
+ return (Color1uint8*)m_byte;
+ }
+
+ inline Color1uint8* pixel1() {
+ debugAssertM(m_channels == 1, format("Tried to call GImage::pixel1 on an image with %d channels", m_channels));
+ return (Color1uint8*)m_byte;
+ }
+
+ /** Returns a pointer to the upper left pixel
+ as Color4uint8.
+ */
+ inline const Color4uint8* pixel4() const {
+ debugAssertM(m_channels == 4, format("Tried to call GImage::pixel4 on an image with %d channels", m_channels));
+ return (Color4uint8*)m_byte;
+ }
+
+ inline Color4uint8* pixel4() {
+ debugAssert(m_channels == 4);
+ return (Color4uint8*)m_byte;
+ }
+
+ /** Returns a pointer to the upper left pixel
+ as Color3uint8.
+ */
+ inline const Color3uint8* pixel3() const {
+ debugAssertM(m_channels == 3, format("Tried to call GImage::pixel3 on an image with %d channels", m_channels));
+ return (Color3uint8*)m_byte;
+ }
+
+ inline Color3uint8* pixel3() {
+ debugAssert(m_channels == 3);
+ return (Color3uint8*)m_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 < m_width);
+ debugAssert(y >= 0 && y < m_height);
+ return pixel1()[x + y * m_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 < m_width);
+ debugAssert(y >= 0 && y < m_height);
+ return pixel1()[x + y * m_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 < m_width);
+ debugAssert(y >= 0 && y < m_height);
+ return pixel3()[x + y * m_width];
+ }
+
+ inline Color3uint8& pixel3(int x, int y) {
+ debugAssert(x >= 0 && x < m_width);
+ debugAssert(y >= 0 && y < m_height);
+ return pixel3()[x + y * m_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 < m_width);
+ debugAssert(y >= 0 && y < m_height);
+ return pixel4()[x + y * m_width];
+ }
+
+ inline Color4uint8& pixel4(int x, int y) {
+ debugAssert(x >= 0 && x < m_width);
+ debugAssert(y >= 0 && y < m_height);
+ return pixel4()[x + y * m_width];
+ }
+
+ inline uint8* byte() {
+ return m_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:
+
+ /** Predicts the image file format of \a filename */
+ static Format resolveFormat(const std::string& filename);
+
+ void flipHorizontal();
+ void flipVertical();
+ void rotate90CW(int numTimes = 1);
+
+ /**
+ Create an empty image of the given size.
+ \sa load()
+ */
+ GImage(
+ int width = 0,
+ int height = 0,
+ int channels = 3,
+ const MemoryManager::Ref& m = MemoryManager::create());
+
+ /**
+ Load an encoded image from disk and decode it.
+ Throws GImage::Error if something goes wrong.
+ */
+ GImage(
+ const std::string& filename,
+ Format format = AUTODETECT,
+ const MemoryManager::Ref& m = MemoryManager::create());
+
+ /**
+ Decodes an image stored in a buffer.
+ */
+ GImage(
+ const unsigned char*data,
+ int length,
+ Format format = AUTODETECT,
+ const MemoryManager::Ref& m = MemoryManager::create());
+
+ GImage(
+ const GImage& other,
+ const MemoryManager::Ref& m = MemoryManager::create());
+
+ 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;
+
+ /**
+ 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;
+
+ /**
+ Loads an image from disk (clearing the old one first),
+ using the existing memory manager.
+ */
+ 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 (\a width x \a height) with the
+ number of \a channels specified.
+
+ \param zero If true, all data is set to 0 (black).
+ */
+ void resize(int width, int height, int channels, bool zero = true);
+
+ /**
+ 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.
+ TODO: provide a memory manager
+ */
+ 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.
+ @param 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);
+
+ static void LtoRGB
+ (const uint8* in,
+ uint8* out,
+ int numPixels);
+
+ static void LtoRGBA
+ (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>
+
+ */
+ static void computeNormalMap(
+ const class GImage& bump,
+ class GImage& normal,
+ const BumpMapPreprocess& preprocess = BumpMapPreprocess());
+
+ static void computeNormalMap
+ (int width,
+ int height,
+ int channels,
+ const uint8* src,
+ GImage& normal,
+ const BumpMapPreprocess& preprocess = BumpMapPreprocess());
+
+ /**
+ 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/dep/include/g3dlite/G3D/GLight.h b/dep/include/g3dlite/G3D/GLight.h
new file mode 100644
index 00000000000..3a95f1a8114
--- /dev/null
+++ b/dep/include/g3dlite/G3D/GLight.h
@@ -0,0 +1,106 @@
+/**
+ @file GLight.h
+
+ @maintainer Morgan McGuire, http://graphics.cs.williams.edu
+
+ @created 2003-11-12
+ @edited 2009-11-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 {
+class Any;
+
+/**
+ A light representation that closely follows the OpenGL light format.
+ */
+class GLight {
+public:
+ /** World space position (for a directional light, w = 0 */
+ Vector4 position;
+
+ /** For a spot or directional light, this is the "right vector" that will be used when constructing
+ a reference frame(). */
+ Vector3 rightDirection;
+
+ /** 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). Values less than 90 = spot light */
+ float spotCutoff;
+
+ /** If true, G3D::SuperShader will render a cone of light large
+ enough to encompass the entire square that bounds the cutoff
+ angle. This produces a square prism instead of a cone of light
+ when used with a G3D::ShadowMap. for an unshadowed light this
+ has no effect.*/
+ bool spotSquare;
+
+ /** 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();
+
+ /** Accepted forms:
+ - GLight::directional( vector3, color3, [bool, [bool]])
+ - GLight::spot(vector3, vector3, #, color3, [#, [#, [#, [#, [bool, [bool]]]])
+ - GLight::point(vector3, color3, [#, [#, [#, [#, [bool, [bool]]]])
+ - GLight { [all fields] }
+ */
+ GLight(const Any& any);
+
+ /** Converts the Color3 to an Any. */
+ operator Any() const;
+
+ /** @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. I.e., a value of 45 produces a light with a 90-degree
+ cone of view.
+ */
+ 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;
+
+ /** Computes a reference frame (e.g., for use with G3D::ShadowMap */
+ class CoordinateFrame frame() const;
+
+ bool operator==(const GLight& other) const;
+ bool operator!=(const GLight& other) const;
+};
+
+} // namespace
+#endif
+
diff --git a/dep/include/g3dlite/G3D/GMutex.h b/dep/include/g3dlite/G3D/GMutex.h
new file mode 100644
index 00000000000..3469b812736
--- /dev/null
+++ b/dep/include/g3dlite/G3D/GMutex.h
@@ -0,0 +1,123 @@
+/**
+ @file GMutex.h
+
+ @created 2005-09-22
+ @edited 2009-03-25
+ */
+
+#ifndef G3D_GMutex_h
+#define G3D_GMutex_h
+
+#include "G3D/platform.h"
+#include "G3D/AtomicInt32.h"
+#include "G3D/debugAssert.h"
+#include <string>
+
+#ifndef G3D_WIN32
+# include <pthread.h>
+# include <signal.h>
+#endif
+
+
+namespace G3D {
+
+/**
+ \brief A mutual exclusion lock that busy-waits when locking.
+
+ On a machine with one (significant) thread per processor core,
+ a spinlock may be substantially faster than a mutex.
+
+ \sa G3D::GThread, G3D::GMutex, G3D::AtomicInt32
+ */
+class Spinlock {
+private:
+
+ AtomicInt32 x;
+
+public:
+
+ inline Spinlock() : x(0) {}
+
+ /** Busy waits until the lock is unlocked, then locks it
+ exclusively. Returns true if the lock succeeded on the first
+ try (indicating no contention). */
+ inline bool lock() {
+ bool first = true;
+ while (x.compareAndSet(0, 1) == 1) {
+ first = false;
+# ifdef G3D_WIN32
+ Sleep(0);
+# else
+ usleep(0);
+# endif
+ }
+ return first;
+ }
+
+ inline void unlock() {
+ x.compareAndSet(1, 0);
+ }
+
+};
+
+/**
+ \brief Mutual exclusion lock used for synchronization.
+
+ @sa G3D::GThread, G3D::AtomicInt32, G3D::Spinlock
+*/
+class GMutex {
+private:
+# ifdef G3D_WIN32
+ CRITICAL_SECTION m_handle;
+# else
+ pthread_mutex_t m_handle;
+ pthread_mutexattr_t m_attr;
+# 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();
+
+ /** Locks the mutex if it not already locked.
+ Returns true if lock successful, false otherwise. */
+ bool tryLock();
+
+ /** 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();
+ }
+};
+
+} // G3D
+
+#endif
diff --git a/dep/include/g3dlite/G3D/GThread.h b/dep/include/g3dlite/G3D/GThread.h
new file mode 100644
index 00000000000..58437efc3fb
--- /dev/null
+++ b/dep/include/g3dlite/G3D/GThread.h
@@ -0,0 +1,121 @@
+/**
+ @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::Spinlock, 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;
+
+protected:
+
+ /** Overriden by the thread implementor */
+ virtual void threadMain() = 0;
+
+public:
+ typedef ReferenceCountedPointer<class GThread> Ref;
+ enum SpawnBehavior {USE_NEW_THREAD, USE_CURRENT_THREAD};
+
+ 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 = NULL);
+
+ /** 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).
+
+ @param behavior If USE_CURRENT_THREAD, rather than spawning a new thread, this routine
+ runs threadMain on the current thread.
+ */
+ bool start(SpawnBehavior behavior = USE_NEW_THREAD);
+
+ /** 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;
+ }
+};
+
+
+} // namespace G3D
+
+#endif //G3D_GTHREAD_H
diff --git a/dep/include/g3dlite/G3D/GUniqueID.h b/dep/include/g3dlite/G3D/GUniqueID.h
new file mode 100644
index 00000000000..c8b775c2e66
--- /dev/null
+++ b/dep/include/g3dlite/G3D/GUniqueID.h
@@ -0,0 +1,69 @@
+/**
+ @file GUniqueID.h
+ @author Morgan McGuire, http://graphics.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/dep/include/g3dlite/G3D/HashTrait.h b/dep/include/g3dlite/G3D/HashTrait.h
new file mode 100644
index 00000000000..ca35da48643
--- /dev/null
+++ b/dep/include/g3dlite/G3D/HashTrait.h
@@ -0,0 +1,92 @@
+/**
+ @file HashTrait.h
+
+ @maintainer Morgan McGuire, http://graphics.cs.williams.edu
+ @created 2008-10-01
+ @edited 2009-11-01
+
+ Copyright 2000-2009, 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); }
+};
+
+#if 0
+template <> struct HashTrait <int> {
+ static size_t hashCode(int k) { return static_cast<size_t>(k); }
+};
+#endif
+
+template <> struct HashTrait <G3D::int16> {
+ static size_t hashCode(G3D::int16 k) { return static_cast<size_t>(k); }
+};
+
+template <> struct HashTrait <G3D::uint16> {
+ static size_t hashCode(G3D::uint16 k) { return static_cast<size_t>(k); }
+};
+
+//template <> struct HashTrait <int> {
+// static size_t hashCode(int k) { return static_cast<size_t>(k); }
+//};
+
+template <> struct HashTrait <G3D::int32> {
+ static size_t hashCode(G3D::int32 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); }
+};
+
+#if 0
+template <> struct HashTrait <long unsigned int> {
+ static size_t hashCode(G3D::uint32 k) { return static_cast<size_t>(k); }
+};
+#endif
+
+template <> struct HashTrait <G3D::int64> {
+ static size_t hashCode(G3D::int64 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(0xCF470AAC6CB293D2ULL, 0xF52F88BF32307F8FULL);
+
+ 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/dep/include/g3dlite/G3D/Image1.h b/dep/include/g3dlite/G3D/Image1.h
new file mode 100644
index 00000000000..711e83f2079
--- /dev/null
+++ b/dep/include/g3dlite/G3D/Image1.h
@@ -0,0 +1,81 @@
+/**
+ @file Image1.h
+
+ @maintainer Morgan McGuire, http://graphics.cs.williams.edu
+
+ @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 ReferenceCountedPointer<class Image1> Ref;
+ typedef Color1 Storage;
+ typedef Color1 Compute;
+
+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/dep/include/g3dlite/G3D/Image1uint8.h b/dep/include/g3dlite/G3D/Image1uint8.h
new file mode 100644
index 00000000000..f32e022e92a
--- /dev/null
+++ b/dep/include/g3dlite/G3D/Image1uint8.h
@@ -0,0 +1,80 @@
+/**
+ @file Image1uint8.h
+
+ @maintainer Morgan McGuire, http://graphics.cs.williams.edu
+
+ @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/dep/include/g3dlite/G3D/Image3.h b/dep/include/g3dlite/G3D/Image3.h
new file mode 100644
index 00000000000..13cb8fa7faf
--- /dev/null
+++ b/dep/include/g3dlite/G3D/Image3.h
@@ -0,0 +1,81 @@
+/**
+ @file Image3.h
+
+ @maintainer Morgan McGuire, http://graphics.cs.williams.edu
+
+ @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 ReferenceCountedPointer<class Image3> Ref;
+ typedef Color3 Storage;
+ typedef Color3 Compute;
+
+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/dep/include/g3dlite/G3D/Image3uint8.h b/dep/include/g3dlite/G3D/Image3uint8.h
new file mode 100644
index 00000000000..d4fdbc169ca
--- /dev/null
+++ b/dep/include/g3dlite/G3D/Image3uint8.h
@@ -0,0 +1,85 @@
+/**
+ @file Image3uint8.h
+
+ @maintainer Morgan McGuire, http://graphics.cs.williams.edu
+
+ @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/dep/include/g3dlite/G3D/Image4.h b/dep/include/g3dlite/G3D/Image4.h
new file mode 100644
index 00000000000..21d7f1e79b1
--- /dev/null
+++ b/dep/include/g3dlite/G3D/Image4.h
@@ -0,0 +1,86 @@
+/**
+ @file Image4.h
+
+ @maintainer Morgan McGuire, http://graphics.cs.williams.edu
+
+ @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 RGBA, A=1 is assumed.
+
+ Bilinear interpolation on Image4 is about 8x faster than on
+ Image4uint8 due to the large cost of converting int->float on modern
+ machines.
+
+ @sa G3D::Image4uint8, G3D::GImage.
+ */
+class Image4 : public Map2D<Color4, Color4> {
+public:
+
+ typedef Image4 Type;
+ typedef ReferenceCountedPointer<class Image4> Ref;
+ typedef Color4 Storage;
+ typedef Color4 Compute;
+
+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/dep/include/g3dlite/G3D/Image4uint8.h b/dep/include/g3dlite/G3D/Image4uint8.h
new file mode 100644
index 00000000000..46df6b490b4
--- /dev/null
+++ b/dep/include/g3dlite/G3D/Image4uint8.h
@@ -0,0 +1,85 @@
+/**
+ @file Image4uint8.h
+
+ @maintainer Morgan McGuire, http://graphics.cs.williams.edu
+
+ @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/dep/include/g3dlite/G3D/ImageFormat.h b/dep/include/g3dlite/G3D/ImageFormat.h
new file mode 100644
index 00000000000..7f098322d26
--- /dev/null
+++ b/dep/include/g3dlite/G3D/ImageFormat.h
@@ -0,0 +1,419 @@
+/**
+ @file ImageFormat.h
+
+ @maintainer Morgan McGuire, http://graphics.cs.williams.edu
+
+ @created 2003-05-23
+ @edited 2010-01-01
+*/
+
+#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_R11G11B10F,
+ CODE_RGB9E5F,
+
+ CODE_RGB8I,
+ CODE_RGB8UI,
+
+ CODE_ARGB8,
+ CODE_BGR8,
+
+ CODE_RG8,
+ CODE_RG8I,
+ CODE_RG8UI,
+
+ CODE_RGBA8,
+ CODE_RGBA16,
+ CODE_RGBA16F,
+ CODE_RGBA32F,
+
+ CODE_RGBA32UI,
+
+ 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_SRGB8,
+ CODE_SRGBA8,
+
+ CODE_SL8,
+ CODE_SLA8,
+
+ CODE_SRGB_DXT1,
+ CODE_SRGBA_DXT1,
+ CODE_SRGBA_DXT3,
+ CODE_SRGBA_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,
+ COLOR_SPACE_SRGB
+ };
+
+ 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 (type) 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 format.*/
+ const std::string& name() const;
+
+ /** Takes the same values that name() returns */
+ static const ImageFormat* fromString(const std::string& s);
+
+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* RG8();
+ static const ImageFormat* RG8I();
+ static const ImageFormat* RG8UI();
+
+ 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* RGBA32UI();
+
+ static const ImageFormat* R11G11B10F();
+
+ static const ImageFormat* RGB9E5F();
+
+ static const ImageFormat* RGB8I();
+
+ static const ImageFormat* RGB8UI();
+
+ static const ImageFormat* RGB_DXT1();
+
+ static const ImageFormat* RGBA_DXT1();
+
+ static const ImageFormat* RGBA_DXT3();
+
+ static const ImageFormat* RGBA_DXT5();
+
+ static const ImageFormat* SRGB8();
+
+ static const ImageFormat* SRGBA8();
+
+ static const ImageFormat* SL8();
+
+ static const ImageFormat* SLA8();
+
+ static const ImageFormat* SRGB_DXT1();
+
+ static const ImageFormat* SRGBA_DXT1();
+
+ static const ImageFormat* SRGBA_DXT3();
+
+ static const ImageFormat* SRGBA_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/dep/include/g3dlite/G3D/Intersect.h b/dep/include/g3dlite/G3D/Intersect.h
new file mode 100644
index 00000000000..4a3c8fb4540
--- /dev/null
+++ b/dep/include/g3dlite/G3D/Intersect.h
@@ -0,0 +1,55 @@
+/**
+ @file Intersect.h
+
+ @maintainer Morgan McGuire, http://graphics.cs.williams.edu
+
+ @created 2009-06-29
+ @edited 2009-06-29
+
+ Copyright 2000-2009, Morgan McGuire.
+ All rights reserved.
+
+ From the G3D Innovation Engine
+ http://g3d.sf.net
+ */
+#ifndef G3D_Intersect
+#define G3D_Intersect
+
+#include "G3D/platform.h"
+#include "G3D/Ray.h"
+#include "G3D/AABox.h"
+
+namespace G3D {
+
+/**
+ @beta
+ */
+class Intersect {
+public:
+
+ /** \brief Returns true if the intersection of the ray and the solid box is non-empty.
+
+ \cite "Fast Ray / Axis-Aligned Bounding Box Overlap Tests using Ray Slopes"
+ by Martin Eisemann, Thorsten Grosch, Stefan Müller and Marcus Magnor
+ Computer Graphics Lab, TU Braunschweig, Germany and
+ University of Koblenz-Landau, Germany
+ */
+ static bool __fastcall rayAABox(const Ray& ray, const AABox& box);
+
+ /** \brief Returns true if the intersection of the ray and the solid box is non-empty.
+
+ \param time If there is an intersection, set to the time to that intersection. If the ray origin is inside the box,
+ this is a negative value indicating the distance backwards from the ray origin to the first intersection.
+ \a time is not set if there is no intersection.
+
+ \cite Slope-Mul method from "Fast Ray / Axis-Aligned Bounding Box Overlap Tests using Ray Slopes"
+ by Martin Eisemann, Thorsten Grosch, Stefan Müller and Marcus Magnor
+ Computer Graphics Lab, TU Braunschweig, Germany and
+ University of Koblenz-Landau, Germany
+ */
+ static bool __fastcall rayAABox(const Ray& ray, const AABox& box, float& time);
+};
+
+}
+
+#endif
diff --git a/dep/include/g3dlite/G3D/KDTree.h b/dep/include/g3dlite/G3D/KDTree.h
new file mode 100644
index 00000000000..4785ef2baea
--- /dev/null
+++ b/dep/include/g3dlite/G3D/KDTree.h
@@ -0,0 +1,1667 @@
+/**
+ @file KDTree.h
+
+ @maintainer Morgan McGuire, http://graphics.cs.williams.edu
+
+ @created 2004-01-11
+ @edited 2009-12-28
+
+ Copyright 2000-2009, 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(); // public constructor of no arguments
+ 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 {
+ (void)ignore;
+ 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
+ }
+
+
+ /** Clear, set the contents to the values in the array, and then balance */
+ void setContents(const Array<T>& array, int valuesPerNode = 5, int numMeanSplits = 3) {
+ clear();
+ insert(array);
+ balance(valuesPerNode, numMeanSplits);
+ }
+
+
+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);
+ }
+
+ void getIntersectingMembers(const Array<Plane>& plane, Array<T>& members) const {
+ Array<T*> temp;
+ getIntersectingMembers(plane, temp, root, 0xFFFFFF);
+ for (int i = 0; i < temp.size(); ++i) {
+ members.append(*temp[i]);
+ }
+ }
+
+ /**
+ 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);
+ }
+
+ void getIntersectingMembers(const GCamera::Frustum& frustum, Array<T>& members) const {
+ Array<T*> temp;
+ getIntersectingMembers(frustum, temp);
+ for (int i = 0; i < temp.size(); ++i) {
+ members.append(*temp[i]);
+ }
+ }
+
+ /**
+ 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);
+ }
+
+ void getIntersectingMembers(const AABox& box, Array<T>& members) const {
+ Array<T*> temp;
+ getIntersectingMembers(box, temp);
+ for (int i = 0; i < temp.size(); ++i) {
+ members.append(*temp[i]);
+ }
+ }
+
+
+ /**
+ 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 = finf();
+ 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);
+ }
+
+ void getIntersectingMembers(const Sphere& sphere, Array<T>& members) const {
+ Array<T*> temp;
+ getIntersectingMembers(sphere, temp);
+ for (int i = 0; i < temp.size(); ++i) {
+ members.append(*temp[i]);
+ }
+ }
+
+ /**
+ 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));
+ }
+ }
+
+
+ /** If a value that is EqualsFunc to @a value is present, returns a pointer to the
+ version stored in the data structure, otherwise returns NULL.
+ */
+ const T* getPointer(const T& value) const {
+ // Temporarily create a handle and member
+ Handle h(value);
+ const Member* member = memberTable.getKeyPointer(Member(&h));
+ if (member == NULL) {
+ // Not found
+ return NULL;
+ } else {
+ return &(member->handle->value);
+ }
+ }
+
+
+ /**
+ 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
+};
+
+
+}
+
+#endif
diff --git a/dep/include/g3dlite/G3D/Line.h b/dep/include/g3dlite/G3D/Line.h
index fedcb4d8cf0..3579a6becec 100644
--- a/dep/include/g3dlite/G3D/Line.h
+++ b/dep/include/g3dlite/G3D/Line.h
@@ -1,10 +1,10 @@
/**
@file Line.h
-
+
Line class
-
- @maintainer Morgan McGuire, matrix@graphics3d.com
-
+
+ @maintainer Morgan McGuire, http://graphics.cs.williams.edu
+
@created 2001-06-02
@edited 2006-02-28
*/
@@ -38,6 +38,12 @@ 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() {}
/**
@@ -77,9 +83,23 @@ public:
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
+#endif
diff --git a/dep/include/g3dlite/G3D/LineSegment.h b/dep/include/g3dlite/G3D/LineSegment.h
new file mode 100644
index 00000000000..70210ec7e00
--- /dev/null
+++ b/dep/include/g3dlite/G3D/LineSegment.h
@@ -0,0 +1,115 @@
+/**
+ @file LineSegment.h
+
+ @maintainer Morgan McGuire, http://graphics.cs.williams.edu
+
+ @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/dep/include/g3dlite/G3D/Log.h b/dep/include/g3dlite/G3D/Log.h
new file mode 100644
index 00000000000..d252d0c1a17
--- /dev/null
+++ b/dep/include/g3dlite/G3D/Log.h
@@ -0,0 +1,109 @@
+/**
+ @file Log.h
+
+ @maintainer Morgan McGuire, http://graphics.cs.williams.edu
+ @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, ...);
+
+/** Does not flush the buffer; follow up with a logPrintf to force the flush. */
+void logLazyPrintf(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;
+
+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;
+ /** Does not flush */
+ void __cdecl lazyvprintf(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/dep/include/g3dlite/G3D/Map2D.h b/dep/include/g3dlite/G3D/Map2D.h
new file mode 100644
index 00000000000..9af9f7242c1
--- /dev/null
+++ b/dep/include/g3dlite/G3D/Map2D.h
@@ -0,0 +1,667 @@
+/**
+ @file Map2D.h
+
+ More flexible support than provided by G3D::GImage.
+
+ @maintainer Morgan McGuire, morgan@cs.brown.edu
+ @created 2004-10-10
+ @edited 2009-03-24
+ */
+#ifndef G3D_Map2D_h
+#define G3D_Map2D_h
+
+#include "G3D/platform.h"
+#include "G3D/g3dmath.h"
+#include "G3D/Array.h"
+#include "G3D/vectorMath.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, http://graphics.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 {
+ int ix = iRound(x);
+ int iy = iRound(y);
+ return Compute(get(ix, iy, 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 {
+ const int i = iFloor(x);
+ const int j = iFloor(y);
+
+ const float fX = x - i;
+ const float fY = y - j;
+
+ // Horizontal interpolation, first row
+ const Compute& t0 = get(i, j, wrap);
+ const Compute& t1 = get(i + 1, j, wrap);
+
+ // Horizontal interpolation, second row
+ const Compute& t2 = get(i, j + 1, wrap);
+ const Compute& t3 = get(i + 1, j + 1, wrap);
+
+ const Compute& A = lerp(t0, t1, fX);
+ const 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;
+
+ Compute vsample[4];
+ for (int v = 0; v < 4; ++v) {
+
+ // Horizontal interpolation
+ 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/dep/include/g3dlite/G3D/Matrix.h b/dep/include/g3dlite/G3D/Matrix.h
new file mode 100644
index 00000000000..3c5394d9a76
--- /dev/null
+++ b/dep/include/g3dlite/G3D/Matrix.h
@@ -0,0 +1,634 @@
+/**
+ @file Matrix.h
+ @author Morgan McGuire, http://graphics.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/dep/include/g3dlite/G3D/Matrix2.h b/dep/include/g3dlite/G3D/Matrix2.h
new file mode 100644
index 00000000000..eaf4aefa220
--- /dev/null
+++ b/dep/include/g3dlite/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/dep/include/g3dlite/G3D/Matrix3.h b/dep/include/g3dlite/G3D/Matrix3.h
index 99e049654ee..06ec7e67474 100644
--- a/dep/include/g3dlite/G3D/Matrix3.h
+++ b/dep/include/g3dlite/G3D/Matrix3.h
@@ -1,26 +1,36 @@
/**
@file Matrix3.h
-
+
3x3 matrix class
-
- @maintainer Morgan McGuire, matrix@graphics3d.com
-
+
+ @maintainer Morgan McGuire, http://graphics.cs.williams.edu
+
@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
+#ifndef G3D_Matrix3_h
+#define G3D_Matrix3_h
#include "G3D/platform.h"
-#include "G3D/System.h"
#include "G3D/Vector3.h"
#include "G3D/Vector4.h"
+#include "G3D/debugAssert.h"
+
+#include <cstring>
namespace G3D {
+#ifdef _MSC_VER
+// Turn off "conditional expression is constant" warning; MSVC generates this
+// for debug assertions in inlined methods.
+# pragma warning (disable : 4127)
+#endif
+
+class Any;
+
/**
3x3 matrix. Do not subclass.
*/
@@ -37,23 +47,34 @@ private:
public:
- /** Initial values are undefined for performance. See also
+ Matrix3(const Any& any);
+
+ operator Any() const;
+
+ /** 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;
+ 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*/
+ @cite Implementation from Watt and Watt, pg 362*/
Matrix3(const class Quat& q);
+ void serialize(class BinaryOutput& b) const;
+ void deserialize(class BinaryInput& b);
+
+ /** Returns true if column(0).cross(column(1)).dot(column(2)) > 0. */
+ bool isRightHanded() const;
+
/**
Sets all elements.
*/
@@ -83,15 +104,16 @@ public:
inline operator const float* () const{
return (const float*)&elt[0][0];
}
+
+ Vector3 column(int c) const;
+ const Vector3& row(int r) const;
- Vector3 getColumn (int iCol) const;
- Vector3 getRow (int iRow) const;
void setColumn(int iCol, const Vector3 &vector);
void setRow(int iRow, const Vector3 &vector);
// assignment and comparison
inline Matrix3& operator= (const Matrix3& rkMatrix) {
- System::memcpy(elt, rkMatrix.elt, 9 * sizeof(float));
+ memcpy(elt, rkMatrix.elt, 9 * sizeof(float));
return *this;
}
@@ -125,6 +147,7 @@ public:
return kProd;
}
+
/**
* vector * matrix [1x3 * 3x3 = 1x3]
*/
@@ -141,12 +164,16 @@ public:
friend Matrix3 operator* (float fScalar, const Matrix3& rkMatrix);
friend Matrix3 operator* (int fScalar, const Matrix3& rkMatrix);
+ Matrix3& operator*= (float k);
+ Matrix3& operator/= (float k);
+
+
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
+ /** 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.
@@ -164,10 +191,10 @@ private:
static void _transpose(const Matrix3& A, Matrix3& out);
public:
- /** Optimized implementation of out = A.transpose(). It is safe (but slow) to call
+ /** 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
+
+ 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) {
@@ -200,11 +227,46 @@ public:
void qDUDecomposition (Matrix3& rkQ, Vector3& rkD,
Vector3& rkU) const;
+ /**
+ Polar decomposition of a matrix. Based on pseudocode from Nicholas J
+ Higham, "Computing the Polar Decomposition -- with Applications Siam
+ Journal of Science and Statistical Computing, Vol 7, No. 4, October
+ 1986.
+
+ Decomposes A into R*S, where R is orthogonal and S is symmetric.
+
+ Ken Shoemake's "Matrix animation and polar decomposition"
+ in Proceedings of the conference on Graphics interface '92
+ seems to be better known in the world of graphics, but Higham's version
+ uses a scaling constant that can lead to faster convergence than
+ Shoemake's when the initial matrix is far from orthogonal.
+ */
+ void polarDecomposition(Matrix3 &R, Matrix3 &S) const;
+
+ /**
+ * Matrix norms.
+ */
float spectralNorm () const;
+ float squaredFrobeniusNorm() const;
+
+ float frobeniusNorm() const;
+
+ float l1Norm() const;
+
+ float lInfNorm() const;
+
+ float diffOneNorm(const Matrix3 &y) 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);
/**
@@ -239,36 +301,31 @@ public:
Matrix3& rkProduct);
std::string toString() const;
- static const float EPSILON;
+ static const float EPSILON;
// Special values.
- // The unguaranteed order of initialization of static variables across
+ // 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
+ // 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,
+ // 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();
-
- // Deprecated.
- /** @deprecated Use Matrix3::zero() */
- static const Matrix3 ZERO;
- /** @deprecated Use Matrix3::identity() */
- static const Matrix3 IDENTITY;
+ static const Matrix3& identity();
protected:
+
// support for eigensolver
void tridiagonal (float afDiag[3], float afSubDiag[3]);
bool qLAlgorithm (float afDiag[3], float afSubDiag[3]);
@@ -286,6 +343,7 @@ protected:
};
+
//----------------------------------------------------------------------------
/** <code>v * M == M.transpose() * v</code> */
inline Vector3 operator* (const Vector3& rkPoint, const Matrix3& rkMatrix) {
@@ -301,8 +359,8 @@ inline Vector3 operator* (const Vector3& rkPoint, const Matrix3& rkMatrix) {
return kProd;
}
+
} // namespace
#endif
-
diff --git a/dep/include/g3dlite/G3D/Matrix4.h b/dep/include/g3dlite/G3D/Matrix4.h
new file mode 100644
index 00000000000..9ce87d875b8
--- /dev/null
+++ b/dep/include/g3dlite/G3D/Matrix4.h
@@ -0,0 +1,249 @@
+/**
+ @file Matrix4.h
+
+ 4x4 matrix class
+
+ @maintainer Morgan McGuire, http://graphics.cs.williams.edu
+
+ @created 2003-10-02
+ @edited 2009-10-20
+ */
+
+#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 {
+
+class Any;
+
+/**
+ 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:
+ /** Must be of the form: <code>Matrix4(#, #, # .... #)</code>*/
+ Matrix4(const Any& any);
+
+ operator Any() const;
+
+ 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();
+
+ /** If this is a perspective projection matrix created by
+ Matrix4::perspectiveProjection, extract its parameters. */
+ void getPerspectiveProjectionParameters
+ (float& left,
+ float& right,
+ float& bottom,
+ float& top,
+ float& nearval,
+ float& farval,
+ float updirection = -1.0f) const;
+
+ 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).
+
+ \param upDirection Use -1.0 for 2D Y increasing downwards (the G3D 8.x default convention),
+ 1.0 for 2D Y increasing upwards (the G3D 7.x default and OpenGL convention)
+ */
+ static Matrix4 orthogonalProjection(
+ float left,
+ float right,
+ float bottom,
+ float top,
+ float nearval,
+ float farval,
+ float upDirection = -1.0f);
+
+
+ /** \param upDirection Use -1.0 for 2D Y increasing downwards (the G3D 8.x default convention),
+ 1.0 for 2D Y increasing upwards (the G3D 7.x default and OpenGL convention)
+ */
+ static Matrix4 orthogonalProjection(
+ const class Rect2D& rect,
+ float nearval,
+ float farval,
+ float upDirection = -1.0f);
+
+ /** \param upDirection Use -1.0 for 2D Y increasing downwards (the G3D 8.x default convention),
+ 1.0 for 2D Y increasing upwards (the G3D 7.x default and OpenGL convention)
+ */
+ static Matrix4 perspectiveProjection(
+ float left,
+ float right,
+ float bottom,
+ float top,
+ float nearval,
+ float farval,
+ float upDirection = -1.0f);
+
+ void setRow(int r, const class Vector4& v);
+ void setColumn(int c, const Vector4& v);
+
+ 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);
+ }
+
+ inline static Matrix4 translation(float x, float y, float z) {
+ return Matrix4(Matrix3::identity(), Vector3(x, y, z));
+ }
+
+ /** Create a rotation matrix that rotates \a deg degrees around the Y axis */
+ inline static Matrix4 yawDegrees(float deg) {
+ return Matrix4(Matrix3::fromAxisAngle(Vector3::unitY(), toRadians(deg)));
+ }
+
+ inline static Matrix4 pitchDegrees(float deg) {
+ return Matrix4(Matrix3::fromAxisAngle(Vector3::unitX(), toRadians(deg)));
+ }
+
+ inline static Matrix4 rollDegrees(float deg) {
+ return Matrix4(Matrix3::fromAxisAngle(Vector3::unitZ(), toRadians(deg)));
+ }
+};
+
+
+
+} // namespace
+
+#ifdef _MSC_VER
+# pragma warning (pop)
+#endif
+
+#endif
diff --git a/dep/include/g3dlite/G3D/MemoryManager.h b/dep/include/g3dlite/G3D/MemoryManager.h
new file mode 100644
index 00000000000..15bf6d8be43
--- /dev/null
+++ b/dep/include/g3dlite/G3D/MemoryManager.h
@@ -0,0 +1,93 @@
+/**
+ @file MemoryManager.h
+
+ @maintainer Morgan McGuire, http://graphics.cs.williams.edu
+ @created 2009-04-20
+ @edited 2009-04-20
+
+ Copyright 2000-2009, Morgan McGuire.
+ All rights reserved.
+ */
+#ifndef G3D_MemoryManager_h
+#define G3D_MemoryManager_h
+
+#include "G3D/platform.h"
+#include "G3D/ReferenceCount.h"
+
+namespace G3D {
+
+/**
+ Abstraction of memory management.
+ Default implementation uses G3D::System::malloc and is threadsafe.
+
+ \sa CRTMemoryManager, AlignedMemoryManager, AreaMemoryManager */
+class MemoryManager : public ReferenceCountedObject {
+protected:
+
+ MemoryManager();
+
+public:
+
+ typedef ReferenceCountedPointer<class MemoryManager> Ref;
+
+ /** Return a pointer to \a s bytes of memory that are unused by
+ the rest of the program. The contents of the memory are
+ undefined */
+ virtual void* alloc(size_t s);
+
+ /** Invoke to declare that this memory will no longer be used by
+ the program. The memory manager is not required to actually
+ reuse or release this memory. */
+ virtual void free(void* ptr);
+
+ /** Returns true if this memory manager is threadsafe (i.e., alloc
+ and free can be called asychronously) */
+ virtual bool isThreadsafe() const;
+
+ /** Return the instance. There's only one instance of the default
+ MemoryManager; it is cached after the first creation. */
+ static MemoryManager::Ref create();
+};
+
+/**
+ Allocates memory on 16-byte boundaries.
+ \sa MemoryManager, CRTMemoryManager, AreaMemoryManager */
+class AlignedMemoryManager : public MemoryManager {
+protected:
+
+ AlignedMemoryManager();
+
+public:
+
+ typedef ReferenceCountedPointer<class AlignedMemoryManager> Ref;
+
+
+ virtual void* alloc(size_t s);
+
+ virtual void free(void* ptr);
+
+ virtual bool isThreadsafe() const;
+
+ static AlignedMemoryManager::Ref create();
+};
+
+
+/** MemoryManager implemented using the C runtime. */
+class CRTMemoryManager : public MemoryManager {
+protected:
+ CRTMemoryManager();
+
+public:
+ typedef ReferenceCountedPointer<class MemoryManager> Ref;
+ virtual void* alloc(size_t s);
+ virtual void free(void* ptr);
+ virtual bool isThreadsafe() const;
+
+ /** There's only one instance of this memory manager; it is
+ cached after the first creation. */
+ static CRTMemoryManager::Ref create();
+};
+
+}
+
+#endif
diff --git a/dep/include/g3dlite/G3D/MeshAlg.h b/dep/include/g3dlite/G3D/MeshAlg.h
new file mode 100644
index 00000000000..1decea10105
--- /dev/null
+++ b/dep/include/g3dlite/G3D/MeshAlg.h
@@ -0,0 +1,683 @@
+/**
+ @file MeshAlg.h
+
+ Indexed Mesh algorithms.
+
+ @maintainer Morgan McGuire, http://graphics.cs.williams.edu
+
+ @created 2003-09-14
+ @edited 2010-01-18
+*/
+
+#ifndef G3D_MeshAlg_h
+#define G3D_MeshAlg_h
+
+#include "G3D/platform.h"
+#include "G3D/Array.h"
+#include "G3D/Vector3.h"
+#include "G3D/CoordinateFrame.h"
+#include "G3D/SmallArray.h"
+#include "G3D/constants.h"
+#include "G3D/Image1.h"
+
+#ifdef G3D_WIN32
+// Turn off "conditional expression is constant" warning; MSVC generates this
+// for debug assertions in inlined methods.
+#pragma warning (disable : 4127)
+#endif
+
+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.
+
+ \sa G3D::ArticulatedModel, G3D::IFSModel
+ */
+class MeshAlg {
+public:
+
+ /** \deprecated */
+ typedef PrimitiveType Primitive;
+
+ /** 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.
+ */
+ SmallArray<int, 6> 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.
+ */
+ SmallArray<int, 6> 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 vertexArray %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:
+
+ /** 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 = 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 = 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 AABox& 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 AABox& box, class Sphere& sphere);
+
+ /**
+ 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.
+ \param elevation If non-NULL, values from this image are used as elevations. Apply an \a xform to adjust the scale
+ */
+ 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(),
+ const Image1::Ref& elevation = NULL);
+
+ /** 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 == PrimitiveType::TRIANGLE_STRIP ||
+ inType == PrimitiveType::TRIANGLE_FAN ||
+ inType == PrimitiveType::QUADS ||
+ inType == PrimitiveType::QUAD_STRIP);
+
+ const int inSize = inIndices.size();
+
+ switch(inType) {
+ case PrimitiveType::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 PrimitiveType::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 PrimitiveType::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 PrimitiveType::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/dep/include/g3dlite/G3D/MeshBuilder.h b/dep/include/g3dlite/G3D/MeshBuilder.h
new file mode 100644
index 00000000000..9920d59d7d3
--- /dev/null
+++ b/dep/include/g3dlite/G3D/MeshBuilder.h
@@ -0,0 +1,82 @@
+/**
+ @file MeshBuilder.h
+
+ @maintainer Morgan McGuire, http://graphics.cs.williams.edu
+
+ @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/dep/include/g3dlite/G3D/NetAddress.h b/dep/include/g3dlite/G3D/NetAddress.h
new file mode 100644
index 00000000000..8ed20a06690
--- /dev/null
+++ b/dep/include/g3dlite/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/dep/include/g3dlite/G3D/NetworkDevice.h b/dep/include/g3dlite/G3D/NetworkDevice.h
new file mode 100644
index 00000000000..ea3290cbc09
--- /dev/null
+++ b/dep/include/g3dlite/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, http://graphics.cs.williams.edu
+ @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::Discovery::Client 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/dep/include/g3dlite/G3D/ParseError.h b/dep/include/g3dlite/G3D/ParseError.h
new file mode 100644
index 00000000000..f02948e3d29
--- /dev/null
+++ b/dep/include/g3dlite/G3D/ParseError.h
@@ -0,0 +1,59 @@
+/**
+ @file ParseError.h
+
+ @maintainer Morgan McGuire
+
+ @created 2009-11-15
+ @edited 2009-11-15
+
+ Copyright 2000-2009, Morgan McGuire.
+ All rights reserved.
+ */
+#ifndef G3D_ParseError_h
+#define G3D_ParseError_h
+
+#include "G3D/platform.h"
+#include "G3D/g3dmath.h"
+#include <string>
+
+namespace G3D {
+
+/** Thrown by TextInput, Any, and other parsers on unexpected input. */
+class ParseError {
+public:
+ enum {UNKNOWN = -1};
+
+ /** Empty means unknown */
+ std::string filename;
+
+ /** For a binary file, the location of the parse error. -1 if unknown.*/
+ int64 byte;
+
+ /** For a text file, the line number is the line number of start of token which caused the exception. 1 is
+ the first line of the file. -1 means unknown. Note that you can use
+ TextInput::Settings::startingLineNumberOffset to shift the effective line
+ number that is reported by that class.
+ */
+ int line;
+
+ /** Character number (in the line) of the start of the token which caused the
+ exception. 1 is the character in the line. May be -1 if unknown.
+ */
+ int character;
+
+ std::string message;
+
+ ParseError() : byte(UNKNOWN), line(UNKNOWN), character(UNKNOWN) {}
+
+ virtual ~ParseError() {}
+
+ ParseError(const std::string& f, int l, int c, const std::string& m) :
+ filename (f), byte(UNKNOWN), line(l), character(c), message(m) {}
+
+ ParseError(const std::string& f, int64 b, const std::string& m) :
+ filename (f), byte(b), line(UNKNOWN), character(UNKNOWN), message(m) {}
+};
+
+}
+
+#endif
diff --git a/dep/include/g3dlite/G3D/PhysicsFrame.h b/dep/include/g3dlite/G3D/PhysicsFrame.h
new file mode 100644
index 00000000000..a5a9305b83e
--- /dev/null
+++ b/dep/include/g3dlite/G3D/PhysicsFrame.h
@@ -0,0 +1,74 @@
+/**
+ @file PhysicsFrame.h
+
+ @maintainer Morgan McGuire, http://graphics.cs.williams.edu
+
+ @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/dep/include/g3dlite/G3D/Plane.h b/dep/include/g3dlite/G3D/Plane.h
index c7043e23c42..360bcd2bc75 100644
--- a/dep/include/g3dlite/G3D/Plane.h
+++ b/dep/include/g3dlite/G3D/Plane.h
@@ -3,7 +3,7 @@
Plane class
- @maintainer Morgan McGuire, matrix@graphics3d.com
+ @maintainer Morgan McGuire, http://graphics.cs.williams.edu
@created 2001-06-02
@edited 2004-07-18
@@ -15,6 +15,7 @@
#include "G3D/platform.h"
#include "G3D/Vector3.h"
#include "G3D/Vector4.h"
+#include "G3D/debugAssert.h"
namespace G3D {
@@ -25,8 +26,8 @@ class Plane {
private:
/** normal.Dot(x,y,z) = distance */
- Vector3 _normal;
- float _distance;
+ Vector3 _normal;
+ float _distance;
/**
Assumes the normal has unit length.
@@ -65,10 +66,14 @@ public:
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
+ Returns true if point is on the side the normal points to or
is in the plane.
*/
inline bool halfSpaceContains(Vector3 point) const {
@@ -81,7 +86,7 @@ public:
}
/**
- Returns true if point is on the side the normal points to or
+ Returns true if point is on the side the normal points to or
is in the plane.
*/
inline bool halfSpaceContains(const Vector4& point) const {
@@ -93,7 +98,7 @@ public:
}
/**
- Returns true if point is on the side the normal points to or
+ 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 {
@@ -108,9 +113,9 @@ public:
return fuzzyEq(point.dot(_normal), _distance);
}
- inline const Vector3& normal() const {
- return _normal;
- }
+ 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.
@@ -154,4 +159,3 @@ public:
} // namespace
#endif
-
diff --git a/dep/include/g3dlite/G3D/PointHashGrid.h b/dep/include/g3dlite/G3D/PointHashGrid.h
new file mode 100644
index 00000000000..0db9e677321
--- /dev/null
+++ b/dep/include/g3dlite/G3D/PointHashGrid.h
@@ -0,0 +1,917 @@
+/**
+ @file PointHashGrid.h
+
+ @maintainer Morgan McGuire, http://graphics.cs.williams.edu
+ @created 2008-07-01
+ @edited 2009-05-28
+
+ Copyright 2000-2009, 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"
+#include "G3D/SmallArray.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;
+
+ /** 1.0 / cell width */
+ float m_invCellWidth;
+
+ /** 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;
+
+ MemoryManager::Ref m_memoryManager;
+
+ /** 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.*/
+ inline void getCellCoord(const Vector3& pos, Vector3int32& cellCoord) const {
+ for (int a = 0; a < 3; ++a) {
+ cellCoord[a] = iFloor(pos[a] * m_invCellWidth);
+ }
+ }
+
+ /** 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, const MemoryManager::Ref& m = MemoryManager::create()) : m_size(0), m_memoryManager(m) {
+ initOffsetArray();
+ m_data.clearAndSetMemoryManager(m_memoryManager);
+
+ debugAssertM(radiusHint > 0, "Cell radius must be positive");
+ m_cellWidth = radiusHint;
+ m_invCellWidth = 1.0f / m_cellWidth;
+ }
+
+ /**
+ 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, const MemoryManager::Ref& m = MemoryManager::create()) : m_size(0), m_memoryManager(m) {
+ initOffsetArray();
+ m_data.clearAndSetMemoryManager(m_memoryManager);
+
+ 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.getCreate(cellCoord);
+
+ if (cell.size() == 0) {
+ // Use the same memory manager as for the whole class
+ cell.clearAndSetMemoryManager(m_memoryManager);
+ }
+
+ 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 shrinkIfNecessary 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;
+ }
+
+ // Intentionally unimplemented
+ BoxIterator& operator=(const BoxIterator&);
+
+ 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.
+
+ // Intentionally unimplemented
+ SphereIterator& operator=(const SphereIterator&);
+ 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();
+ }
+ </pre>
+ */
+ 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();
+ }
+
+ // Intentionally unimplemented
+ CellIterator& operator=(const CellIterator&);
+
+ 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 v. 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();
+ }
+
+ void clearAndSetMemoryManager(const MemoryManager::Ref& m) {
+ ++m_epoch;
+ m_size = 0;
+ m_bounds = AABox();
+
+ m_data.clearAndSetMemoryManager(m);
+ m_memoryManager = m;
+ }
+
+ /** 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/dep/include/g3dlite/G3D/PointKDTree.h b/dep/include/g3dlite/G3D/PointKDTree.h
new file mode 100644
index 00000000000..151cbd5f2f3
--- /dev/null
+++ b/dep/include/g3dlite/G3D/PointKDTree.h
@@ -0,0 +1,1185 @@
+/**
+ @file PointKDTree.h
+
+ @maintainer Morgan McGuire, http://graphics.cs.williams.edu
+
+ @created 2004-01-11
+ @edited 2008-11-02
+
+ Copyright 2000-2009, 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>
+
+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
+};
+
+}
+
+#endif
diff --git a/dep/include/g3dlite/G3D/Pointer.h b/dep/include/g3dlite/G3D/Pointer.h
new file mode 100644
index 00000000000..6e35062a746
--- /dev/null
+++ b/dep/include/g3dlite/G3D/Pointer.h
@@ -0,0 +1,292 @@
+/**
+ @file Pointer.h
+
+ @maintainer Morgan McGuire, http://graphics.cs.williams.edu
+
+ @created 2007-05-16
+ @edited 2009-03-26
+
+ Copyright 2000-2009, 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;
+ virtual bool isNull() 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);
+ }
+
+ virtual bool isNull() const {
+ return value == NULL;
+ }
+ };
+
+ 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);
+ }
+
+ virtual bool isNull() const {
+ return object == NULL;
+ }
+ };
+
+
+ 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);
+ }
+
+ virtual bool isNull() const {
+ return object.isNull();
+ }
+ };
+
+
+ Interface* m_interface;
+
+public:
+
+ Pointer() : m_interface(NULL) {};
+
+ /** Allows implicit cast from real pointer */
+ Pointer(ValueType* v) : m_interface(new Memory(v)) {}
+
+ inline bool isNull() const {
+ return (m_interface == NULL) || m_interface->isNull();
+ }
+
+ // Assignment
+ inline Pointer& operator=(const Pointer& r) {
+ delete m_interface;
+ if (r.m_interface != NULL) {
+ m_interface = r.m_interface->clone();
+ } else {
+ m_interface = NULL;
+ }
+ return this[0];
+ }
+
+ Pointer(const Pointer& p) : m_interface(NULL) {
+ this[0] = p;
+ }
+
+ template<class Class>
+ Pointer(const ReferenceCountedPointer<Class>& object,
+ ValueType (Class::*getMethod)() const,
+ void (Class::*setMethod)(ValueType)) :
+ m_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)) :
+ m_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&)) :
+ m_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&)) :
+ m_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&)) :
+ m_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&)) :
+ m_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)) :
+ m_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)) :
+ m_interface(new Accessor<Class, ValueType (Class::*)() const, void (Class::*)(ValueType)>(object, getMethod, setMethod)) {}
+
+ ~Pointer() {
+ delete m_interface;
+ }
+
+ inline const ValueType getValue() const {
+ debugAssert(m_interface != NULL);
+ return m_interface->get();
+ }
+
+ inline void setValue(const ValueType& v) {
+ debugAssert(m_interface != NULL);
+ m_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/dep/include/g3dlite/G3D/PositionTrait.h b/dep/include/g3dlite/G3D/PositionTrait.h
new file mode 100644
index 00000000000..67a4f64138a
--- /dev/null
+++ b/dep/include/g3dlite/G3D/PositionTrait.h
@@ -0,0 +1,7 @@
+#ifndef G3D_POSITIONTRAIT_H
+#define G3D_POSITIONTRAIT_H
+
+template<typename Value>
+struct PositionTrait{};
+
+#endif
diff --git a/dep/include/g3dlite/G3D/PrecomputedRandom.h b/dep/include/g3dlite/G3D/PrecomputedRandom.h
new file mode 100644
index 00000000000..411d128c582
--- /dev/null
+++ b/dep/include/g3dlite/G3D/PrecomputedRandom.h
@@ -0,0 +1,110 @@
+/**
+ @file PrecomputedRandom.h
+
+ @maintainer Morgan McGuire, http://graphics.cs.williams.edu
+
+ @created 2009-03-31
+ @edited 2009-03-31
+
+ Copyright 2000-2009, Morgan McGuire.
+ All rights reserved.
+ */
+#ifndef G3D_PrecomputedRandom_h
+#define G3D_PrecomputedRandom_h
+
+#include "G3D/platform.h"
+#include "G3D/Random.h"
+
+namespace G3D {
+
+/** Fast random numbers using a precomputed data table.
+
+ e.g., generates cosHemi about 13x faster than Random.
+ This is useful for quickly generating seeded random
+ numbers for reproducibility. G3D::Random takes a long
+ time to seed; this is instantaneous (providing the
+ precomputed data is already available.)
+
+ Not threadsafe.*/
+class PrecomputedRandom : public Random {
+public:
+ /** Put the cosHemi and the uniform together so that when
+ alternating between them we stay in cache. This is also packed
+ into a good size for SIMD and GPU operations.*/
+ class HemiUniformData {
+ public:
+ float cosHemiX;
+ float cosHemiY;
+ float cosHemiZ;
+ float uniform;
+ };
+
+ class SphereBitsData {
+ public:
+ float sphereX;
+ float sphereY;
+ float sphereZ;
+ uint32 bits;
+ };
+
+protected:
+
+ /** Array of 2^n elements. */
+ const HemiUniformData* m_hemiUniform;
+ const SphereBitsData* m_sphereBits;
+
+ /** 2^n - 1; the AND mask for computing a fast modulo */
+ int m_modMask;
+
+ int m_index;
+
+ /** If true, free m_hemiUniform and m_sphereBits in destructor */
+ bool m_freeData;
+
+public:
+
+ /*
+ \param dataSize Must be a power of 2
+ \param data Will NOT be deleted by the destructor.
+ */
+ PrecomputedRandom(const HemiUniformData* data1, const SphereBitsData* data2, int dataSize, uint32 seed = 0xF018A4D2);
+
+ /**
+ \param dataSize Number of random numbers that can be requested before periodicity. Must be a power of 2.
+ */
+ PrecomputedRandom(int dataSize, uint32 seed = 0xF018A4D2);
+
+ ~PrecomputedRandom();
+
+ /** Each bit is random. Subclasses can choose to override just
+ this method and the other methods will all work automatically. */
+ virtual uint32 bits();
+
+ // integer is inherited
+
+ /** Uniform random float on the range [min, max] */
+ virtual float uniform(float low, float high);
+
+ /** Uniform random float on the range [0, 1] */
+ virtual float uniform();
+
+ // gaussian is inherited
+
+ /** Returns 3D unit vectors distributed according to
+ a cosine distribution about the z axis. */
+ virtual void cosHemi(float& x, float& y, float& z);
+
+ /** Returns 3D unit vectors distributed according to a cosine
+ power distribution (\f$ \mbox{cos}^k \theta \f$) about
+ the z-axis. */
+ virtual void cosPowHemi(const float k, float& x, float& y, float& z);
+
+ // hemi is inherited
+
+ /** Returns 3D unit vectors uniformly distributed on the sphere */
+ virtual void sphere(float& x, float& y, float& z);
+};
+
+}
+
+#endif
diff --git a/dep/include/g3dlite/G3D/Quat.h b/dep/include/g3dlite/G3D/Quat.h
index 5fc30e8ec49..9ef3d57b301 100644
--- a/dep/include/g3dlite/G3D/Quat.h
+++ b/dep/include/g3dlite/G3D/Quat.h
@@ -1,16 +1,16 @@
/**
@file Quat.h
-
+
Quaternion
-
- @maintainer Morgan McGuire, matrix@graphics3d.com
-
+
+ @maintainer Morgan McGuire, http://graphics.cs.williams.edu
+
@created 2002-01-23
- @edited 2006-05-10
+ @edited 2009-05-10
*/
-#ifndef G3D_QUAT_H
-#define G3D_QUAT_H
+#ifndef G3D_Quat_h
+#define G3D_Quat_h
#include "G3D/platform.h"
#include "G3D/g3dmath.h"
@@ -27,11 +27,11 @@ namespace G3D {
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
+ 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).
-
+ (negate both the axis and angle).
+
A non-unit quaterion q represents the same rotation as
q.unitize() (Dam98 pg 28).
@@ -56,9 +56,9 @@ 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
+ In the Real-Time Rendering notation, u = (x, y, z), w = w
*/
float x, y, z, w;
@@ -90,19 +90,19 @@ public:
/** 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);
+ 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
+
+ /** 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);
- }
+ inline Quat operator-() const {
+ return Quat(-x, -y, -z, -w);
+ }
/**
Returns the imaginary part (x, y, z)
@@ -129,34 +129,34 @@ public:
void toAxisAngleRotation(
Vector3& axis,
float& angle) const {
- double d;
- toAxisAngleRotation(axis, d);
- angle = (float)d;
- }
+ double d;
+ toAxisAngleRotation(axis, d);
+ angle = (float)d;
+ }
Matrix3 toRotationMatrix() const;
void toRotationMatrix(
Matrix3& rot) const;
-
+
/**
- Spherical linear interpolation: linear interpolation along the
+ 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.
+ 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;
+ /** Normalized linear interpolation of quaternion components. */
+ Quat nlerp(const Quat& other, float alpha) const;
/**
Negates the imaginary part.
@@ -177,7 +177,15 @@ public:
return Quat(x * s, y * s, z * s, w * s);
}
- /** @cite Based on Watt & Watt, page 360 */
+ 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 {
@@ -188,7 +196,7 @@ public:
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.
+ /** Note that q<SUP>-1</SUP> = q.conj() for a unit quaternion.
@cite Dam99 page 13 */
inline Quat inverse() const {
return conj() / dot(*this);
@@ -209,10 +217,12 @@ public:
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));
@@ -225,7 +235,7 @@ public:
} else if (w < 0) {
// Log of a negative number. Multivalued, any number of the form
// (PI * v, ln(-q.w))
- return Quat((float)G3D_PI, 0, 0, ::logf(-w));
+ return Quat((float)pi(), 0, 0, ::logf(-w));
} else {
// log of zero!
return Quat((float)nan(), (float)nan(), (float)nan(), (float)nan());
@@ -240,18 +250,18 @@ public:
}
}
/** log q = [Av, 0] where q = [sin(A) * v, cos(A)].
- Only for unit quaternions
+ 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
+ return
}
double A = atan2((double)w, len);
Vector3 v = u / len;
-
+
return Quat(v * A, 0);
}
*/
@@ -266,6 +276,7 @@ public:
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
@@ -278,16 +289,10 @@ public:
return (log() * x).exp();
}
- /**
- @deprecated
- Use toUnit()
- */
- inline Quat unitize() const {
+ inline void unitize() {
float mag2 = dot(*this);
- if (G3D::fuzzyEq(mag2, 1.0f)) {
- return *this;
- } else {
- return *this / sqrtf(mag2);
+ if (! G3D::fuzzyEq(mag2, 1.0f)) {
+ *this *= rsq(mag2);
}
}
@@ -296,7 +301,9 @@ public:
the magnitude.
*/
inline Quat toUnit() const {
- return unitize();
+ Quat x = *this;
+ x.unitize();
+ return x;
}
/**
@@ -317,11 +324,14 @@ public:
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.
+ /** 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;
@@ -684,6 +694,26 @@ 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++
@@ -691,7 +721,5 @@ inline G3D::Quat pow(const G3D::Quat& q, double x) {
return q.pow((float)x);
}
-#include "Quat.inl"
#endif
-
diff --git a/dep/include/g3dlite/G3D/Queue.h b/dep/include/g3dlite/G3D/Queue.h
new file mode 100644
index 00000000000..36573265d1a
--- /dev/null
+++ b/dep/include/g3dlite/G3D/Queue.h
@@ -0,0 +1,364 @@
+/**
+ @file Queue.h
+
+ @maintainer Morgan McGuire, http://graphics.cs.williams.edu
+
+ @created 2002-07-09
+ @edited 2008-12-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 the back element */
+ inline const T& last() const {
+ return (*this)[size() - 1];
+ }
+
+ inline T& last() {
+ return (*this)[size() - 1];
+ }
+
+ /**
+ 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/dep/include/g3dlite/G3D/Random.h b/dep/include/g3dlite/G3D/Random.h
new file mode 100644
index 00000000000..54491d06f1b
--- /dev/null
+++ b/dep/include/g3dlite/G3D/Random.h
@@ -0,0 +1,139 @@
+/**
+ @file Random.h
+
+ @maintainer Morgan McGuire, http://graphics.cs.williams.edu
+
+ @created 2009-01-02
+ @edited 2009-03-20
+
+ Copyright 2000-2009, Morgan McGuire.
+ All rights reserved.
+ */
+#ifndef G3D_Random_h
+#define G3D_Random_h
+
+#include "G3D/platform.h"
+#include "G3D/g3dmath.h"
+#include "G3D/GMutex.h"
+
+namespace G3D {
+
+/** Random number generator.
+
+ Threadsafe.
+
+ Useful for generating consistent random numbers across platforms
+ and when multiple threads are involved.
+
+ Uses the Fast Mersenne Twister (FMT-19937) algorithm.
+
+ On average, uniform() runs about 2x-3x faster than rand().
+
+ @cite http://www.math.sci.hiroshima-u.ac.jp/~m-mat/MT/SFMT/index.html
+
+ On OS X, Random is about 10x faster than drand48() (which is
+ threadsafe) and 4x faster than rand() (which is not threadsafe).
+ */
+class Random {
+protected:
+
+ /** Constants (important for the algorithm; do not modify) */
+ enum {
+ N = 624,
+ M = 397,
+ R = 31,
+ U = 11,
+ S = 7,
+ T = 15,
+ L = 18,
+ A = 0x9908B0DF,
+ B = 0x9D2C5680,
+ C = 0xEFC60000};
+
+ /**
+ Prevents multiple overlapping calls to generate().
+ */
+ Spinlock lock;
+
+ /** State vector (these are the next N values that will be returned) */
+ uint32* state;
+
+ /** Index into state */
+ int index;
+
+ bool m_threadsafe;
+
+ /** Generate the next N ints, and store them for readback later.
+ Called from bits() */
+ virtual void generate();
+
+ /** For subclasses. The void* parameter is just to distinguish this from the
+ public constructor.*/
+ Random(void*);
+
+public:
+
+ /** \param threadsafe Set to false if you know that this random
+ will only be used on a single thread. This eliminates the
+ lock and improves performance on some platforms.
+ */
+ Random(uint32 seed = 0xF018A4D2, bool threadsafe = true);
+
+ virtual ~Random();
+
+ /** Each bit is random. Subclasses can choose to override just
+ this method and the other methods will all work automatically. */
+ virtual uint32 bits();
+
+ /** Uniform random integer on the range [min, max] */
+ virtual int integer(int min, int max);
+
+ /** Uniform random float on the range [min, max] */
+ virtual inline float uniform(float low, float high) {
+ // We could compute the ratio in double precision here for
+ // about 1.5x slower performance and slightly better
+ // precision.
+ return low + (high - low) * ((float)bits() / (float)0xFFFFFFFFUL);
+ }
+
+ /** Uniform random float on the range [0, 1] */
+ virtual inline float uniform() {
+ // We could compute the ratio in double precision here for
+ // about 1.5x slower performance and slightly better
+ // precision.
+ const float norm = 1.0f / (float)0xFFFFFFFFUL;
+ return (float)bits() * norm;
+ }
+
+ /** Normally distributed reals. */
+ virtual float gaussian(float mean, float stdev);
+
+ /** Returns 3D unit vectors distributed according to
+ a cosine distribution about the z-axis. */
+ virtual void cosHemi(float& x, float& y, float& z);
+
+ /** Returns 3D unit vectors distributed according to a cosine
+ power distribution (\f$ \cos^k \theta \f$) about
+ the z-axis. */
+ virtual void cosPowHemi(const float k, float& x, float& y, float& z);
+
+ /** Returns 3D unit vectors uniformly distributed on the
+ hemisphere about the z-axis. */
+ virtual void hemi(float& x, float& y, float& z);
+
+ /** Returns 3D unit vectors uniformly distributed on the sphere */
+ virtual void sphere(float& x, float& y, float& z);
+
+ /**
+ A shared instance for when the performance and features but not
+ consistency of the class are desired. It is slightly (10%)
+ faster to use a distinct instance than to use the common one.
+
+ Threadsafe.
+ */
+ static Random& common();
+};
+
+}
+
+#endif
diff --git a/dep/include/g3dlite/G3D/Ray.h b/dep/include/g3dlite/G3D/Ray.h
index 740f307d88c..80df5828aff 100644
--- a/dep/include/g3dlite/G3D/Ray.h
+++ b/dep/include/g3dlite/G3D/Ray.h
@@ -1,16 +1,16 @@
/**
@file Ray.h
-
+
Ray class
-
- @maintainer Morgan McGuire, matrix@graphics3d.com
-
+
+ @maintainer Morgan McGuire, http://graphics.cs.williams.edu
+
@created 2002-07-12
- @edited 2006-02-21
+ @edited 2009-06-29
*/
-#ifndef G3D_RAY_H
-#define G3D_RAY_H
+#ifndef G3D_Ray_h
+#define G3D_Ray_h
#include "G3D/platform.h"
#include "G3D/Vector3.h"
@@ -23,43 +23,85 @@ namespace G3D {
*/
class Ray {
private:
- Ray(const Vector3& origin, const Vector3& direction) {
- this->origin = origin;
- this->direction = direction;
- }
+ friend class Intersect;
+
+ Vector3 m_origin;
+
+ /** Unit length */
+ Vector3 m_direction;
+
+ /** 1.0 / direction */
+ Vector3 m_invDirection;
+
+
+ // The following are for the "ray slope" optimization from
+ // "Fast Ray / Axis-Aligned Bounding Box Overlap Tests using Ray Slopes"
+ // by Martin Eisemann, Thorsten Grosch, Stefan Müller and Marcus Magnor
+ // Computer Graphics Lab, TU Braunschweig, Germany and
+ // University of Koblenz-Landau, Germany*/
+ enum Classification {MMM, MMP, MPM, MPP, PMM, PMP, PPM, PPP, POO, MOO, OPO, OMO, OOP, OOM, OMM, OMP, OPM, OPP, MOM, MOP, POM, POP, MMO, MPO, PMO, PPO}; Classification classification;
+ // ray slope
+ float ibyj, jbyi, kbyj, jbyk, ibyk, kbyi;
+ // Precomputed components
+ float c_xy, c_xz, c_yx, c_yz, c_zx, c_zy;
public:
- Vector3 origin;
- /**
- Not unit length
- */
- Vector3 direction;
+ void set(const Vector3& origin, const Vector3& direction);
+
+ inline const Vector3& origin() const {
+ return m_origin;
+ }
+
+ /** Unit direction vector. */
+ inline const Vector3& direction() const {
+ return m_direction;
+ }
+
+ /** Component-wise inverse of direction vector. May have inf() components */
+ inline const Vector3& invDirection() const {
+ return m_invDirection;
+ }
+
+ inline Ray() {
+ set(Vector3::zero(), Vector3::unitX());
+ }
- Ray() : origin(Vector3::zero()), direction(Vector3::zero()) {}
+ inline Ray(const Vector3& origin, const Vector3& direction) {
+ set(origin, direction);
+ }
- virtual ~Ray() {}
+ 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.
+ Creates a Ray from a origin and a (nonzero) unit direction.
*/
static Ray fromOriginAndDirection(const Vector3& point, const Vector3& direction) {
return Ray(point, direction);
}
- Ray unit() const {
- return Ray(origin, direction.unit());
- }
+ /** Advances the origin along the direction by @a distance */
+ inline Ray bump(float distance) const {
+ return Ray(m_origin + m_direction * distance, m_direction);
+ }
+
+ /** Advances the origin along the @a bumpDirection by @a distance and returns the new ray*/
+ inline Ray bump(float distance, const Vector3& bumpDirection) const {
+ return Ray(m_origin + bumpDirection * distance, m_direction);
+ }
/**
Returns the closest point on the Ray to point.
*/
Vector3 closestPoint(const Vector3& point) const {
- float t = direction.dot(point - this->origin);
+ float t = m_direction.dot(point - m_origin);
if (t < 0) {
- return this->origin;
+ return m_origin;
} else {
- return this->origin + direction * t;
+ return m_origin + m_direction * t;
}
}
@@ -80,7 +122,7 @@ public:
Vector3 intersection(const class Plane& plane) const;
/**
- Returns the distance until intersection with the (solid) sphere.
+ Returns the distance until intersection with the sphere or the (solid) ball bounded by the 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
@@ -90,8 +132,10 @@ public:
See also the G3D::CollisionDetection "movingPoint" methods,
which give more information about the intersection.
+
+ \param solid If true, rays inside the sphere immediately intersect (good for collision detection). If false, they hit the opposite side of the sphere (good for ray tracing).
*/
- float intersectionTime(const class Sphere& sphere) const;
+ float intersectionTime(const class Sphere& sphere, bool solid = false) const;
float intersectionTime(const class Plane& plane) const;
@@ -121,6 +165,7 @@ public:
const Vector3& edge01,
const Vector3& edge02) const;
+
inline float intersectionTime(
const Vector3& vert0,
const Vector3& vert1,
@@ -129,6 +174,7 @@ public:
return intersectionTime(vert0, vert1, vert2, vert1 - vert0, vert2 - vert0);
}
+
inline float intersectionTime(
const Vector3& vert0,
const Vector3& vert1,
@@ -140,12 +186,12 @@ public:
return intersectionTime(vert0, vert1, vert2, vert1 - vert0, vert2 - vert0, w0, w1, w2);
}
- /* One-sided triangle
+ /* 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);
+ triangle.edge01(), triangle.edge02());
}
inline float intersectionTime(
@@ -154,7 +200,7 @@ public:
double& w1,
double& w2) const {
return intersectionTime(triangle.vertex(0), triangle.vertex(1), triangle.vertex(2),
- triangle.edge01, triangle.edge02, w0, w1, w2);
+ triangle.edge01(), triangle.edge02(), w0, w1, w2);
}
/** Refracts about the normal
@@ -175,6 +221,7 @@ public:
const Vector3& normal) const;
};
+
#define EPSILON 0.000001
#define CROSS(dest,v1,v2) \
dest[0]=v1[1]*v2[2]-v1[2]*v2[1]; \
@@ -186,7 +233,7 @@ public:
#define SUB(dest,v1,v2) \
dest[0]=v1[0]-v2[0]; \
dest[1]=v1[1]-v2[1]; \
- dest[2]=v1[2]-v2[2];
+ dest[2]=v1[2]-v2[2];
inline float Ray::intersectionTime(
const Vector3& vert0,
@@ -202,49 +249,51 @@ inline float Ray::intersectionTime(
float u, v;
float tvec[3], pvec[3], qvec[3];
-
+
// begin calculating determinant - also used to calculate U parameter
- CROSS(pvec, direction, edge2);
-
+ CROSS(pvec, m_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();
+ return finf();
}
-
+
// calculate distance from vert0 to ray origin
- SUB(tvec, origin, vert0);
-
+ SUB(tvec, m_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();
+ return finf();
}
-
+
// prepare to test V parameter
CROSS(qvec, tvec, edge1);
-
+
// calculate V parameter and test bounds
- v = DOT(direction, qvec);
+ v = DOT(m_direction, qvec);
if ((v < 0.0f) || (u + v > det)) {
// Hit the plane outside the triangle
- return (float)inf();
+ return finf();
}
+
// 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();
+ return finf();
}
}
+
inline float Ray::intersectionTime(
const Vector3& vert0,
const Vector3& vert1,
@@ -264,37 +313,37 @@ inline float Ray::intersectionTime(
float tvec[3], pvec[3], qvec[3];
// begin calculating determinant - also used to calculate U parameter
- CROSS(pvec, direction, edge2);
-
+ CROSS(pvec, m_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();
+ return finf();
}
-
+
// calculate distance from vert0 to ray origin
- SUB(tvec, origin, vert0);
-
+ SUB(tvec, m_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();
+ return finf();
}
-
+
// prepare to test V parameter
CROSS(qvec, tvec, edge1);
-
+
// calculate V parameter and test bounds
- v = DOT(direction, qvec);
+ v = DOT(m_direction, qvec);
if ((v < 0.0f) || (u + v > det)) {
// Hit the plane outside the triangle
- return (float)inf();
+ return finf();
}
-
+
float t = DOT(edge2, qvec);
-
+
if (t >= 0) {
const float inv_det = 1.0f / det;
t *= inv_det;
@@ -308,7 +357,7 @@ inline float Ray::intersectionTime(
return t;
} else {
// We had to travel backwards in time to intersect
- return (float)inf();
+ return finf();
}
}
@@ -320,4 +369,3 @@ inline float Ray::intersectionTime(
}// namespace
#endif
-
diff --git a/dep/include/g3dlite/G3D/Rect2D.h b/dep/include/g3dlite/G3D/Rect2D.h
new file mode 100644
index 00000000000..2fb58c50465
--- /dev/null
+++ b/dep/include/g3dlite/G3D/Rect2D.h
@@ -0,0 +1,417 @@
+/**
+ @file Rect2D.h
+
+ @maintainer Morgan McGuire, http://graphics.cs.williams.edu
+
+ @created 2003-11-13
+ @created 2009-11-16
+
+ Copyright 2000-2009, 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"
+
+#ifdef _MSC_VER
+// Turn off "conditional expression is constant" warning; MSVC generates this
+// for debug assertions in inlined methods.
+# pragma warning (disable : 4127)
+#endif
+
+
+namespace G3D {
+
+class Any;
+
+/**
+ 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:
+
+ /** \param any Must either Rect2D::xywh(#, #, #, #) or Rect2D::xyxy(#, #, #, #)*/
+ Rect2D(const Any& any);
+
+ /** Converts the Rect2D to an Any. */
+ operator Any() const;
+
+ Rect2D() : min(0, 0), max(0, 0) {}
+
+ /** Creates a rectangle at 0,0 with the given width and height*/
+ 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.*/
+ 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 */
+ Vector2 randomPoint() const {
+ return Vector2(uniformRandom(0, max.x - min.x) + min.x,
+ uniformRandom(0, max.y - min.y) + min.y);
+ }
+
+ float width() const {
+ return max.x - min.x;
+ }
+
+ float height() const {
+ return max.y - min.y;
+ }
+
+ float x0() const {
+ return min.x;
+ }
+
+ float x1() const {
+ return max.x;
+ }
+
+ float y0() const {
+ return min.y;
+ }
+
+ float y1() const {
+ return max.y;
+ }
+
+ /** Min, min corner */
+ Vector2 x0y0() const {
+ return min;
+ }
+
+ Vector2 x1y0() const {
+ return Vector2(max.x, min.y);
+ }
+
+ Vector2 x0y1() const {
+ return Vector2(min.x, max.y);
+ }
+
+ /** Max,max corner */
+ Vector2 x1y1() const {
+ return max;
+ }
+
+ /** Width and height */
+ Vector2 wh() const {
+ return max - min;
+ }
+
+ Vector2 center() const {
+ return (max + min) * 0.5;
+ }
+
+ float area() const {
+ return width() * height();
+ }
+
+ bool isFinite() const {
+ return (min.isFinite() && max.isFinite());
+ }
+
+ 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;
+ }
+
+ 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;
+ }
+
+ static Rect2D xyxy(const Vector2& v0, const Vector2& v1) {
+ Rect2D r;
+
+ r.min = v0.min(v1);
+ r.max = v0.max(v1);
+
+ return r;
+ }
+
+ static Rect2D xywh(float x, float y, float w, float h) {
+ return xyxy(x, y, x + w, y + h);
+ }
+
+ static Rect2D xywh(const Vector2& v, const Vector2& w) {
+ return xyxy(v.x, v.y, v.x + w.x, v.y + w.y);
+ }
+
+ /** Constructs a Rect2D with infinite boundaries.
+ Use isFinite() to test either min or max.
+ */
+ static Rect2D inf() {
+ return xyxy(Vector2::inf(), Vector2::inf());
+ }
+
+ bool contains(const Vector2& v) const {
+ return (v.x >= min.x) && (v.y >= min.y) && (v.x <= max.x) && (v.y <= max.y);
+ }
+
+ 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.*/
+ 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. */
+ 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);
+ }
+
+ Rect2D operator*(float s) const {
+ return xyxy(min.x * s, min.y * s, max.x * s, max.y * s);
+ }
+
+ Rect2D operator/(float s) const {
+ return xyxy(min / s, max / s);
+ }
+
+ Rect2D operator/(const Vector2& s) const {
+ return xyxy(min / s, max / s);
+ }
+
+ Rect2D operator+(const Vector2& v) const {
+ return xyxy(min + v, max + v);
+ }
+
+ Rect2D operator-(const Vector2& v) const {
+ return xyxy(min - v, max - v);
+ }
+
+ bool operator==(const Rect2D& other) const {
+ return (min == other.min) && (max == other.max);
+ }
+
+ 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). */
+ 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() */
+ 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.) */
+ 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);
+ }
+ }
+};
+
+typedef Rect2D AABox2D;
+}
+
+#endif
diff --git a/dep/include/g3dlite/G3D/ReferenceCount.h b/dep/include/g3dlite/G3D/ReferenceCount.h
new file mode 100644
index 00000000000..84591c6d8e5
--- /dev/null
+++ b/dep/include/g3dlite/G3D/ReferenceCount.h
@@ -0,0 +1,570 @@
+/**
+ @file ReferenceCount.h
+
+ Reference Counting Garbage Collector for C++
+
+ @maintainer Morgan McGuire, http://graphics.cs.williams.edu
+ @cite Adapted and extended from Justin Miller's "RGC" class that appeared in BYTE magazine.
+ @cite See also http://www.jelovic.com/articles/cpp_without_memory_errors_slides.htm
+
+ @created 2001-10-23
+ @edited 2009-04-25
+*/
+#ifndef G3D_ReferenceCount_h
+#define G3D_ReferenceCount_h
+
+#include "G3D/platform.h"
+#include "G3D/debug.h"
+#include "G3D/AtomicInt32.h"
+
+namespace G3D {
+
+#ifdef _MSC_VER
+// Turn off "conditional expression is constant" warning; MSVC generates this
+// for debug assertions in inlined methods.
+# pragma warning (disable : 4127)
+#endif
+
+/** 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>
+ */
+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();
+
+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();
+
+ virtual ~ReferenceCountedObject();
+
+
+ /**
+ Note: copies will initially start out with 0
+ references and 0 weak references like any other object.
+ */
+ ReferenceCountedObject(const ReferenceCountedObject& notUsed);
+
+ ReferenceCountedObject& operator=(const ReferenceCountedObject& other);
+};
+
+
+
+/**
+ 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) {
+
+ ReferenceCountedObject* pointer = ((ReferenceCountedObject*)m_pointer);
+ debugAssert(G3D::isValidHeapPointer(m_pointer));
+ debugAssertM(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 (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.
+ pointer->ReferenceCountedObject_zeroWeakPointers();
+ delete 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
+ ReferenceCountedObject* pointer = (ReferenceCountedObject*)m_pointer;
+ debugAssertM(pointer->ReferenceCountedObject_refCount.value() >= 0,
+ "Negative reference count detected.");
+ 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.
+
+ Use of const allows downcast on const references */
+ inline ReferenceCountedPointer(const T* p) : m_pointer(NULL) {
+ // only const constructor is defined to remove ambiguity using NULL
+ setPointer(const_cast<T*>(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 != NULL) {
+ // 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(((ReferenceCountedObject*)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 = &((ReferenceCountedObject*)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. */
+ void objectCollected() {
+ debugAssertM(pointer != NULL,
+ "Removed a weak pointer twice.");
+ pointer = NULL;
+ }
+
+};
+
+} // namespace
+
+#endif
+
diff --git a/dep/include/g3dlite/G3D/RegistryUtil.h b/dep/include/g3dlite/G3D/RegistryUtil.h
index 85b5d0ab1be..4b47be5f4bd 100644
--- a/dep/include/g3dlite/G3D/RegistryUtil.h
+++ b/dep/include/g3dlite/G3D/RegistryUtil.h
@@ -21,11 +21,14 @@
namespace G3D {
-/**
+/**
Provides generalized Windows registry querying.
All key names are one string in the format:
- "[base key]\[sub-keys]\value"
+ "[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
@@ -37,45 +40,53 @@ namespace G3D {
HKEY_PERFORMANCE_TEXT
HKEY_USERS
- keyExists() should be used to validate a key before reading or writing
- to ensure that a debug assert or false return is for a different error.
+ 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 */
+ /** 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, int32& valueData);
+ 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 valueData pointer to the output buffer of sufficient size. Pass NULL as valueData in order to have available data size returned in dataSize.
- @param dataSize size of the output buffer. When NULL is passed for valueData, contains the size of available data on successful return.
+ @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, uint8* valueData, uint32& dataSize);
+ 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, std::string& valueData);
+ 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, int32 valueData);
+ 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 valueData pointer to the input buffer
+
+ @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 uint8* valueData, uint32 dataSize);
+ 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& valueData);
+ static bool writeString(const std::string& key, const std::string& value, const std::string& data);
};
@@ -84,4 +95,3 @@ public:
#endif // G3D_WIN32
#endif // G3D_REGISTRYTUIL_H
-
diff --git a/dep/include/g3dlite/G3D/Set.h b/dep/include/g3dlite/G3D/Set.h
new file mode 100644
index 00000000000..9a8e1b619bb
--- /dev/null
+++ b/dep/include/g3dlite/G3D/Set.h
@@ -0,0 +1,186 @@
+/**
+ @file Set.h
+
+ Hash set
+
+ @maintainer Morgan McGuire, http://graphics.cs.williams.edu
+
+ @created 2001-12-09
+ @edited 2009-06-10
+ */
+
+#ifndef G3D_Set_h
+#define G3D_Set_h
+
+#include "G3D/platform.h"
+#include "G3D/Table.h"
+#include "G3D/MemoryManager.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 HashFunc = HashTrait<T>, class EqualsFunc = EqualsTrait<T> >
+class Set {
+
+ /**
+ If an object is a member, it is contained in
+ this table.
+ */
+ Table<T, bool, HashFunc, EqualsFunc> memberTable;
+
+public:
+
+ void clearAndSetMemoryManager(const MemoryManager::Ref& m) {
+ memberTable.clearAndSetMemoryManager(m);
+ }
+
+ 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);
+ }
+
+ /**
+ Returns true if the element was present and removed. Returns false
+ if the element was not present.
+ */
+ bool remove(const T& member) {
+ return memberTable.remove(member);
+ }
+
+ /** If @a member is present, sets @a removed to the element
+ being removed and returns true. Otherwise returns false
+ and does not write to @a removed. This is useful when building
+ efficient hashed data structures that wrap Set.
+ */
+ bool getRemove(const T& member, T& removed) {
+ bool ignore;
+ return memberTable.getRemove(member, removed, ignore);
+ }
+
+ /** If a value that is EqualsFunc to @a member is present, returns a pointer to the
+ version stored in the data structure, otherwise returns NULL.
+ */
+ const T* getPointer(const T& member) const {
+ return memberTable.getKeyPointer(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 hasMore() const {
+ return it.hasMore();
+ }
+
+ 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/dep/include/g3dlite/G3D/SmallArray.h b/dep/include/g3dlite/G3D/SmallArray.h
new file mode 100644
index 00000000000..41f9959e264
--- /dev/null
+++ b/dep/include/g3dlite/G3D/SmallArray.h
@@ -0,0 +1,155 @@
+/**
+ @file SmallArray.h
+
+ @created 2009-04-26
+ @edited 2009-04-26
+
+ Copyright 2000-2009, Morgan McGuire, http://graphics.cs.williams.edu
+ All rights reserved.
+ */
+#ifndef G3D_SmallArray_h
+#define G3D_SmallArray_h
+
+#include "G3D/platform.h"
+#include "G3D/Array.h"
+
+namespace G3D {
+
+/** Embeds \a N elements to reduce allocation time and increase
+ memory coherence when working with arrays of arrays.
+ Offers a limited subset of the functionality of G3D::Array.*/
+template<class T, int N>
+class SmallArray {
+private:
+ int m_size;
+
+ /** First N elements */
+ T m_embedded[N];
+
+ /** Remaining elements */
+ Array<T> m_rest;
+
+public:
+
+ SmallArray() : m_size(0) {}
+
+ inline int size() const {
+ return m_size;
+ }
+
+ void resize(int n, bool shrinkIfNecessary = true) {
+ m_rest.resize(std::max(0, n - N), shrinkIfNecessary);
+ m_size = n;
+ }
+
+ void clear(bool shrinkIfNecessary = true) {
+ resize(0, shrinkIfNecessary);
+ }
+
+ inline T& operator[](int i) {
+ debugAssert(i < m_size && i >= 0);
+ if (i < N) {
+ return m_embedded[i];
+ } else {
+ return m_rest[i - N];
+ }
+ }
+
+ inline const T& operator[](int i) const {
+ debugAssert(i < m_size && i >= 0);
+ if (i < N) {
+ return m_embedded[i];
+ } else {
+ return m_rest[i - N];
+ }
+ }
+
+ inline void push(const T& v) {
+ ++m_size;
+ if (m_size <= N) {
+ m_embedded[m_size - 1] = v;
+ } else {
+ m_rest.append(v);
+ }
+ }
+
+ inline void append(const T& v) {
+ push(v);
+ }
+
+ void fastRemove(int i) {
+ debugAssert(i < m_size && i >= 0);
+ if (i < N) {
+ if (m_size <= N) {
+ // Exclusively embedded
+ m_embedded[i] = m_embedded[m_size - 1];
+ } else {
+ // Move one down from the rest array
+ m_embedded[i] = m_rest.pop();
+ }
+ } else {
+ // Removing from the rest array
+ m_rest.fastRemove(i - N);
+ }
+ --m_size;
+ }
+
+ T pop() {
+ debugAssert(m_size > 0);
+ if (m_size <= N) {
+ // Popping from embedded, don't need a temporary
+ --m_size;
+ return m_embedded[m_size];
+ } else {
+ // Popping from rest
+ --m_size;
+ return m_rest.pop();
+ }
+ }
+
+ inline void popDiscard() {
+ debugAssert(m_size > 0);
+ if (m_size > N) {
+ m_rest.popDiscard();
+ }
+ --m_size;
+ }
+
+ inline T& next() {
+ ++m_size;
+ if (m_size <= N) {
+ return m_embedded[m_size - 1];
+ } else {
+ return m_rest.next();
+ }
+ }
+
+ bool contains(const T& value) const {
+ for (int i = std::min(m_size, N) - 1; i >= 0; --i) {
+ if (m_embedded[i] == value) {
+ return true;
+ }
+ }
+ return m_rest.contains(value);
+ }
+
+ template<int MIN_ELEMENTS, int MIN_BYTES>
+ SmallArray<T, N>& operator=(const Array<T, MIN_ELEMENTS, MIN_BYTES>& src) {
+ resize(src.size());
+ for (int i = 0; i < src.size(); ++i) {
+ (*this)[i] = src[i];
+ }
+ return *this;
+ }
+
+ inline const T& last() const {
+ return (*this)[size() - 1];
+ }
+
+ inline T& last() {
+ return (*this)[size() - 1];
+ }
+};
+
+}
+#endif
diff --git a/dep/include/g3dlite/G3D/Sphere.h b/dep/include/g3dlite/G3D/Sphere.h
index 122e4d41f65..595b61c4bf1 100644
--- a/dep/include/g3dlite/G3D/Sphere.h
+++ b/dep/include/g3dlite/G3D/Sphere.h
@@ -1,12 +1,12 @@
/**
@file Sphere.h
-
+
Sphere class
-
- @maintainer Morgan McGuire, matrix@graphics3d.com
-
+
+ @maintainer Morgan McGuire, http://graphics.cs.williams.edu
+
@created 2001-06-02
- @edited 2004-07-05
+ @edited 2008-10-07
*/
#ifndef G3D_SPHERE_H
@@ -36,6 +36,10 @@ public:
radius = 0;
}
+ Sphere(class BinaryInput& b);
+ void serialize(class BinaryOutput& b) const;
+ void deserialize(class BinaryInput& b);
+
Sphere(
const Vector3& center,
float radius) {
@@ -60,51 +64,49 @@ public:
*/
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;
+ bool contains(const Sphere& other) const;
/**
- @deprecated Use culledBy(Array<Plane>&)
+ @deprecated Use culledBy(Array<Plane>&)
*/
bool culledBy(
- const class Plane* plane,
- int numPlanes,
- int32& cullingPlaneIndex = dummy,
- const uint32 testMask = -1) const;
-
+ const class Plane* plane,
+ int numPlanes,
+ int32& cullingPlaneIndex,
+ const uint32 testMask,
+ uint32& childMask) const;
+
/**
- See AABox::culledBy
+ @deprecated Use culledBy(Array<Plane>&)
*/
bool culledBy(
- const Array<Plane>& plane,
- int32& cullingPlaneIndex,
- const uint32 testMask,
- uint32& childMask) const;
+ 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 = -1) const;
+ const Array<Plane>& plane,
+ int32& cullingPlaneIndex = dummy,
+ const uint32 testMask = 0xFFFFFFFF) const;
+
virtual std::string toString() const;
float volume() const;
- /** @deprecated */
- float surfaceArea() const;
-
- inline float area() const {
- return surfaceArea();
- }
+ float area() const;
/**
Uniformly distributed on the surface.
@@ -117,13 +119,30 @@ public:
Vector3 randomInteriorPoint() const;
void getBounds(class AABox& out) const;
-};
-} // namespace
+ 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);
+ }
+
+ /** Sets this to the smallest sphere that encapsulates both */
+ void merge(const Sphere& s);
+};
-inline unsigned int hashCode(const G3D::Sphere& sphere) {
- return (unsigned int)(hashCode(sphere.center) + (sphere.radius * 13));
}
-#endif
+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/dep/include/g3dlite/G3D/Spline.h b/dep/include/g3dlite/G3D/Spline.h
new file mode 100644
index 00000000000..fdd29e69ce9
--- /dev/null
+++ b/dep/include/g3dlite/G3D/Spline.h
@@ -0,0 +1,367 @@
+/**
+ @file Spline.h
+
+ @author Morgan McGuire, http://graphics.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
+ @a s = time[@a i] * @a u + time[@a i + 1] * (1 - @a u). Note that
+ @a 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 { (void)A; (void) N;}
+
+ /** Normalize or otherwise adjust this interpolated Control. */
+ virtual void correct(Control& A) const { (void)A; }
+
+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/dep/include/g3dlite/G3D/Stopwatch.h b/dep/include/g3dlite/G3D/Stopwatch.h
new file mode 100644
index 00000000000..3f2aa9c8d86
--- /dev/null
+++ b/dep/include/g3dlite/G3D/Stopwatch.h
@@ -0,0 +1,144 @@
+/**
+ @file Stopwatch.h
+
+ @maintainer Morgan McGuire, http://graphics.cs.williams.edu
+
+ @created 2005-10-05
+ @edited 2009-05-10
+
+ Copyright 2000-2009, 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 {
+
+/**
+ \brief Accurately measure durations and framerates.
+
+ Example 1: For profiling code in the context of a rendering loop:
+ <pre>
+ sw.tick();
+ ...timed code...
+ sw.tock();
+
+ screenPrintf("%f\n", sw.smoothFPS());
+ </pre>
+
+
+ Example 2: For profiling pieces of a sequence:
+ <pre>
+ Stopwatch sw;
+ slowOperation();
+ sw.after("slowOperation");
+ kdTree.balance();
+ sw.after("Balance tree");
+ </pre>
+ */
+class Stopwatch {
+private:
+
+ std::string myName;
+ double startTime;
+ std::string prevMark;
+ double prevTime;
+
+ /** 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(const std::string& name = "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();
+
+
+ /** Reset the start time used by after() and the emwa value.*/
+ void reset();
+
+ /** Call after an operation has completed, with the name of the operation, to
+ print a debug message listing the time since the previous after() call. */
+ void after(const std::string& s = "");
+
+};
+
+/** Because it is hard to remember the proper capitalization. */
+typedef Stopwatch StopWatch;
+
+}
+
+#endif
+
diff --git a/dep/include/g3dlite/G3D/System.h b/dep/include/g3dlite/G3D/System.h
index 81abff3fb50..56ef9c8e3dc 100644
--- a/dep/include/g3dlite/G3D/System.h
+++ b/dep/include/g3dlite/G3D/System.h
@@ -1,21 +1,23 @@
/**
@file System.h
- @maintainer Morgan McGuire, matrix@graphics3d.com
+ @maintainer Morgan McGuire, http://graphics.cs.williams.edu
@cite Rob Wyatt http://www.gamasutra.com/features/wyatts_world/19990709/processor_detection_01.htm
@cite Benjamin Jurke http://www.flipcode.com/cgi-bin/msg.cgi?showThread=COTD-ProcessorDetectionClass&forum=cotd&id=-1
@cite Michael Herf http://www.stereopsis.com/memcpy.html
@created 2003-01-25
- @edited 2006-04-26
+ @edited 2008-10-14
*/
-#ifndef G3D_SYSTEM_H
-#define G3D_SYSTEM_H
+#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
@@ -24,64 +26,249 @@
namespace G3D {
-typedef double RealTime;
+/**
+ Routine used by the demos to find the data. Searches in
+ ../data, ../../data, etc. up to 5 levels back. Checks
+ common locations like \verbatim c:\libraries\g3d-<version>\data \endverbatim
+ and some hard-coded paths on the Brown University file
+ system.
+
+ @deprecated
+ */
+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.
+ @deprecated Use System::license
+*/
+std::string license();
+
+/**
+@brief 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
+};
+
+/**
+ @brief 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:
+ /**
+ @param size Size of memory that the system was trying to allocate
- /** Called automatically by the other System routines.*/
- static void init();
+ @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.
- /**
- Guarantees that the start of the array is aligned to the
- specified number of bytes.
+ @return Return true to force malloc to attempt allocation again if the
+ error was recoverable.
*/
- static void* alignedMalloc(size_t bytes, size_t alignment);
+ typedef bool (*OutOfMemoryCallback)(size_t size, bool recoverable);
- /**
- Uses pooled storage to optimize small allocations (1 byte to 5 kilobytes).
- Can be 10x to 100x faster than calling ::malloc or new.
+private:
- The result must be freed with free.
+ bool m_initialized;
+ int m_cpuSpeed;
+ bool m_hasCPUID;
+ bool m_hasRDTSC;
+ bool m_hasMMX;
+ bool m_hasSSE;
+ bool m_hasSSE2;
+ bool m_hasSSE3;
+ bool m_has3DNOW;
+ bool m_has3DNOW2;
+ bool m_hasAMDMMX;
+ std::string m_cpuVendor;
+ int m_numCores;
- Threadsafe on Win32.
+ /** this holds the data directory set by the application (currently
+ GApp) for use by findDataFile */
+ std::string m_appDataDir;
+
+ G3DEndian m_machineEndian;
+ std::string m_cpuArch;
+ std::string m_operatingSystem;
+
+# ifdef G3D_WIN32
+ /** Used by getTick() for timing */
+ LARGE_INTEGER m_start;
+ LARGE_INTEGER m_counterFrequency;
+#else
+ struct timeval m_start;
+#endif
+
+ std::string m_version;
+ OutOfMemoryCallback m_outOfMemoryCallback;
+
+#ifdef G3D_OSX
+ /** In Cycles/Second */
+ SInt32 m_OSXCPUSpeed;
+ double m_secondsPerNS;
+#endif
+
+ /** The Real-World time of System::getTick() time 0. Set by initTime */
+ RealTime m_realWorldGetTickTime0;
- @sa calloc realloc OutOfMemoryCallback free
+ uint32 m_highestCPUIDFunction;
+
+ /** @brief Used for the singleton instance only. */
+ System();
+
+ /** @brief The singleton instance.
+
+ Used instead of a global variable to ensure that the order of
+ intialization is correct, which is critical because other
+ globals may allocate memory using System::malloc.
*/
- static void* malloc(size_t bytes);
+ static System& instance();
- static void* calloc(size_t n, size_t x);
+ enum CPUIDFunction {
+ CPUID_VENDOR_ID = 0x00000000,
+ CPUID_PROCESSOR_FEATURES = 0x00000001,
+ CPUID_NUM_CORES = 0x00000004,
+ CPUID_GET_HIGHEST_FUNCTION = 0x80000000,
+ CPUID_EXTENDED_FEATURES = 0x80000001};
+
+ /** Helper macro to call cpuid functions and return all values
+
+ See http://software.intel.com/en-us/articles/intel-64-architecture-processor-topology-enumeration/
+ or http://www.amd.com/us-en/assets/content_type/white_papers_and_tech_docs/25481.pdf
+
+ for description of the arguments.
+ */
+ static void cpuid(CPUIDFunction func, uint32& areg, uint32& breg, uint32& creg, uint32& dreg);
+
+ void init();
+
+ /** Called from init() */
+ void getStandardProcessorExtensions();
+
+ /** Called from init() */
+ void initTime();
+
+public:
+
+ /** Returns the speed of processor 0 in MHz.
+ Always returns 0 on linux.*/
+ inline static int cpuSpeedMHz() {
+ return instance().m_cpuSpeed;
+ }
+
+ /** Returns the number of logical processor cores (i.e., the
+ number of execution units for threads) */
+ inline static int numCores() {
+ return instance().m_numCores;
+ }
+
+ inline static bool hasCPUID() {
+ return instance().m_hasCPUID;
+ }
+
+ inline static bool hasRDTSC() {
+ return instance().m_hasRDTSC;
+ }
+
+ inline static bool hasSSE() {
+ return instance().m_hasSSE;
+ }
+
+ inline static bool hasSSE2() {
+ return instance().m_hasSSE2;
+ }
+
+ inline static bool hasSSE3() {
+ return instance().m_hasSSE3;
+ }
+
+ inline static bool hasMMX() {
+ return instance().m_hasMMX;
+ }
+
+ inline static bool has3DNow() {
+ return instance().m_has3DNOW;
+ }
+
+ inline static const std::string& cpuVendor() {
+ return instance().m_cpuVendor;
+ }
/**
- @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);
+ Returns the endianness of this machine.
+ */
+ inline static G3DEndian machineEndian() {
+ return instance().m_machineEndian;
+ }
+
+ /** e.g., "Windows", "GNU/Linux" */
+ inline static const std::string& operatingSystem() {
+ return instance().m_operatingSystem;
+ }
+
+ /** e.g., 80686 */
+ inline static const std::string& cpuArchitecture() {
+ return instance().m_cpuArch;
+ }
/**
- 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.
+ Returns the current date as a string in the form YYYY-MM-DD
+ */
+ static std::string currentDateString();
- You can use outOfMemoryCallback to free data structures or to
- register the failure.
- */
- static OutOfMemoryCallback outOfMemoryCallback;
+ /**
+ 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);
/**
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.*/
+ /** 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();
@@ -104,18 +291,217 @@ public:
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. */
+ 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. */
+ 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 */
+ inline static const std::string& version() {
+ return instance().m_version;
+ }
+
+ /**
+ @brief The optimization status of the G3D library (not the program compiled against it)
+
+ 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.
+ </PRE>
+ */
+ /* static void beginCycleCount(uint64& cycleCount);
+ static void endCycleCount(uint64& cycleCount);
+
+ static uint64 getCycleCount(); */
+
+ inline static void setOutOfMemoryCallback(OutOfMemoryCallback c) {
+ instance().m_outOfMemoryCallback = c;
+ }
+
+ /**
+ 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.
+ */
+ inline static OutOfMemoryCallback outOfMemoryCallback() {
+ return instance().m_outOfMemoryCallback;
+ }
+
+ /** 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);
+
+ /** 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);
+
};
-} // namespace
+/* don't need that for MaNGOS, not portable to Win64...
+#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();
+ Nanoseconds diffNS =
+ AbsoluteDeltaToNanoseconds(end, UInt64ToUnsignedWide(cycleCount));
+ cycleCount =
+ (uint64) ((double) (instance().m_OSXCPUSpeed) *
+ (double) UnsignedWideToUInt64(diffNS) * instance().m_secondsPerNS);
+#endif
+}
+ */
+
+} // namespace
+
+#endif
diff --git a/dep/include/g3dlite/G3D/Table.h b/dep/include/g3dlite/G3D/Table.h
index aded5c38555..287efa94d97 100644
--- a/dep/include/g3dlite/G3D/Table.h
+++ b/dep/include/g3dlite/G3D/Table.h
@@ -3,109 +3,101 @@
Templated hash table class.
- @maintainer Morgan McGuire, matrix@graphics3d.com
+ @maintainer Morgan McGuire, http://graphics.cs.williams.edu
@created 2001-04-22
- @edited 2006-10-14
- Copyright 2000-2006, Morgan McGuire.
+ @edited 2010-01-28
+ Copyright 2000-2010, Morgan McGuire.
All rights reserved.
*/
-#ifndef G3D_TABLE_H
-#define G3D_TABLE_H
+#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/Crypto.h"
-#include <cstddef>
-#include <string>
+#include "G3D/EqualsTrait.h"
+#include "G3D/HashTrait.h"
+#include "G3D/MemoryManager.h"
-#ifdef G3D_WIN32
+#ifdef _MSC_VER
# pragma warning (push)
// Debug name too long warning
# pragma warning (disable : 4786)
#endif
-template<typename Key>
-struct GHashCode{};
+namespace G3D {
-template <>
-struct GHashCode<int>
-{
- size_t operator()(int key) const { return static_cast<size_t>(key); }
-};
+/**
+ An unordered data structure mapping keys to values.
-template <>
-struct GHashCode<G3D::uint32>
-{
- size_t operator()(G3D::uint32 key) const { return static_cast<size_t>(key); }
-};
+ There are two ways of definining custom hash functions (G3D provides built-in ones for most classes):
-template <>
-struct GHashCode<G3D::uint64>
-{
- size_t operator()(G3D::uint64 key) const { return static_cast<size_t>(key); }
-};
+ <pre>
+ class Foo {
+ public:
+ std::string name;
+ int index;
+ static size_t hashCode(const Foo& key) {
+ return HashTrait<std::string>::hashCode(key.name) + key.index;
+ }
+ };
-template <>
-struct GHashCode<void*>
-{
- size_t operator()(const void* key) const { return reinterpret_cast<size_t>(key); }
-};
+ template<> struct HashTrait<class Foo> {
+ static size_t hashCode(const Foo& key) { return HashTrait<std::string>::hashCode(key.name) + key.index; }
+ };
-template<class T>
-struct GHashCode<T*>
-{
- size_t operator()(const T* key) const { return reinterpret_cast<size_t>(key); }
-};
-template <>
-struct GHashCode<const std::string>
-{
- size_t operator()(const std::string& key) const { return static_cast<size_t>(G3D::Crypto::crc32(key.c_str(), key.size())); }
-};
+ // Use Foo::hashCode
+ Table<Foo, std::string, Foo> fooTable1;
-template <>
-struct GHashCode<std::string>
-{
- size_t operator()(const std::string& key) const { return static_cast<size_t>(G3D::Crypto::crc32(key.c_str(), key.size())); }
-};
+ // Use HashTrait<Foo>
+ Table<Foo, std::string> fooTable2;
+ </pre>
-namespace G3D {
-/**
- An unordered data structure mapping keys to values.
+ Key must be a pointer, an int, a std::string or provide overloads for:
- Key must be a pointer, an int, a std::string, a class with a hashCode() method,
- 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 GHashCode<class Key> {
- size_t operator()(Key key) const { return reinterpret_cast<size_t>( ... ); }
+ template<> struct EqualsTrait<class Key>{
+ static bool equals(const Key& a, const Key& b) { return ... ; }
};
- bool operator==(const Key&, const Key&);
+
+ bool operator==(const Key&, const Key&);
</PRE>
- G3D pre-defines GHashCode functions for common types (like <CODE>int</CODE> and <CODE>std::string</CODE>).
+ 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 GHashCode<MyEnum> {
- size_t operator()(MyEnum key) const { return reinterpret_cast<size_t>( key ); }
+ 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 = GHashCode<Key> >
+template<class Key, class Value, class HashFunc = HashTrait<Key>, class EqualsFunc = EqualsTrait<Key> >
class Table {
public:
@@ -116,164 +108,259 @@ public:
public:
Key key;
Value value;
+ Entry() {}
+ Entry(const Key& k) : key(k) {}
+ Entry(const Key& k, const Value& v) : key(k), value(v) {}
+ bool operator==(const Entry &peer) const { return (key == peer.key && value == peer.value); }
+ bool operator!=(const Entry &peer) const { return !operator==(peer); }
};
private:
+
+ typedef Table<Key, Value, HashFunc, EqualsFunc> ThisType;
+
/**
Linked list nodes used internally by HashTable.
*/
class Node {
public:
- size_t hashCode;
Entry entry;
+ size_t hashCode;
Node* next;
- /** Provide pooled allocation for speed. */
- inline void* operator new (size_t size) {
- return System::malloc(size);
+ private:
+
+ // Private to require use of the allocator
+ Node(const Key& k, const Value& v, size_t h, Node* n)
+ : entry(k, v), hashCode(h), next(n) {
}
- inline void operator delete (void* p) {
- System::free(p);
+ Node(const Key& k, size_t h, Node* n)
+ : entry(k), hashCode(h), next(n) {
+ }
+
+ public:
+
+ static Node* create(const Key& k, const Value& v, size_t h, Node* n, MemoryManager::Ref& mm) {
+ Node* node = (Node*)mm->alloc(sizeof(Node));
+ return new (node) Node(k, v, h, n);
}
- Node(Key key, Value value, size_t hashCode, Node* next) {
- this->entry.key = key;
- this->entry.value = value;
- this->hashCode = hashCode;
- this->next = next;
+ static Node* create(const Key& k, size_t hashCode, Node* n, MemoryManager::Ref& mm) {
+ Node* node = (Node*)mm->alloc(sizeof(Node));
+ return new (node) Node(k, hashCode, n);
+ }
+
+ static void destroy(Node* n, MemoryManager::Ref& mm) {
+ n->~Node();
+ mm->free(n);
}
/**
Clones a whole chain;
*/
- Node* clone() {
- return new Node(this->entry.key, this->entry.value, hashCode, (next == NULL) ? NULL : next->clone());
+ Node* clone(MemoryManager::Ref& mm) {
+ return create(this->entry.key, this->entry.value, hashCode, (next == NULL) ? NULL : next->clone(mm), mm);
}
};
- HashFunc m_HashFunc;
+ void checkIntegrity() const {
+# ifdef G3D_DEBUG
+ debugAssert(m_bucket == NULL || isValidHeapPointer(m_bucket));
+ for (size_t b = 0; b < m_numBuckets; ++b) {
+ Node* node = m_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;
+ /** Number of elements in the table.*/
+ size_t m_size;
/**
- Array of Node*.
- We don't use Array<Node*> because Table is lower level.
+ Array of Node*.
+
+ We don't use Array<Node*> because Table is lower-level than Array.
Some elements may be NULL.
*/
- Node** bucket;
-
+ Node** m_bucket;
+
/**
- Length of the bucket array.
+ Length of the m_bucket array.
*/
- size_t numBuckets;
+ size_t m_numBuckets;
- /**
- Re-hashes for a larger bucket size.
- */
- void resize(size_t numBuckets) {
+ MemoryManager::Ref m_memoryManager;
- Node** oldBucket = bucket;
- bucket = (Node**)System::alignedMalloc(sizeof(Node*) * numBuckets, 16);
- System::memset(bucket, 0, sizeof(Node*) * numBuckets);
+ void* alloc(size_t s) const {
+ return m_memoryManager->alloc(s);
+ }
- for (size_t b = 0; b < this->numBuckets; b++) {
- Node* node = oldBucket[b];
+ void free(void* p) const {
+ return m_memoryManager->free(p);
+ }
+ /**
+ Re-hashes for a larger m_bucket size.
+ */
+ void resize(size_t newSize) {
+
+ // Hang onto the old m_bucket array
+ Node** oldBucket = m_bucket;
+
+ // Allocate a new m_bucket array with the new size
+ m_bucket = (Node**)alloc(sizeof(Node*) * newSize);
+ // Set all pointers to NULL
+ System::memset(m_bucket, 0, newSize * sizeof(Node*));
+ debugAssertM(m_bucket != NULL, "MemoryManager::alloc returned NULL. Out of memory.");
+ // Move each node to its new hash location
+ for (size_t b = 0; b < m_numBuckets; ++b) {
+ Node* node = oldBucket[b];
+
+ // There is a linked list of nodes at this m_bucket
while (node != NULL) {
+ // Hang onto the old next pointer
Node* nextNode = node->next;
+
+ // Insert at the head of the list for m_bucket[i]
+ size_t i = node->hashCode % newSize;
+ node->next = m_bucket[i];
+ m_bucket[i] = node;
- // insert at the head of the list for bucket[i]
- size_t i = node->hashCode % numBuckets;
- 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;
}
- System::alignedFree(oldBucket);
- this->numBuckets = numBuckets;
+ // Delete the old storage
+ free(oldBucket);
+ this->m_numBuckets = newSize;
+
+ checkIntegrity();
}
- void copyFrom(const Table<Key, Value>& h) {
- this->_size = h._size;
- this->numBuckets = h.numBuckets;
- this->bucket = (Node**)System::alignedMalloc(sizeof(Node*) * numBuckets, 16);
- System::memset(this->bucket, 0, sizeof(Node*) * numBuckets);
- for (size_t b = 0; b < this->numBuckets; b++) {
- if (h.bucket[b] != NULL) {
- bucket[b] = h.bucket[b]->clone();
+
+ void copyFrom(const ThisType& h) {
+ if (&h == this) {
+ return;
+ }
+
+ debugAssert(m_bucket == NULL);
+ m_size = h.m_size;
+ m_numBuckets = h.m_numBuckets;
+ m_bucket = (Node**)alloc(sizeof(Node*) * m_numBuckets);
+ // No need to NULL elements since we're about to overwrite them
+
+ for (size_t b = 0; b < m_numBuckets; ++b) {
+ if (h.m_bucket[b] != NULL) {
+ m_bucket[b] = h.m_bucket[b]->clone(m_memoryManager);
+ } else {
+ m_bucket[b] = NULL;
}
}
+
+ checkIntegrity();
}
/**
Frees the heap structures for the nodes.
*/
void freeMemory() {
- for (size_t b = 0; b < numBuckets; b++) {
- Node* node = bucket[b];
- while (node != NULL) {
+ checkIntegrity();
+
+ for (size_t b = 0; b < m_numBuckets; b++) {
+ Node* node = m_bucket[b];
+ while (node != NULL) {
Node* next = node->next;
- delete node;
+ Node::destroy(node, m_memoryManager);
node = next;
- }
+ }
+ m_bucket[b] = NULL;
}
- System::alignedFree(bucket);
- bucket = NULL;
- numBuckets = 0;
- _size = 0;
+ free(m_bucket);
+ m_bucket = NULL;
+ m_numBuckets = 0;
+ m_size = 0;
}
public:
/**
- Creates an empty hash table. This causes some heap allocation to occur.
+ Creates an empty hash table using the default MemoryManager.
*/
- Table() {
- numBuckets = 10;
- _size = 0;
- bucket = (Node**)System::alignedMalloc(sizeof(Node*) * numBuckets, 16);
- System::memset(bucket, 0, sizeof(Node*) * numBuckets);
+ Table() : m_bucket(NULL) {
+ m_memoryManager = MemoryManager::create();
+ m_numBuckets = 0;
+ m_size = 0;
+ m_bucket = NULL;
+ checkIntegrity();
+ }
+
+ /** Changes the internal memory manager to m */
+ void clearAndSetMemoryManager(const MemoryManager::Ref& m) {
+ clear();
+ debugAssert(m_bucket == NULL);
+ m_memoryManager = m;
}
+ /**
+ 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 > m_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.
+ 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 Table<Key, Value>& h) {
+ /** Uses the default memory manager */
+ Table(const ThisType& h) {
+ m_memoryManager = MemoryManager::create();
+ m_numBuckets = 0;
+ m_size = 0;
+ m_bucket = NULL;
this->copyFrom(h);
+ checkIntegrity();
}
- Table& operator=(const Table<Key, Value>& h) {
+
+ 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.
+ Returns the length of the deepest m_bucket.
*/
size_t debugGetDeepestBucketSize() const {
size_t deepest = 0;
- for (size_t b = 0; b < numBuckets; b++) {
+ for (size_t b = 0; b < m_numBuckets; b++) {
size_t count = 0;
- Node* node = bucket[b];
+ Node* node = m_bucket[b];
while (node != NULL) {
node = node->next;
++count;
@@ -288,8 +375,29 @@ public:
}
/**
+ 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 < m_numBuckets; b++) {
+ Node* node = m_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
+ 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.
@@ -302,7 +410,7 @@ public:
Returns the number of buckets.
*/
size_t debugGetNumBuckets() const {
- return numBuckets;
+ return m_numBuckets;
}
/**
@@ -310,7 +418,7 @@ public:
*/
class Iterator {
private:
- friend class Table<Key, Value>;
+ friend class Table<Key, Value, HashFunc, EqualsFunc>;
/**
Bucket index.
@@ -321,31 +429,31 @@ public:
Linked list node.
*/
Node* node;
- Table<Key, Value>* table;
- size_t numBuckets;
- Node** bucket;
+ ThisType* table;
+ size_t m_numBuckets;
+ Node** m_bucket;
bool isDone;
/**
Creates the end iterator.
*/
- Iterator(const Table<Key, Value>* table) : table(const_cast<Table<Key, Value>*>(table)) {
+ Iterator(const ThisType* table) : table(const_cast<ThisType*>(table)) {
isDone = true;
}
- Iterator(const Table<Key, Value>* table, size_t numBuckets, Node** bucket) :
- table(const_cast<Table<Key, Value>*>(table)),
- numBuckets(numBuckets),
- bucket(bucket) {
-
- if (numBuckets == 0) {
+ Iterator(const ThisType* table, size_t m_numBuckets, Node** m_bucket) :
+ table(const_cast<ThisType*>(table)),
+ m_numBuckets(m_numBuckets),
+ m_bucket(m_bucket) {
+
+ if (m_numBuckets == 0) {
// Empty table
isDone = true;
return;
}
index = 0;
- node = bucket[index];
+ node = m_bucket[index];
isDone = false;
findNext();
}
@@ -357,11 +465,11 @@ public:
void findNext() {
while (node == NULL) {
index++;
- if (index >= numBuckets) {
+ if (index >= m_numBuckets) {
isDone = true;
break;
} else {
- node = bucket[index];
+ node = m_bucket[index];
}
}
}
@@ -378,7 +486,7 @@ public:
} else {
return
(table == other.table) &&
- (node == other.node) &&
+ (node == other.node) &&
(index == other.index);
}
}
@@ -412,15 +520,20 @@ public:
operator Entry*() const {
return &(node->entry);
}
+
+ bool hasMore() const {
+ return ! isDone;
+ }
};
+
/**
- C++ STL style iterator method. Returns the first Entry, which
+ 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);
+ return Iterator(this, m_numBuckets, m_bucket);
}
/**
@@ -435,134 +548,175 @@ public:
Removes all elements
*/
void clear() {
- freeMemory();
- numBuckets = 20;
- _size = 0;
- bucket = (Node**)System::alignedMalloc(sizeof(Node*) * numBuckets, 16);
- System::memset(bucket, 0, sizeof(Node*) * numBuckets);
+ freeMemory();
+ m_numBuckets = 0;
+ m_size = 0;
+ m_bucket = NULL;
}
+
/**
Returns the number of keys.
*/
size_t size() const {
- return _size;
+ return m_size;
}
+
/**
If you insert a pointer into the key or value of a table, you are
- responsible for deallocating the object eventually. Inserting
+ 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 = m_HashFunc(key);
- size_t b = code % numBuckets;
+ getCreateEntry(key).value = value;
+ }
- // Go to the bucket
- Node* n = bucket[b];
+private:
- // No bucket, so this must be the first
- if (n == NULL) {
- bucket[b] = new Node(key, value, code, NULL);
- ++_size;
- return;
+ /** Helper for remove() and getRemove() */
+ bool remove(const Key& key, Key& removedKey, Value& removedValue, bool updateRemoved) {
+ if (m_numBuckets == 0) {
+ return false;
}
+ size_t code = HashFunc::hashCode(key);
+ size_t b = code % m_numBuckets;
- size_t bucketLength = 1;
+ // Go to the m_bucket
+ Node* n = m_bucket[b];
- // 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;
+ if (n == NULL) {
+ return false;
+ }
- // Try to find the node
- do {
- allSameCode = allSameCode && (code == n->hashCode);
+ 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
- if ((code == n->hashCode) && (n->entry.key == key)) {
- // Replace the existing node.
- n->entry.value = value;
- return;
- }
+ // Replace the previous's next pointer
+ if (previous == NULL) {
+ m_bucket[b] = n->next;
+ } else {
+ previous->next = n->next;
+ }
- n = n->next;
- ++bucketLength;
- } while (n != NULL);
+ if (updateRemoved) {
+ removedKey = n->entry.key;
+ removedValue = n->entry.value;
+ }
+ // Delete the node
+ Node::destroy(n, m_memoryManager);
+ --m_size;
+ return true;
+ }
- const size_t maxBucketLength = 5;
- if ((bucketLength > maxBucketLength) & ! allSameCode && (numBuckets < _size * 20)) {
- // This bucket was really large; rehash if all elements
- // don't have the same hashcode the number of buckets is reasonable.
- resize(numBuckets * 2 + 1);
- }
+ previous = n;
+ n = n->next;
+ } while (n != NULL);
- // Not found; insert at the head.
- b = code % numBuckets;
- bucket[b] = new Node(key, value, code, bucket[b]);
- ++_size;
+ return false;
+ //alwaysAssertM(false, "Tried to remove a key that was not in the table.");
}
- /**
- Removes an element from the table if it is present. It is an error
- to remove an element that isn't present.
+public:
+
+ /** If @a member is present, sets @a removed to the element
+ being removed and returns true. Otherwise returns false
+ and does not write to @a removed. */
+ bool getRemove(const Key& key, Key& removedKey, Value& removedValue) {
+ return remove(key, removedKey, removedValue, true);
+ }
+
+ /**
+ Removes an element from the table if it is present.
+ @return true if the element was found and removed, otherwise false
*/
- void remove(const Key& key) {
+ bool remove(const Key& key) {
+ Key x;
+ Value v;
+ return remove(key, x, v, false);
+ }
- size_t code = m_HashFunc(key);
- size_t b = code % numBuckets;
+private:
- // Go to the bucket
- Node* n = bucket[b];
+ Entry* getEntryPointer(const Key& key) const {
+ if (m_numBuckets == 0) {
+ return NULL;
+ }
- // Make sure it was found
- alwaysAssertM(n != NULL, "Tried to remove a key that was not in the table.");
+ size_t code = HashFunc::hashCode(key);
+ size_t b = code % m_numBuckets;
- Node* previous = NULL;
+ Node* node = m_bucket[b];
- // Try to find the node
- do {
- if ((code == n->hashCode) && (n->entry.key == key)) {
- // This is the node; remove it
- if (previous == NULL) {
- bucket[b] = n->next;
- } else {
- previous->next = n->next;
- }
- // Delete the node
- delete n;
- --_size;
- return;
- }
+ while (node != NULL) {
+ if ((node->hashCode == code) && EqualsFunc::equals(node->entry.key, key)) {
+ return &(node->entry);
+ }
+ node = node->next;
+ }
- previous = n;
- n = n->next;
- } while (n != NULL);
+ return NULL;
+ }
- alwaysAssertM(false, "Tried to remove a key that was not in the table.");
+public:
+
+ /** If a value that is EqualsFunc to @a member is present, returns a pointer to the
+ version stored in the data structure, otherwise returns NULL.
+ */
+ const Key* getKeyPointer(const Key& key) const {
+ const Entry* e = getEntryPointer(key);
+ if (e == NULL) {
+ return NULL;
+ } else {
+ return &(e->key);
+ }
}
/**
Returns the value associated with key.
- @deprecated Use get(key, val) or
+ @deprecated Use get(key, val) or getPointer(key)
*/
Value& get(const Key& key) const {
+ Entry* e = getEntryPointer(key);
+ debugAssertM(e != NULL, "Key not found");
+ return e->value;
+ }
- size_t code = m_HashFunc(key);
- size_t b = code % numBuckets;
- Node* node = bucket[b];
+ /** 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.
- while (node != NULL) {
- if ((node->hashCode == code) && (node->entry.key == key)) {
- return node->entry.value;
- }
- node = node->next;
- }
+ 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 {
+ if (m_numBuckets == 0) {
+ return NULL;
+ }
+
+ size_t code = HashFunc::hashCode(key);
+ size_t b = code % m_numBuckets;
+
+ Node* node = m_bucket[b];
- debugAssertM(false, "Key not found");
- // The next line is here just to make
- // a compiler warning go away.
- return node->entry.value;
+ 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;
}
/**
@@ -570,43 +724,134 @@ public:
If the key is not present, returns false.
*/
bool get(const Key& key, Value& val) const {
- size_t code = m_HashFunc(key);
- size_t b = code % numBuckets;
+ Value* v = getPointer(key);
+ if (v != NULL) {
+ val = *v;
+ return true;
+ } else {
+ return false;
+ }
+ }
- Node* node = bucket[b];
- while (node != NULL) {
- if ((node->hashCode == code) && (node->entry.key == key)) {
- // found key
- val = node->entry.value;
- return true;
- }
- node = node->next;
- }
- // Failed to find key
- return false;
+ /** Called by getCreate() and set()
+
+ \param created Set to true if the entry was created by this method.
+ */
+ Entry& getCreateEntry(const Key& key, bool& created) {
+ created = false;
+
+ if (m_numBuckets == 0) {
+ resize(10);
+ }
+
+ size_t code = HashFunc::hashCode(key);
+ size_t b = code % m_numBuckets;
+
+ // Go to the m_bucket
+ Node* n = m_bucket[b];
+
+ // No m_bucket, so this must be the first
+ if (n == NULL) {
+ m_bucket[b] = Node::create(key, code, NULL, m_memoryManager);
+ ++m_size;
+ created = true;
+ return m_bucket[b]->entry;
+ }
+
+ 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)) {
+ // This is the a pre-existing node
+ return n->entry;
+ }
+
+ 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 &&
+ (m_numBuckets < m_size * 15)) {
+
+ // This m_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 (m_numBuckets > 1000000) {
+ f = 1.5f;
+ } else if (m_numBuckets > 100000) {
+ f = 2.0f;
+ }
+ int newSize = iMax((int)(m_numBuckets * f) + 1, (int)(m_size * f));
+ resize(newSize);
+ }
+
+ // Not found; insert at the head.
+ b = code % m_numBuckets;
+ m_bucket[b] = Node::create(key, code, m_bucket[b], m_memoryManager);
+ ++m_size;
+ created = true;
+ return m_bucket[b]->entry;
}
+ Entry& getCreateEntry(const Key& key) {
+ bool ignore;
+ return getCreateEntry(key, ignore);
+ }
+
+
+ /** Returns the current value that key maps to, creating it if necessary.*/
+ Value& getCreate(const Key& key) {
+ return getCreateEntry(key).value;
+ }
+
+ /** \param created True if the element was created. */
+ Value& getCreate(const Key& key, bool& created) {
+ return getCreateEntry(key, created).value;
+ }
+
+
/**
Returns true if key is in the table.
*/
bool containsKey(const Key& key) const {
- size_t code = m_HashFunc(key);
- size_t b = code % numBuckets;
+ if (m_numBuckets == 0) {
+ return false;
+ }
- Node* node = bucket[b];
+ size_t code = HashFunc::hashCode(key);
+ size_t b = code % m_numBuckets;
+
+ Node* node = m_bucket[b];
while (node != NULL) {
- if ((node->hashCode == code) && (node->entry.key == key)) {
+ if ((node->hashCode == code) && EqualsFunc::equals(node->entry.key, key)) {
return true;
}
node = node->next;
- }
+ } while (node != NULL);
return false;
}
+
/**
Short syntax for get.
*/
@@ -617,6 +862,7 @@ public:
/**
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;
@@ -626,8 +872,8 @@ public:
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];
+ for (size_t i = 0; i < m_numBuckets; i++) {
+ Node* node = m_bucket[i];
while (node != NULL) {
keyArray.append(node->entry.key);
node = node->next;
@@ -636,24 +882,17 @@ public:
}
/**
- Calls delete on all of the keys. Does not clear the table,
- however, so you are left with a table of dangling pointers.
-
- Same as <CODE>getKeys().deleteAll();</CODE>
-
- To delete all of the values, you may want something like
- <PRE>
- Array<Key> keys = table.getKeys();
- Set<Value> value;
- for (int k = 0; k < keys.length(); k++) {
- value.insert(keys[k]);
- }
- value.getMembers().deleteAll();
- keys.deleteAll();
- </PRE>
+ Calls delete on all of the keys and then clears the table.
*/
void deleteKeys() {
- getKeys().deleteAll();
+ for (size_t i = 0; i < m_numBuckets; i++) {
+ Node* node = m_bucket[i];
+ while (node != NULL) {
+ delete node->entry.key;
+ node = node->next;
+ }
+ }
+ clear();
}
/**
@@ -662,13 +901,14 @@ public:
at most once.
Does not clear the table, so you are left with a table
- of dangling pointers.
+ of NULL pointers.
*/
void deleteValues() {
- for (int i = 0; i < numBuckets; i++) {
- Node* node = bucket[i];
+ for (size_t i = 0; i < m_numBuckets; ++i) {
+ Node* node = m_bucket[i];
while (node != NULL) {
delete node->entry.value;
+ node->entry.value = NULL;
node = node->next;
}
}
@@ -677,9 +917,8 @@ public:
} // namespace
-#ifdef G3D_WIN32
+#ifdef _MSC_VER
# pragma warning (pop)
#endif
#endif
-
diff --git a/dep/include/g3dlite/G3D/TextInput.h b/dep/include/g3dlite/G3D/TextInput.h
new file mode 100644
index 00000000000..33eb8c48e53
--- /dev/null
+++ b/dep/include/g3dlite/G3D/TextInput.h
@@ -0,0 +1,801 @@
+/**
+ @file TextInput.h
+
+ Simple text lexer/tokenizer.
+
+ @maintainer Morgan McGuire, http://graphics.cs.williams.edu
+
+ @cite Based on a lexer written by Aaron Orenstein.
+
+ @created 2002-11-27
+ @edited 2009-11-24
+
+ Copyright 2000-2009, 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 "G3D/ParseError.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,
+ LINE_COMMENT_TYPE,
+ BLOCK_COMMENT_TYPE,
+ NEWLINE_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,
+ COMMENT = LINE_COMMENT_TYPE,
+ NEWLINE = NEWLINE_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
+ <li><CODE>Token::LINE_COMMENT_TYPE</CODE> (disabled by default); generated for line comments as specified by TextInput::Settings
+ <li><CODE>Token::BLOCK_COMMENT_TYPE</CODE> (disabled by default); generated for c-style block comments as specified by TextInput::Settings
+ <li><CODE>Token::NEWLINE_TYPE</CODE> (disabled by default); generated for any of "\\r", "\\n" or "\\r\\n"
+ </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, C-style slash-star marks a multi-line comment.
+
+ See generateCommentTokens for rules on how this is applied.
+
+ Default is true.
+ */
+ bool cppBlockComments;
+
+ /** If true, // begins a single line comment.
+
+ See generateCommentTokens for rules on how this is applied.
+
+ Default is true.
+ */
+ bool cppLineComments;
+
+ /** If true, otherCommentCharacter and otherCommentCharacter2
+ are used to begin single line comments in the same way
+ cppLineComments is.
+
+ See generateCommentTokens for rules on how this is applied.
+
+ Default is true.
+ */
+ bool otherLineComments;
+
+ /** 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 not '\\0', specifies a character that begins single line
+ comments ('#' and '%' are popular choices). This is independent
+ of the cppLineComments 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, comments enabled by cppBlockComments, cppLineComments
+ and otherLineComments will generate their respective tokens.
+ If false, the same settings will enable parsing and ignoring
+ comments
+
+ Default is false.
+ */
+ bool generateCommentTokens;
+
+ /** If true, newlines will generate tokens.
+ If false, newlines will be discarded as whitespace when parsed
+ outside of other tokens.
+
+ Default is false.
+ */
+ bool generateNewlineTokens;
+
+ /** 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;
+
+ /** The character to use as a single quote. Defaults to "'" (backquote),
+ occasionally useful to set to "`" (forward quote) or to "," (comma) for
+ reading CSV files. */
+ char singleQuoteCharacter;
+
+ /** 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.
+ */
+ 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!
+ */
+ 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!
+ */
+ 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(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 ParseError {
+ public:
+ /** Name of file being parsed when exception occurred.
+ \deprecated Use filename
+ */
+ std::string sourceFile;
+
+ 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();
+
+ /** Calls read() until the result is not a newline or comment */
+ Token readSignificant();
+
+ /** 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 comment token or throws WrongTokenType, and returns the token.
+
+ Use this method (rather than readComment) 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 comment. When an exception is thrown, no tokens are
+ consumed.
+ */
+ Token readCommentToken();
+
+ /** Like readCommentToken, but returns the token's string.
+
+ Use this method (rather than readCommentToken) if you want the token's
+ value but don't really care about its location in the input. Use of
+ readCommentToken is encouraged for better error reporting.
+ */
+ std::string readComment();
+
+ /** Reads a specific comment token or throws either WrongTokenType or
+ WrongString. If the next token in the input is a comment matching @p
+ s, it will be consumed.
+
+ Use this method if you want to match a specific comment 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 comment. WrongString will be thrown if the next token in the
+ input stream is a comment but does not match the @p s parameter. When
+ an exception is thrown, no tokens are consumed.
+ */
+ void readComment(const std::string& s);
+
+ /** Reads a newline token or throws WrongTokenType, and returns the token.
+
+ Use this method (rather than readNewline) 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 newline. When an exception is thrown, no tokens are
+ consumed.
+ */
+ Token readNewlineToken();
+
+ /** Like readNewlineToken, but returns the token's string.
+
+ Use this method (rather than readNewlineToken) if you want the token's
+ value but don't really care about its location in the input. Use of
+ readNewlineToken is encouraged for better error reporting.
+ */
+ std::string readNewline();
+
+ /** Reads a specific newline token or throws either WrongTokenType or
+ WrongString. If the next token in the input is a newline matching @p
+ s, it will be consumed.
+
+ Use this method if you want to match a specific newline 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 newline. WrongString will be thrown if the next token in the
+ input stream is a newlin but does not match the @p s parameter. When
+ an exception is thrown, no tokens are consumed.
+ */
+ void readNewline(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 settings::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/dep/include/g3dlite/G3D/TextOutput.h b/dep/include/g3dlite/G3D/TextOutput.h
new file mode 100644
index 00000000000..4c22b7d5653
--- /dev/null
+++ b/dep/include/g3dlite/G3D/TextOutput.h
@@ -0,0 +1,249 @@
+/**
+ @file TextOutput.h
+
+ @maintainer Morgan McGuire, http://graphics.cs.williams.edu
+ @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/dep/include/g3dlite/G3D/ThreadSet.h b/dep/include/g3dlite/G3D/ThreadSet.h
new file mode 100644
index 00000000000..121f1415a1d
--- /dev/null
+++ b/dep/include/g3dlite/G3D/ThreadSet.h
@@ -0,0 +1,87 @@
+#ifndef G3D_THREADSET_H
+#define G3D_THREADSET_H
+
+#include "G3D/platform.h"
+#include "G3D/Array.h"
+#include "G3D/ReferenceCount.h"
+#include "G3D/GThread.h"
+#include "G3D/GMutex.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.
+
+ @param lastThreadBehavior If USE_CURRENT_THREAD, takes the last unstarted thread and executes it manually on
+ the current thread. This helps to take full advantage of the machine when
+ running a large number of jobs and avoids the overhead of a thread start for single-thread groups.
+ Note that this forces start() to block until
+ that thread is complete.
+ */
+ void start(GThread::SpawnBehavior lastThreadBehavior = GThread::USE_NEW_THREAD) 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/dep/include/g3dlite/G3D/Triangle.h b/dep/include/g3dlite/G3D/Triangle.h
index 6852dac9492..590dbaad946 100644
--- a/dep/include/g3dlite/G3D/Triangle.h
+++ b/dep/include/g3dlite/G3D/Triangle.h
@@ -1,14 +1,14 @@
/**
@file Triangle.h
-
- @maintainer Morgan McGuire, matrix@graphics3d.com
-
+
+ @maintainer Morgan McGuire, http://graphics.cs.williams.edu
+
@created 2003-04-05
- @edited 2004-03-14
+ @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-2006, Morgan McGuire.
+ Copyright 2000-2009, Morgan McGuire.
All rights reserved.
*/
@@ -19,6 +19,8 @@
#include "G3D/g3dmath.h"
#include "G3D/Vector3.h"
#include "G3D/Plane.h"
+#include "G3D/BoundsTrait.h"
+#include "G3D/debugAssert.h"
#include <string>
namespace G3D {
@@ -38,25 +40,30 @@ private:
/** edgeDirection[i] is the normalized vector v[i+1] - v[i] */
Vector3 edgeDirection[3];
- double edgeMagnitude[3];
+ float edgeMagnitude[3];
Plane _plane;
Vector3::Axis _primaryAxis;
/** vertex[1] - vertex[0] */
- Vector3 edge01;
+ Vector3 _edge01;
+
/** vertex[2] - vertex[0] */
- Vector3 edge02;
+ 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 */
@@ -65,7 +72,17 @@ public:
return _vertex[n];
}
- double area() const;
+ /** 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;
@@ -81,6 +98,13 @@ public:
/** Returns a random point in the triangle. */
Vector3 randomPoint() const;
+ inline void getRandomSurfacePoint
+ (Vector3& P,
+ Vector3& N = Vector3::ignore()) const {
+ P = randomPoint();
+ N = normal();
+ }
+
/**
For two triangles to be equal they must have
the same vertices <I>in the same order</I>.
@@ -96,22 +120,41 @@ public:
return true;
}
- inline unsigned int hashCode() const {
+ inline size_t hashCode() const {
return
_vertex[0].hashCode() +
(_vertex[1].hashCode() >> 2) +
- _vertex[2].hashCode();
+ (_vertex[2].hashCode() >> 3);
}
void getBounds(class AABox&) const;
+ /**
+ @brief Intersect the ray at distance less than @a distance.
+
+ @param distance Set to the maximum distance (can be G3D::inf())
+ to search for an intersection. On return, this is the smaller
+ of the distance to the intersection, if one exists, and the original
+ value.
+
+ @param baryCoord If a triangle is hit before @a distance, a
+ the barycentric coordinates of the hit location on the triangle.
+ Otherwise, unmodified.
+
+ @return True if there was an intersection before the original distance.
+ */
+ bool intersect(const class Ray& ray, float& distance, float baryCoord[3]) const;
+};
+
+} // namespace G3D
+
+template <> struct HashTrait<G3D::Triangle> {
+ static size_t hashCode(const G3D::Triangle& key) { return key.hashCode(); }
};
-} // namespace
-inline unsigned int hashCode(const G3D::Triangle& t) {
- return t.hashCode();
-}
+template<> struct BoundsTrait<class G3D::Triangle> {
+ static void getBounds(const G3D::Triangle& t, G3D::AABox& out) { t.getBounds(out); }
+};
#endif
-
diff --git a/dep/include/g3dlite/G3D/UprightFrame.h b/dep/include/g3dlite/G3D/UprightFrame.h
new file mode 100644
index 00000000000..ad5157cb14b
--- /dev/null
+++ b/dep/include/g3dlite/G3D/UprightFrame.h
@@ -0,0 +1,83 @@
+/**
+ @file UprightFrame.h
+
+ @author Morgan McGuire, http://graphics.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/dep/include/g3dlite/G3D/Vector2.h b/dep/include/g3dlite/G3D/Vector2.h
index 273ab6b4d07..dba7353785e 100644
--- a/dep/include/g3dlite/G3D/Vector2.h
+++ b/dep/include/g3dlite/G3D/Vector2.h
@@ -1,29 +1,35 @@
/**
@file Vector2.h
-
+
2D vector class
-
- @maintainer Morgan McGuire, matrix@graphics3d.com
-
+
+ @maintainer Morgan McGuire, http://graphics.cs.williams.edu
+
@created 2001-06-02
- @edited 2006-01-14
- Copyright 2000-2006, Morgan McGuire.
+ @edited 2008-11-30
+
+ Copyright 2000-2009, Morgan McGuire.
All rights reserved.
*/
#ifndef G3D_VECTOR2_H
#define G3D_VECTOR2_H
+#include <string>
+
#include "G3D/platform.h"
#include "G3D/g3dmath.h"
-#include "Vector2int16.h"
-#include <string>
+#include "G3D/Table.h"
+#include "G3D/HashTrait.h"
+#include "G3D/Vector2int16.h"
+#include "G3D/Random.h"
namespace G3D {
-class Vector2;
+class Vector2;
class Vector3;
class Vector4;
+class Any;
/**
Do not subclass-- this implementation makes assumptions about the
@@ -38,29 +44,42 @@ private:
bool operator>=(const Vector2&) const;
public:
- // coordinates
- float x, y;
+ float x;
+ float y;
+
+ /** \param any Must either Vector2(#, #) or Vector2 {x = #, y = #}*/
+ Vector2(const Any& any);
+
+ /** Converts the Vector2 to an Any. */
+ operator Any() const;
- // construction
+ /** 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& rkVector);
- Vector2(const class Vector2int16& v);
+ Vector2(const Vector2& other);
+ Vector2(const 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;
+ float& operator[](int i);
+ const float& operator[](int i) const;
// assignment and comparison
- Vector2& operator= (const Vector2& rkVector);
- bool operator== (const Vector2& rkVector) const;
- bool operator!= (const Vector2& rkVector) const;
- unsigned int hashCode() const;
+ 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;
@@ -71,14 +90,21 @@ public:
bool isUnit() const;
// arithmetic operations
- Vector2 operator+ (const Vector2& rkVector) const;
- Vector2 operator- (const Vector2& rkVector) const;
- Vector2 operator* (float fScalar) const;
- Vector2 operator* (const Vector2& rkVector) const;
- Vector2 operator/ (const Vector2& rkVector) const;
- Vector2 operator/ (float fScalar) const;
- Vector2 operator- () const;
+ 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;
}
@@ -87,7 +113,7 @@ public:
Linear interpolation
*/
inline Vector2 lerp(const Vector2& v, float alpha) const {
- return (*this) + (v - *this) * alpha;
+ return (*this) + (v - *this) * alpha;
}
inline Vector2 clamp(const Vector2& low, const Vector2& high) const {
@@ -103,16 +129,21 @@ public:
}
// arithmetic updates
- Vector2& operator+= (const Vector2& rkVector);
- Vector2& operator-= (const Vector2& rkVector);
- Vector2& operator*= (float fScalar);
- Vector2& operator/= (float fScalar);
- Vector2& operator*= (const Vector2& rkVector);
- Vector2& operator/= (const Vector2& rkVector);
+ 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.
@@ -121,37 +152,34 @@ public:
return direction();
}
- float squaredLength () const;
- float dot (const Vector2& rkVector) const;
- float unitize (float fTolerance = 1e-06);
+ float squaredLength() const;
+ float dot(const Vector2& s) const;
- Vector2 min(const Vector2 &v) const;
- Vector2 max(const Vector2 &v) 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);
- // Random unit vector
- static Vector2 random();
+ Vector2 min(const Vector2& v) const;
+ Vector2 max(const Vector2& v) const;
+
+ /** Uniformly distributed random vector on the unit sphere */
+ static Vector2 random(Random& r = Random::common());
// 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& one();
static const Vector2& unitX();
static const Vector2& unitY();
- static const Vector2& inf();
+ static const Vector2& inf();
static const Vector2& nan();
/** smallest (most negative) representable vector */
- static const Vector2& minFinite();
+ static const Vector2& minFinite();
/** Largest representable vector */
static const Vector2& maxFinite();
- // Deprecated. See Matrix3::identity() for details.
- /** @deprecated Use Vector2::zero() */
- static const Vector2 ZERO;
- /** @deprecated Use Vector2::unitX() */
- static const Vector2 UNIT_S;
- /** @deprecated Use Vector2::unitY() */
- static const Vector2 UNIT_T;
-
std::string toString() const;
// 2-char swizzles
@@ -205,49 +233,47 @@ inline Vector2 operator*(int s, const Vector2& v) {
return v * (float)s;
}
-inline unsigned int hashCode(const G3D::Vector2& v) {
- return v.hashCode();
-}
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;
@@ -255,42 +281,55 @@ inline Vector2& Vector2::operator= (const Vector2& rkVector) {
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;
@@ -298,34 +337,44 @@ inline Vector2& Vector2::operator*= (float fScalar) {
}
+
+
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;
@@ -336,39 +385,63 @@ inline Vector2 Vector2::direction () const {
}
}
+
+
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) {
@@ -378,7 +451,4 @@ inline G3D::Vector2 operator*(int s, const G3D::Vector2& v) {
return v * (float)s;
}
-inline unsigned int hashCode(const G3D::Vector2& v);
-
#endif
-
diff --git a/dep/include/g3dlite/G3D/Vector2int16.h b/dep/include/g3dlite/G3D/Vector2int16.h
index c4211b0aaab..ba72266d75a 100644
--- a/dep/include/g3dlite/G3D/Vector2int16.h
+++ b/dep/include/g3dlite/G3D/Vector2int16.h
@@ -1,6 +1,6 @@
/**
@file Vector2int16.h
-
+
@maintainer Morgan McGuire, matrix@brown.edu
@created 2003-08-09
@@ -15,17 +15,15 @@
#include "G3D/platform.h"
#include "G3D/g3dmath.h"
+#include "G3D/HashTrait.h"
namespace G3D {
/**
+ \class Vector2int16
A Vector2 that packs its fields into uint16s.
*/
-#ifdef G3D_WIN32
- // Switch to tight alignment
- #pragma pack(push, 2)
-#endif
-
+G3D_BEGIN_PACKED_CLASS(2)
class Vector2int16 {
private:
// Hidden operators
@@ -41,6 +39,7 @@ public:
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);
@@ -52,6 +51,52 @@ public:
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];
}
@@ -60,16 +105,23 @@ public:
return ((int32*)this)[0] != ((int32*)&rkVector)[0];
}
-}
-#if defined(G3D_LINUX) || defined(G3D_OSX)
- __attribute((aligned(1)))
-#endif
-;
+ Vector2int16 max(const Vector2int16& v) const {
+ return Vector2int16(iMax(x, v.x), iMax(y, v.y));
+ }
-#ifdef G3D_WIN32
- #pragma pack(pop)
-#endif
+ 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);
}
-#endif
+G3D_END_PACKED_CLASS(2)
+}
+
+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/dep/include/g3dlite/G3D/Vector3.h b/dep/include/g3dlite/G3D/Vector3.h
index 140eb284e12..4825efb9985 100644
--- a/dep/include/g3dlite/G3D/Vector3.h
+++ b/dep/include/g3dlite/G3D/Vector3.h
@@ -1,21 +1,26 @@
/**
@file Vector3.h
-
+
3D vector class
-
- @maintainer Morgan McGuire, matrix@graphics3d.com
+
+ @maintainer Morgan McGuire, http://graphics.cs.williams.edu
@created 2001-06-02
- @edited 2005-08-23
- Copyright 2000-2006, Morgan McGuire.
+ @edited 2009-11-01
+ Copyright 2000-2009, Morgan McGuire.
All rights reserved.
*/
-#ifndef G3D_VECTOR3_H
-#define G3D_VECTOR3_H
+#ifndef G3D_Vector3_h
+#define G3D_Vector3_h
#include "G3D/platform.h"
#include "G3D/g3dmath.h"
+#include "G3D/Random.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>
@@ -23,13 +28,15 @@
namespace G3D {
class Vector2;
-class Vector3;
class Vector4;
+class Vector4int8;
+class Vector3int32;
+class Any;
/**
<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
+ allow selection of arbitrary sub-fields. These cannot be used as write
masks. Examples
<PRE>
@@ -41,30 +48,19 @@ 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
+public:
- r ^ -,
- \ | /
- \|/
- </PRE>
+ // coordinates
+ float x, y, z;
- See also Vector3::reflectionDirection
- */
- Vector3 reflectAbout(const Vector3& normal) const;
+private:
// Hidden operators
bool operator<(const Vector3&) const;
@@ -73,34 +69,43 @@ private:
bool operator>=(const Vector3&) const;
public:
- // construction
+ /** Initializes to zero */
Vector3();
+
+ /** \param any Must either Vector3(#, #, #) or Vector3 {x = #, y = #, z = #}*/
+ Vector3(const Any& any);
+
+ /** Converts the Vector3 to an Any. */
+ operator Any() const;
+
+ /** Divides by 127 */
+ Vector3(const Vector4int8&);
+ Vector3(const class Vector3int32& v);
+ explicit Vector3(class BinaryInput& b);
Vector3(float _x, float _y, float _z);
- Vector3(const class Vector2& v, float _z);
- Vector3(float coordinate[3]);
- Vector3(double coordinate[3]);
- Vector3(const Vector3& rkVector);
+ explicit Vector3(const class Vector2& v, float _z);
+ explicit Vector3(float coordinate[3]);
+ explicit Vector3(double coordinate[3]);
Vector3(const class Vector3int16& v);
+ explicit Vector3(class TextInput& t);
+ explicit Vector3(const class Color3& c);
- // coordinates
- float x, y, z;
+ /** 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);
// 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;
+ const float& __fastcall 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};
/**
@@ -110,10 +115,10 @@ public:
Axis primaryAxis() const;
// assignment and comparison
- Vector3& operator= (const Vector3& rkVector);
+ Vector3& __fastcall operator= (const Vector3& rkVector);
bool operator== (const Vector3& rkVector) const;
bool operator!= (const Vector3& rkVector) const;
- unsigned int hashCode() const;
+ size_t hashCode() const;
bool fuzzyEq(const Vector3& other) const;
bool fuzzyNe(const Vector3& other) const;
@@ -125,29 +130,33 @@ public:
/** 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;
+ Vector3 __fastcall operator+ (const Vector3& v) const;
+ Vector3 __fastcall operator- (const Vector3& v) const;
+ Vector3 __fastcall operator* (float s) const;
+ inline Vector3 __fastcall operator/ (float s) const {
+ return *this * (1.0f / s);
+ }
+ Vector3 __fastcall operator* (const Vector3& v) const;
+ Vector3 __fastcall operator/ (const Vector3& v) const;
+ Vector3 __fastcall 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);
+ Vector3& __fastcall operator+= (const Vector3& v);
+ Vector3& __fastcall operator-= (const Vector3& v);
+ Vector3& __fastcall operator*= (float s);
+ inline Vector3& __fastcall operator/= (float s) {
+ return (*this *= (1.0f / s));
+ }
+ Vector3& __fastcall operator*= (const Vector3& v);
+ Vector3& __fastcall operator/= (const Vector3& v);
- /** @deprecated Use magnitude */
- float G3D_DEPRECATED length() const;
+ /** Same as magnitude */
+ float length() const;
float magnitude() const;
-
+
/**
The result is a nan vector if the length is almost zero.
*/
@@ -160,17 +169,39 @@ public:
Vector3 fastDirection() const;
/**
+ Reflect this vector about the (not necessarily unit) normal.
+ Assumes that both the before and after vectors point away from
+ the base of the 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;
+
+ /**
See also G3D::Ray::reflect.
- The length is 1.
+ 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
@@ -192,7 +223,7 @@ public:
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
+ the result has length 1 and is
pointed <I>away</I> from the intersection.
Returns Vector3::zero() in the case of total internal refraction.
@@ -206,7 +237,7 @@ public:
See also G3D::Ray::refract.
<PRE>
N V
-
+
^ /
| /
|'-
@@ -224,30 +255,26 @@ public:
return direction();
}
- /** Returns a normalized vector. May be computed with lower precision than unit */
+ /** Returns a normalized vector. May be computed with lower
+ precision than unit */
inline Vector3 fastUnit() const {
return fastDirection();
}
- /** @deprecated Use squaredMagnitude */
- float G3D_DEPRECATED squaredLength() const;
+ /** Same as squaredMagnitude */
+ float squaredLength() const;
float squaredMagnitude () const;
-
- /** @deprecated Use squaredMagnitude */
- inline float G3D_DEPRECATED norm() const {
- return squaredMagnitude();
- }
-
- float dot(const Vector3& rkVector) const;
-
- float G3D_DEPRECATED unitize(float fTolerance = 1e-06);
+
+ float __fastcall 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;
+ Vector3 __fastcall cross(const Vector3& rkVector) const;
+ Vector3 unitCross(const Vector3& rkVector) const;
/**
Returns a matrix such that v.cross() * w = v.cross(w).
@@ -259,8 +286,18 @@ public:
*/
class Matrix3 cross() const;
- Vector3 min(const Vector3 &v) const;
- Vector3 max(const Vector3 &v) const;
+ Vector3 __fastcall min(const Vector3 &v) const;
+ Vector3 __fastcall 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;
@@ -282,31 +319,62 @@ public:
Linear interpolation
*/
inline Vector3 lerp(const Vector3& v, float alpha) const {
- return (*this) + (v - *this) * alpha;
+ return (*this) + (v - *this) * alpha;
}
/** Gram-Schmidt orthonormalization. */
static void orthonormalize (Vector3 akVector[3]);
- /** Random unit vector, uniformly distributed */
- static Vector3 random();
+ /** \brief Random unit vector, uniformly distributed on the sphere.
+
+ Distribution rendered by G3D::DirectionHistogram:
+ \image html vector3-random.png
+ */
+ static Vector3 random(Random& r = Random::common());
+
+ /** \brief Random unit vector, distributed according to \f$\max(\cos \theta,0)\f$.
- /** Random unit vector, distributed
- so that the probability of V is proportional
- to max(V dot Normal, 0).
+ That is, so that the probability of \f$\vec{V}\f$ is proportional
+ to \f$\max(\vec{v} \cdot \vec{n}, 0)\f$. Useful in photon mapping for
+ Lambertian scattering.
+
+ Distribution rendered by G3D::DirectionHistogram:
+ \image html vector3-coshemirandom.png
+
+ \param n Unit vector at the center of the distribution.
@cite Henrik Wann Jensen, Realistic Image Synthesis using Photon Mapping eqn 2.24
*/
- static Vector3 cosRandom(const Vector3& normal);
+ static Vector3 cosHemiRandom(const Vector3& n, Random& r = Random::common());
+
+ /** \brief Random unit vector, distributed according to \f$\max(\cos^k \theta,0)\f$.
+
+ That is, so that the probability of \f$\vec{V}\f$ is
+ proportional to \f$\max((\vec{v} \cdot \vec{n})^k, 0)\f$.
+ Useful in photon mapping for glossy scattering.
+
+ Distribution rendered by G3D::DirectionHistogram:
+ \image html vector3-cospowhemirandom.png
+
+ \param n Unit vector at the center of the distribution.
+
+ @cite Ashikhmin and Shirley, An anisotropic Phong BRDF model, Journal of Graphics Tools, 2002
+ */
+ static Vector3 cosPowHemiRandom(const Vector3& n, const float k, Random& r = Random::common());
/**
- Random vector distributed over the hemisphere about normal.
+ \brief Random vector distributed over the hemisphere about normal.
+
+ Distribution rendered by G3D::DirectionHistogram:
+ \image html vector3-hemirandom.png
*/
- static Vector3 hemiRandom(const Vector3& normal);
+ static Vector3 hemiRandom(const Vector3& normal, Random& r = Random::common());
- // 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.
+ /** 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.
+ @deprecated Use getTangents
+ */
static void generateOrthonormalBasis (Vector3& rkU, Vector3& rkV,
Vector3& rkW, bool bUnitLengthW = true);
@@ -319,33 +387,37 @@ public:
}
// 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; }
+ static const Vector3& zero();
+ static const Vector3& one();
+ static const Vector3& unitX();
+ static const Vector3& unitY();
+ static const Vector3& unitZ();
+ static const Vector3& inf();
+ static const Vector3& nan();
+
/** Smallest (most negative) representable vector */
- inline static const Vector3& minFinite(){ static Vector3 v(-FLT_MAX, -FLT_MAX, -FLT_MAX); return v; }
+ static const Vector3& minFinite();
+
/** Largest representable vector */
- inline static const Vector3& maxFinite(){ static Vector3 v(FLT_MAX, FLT_MAX, FLT_MAX); return v; }
-
- // Deprecated. See Matrix3::identity() for details.
- /** @deprecated Use Vector3::zero() */
- static const Vector3 ZERO;
- /** @deprecated Use Vector3::zero() */
- static const Vector3 ZERO3;
- /** @deprecated Use Vector3::unitX() */
- static const Vector3 UNIT_X;
- /** @deprecated Use Vector3::unitY() */
- static const Vector3 UNIT_Y;
- /** @deprecated Use Vector3::unitZ() */
- static const Vector3 UNIT_Z;
- /** @deprecated Use Vector3::inf() */
- static const Vector3 INF3;
- /** @deprecated Use Vector3::nan() */
- static const Vector3 NAN3;
+ static const Vector3& maxFinite();
+
+
+ /** Creates two orthonormal tangent vectors X and Y such that
+ if Z = this, X x Y = Z.*/
+ inline void getTangents(Vector3& X, Vector3& Y) const {
+ debugAssertM(G3D::fuzzyEq(length(), 1.0f),
+ "makeAxes requires Z to have unit length");
+
+ // Choose another vector not perpendicular
+ X = (abs(x) < 0.9f) ? Vector3::unitX() : Vector3::unitY();
+
+ // Remove the part that is parallel to Z
+ X -= *this * this->dot(X);
+ X /= X.length();
+
+ Y = this->cross(X);
+ }
+
// 2-char swizzles
@@ -473,8 +545,8 @@ public:
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;
+ /** Can be passed to ignore a vector3 parameter */
+ static Vector3& ignore();
};
inline G3D::Vector3 operator*(float s, const G3D::Vector3& v) {
@@ -491,11 +563,236 @@ inline G3D::Vector3 operator*(int s, const G3D::Vector3& v) {
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) {
}
-unsigned int hashCode(const G3D::Vector3& v);
+//----------------------------------------------------------------------------
-#include "Vector3.inl"
+inline Vector3::Vector3 (float fX, float fY, float fZ) : x(fX), y(fY), z(fZ) {
+}
-#endif
+//----------------------------------------------------------------------------
+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 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 {
+ const float lenSquared = squaredMagnitude();
+ const 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/dep/include/g3dlite/G3D/Vector3int16.h b/dep/include/g3dlite/G3D/Vector3int16.h
index e0631125960..3197ea49d1a 100644
--- a/dep/include/g3dlite/G3D/Vector3int16.h
+++ b/dep/include/g3dlite/G3D/Vector3int16.h
@@ -1,6 +1,6 @@
/**
@file Vector3int16.h
-
+
@maintainer Morgan McGuire, matrix@brown.edu
@created 2003-04-07
@@ -14,17 +14,22 @@
#include "G3D/platform.h"
#include "G3D/g3dmath.h"
+#include "G3D/HashTrait.h"
+
+#ifdef _MSC_VER
+// Turn off "conditional expression is constant" warning; MSVC generates this
+// for debug assertions in inlined methods.
+#pragma warning (disable : 4127)
+#endif
+
namespace G3D {
/**
+ \class Vector3int16
A Vector3 that packs its fields into uint16s.
*/
-#ifdef G3D_WIN32
- // Switch to tight alignment
- #pragma pack(push, 2)
-#endif
-
+G3D_BEGIN_PACKED_CLASS(2)
class Vector3int16 {
private:
// Hidden operators
@@ -41,16 +46,82 @@ public:
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);
-}
-#if defined(G3D_LINUX) || defined(G3D_OSX)
- __attribute((aligned(1)))
-#endif
-;
+ Vector3int16(class BinaryInput& bi);
-#ifdef G3D_WIN32
- #pragma pack(pop)
-#endif
+ 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(int16(x * s), int16(y * s), int16(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(std::max(x, v.x), std::max(y, v.y), std::max(z, v.z));
+ }
+
+ Vector3int16 min(const Vector3int16& v) const {
+ return Vector3int16(std::min(x, v.x), std::min(y, v.y), std::min(z, v.z));
+ }
+
+ std::string toString() const;
}
-#endif
+G3D_END_PACKED_CLASS(2)
+
+}
+
+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/dep/include/g3dlite/G3D/Vector3int32.h b/dep/include/g3dlite/G3D/Vector3int32.h
new file mode 100644
index 00000000000..2f256ea0300
--- /dev/null
+++ b/dep/include/g3dlite/G3D/Vector3int32.h
@@ -0,0 +1,128 @@
+/**
+ @file Vector3int32.h
+
+ @maintainer Morgan McGuire, matrix@brown.edu
+
+ @created 2008-07-01
+ @edited 2008-07-01
+ Copyright 2000-2009, 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 {
+
+/**
+ \ Vector3int32
+ A Vector3 that packs its fields into uint32s.
+ */
+G3D_BEGIN_PACKED_CLASS(4)
+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;
+}
+G3D_END_PACKED_CLASS(4)
+
+}
+
+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 = (1UL << 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/dep/include/g3dlite/G3D/Vector4.h b/dep/include/g3dlite/G3D/Vector4.h
index 1bf243e5ed7..5e511451f86 100644
--- a/dep/include/g3dlite/G3D/Vector4.h
+++ b/dep/include/g3dlite/G3D/Vector4.h
@@ -1,31 +1,36 @@
/**
@file Vector4.h
-
+
Homogeneous vector class.
-
- @maintainer Morgan McGuire, matrix@graphics3d.com
-
+
+ @maintainer Morgan McGuire, http://graphics.cs.williams.edu
+
@created 2002-07-09
- @edited 2005-03-28
+ @edited 2008-11-01
- Copyright 2000-2006, Morgan McGuire.
+ Copyright 2000-2009, Morgan McGuire.
All rights reserved.
*/
-#ifndef G3D_VECTOR4_H
-#define G3D_VECTOR4_H
+#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 Vector2;
class Vector3;
class Vector4;
+class Vector4int8;
+class Any;
/**
Do not subclass-- this implementation makes assumptions about the
@@ -40,6 +45,13 @@ private:
bool operator>=(const Vector4&) const;
public:
+
+ /** \param any Must either Vector4(#, #, #, #) or Vector3 {x = #, y = #, z = #, w =#}*/
+ Vector4(const Any& any);
+
+ /** Converts the Vector4 to an Any. */
+ operator Any() const;
+
// construction
Vector4();
Vector4(float fX, float fY, float fZ, float fW);
@@ -50,6 +62,13 @@ public:
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;
@@ -60,14 +79,14 @@ public:
// (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;
+ static const Vector4& zero();
+
inline void set(float _x, float _y, float _z, float _w) {
x = _x;
y = _y;
@@ -89,12 +108,12 @@ public:
w = _w;
}
- unsigned int hashCode() const;
+ 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; }
+ static const Vector4& inf();
+ static const Vector4& nan();
/** sqrt(this->dot(*this)) */
float length() const;
@@ -116,7 +135,7 @@ public:
// 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);
}
@@ -125,6 +144,8 @@ public:
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;
@@ -511,15 +532,185 @@ public:
};
+
+//----------------------------------------------------------------------------
+inline Vector4::Vector4() {
+ x = y = z = w = 0;
}
-inline G3D::Vector4 operator* (float s, const G3D::Vector4& v) {
- return v * s;
+//----------------------------------------------------------------------------
+
+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];
}
-unsigned int hashCode(const G3D::Vector4& v);
+//----------------------------------------------------------------------------
+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;
+}
-#include "Vector4.inl"
+//----------------------------------------------------------------------------
+inline float& Vector4::operator[] (int i) {
+ return ((float*)this)[i];
+}
-#endif
+//----------------------------------------------------------------------------
+inline const float& Vector4::operator[] (int i) const {
+ return ((float*)this)[i];
+}
+
+//----------------------------------------------------------------------------
+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/dep/include/g3dlite/G3D/Vector4int8.h b/dep/include/g3dlite/G3D/Vector4int8.h
new file mode 100644
index 00000000000..544b693e8b3
--- /dev/null
+++ b/dep/include/g3dlite/G3D/Vector4int8.h
@@ -0,0 +1,113 @@
+/**
+ @file Vector4int8.h
+
+ Homogeneous vector class.
+
+ @maintainer Morgan McGuire, http://graphics.cs.williams.edu
+
+ @created 2007-02-09
+ @edited 2007-02-09
+
+ Copyright 2000-2007, Morgan McGuire.
+ All rights reserved.
+ */
+
+#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/dep/include/g3dlite/G3D/WeakCache.h b/dep/include/g3dlite/G3D/WeakCache.h
new file mode 100644
index 00000000000..f9fdc4bbd5b
--- /dev/null
+++ b/dep/include/g3dlite/G3D/WeakCache.h
@@ -0,0 +1,122 @@
+/**
+ @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);
+ }
+ }
+};
+
+#if 0 // To turn off all WeakCaching
+template<class Key, class ValueRef>
+class WeakCache {
+private:
+
+ Table<Key, ValueRef> table;
+
+public:
+ /**
+ Returns NULL if the object is not in the cache
+ */
+ ValueRef operator[](const Key& k) {
+ if (table.containsKey(k)) {
+ return table[k];
+ } 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
+
+}
+#endif
+
diff --git a/dep/include/g3dlite/G3D/Welder.h b/dep/include/g3dlite/G3D/Welder.h
new file mode 100644
index 00000000000..2c2554da7b6
--- /dev/null
+++ b/dep/include/g3dlite/G3D/Welder.h
@@ -0,0 +1,82 @@
+#ifndef G3D_Welder_h
+#define G3D_Welder_h
+
+#include "G3D/platform.h"
+#include "G3D/Array.h"
+#include "G3D/Vector3.h"
+#include "G3D/Vector2.h"
+
+namespace G3D {
+
+class Any;
+
+class Welder {
+private:
+
+ Welder() {}
+
+public:
+
+ class Settings {
+ public:
+ /** Surfaces with normals that are within this angle of each
+ other are considered to be curved. Default value is toRadians(70.0f).*/
+ float normalSmoothingAngle;
+ float vertexWeldRadius;
+ float textureWeldRadius;
+ float normalWeldRadius;
+
+ inline Settings(float normalSmoothAngle = toRadians(70.0f)) :
+ normalSmoothingAngle(normalSmoothAngle),
+ vertexWeldRadius(0.0001f),
+ textureWeldRadius(0.0001f),
+ normalWeldRadius(0.01f) {}
+
+
+ Settings(const Any& any);
+ operator Any() const;
+ };
+
+/**
+ 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,
+ const Settings& settings);
+
+ /**
+ 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)
+ */
+ inline static void weld(
+ Array<Vector3>& vertices,
+ Array<Vector2>& textureCoords,
+ Array<Vector3>& normals,
+ Array<int>& indices,
+ const Settings& settings) {
+
+ Array<Array<int>*> meta;
+ meta.append(&indices);
+ weld(vertices, textureCoords, normals, meta, settings);
+ }
+};
+
+}
+
+#endif
diff --git a/dep/include/g3dlite/G3D/WrapMode.h b/dep/include/g3dlite/G3D/WrapMode.h
new file mode 100644
index 00000000000..8ef38a77c23
--- /dev/null
+++ b/dep/include/g3dlite/G3D/WrapMode.h
@@ -0,0 +1,93 @@
+/**
+ @file WrapMode.h
+
+ @maintainer Morgan McGuire, http://graphics.cs.williams.edu
+
+ @created 2007-04-17
+ @edited 2007-04-17
+
+ Copyright 2000-2010, 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);
+
+ inline const char* toString() const {
+ static const char* s[] = {"CLAMP", "TILE", "ZERO", "IGNORE", "ERROR"};
+ return s[value];
+ }
+
+ inline explicit WrapMode(const std::string& x) : value(ERROR) {
+ static const char* s[] = {"CLAMP", "TILE", "ZERO", "IGNORE", "ERROR"};
+ for (int i = 0; i < 5; ++i) {
+ if (x == s[i]) {
+ value = (Value)i;
+ }
+ }
+ }
+};
+
+} // namespace G3D
+
+G3D_DECLARE_ENUM_CLASS_HASHCODE(G3D::WrapMode);
+
+#endif
diff --git a/dep/include/g3dlite/G3D/constants.h b/dep/include/g3dlite/G3D/constants.h
new file mode 100644
index 00000000000..dd5cb3649e5
--- /dev/null
+++ b/dep/include/g3dlite/G3D/constants.h
@@ -0,0 +1,129 @@
+/**
+ @file G3D/constants.h
+
+ @maintainer Morgan McGuire, http://graphics.cs.williams.edu
+ @created 2009-05-20
+ @edited 2009-05-20
+*/
+#ifndef G3D_constants_h
+#define G3D_constants_h
+
+#include "G3D/platform.h"
+#include "G3D/enumclass.h"
+
+namespace G3D {
+
+/** These are defined to have the same value as the equivalent OpenGL
+ constant. */
+class PrimitiveType {
+public:
+ enum Value {
+ POINTS = 0x0000,
+ LINES = 0x0001,
+ LINE_STRIP = 0x0003,
+ TRIANGLES = 0x0004,
+ TRIANGLE_STRIP = 0x0005,
+ TRIANGLE_FAN = 0x0006,
+ QUADS = 0x0007,
+ QUAD_STRIP = 0x0008
+ };
+
+private:
+
+ Value value;
+
+public:
+
+ G3D_DECLARE_ENUM_CLASS_METHODS(PrimitiveType);
+};
+
+
+/** Values for SuperSurface::GPUGeom::refractionHint. */
+class RefractionQuality {
+public:
+ enum Value {
+ /** No refraction; a translucent object will appear as if it had the same index of refraction
+ as the surrounding medium and objects will be undistorted in the background. */
+ NONE = 0,
+
+ /** Use a static environment map (cube or paraboloid) for computing transmissivity.*/
+ STATIC_ENV = 25,
+
+ /** Use a dynamically rendered 2D environment map; distort the background. This looks good for many scenes
+ but avoids the cost of rendering a cube map for DYNAMIC_ENV. */
+ DYNAMIC_FLAT = 50,
+
+ /** Use a dynamically rendered 2D environment map that is re-captured per transparent object. This works well
+ for transparent objects that are separated by a significant camera space z distance but overlap in screen space.*/
+ DYNAMIC_FLAT_MULTILAYER = 55,
+
+ /** Render a dynamic environment map */
+ DYNAMIC_ENV = 75,
+
+ /** Use the best method available, ideally true ray tracing. */
+ BEST = 100
+ };
+
+private:
+
+ /** Used for to/from string conversion. Last is the emtpy string as a sentinel */
+ static const std::string str[7];
+ static const Value enm[6];
+ Value value;
+
+public:
+ G3D_DECLARE_ENUM_CLASS_METHODS(RefractionQuality);
+
+ RefractionQuality(const class Any&);
+ RefractionQuality& operator=(const Any&);
+ operator Any() const;
+ const std::string& toString() const;
+};
+
+
+/** Values for SuperSurface::GPUGeom::mirrorHint. */
+class MirrorQuality {
+public:
+
+ enum Value {
+ /** Reflections are black */
+ NONE = 0,
+
+ /** Use a static environment map. This is what most games use */
+ STATIC_ENV = 25,
+
+ /** Planar reflection, typically for water or glass windows. This assumes that the mirror is flat;
+ it is distinct from RefractionQuality::DYNAMIC_FLAT, which assumes the <i>background</i> is flat.*/
+ DYNAMIC_PLANAR = 50,
+
+ /** Render a dynamic environment map. */
+ DYNAMIC_ENV = 75,
+
+ /** Use the best method available, ideally true ray tracing. */
+ BEST = 100
+ };
+
+private:
+
+ /** Used for to/from string conversion. Last is the emtpy string as a sentinel */
+ static const std::string str[6];
+ static const Value enm[5];
+
+ Value value;
+
+public:
+ G3D_DECLARE_ENUM_CLASS_METHODS(MirrorQuality);
+ MirrorQuality(const class Any&);
+ MirrorQuality& operator=(const Any&);
+ operator Any() const;
+ const std::string& toString() const;
+};
+
+} // namespace G3D
+
+G3D_DECLARE_ENUM_CLASS_HASHCODE(G3D::PrimitiveType)
+G3D_DECLARE_ENUM_CLASS_HASHCODE(G3D::RefractionQuality)
+G3D_DECLARE_ENUM_CLASS_HASHCODE(G3D::MirrorQuality)
+
+#endif
+
diff --git a/dep/include/g3dlite/G3D/debug.h b/dep/include/g3dlite/G3D/debug.h
index 408dd3ea146..a7697fe9c01 100644
--- a/dep/include/g3dlite/G3D/debug.h
+++ b/dep/include/g3dlite/G3D/debug.h
@@ -1,12 +1,66 @@
+/**
+ @file debug.h
-#ifndef G3D_LITE_DEBUG_H
-#define G3D_LITE_DEBUG_H
+ @maintainer Morgan McGuire, http://graphics.cs.williams.edu
-#define debugStatement(x)
-#define debugAssert(x)
-#define debugAssertM(x, y)
-#define alwaysAssertM(x, y)
+ @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/dep/include/g3dlite/G3D/debugAssert.h b/dep/include/g3dlite/G3D/debugAssert.h
new file mode 100644
index 00000000000..432e97e679d
--- /dev/null
+++ b/dep/include/g3dlite/G3D/debugAssert.h
@@ -0,0 +1,233 @@
+/**
+ @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, http://graphics.cs.williams.edu
+
+ @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.
+#if SOMEONE_MADE_THIS_USEFUL
+ #include <X11/Xlib.h>
+ #include <X11/Xutil.h>
+ #include <X11/Xatom.h>
+#endif
+#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, 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, or halt the program.
+
+ The assertion is also posted to the clipboard under Win32.
+ */
+
+/**
+ @def alwaysAssertM(exp, msg)
+ Same as debugAssertM except that it asserts in release builds as well.
+ */
+
+namespace G3D {
+typedef bool (*AssertionHook)(
+ const char* _expression,
+ const std::string& message,
+ const char* filename,
+ int lineNumber,
+ 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
+
+
+# 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 { \
+ if (!(exp)) { \
+ G3D::_internal::_releaseInputGrab_(); \
+ if ((G3D::_internal::_debugHook != NULL) && \
+ G3D::_internal::_debugHook((const char*)(#exp), message, __FILE__, __LINE__, __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) { \
+ if (!(exp)) { \
+ G3D::_internal::_releaseInputGrab_(); \
+ if ((G3D::_internal::_failureHook != NULL) && \
+ G3D::_internal::_failureHook(#exp, message, __FILE__, __LINE__, __debugPromptShowDialog__)) { \
+ ::exit(-1); \
+ } \
+ G3D::_internal::_restoreInputGrab_(); \
+ } \
+ }
+
+#endif // if debug
+
+
+
+namespace G3D { namespace _internal {
+
+#ifdef G3D_LINUX
+#if SOMEONE_MADE_THIS_USEFUL
+ /**
+ 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
+#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 useGuiPrompt);
+
+bool _handleErrorCheck_(
+ const char* expression,
+ const std::string& message,
+ const char* filename,
+ int lineNumber,
+ 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/dep/include/g3dlite/G3D/debugPrintf.h b/dep/include/g3dlite/G3D/debugPrintf.h
new file mode 100644
index 00000000000..b42151cae9e
--- /dev/null
+++ b/dep/include/g3dlite/G3D/debugPrintf.h
@@ -0,0 +1,62 @@
+/**
+ @file debugPrintf.h
+
+ @maintainer Morgan McGuire, http://graphics.cs.williams.edu
+
+ @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/dep/include/g3dlite/G3D/enumclass.h b/dep/include/g3dlite/G3D/enumclass.h
new file mode 100644
index 00000000000..c7dfe45f14f
--- /dev/null
+++ b/dep/include/g3dlite/G3D/enumclass.h
@@ -0,0 +1,147 @@
+/**
+ @file G3D/enumclass.h
+
+ @maintainer Morgan McGuire, http://graphics.cs.williams.edu
+ @created 2007-01-27
+ @edited 2007-07-20
+*/
+#ifndef G3D_enumclass_h
+#define G3D_enumclass_h
+
+#include "G3D/HashTrait.h"
+#include "G3D/BinaryInput.h"
+#include "G3D/BinaryOutput.h"
+
+/**
+\def G3D_DECLARE_ENUM_CLASS_METHODS
+
+ \brief 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.
+ \sa G3D_DECLARE_ENUM_CLASS_HASHCODE
+ */
+#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;\
+ }\
+\
+ inline void serialize(BinaryOutput& b) const {\
+ b.writeInt32(value);\
+ }\
+\
+ inline void deserialize(BinaryInput& b) {\
+ value = (Value)b.readInt32();\
+ }
+
+/** \def G3D_DECLARE_ENUM_CLASS_HASHCODE
+*/
+#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/dep/include/g3dlite/G3D/fileutils.h b/dep/include/g3dlite/G3D/fileutils.h
new file mode 100644
index 00000000000..9e49777d93a
--- /dev/null
+++ b/dep/include/g3dlite/G3D/fileutils.h
@@ -0,0 +1,254 @@
+/**
+ @file fileutils.h
+
+ @maintainer Morgan McGuire, http://graphics.cs.williams.edu
+
+ @author 2002-06-06
+ @edited 2010-02-06
+
+ Copyright 2000-2010, 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()
+ \param trustCache If true and \a lookInZipfiles is true, cache directory and zipfile contents
+ so that subsequent calls to the same directory are fast.
+
+ \sa G3D::clearFileSystemCache, G3D::zipfileExists
+ */
+bool fileExists
+(const std::string& filename,
+ bool lookInZipfiles = true,
+ bool trustCache = true);
+
+
+/** Clears the cache used by fileExists */
+void clearFileSystemCache();
+
+/**
+ Returns true if the given file (or directory) exists
+ 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 and suffix.*/
+std::string generateFilenameBase(const std::string& prefix = "", const std::string& suffix = "");
+
+/**
+ 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/dep/include/g3dlite/G3D/filter.h b/dep/include/g3dlite/G3D/filter.h
new file mode 100644
index 00000000000..609477b79c9
--- /dev/null
+++ b/dep/include/g3dlite/G3D/filter.h
@@ -0,0 +1,29 @@
+/**
+ @file G3D/filter.h
+
+ @author Morgan McGuire, http://graphics.cs.williams.edu
+ @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/dep/include/g3dlite/G3D/format.h b/dep/include/g3dlite/G3D/format.h
index b2948bfcd25..3c7f0678876 100644
--- a/dep/include/g3dlite/G3D/format.h
+++ b/dep/include/g3dlite/G3D/format.h
@@ -1,8 +1,8 @@
/**
@file format.h
-
- @maintainer Morgan McGuire, matrix@graphics3d.com
-
+
+ @maintainer Morgan McGuire, http://graphics.cs.williams.edu
+
@author 2000-09-09
@edited 2005-11-03
@@ -17,19 +17,6 @@
#include <string>
#include <stdio.h>
#include <cstdarg>
-#include <assert.h>
-#ifndef G3D_WIN32
- // Don't include varargs.h for some random
- // gcc reason
- //#include <varargs.h>
- #include <stdarg.h>
-#endif
-
-#ifndef _MSC_VER
- #ifndef __cdecl
- #define __cdecl __attribute__((cdecl))
- #endif
-#endif
namespace G3D {
@@ -40,7 +27,7 @@ namespace G3D {
string is under 160 characters (not including terminator) and slower
when the string is longer.
*/
-std::string format(
+std::string __cdecl format(
const char* fmt
...) G3D_CHECK_PRINTF_ARGS;
@@ -51,7 +38,7 @@ std::string vformat(
const char* fmt,
va_list argPtr) G3D_CHECK_VPRINTF_ARGS;
-}; // namespace
-#endif
+} // namespace
+#endif
diff --git a/dep/include/g3dlite/G3D/g3dfnmatch.h b/dep/include/g3dlite/G3D/g3dfnmatch.h
new file mode 100644
index 00000000000..464b3927eee
--- /dev/null
+++ b/dep/include/g3dlite/G3D/g3dfnmatch.h
@@ -0,0 +1,83 @@
+/*-
+ * Copyright (c) 1992, 1993
+ *The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ *This product includes software developed by the University of
+ *California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ *@(#)fnmatch.h8.1 (Berkeley) 6/2/93
+ *
+ * From FreeBSD fnmatch.h 1.7
+ * $Id: g3dfnmatch.h,v 1.1 2010/02/06 06:51:28 morgan3d Exp $
+ */
+#ifndef G3D_g3dfnmatch_h
+#define G3D_g3dfnmatch_h
+
+#include "G3D/platform.h"
+
+namespace G3D {
+
+#if defined(G3D_WIN32)
+
+# if ! defined(FNM_NOMATCH)
+# define FNM_NOMATCH 1 /* Match failed. */
+# define FNM_NOESCAPE 0x01 /* Disable backslash escaping. */
+# define FNM_PATHNAME 0x02 /* Slash must be matched by slash. */
+# define FNM_PERIOD 0x04 /* Period must be matched by period. */
+# define FNM_LEADING_DIR 0x08 /* Ignore /<tail> after Imatch. */
+# define FNM_CASEFOLD 0x10 /* Case insensitive search. */
+# define FNM_PREFIX_DIRS 0x20 /* Directory prefixes of pattern match too. */
+# endif
+
+#else
+
+ // On non-windows systems, include fnmatch directly
+# include <fnmatch.h>
+#endif
+
+
+/**
+ Function fnmatch() as specified in POSIX 1003.2-1992, section B.6.
+ Compares a filename or pathname to a pattern.
+
+The fnmatch() function checks whether the string argument matches the pattern argument, which is a shell wildcard pattern.
+The flags argument modifies the behaviour; it is the bitwise OR of zero or more of the following flags:
+
+- FNM_NOESCAPE If this flag is set, treat backslash as an ordinary character, instead of an escape character.
+- FNM_PATHNAME If this flag is set, match a slash in string only with a slash in pattern and not by an asterisk (*) or a question mark (?) metacharacter, nor by a bracket expression ([]) containing a slash.
+- FNM_PERIOD If this flag is set, a leading period in string has to be matched exactly by a period in pattern. A period is considered to be leading if it is the first character in string, or if both FNM_PATHNAME is set and the period immediately follows a slash.
+- FNM_FILE_NAME This is a GNU synonym for FNM_PATHNAME.
+- FNM_LEADING_DIR If this flag (a GNU extension) is set, the pattern is considered to be matched if it matches an initial segment of string which is followed by a slash. This flag is mainly for the internal use of glibc and is only implemented in certain cases.
+- FNM_CASEFOLD If this flag (a GNU extension) is set, the pattern is matched case-insensitively.
+
+\return Zero if \a string matches \a pattern, FNM_NOMATCH if there is no match or another non-zero value if there is an error
+
+ */
+int g3dfnmatch(const char *pattern, const char *string, int flags);
+}
+#endif
diff --git a/dep/include/g3dlite/G3D/g3dmath.h b/dep/include/g3dlite/G3D/g3dmath.h
index ecf355157c1..d16214ebb37 100644
--- a/dep/include/g3dlite/G3D/g3dmath.h
+++ b/dep/include/g3dlite/G3D/g3dmath.h
@@ -1,20 +1,20 @@
/**
@file g3dmath.h
-
+
Math util class.
-
- @maintainer Morgan McGuire, matrix@graphics3d.com
+
+ @maintainer Morgan McGuire, http://graphics.cs.williams.edu
@cite highestBit by Jukka Liimatta
-
+
@created 2001-06-02
- @edited 2006-01-16
+ @edited 2009-04-07
Copyright 2000-2006, Morgan McGuire.
All rights reserved.
*/
-#ifndef G3DMATH_H
-#define G3DMATH_H
+#ifndef G3D_g3dmath_h
+#define G3D_g3dmath_h
#ifdef _MSC_VER
// Disable conditional expression is constant, which occurs incorrectly on inlined functions
@@ -26,13 +26,22 @@
#include "G3D/platform.h"
#include <ctype.h>
-#include <string>
#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
+**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.
*/
@@ -51,20 +60,23 @@
namespace G3D {
-#if defined(_MSC_VER)
+#ifdef _MSC_VER
+inline double __fastcall drand48() {
+ return ::rand() / double(RAND_MAX);
+}
#if !defined(_WIN64)
/**
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
+
+ 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
+ made about the suitability of this software for any purpose. It is
provided "as is" without express or implied warranty.
*/
@@ -104,83 +116,50 @@ __inline long int lrintf(float flt) {
#endif
-const double fuzzyEpsilon = 0.00001;
-/** Returns a reference to a static double.
+#define fuzzyEpsilon (0.00001f)
+/**
This value should not be tested against directly, instead
G3D::isNan() and G3D::isFinite() will return reliable results. */
-inline const double& inf() {
+double inf();
-// We already have <limits> included but
-// not using it in older gcc for safe compilations
-#if (__GNUC__ == 2)
- static const double i = 1.0/sin(0.0);
-#else
- // double is a standard type and should have infinity
- static const double i = std::numeric_limits<double>::infinity();
-#endif
- return i;
-}
-
-/** Returns a reference to a static double.
- This value should not be tested against directly, instead
+/** This value should not be tested against directly, instead
G3D::isNan() and G3D::isFinite() will return reliable results. */
-inline const double& nan() {
+double nan();
-// We already have <limits> included but
-// not using it in older gcc for safe compilations
-#if (__GNUC__ == 2)
- static const double n = 0.0/sin(0.0);
-#else
- // double is a standard type and should have quiet NaN
- static const double n = std::numeric_limits<double>::quiet_NaN();
-#endif
- return n;
-}
+float finf();
-/** Returns a reference to a static double. Use instead of G3D_PI. */
-inline const double& pi() {
- static const double p = 3.1415926535898;
- return p;
-}
+float fnan();
-/** Returns a reference to a static double. Use instead of G3D_HALF_PI. */
-inline const double& halfPi() {
- static const double p = 1.5707963267949;
- return p;
+inline double pi() {
+ return 3.1415926535898;
}
-/** Returns a reference to a static double. Use instead of G3D_TWO_PI. */
-inline const double& twoPi() {
- static const double p = 6.283185;
- return p;
+inline double halfPi() {
+ return 1.57079633;
}
-/** @def G3D_PI
- @deprecated Use G3D::pi() instead. */
-#define G3D_PI (3.1415926535898)
-/** @def G3D_HALF_PI
- @deprecated Use G3D::halfPi() instead. */
-#define G3D_HALF_PI (1.5707963267949)
-/** @def G3D_TWO_PI
- @deprecated Use G3D::twoPi() instead. */
-#define G3D_TWO_PI (6.283185)
+inline double twoPi() {
+ return 6.28318531;
+}
typedef signed char int8;
-typedef unsigned char uint8;
+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;
+ 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;
+ typedef long long int64;
+ typedef unsigned long long uint64;
#endif
-typedef unsigned int uint;
typedef float float32;
typedef double float64;
@@ -192,6 +171,7 @@ 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);
@@ -223,7 +203,8 @@ inline int iSign(float f) {
return iSign((double)f);
}
-/**
+
+/**
Fast round to integer using the lrint routine.
Typically 6x faster than casting to integer.
*/
@@ -231,7 +212,7 @@ inline int iRound(double fValue) {
return lrint(fValue);
}
-/**
+/**
Fast round to integer using the lrint routine.
Typically 6x faster than casting to integer.
*/
@@ -242,6 +223,7 @@ inline int iRound(float f) {
/**
Returns a random number uniformly at random between low and hi
(inclusive).
+ @deprecated Use Random::integer
*/
int iRandom(int low, int hi);
@@ -264,6 +246,11 @@ bool isFinite(double x);
comparisons against nan return false.
*/
bool isNaN(double x);
+bool isNaN(float x);
+inline bool isNaN(int x) {
+ (void)x;
+ return false;
+}
/**
Computes x % 3.
@@ -271,77 +258,58 @@ bool isNaN(double x);
int iMod3(int x);
/**
- [0, 1]
- @deprecated use uniformRandom()
- */
-double unitRandom ();
-
-/**
- Uniform random number between low and hi, inclusive.
- @deprecated use uniformRandom()
- */
-double random(double low, double hi);
-
-/**
- [-1, 1]
- @deprecated use uniformRandom()
- */
-double symmetricRandom ();
-
-/**
Uniform random number between low and hi, inclusive. [low, hi]
+ @deprecated
+ @sa Random::uniform
*/
float uniformRandom(float low = 0.0f, float hi = 1.0f);
/**
- Normally distributed random number.
+ Normally distributed random number.
+
+ @deprecated
+ @sa Random::gaussian
*/
float gaussRandom(float mean = 0.0f, float stdev = 1.0f);
-#if defined(_MSC_VER) && (_MSC_VER <= 1200)
-
- /** VC6 lacks std::min and std::max */
- inline double min(double x, double y) {
- return std::_cpp_min(x, y);
- }
- /** VC6 lacks std::min and std::max */
- inline float min(float x, float y) {
- return std::_cpp_min(x, y);
- }
+/** Returns x<sup>5</sup> */
+template <class T>
+inline T pow5(T x) {
+ const T y = x * x;
+ return y * y * x;
+}
- /** VC6 lacks std::min and std::max */
- inline int min(int x, int y) {
- return std::_cpp_min(x, y);
- }
- /** VC6 lacks std::min and std::max */
- inline double max(double x, double y) {
- return std::_cpp_max(x, y);
- }
+template <class T>
+inline T min(const T& x, const T& y) {
+ return std::min<T>(x, y);
+}
- /** VC6 lacks std::min and std::max */
- inline float max(float x, float y) {
- return std::_cpp_max(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);
+}
- /** VC6 lacks std::min and std::max */
- inline int max(int x, int y) {
- return std::_cpp_max(x, y);
- }
+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));
+}
-#else
- template <class T>
- inline T min(const T& x, const T& y) {
- return std::min<T>(x, y);
- }
+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) {
- 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);
+}
-#endif
+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);
@@ -357,7 +325,7 @@ double distance(double x, double y, double z);
the left. -1 means the number was 0.
@cite Based on code by jukka@liimatta.org
- */
+ */
int highestBit(uint32 x);
/**
@@ -367,8 +335,8 @@ int highestBit(uint32 x);
*/
bool fuzzyEq(double a, double b);
-/** True if a is definitely not equal to b.
- Guaranteed false if a == 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);
@@ -393,30 +361,30 @@ inline float rsq(float x) {
}
/**
- Uses SSE to implement rsq.
- @cite Nick nicolas@capens.net
- */
-inline float SSErsq(float x) {
-
- #if defined(SSE) && defined(G3D_WIN32) && !defined(_WIN64)
- __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
+ 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;
+}
+
+inline double log2(double x) {
+ return ::log(x) * 1.442695;
+}
+
+inline float log2(float x) {
+ return ::logf(x) * 1.442695f;
+}
+
+inline double log2(int x) {
+ return log2((double)x);
+}
+
+
/**
* True if num is a power of two.
*/
@@ -456,6 +424,7 @@ inline float dot(float a, float b) {
return a * b;
}
+
/**
a * b (for DirectX/Cg support)
*/
@@ -470,10 +439,21 @@ inline double exp2(double x) {
return pow(2.0, x);
}
+inline float exp2(float x) {
+ return powf(2.0f, x);
+}
+
+/** @deprecated Use rsq */
inline double rsqrt(double x) {
return 1.0 / sqrt(x);
}
+/** @deprecated Use rsq */
+inline float rsqrt(float x) {
+ // TODO: default this to using the SSE2 instruction
+ return 1.0 / sqrtf(x);
+}
+
/**
sin(x)/x
*/
@@ -490,6 +470,19 @@ inline double sinc(double x) {
/**
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;
@@ -500,21 +493,353 @@ inline double wrap(double t, double lo, double hi) {
double interval = hi - lo;
return t - interval * iFloor((t - lo) / interval);
-
}
inline double wrap(double t, double hi) {
- return wrap(t, 0, hi);
+ return wrap(t, 0.0, hi);
+}
+
+
+inline bool isFinite(double x) {
+ return ! isNaN(x) && (x < G3D::inf()) && (x > -G3D::inf());
+}
+
+inline bool isFinite(float x) {
+ return ! isNaN(x) && (x < G3D::finf()) && (x > -G3D::finf());
+}
+
+//----------------------------------------------------------------------------
+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 float square(float x) {
+ return x * x;
+}
+
+inline int square(int x) {
+ return x * x;
+}
+
+//----------------------------------------------------------------------------
+inline double sumSquares(double x, double y) {
+ return x*x + y*y;
+}
+
+//----------------------------------------------------------------------------
+inline float sumSquares(float x, float y) {
+ return x*x + y*y;
+}
+
+//----------------------------------------------------------------------------
+inline double sumSquares(double x, double y, double z) {
+ return x*x + y*y + z*z;
+}
+
+//----------------------------------------------------------------------------
+inline float sumSquares(float x, float y, float z) {
+ return x*x + y*y + z*z;
+}
+
+//----------------------------------------------------------------------------
+inline double distance(double x, double y) {
+ return sqrt(sumSquares(x, y));
+}
+
+//----------------------------------------------------------------------------
+inline float distance(float x, float y) {
+ return sqrt(sumSquares(x, y));
+}
+
+//----------------------------------------------------------------------------
+inline double distance(double x, double y, double z) {
+ return sqrt(sumSquares(x, y, z));
+}
+
+//----------------------------------------------------------------------------
+inline float distance(float x, float y, float 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.0;
+ 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
-#include "g3dmath.inl"
-
#endif
-
diff --git a/dep/include/g3dlite/G3D/platform.h b/dep/include/g3dlite/G3D/platform.h
index 136413d1d9d..11ba0127a16 100644
--- a/dep/include/g3dlite/G3D/platform.h
+++ b/dep/include/g3dlite/G3D/platform.h
@@ -1,22 +1,27 @@
/**
@file platform.h
- #defines for platform specific issues.
+ \#defines for platform specific issues.
- @maintainer Morgan McGuire, matrix@graphics3d.com
+ @maintainer Morgan McGuire, http://graphics.cs.williams.edu
@created 2003-06-09
- @edited 2006-01-16
+ @edited 2010-01-11
*/
-#ifndef G3D_PLATFORM_H
-#define G3D_PLATFORM_H
+#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 61000
+#define G3D_VER 80004
+
+// fatal error for unsupported architectures
+#if defined(__powerpc__)
+# error PowerPC is not supported by G3D!
+#endif
#if defined(G3D_RELEASEDEBUG)
# define G3D_DEBUGRELEASE
@@ -26,51 +31,50 @@
# undef _DEBUG
#endif
+/** @def G3D_DEBUG()
+ Defined if G3D is built in debug mode. */
#if !defined(G3D_DEBUG) && (defined(_DEBUG) || defined(G3D_DEBUGRELEASE))
# define G3D_DEBUG
#endif
+#ifndef _MSC_VER
+/// Fast call is a register-based optimized calling convention supported only by Visual C++
+#define __fastcall
+
+#endif
+
#ifdef _MSC_VER
#define G3D_WIN32
-#elif defined(__MINGW32__)
- #define G3D_WIN32
- #define G3D_MINGW32
-#elif defined(__linux__)
+#elif defined(__FreeBSD__) || defined(__OpenBSD__)
+ #define G3D_FREEBSD
#define G3D_LINUX
-#elif defined(__OpenBSD__)
- #define G3D_LINUX
-#elif defined(__FreeBSD__)
- #define G3D_LINUX
-#elif defined(__NetBSD__)
+#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
-
-#ifdef G3D_WIN32
-// Turn off warnings about deprecated C routines (TODO: revisit)
-# pragma warning (disable : 4996)
+// Detect 64-bit under various compilers
+#if (defined(_M_X64) || defined(_WIN64) || defined(__LP64__) || defined(_LP64))
+# define G3D_64BIT
+ #if defined(WIN32)
+ #include <intrin.h>
+ #endif
+#else
+# define G3D_32BIT
#endif
-// On g++, recognize cases where the -msse2 flag was not specified
-#if defined(SSE) && defined(__GNUC__) && ! defined (__SSE__)
-# undef SSE
+// Strongly encourage inlining on gcc
+#ifdef __GNUC__
+#define inline __inline__
#endif
-#if defined(__GNUC__)
-# if __STDC_VERSION__ < 199901
-# define restrict __restrict__
-# endif
-#endif
// Verify that the supported compilers are being used and that this is a known
// processor.
@@ -79,81 +83,46 @@
# ifndef __GNUC__
# error G3D only supports the gcc compiler on Linux.
# endif
-
-//# ifndef __i386__
-//# error G3D only supports x86 machines on Linux.
-//# endif
-
-# define G3D_DEPRECATED __attribute__((__deprecated__))
-
-# ifndef __cdecl
-# define __cdecl __attribute__((cdecl))
-# endif
-
-# ifndef __stdcall
-# define __stdcall __attribute__((stdcall))
-# endif
-
-# 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
#ifdef G3D_OSX
- #ifndef __GNUC__
- #error G3D only supports the gcc compiler on OS X.
- #endif
+# 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
+# if defined(__i386__)
+# define G3D_OSX_INTEL
+# elif defined(__PPC__)
+# define G3D_OSX_PPC
+# else
+# define G3D_OSX_UNKNOWN
+# endif
-# ifndef __cdecl
-# define __cdecl __attribute__((cdecl))
-# endif
+#endif
-# ifndef __stdcall
-# define __stdcall __attribute__((stdcall))
-# endif
-# define G3D_DEPRECATED __attribute__((__deprecated__))
+#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
-# 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
+// Turn off warnings about deprecated C routines
+# pragma warning (disable : 4996)
-#ifdef G3D_WIN32
-// Microsoft Visual C++ 7.1 _MSC_VER = 1310
-// Microsoft Visual C++ 7.0 _MSC_VER = 1300
-// Microsoft Visual C++ 6.0 _MSC_VER = 1200
-// Microsoft Visual C++ 5.0 _MSC_VER = 1100
-
- // Old versions of MSVC (6.0 and previous) don't
- // support C99 for loop scoping rules. This fixes them.
-# if (_MSC_VER <= 1200)
- // This trick will generate a warning; disable the warning
-# pragma warning (disable : 4127)
-# define for if (false) {} else for
-# endif
+// Turn off "conditional expression is constant" warning; MSVC generates this
+// for debug assertions in inlined methods.
+# pragma warning (disable : 4127)
-# if (_MSC_VER <= 1200)
-// Nothing we can do on VC6 for deprecated functions
-# define G3D_DEPRECATED
-# else
-# define G3D_DEPRECATED __declspec(deprecated)
-# endif
+/** @def G3D_DEPRECATED()
+ Creates deprecated warning. */
+# define G3D_DEPRECATED __declspec(deprecated)
// Prevent Winsock conflicts by hiding the winsock API
-#ifndef _WINSOCKAPI_
-# define _G3D_INTERNAL_HIDE_WINSOCK_
-# define _WINSOCKAPI_
+# ifndef _WINSOCKAPI_
+# define _G3D_INTERNAL_HIDE_WINSOCK_
+# define _WINSOCKAPI_
# endif
// Disable 'name too long for browse information' warning
@@ -161,21 +130,22 @@
// TODO: remove
# pragma warning (disable : 4244)
-# if defined(_MSC_VER) && (_MSC_VER <= 1200)
- // VC6 std:: has signed problems in it
-# pragma warning (disable : 4018)
-# endif
-
-# define ZLIB_WINAPI
-
-// Mingw32 defines restrict
-# ifndef G3D_MINGW32
-# define restrict
-# endif
+# define restrict
+/** @def G3D_CHECK_PRINTF_METHOD_ARGS()
+ Enables printf parameter validation on gcc. */
# define G3D_CHECK_PRINTF_ARGS
+
+/** @def G3D_CHECK_PRINTF_METHOD_ARGS()
+ Enables printf parameter validation on gcc. */
# define G3D_CHECK_VPRINTF_ARGS
+
+/** @def G3D_CHECK_PRINTF_METHOD_ARGS()
+ Enables printf parameter validation on gcc. */
# define G3D_CHECK_PRINTF_METHOD_ARGS
+
+/** @def G3D_CHECK_PRINTF_METHOD_ARGS()
+ Enables printf parameter validation on gcc. */
# define G3D_CHECK_VPRINTF_METHOD_ARGS
// On MSVC, we need to link against the multithreaded DLL version of
@@ -188,78 +158,174 @@
// http://msdn.microsoft.com/library/default.asp?url=/library/en-us/vccore98/HTML/_core_Compiler_Reference.asp
//
-#if 0 //ignore that for Trinity
// DLL runtime
#ifndef _DLL
- #define _DLL
+ #define _DLL
#endif
// Multithreaded runtime
#ifndef _MT
- #define _MT 1
+ #define _MT 1
#endif
+
// Ensure that we aren't forced into the static lib
#ifdef _STATIC_CPPLIB
- #undef _STATIC_CPPLIB
+ #undef _STATIC_CPPLIB
#endif
-#endif
#ifdef _DEBUG
- #pragma comment (linker, "/NODEFAULTLIB:libc.lib")
- #pragma comment (linker, "/NODEFAULTLIB:libcmt.lib")
- #pragma comment (linker, "/NODEFAULTLIB:msvcrt.lib")
- #pragma comment (linker, "/NODEFAULTLIB:libcd.lib")
- #pragma comment (linker, "/NODEFAULTLIB:msvcrtd.lib")
+ #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, "/NODEFAULTLIB:msvcrt.lib")
- #pragma comment(linker, "/NODEFAULTLIB:libcd.lib")
- #pragma comment(linker, "/NODEFAULTLIB:libcmtd.lib")
- #pragma comment(linker, "/NODEFAULTLIB:msvcrtd.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 and SDL were linked against the release MSVCRT; force
+# ifdef _DEBUG
+ // zlib was linked against the release MSVCRT; force
// the debug version.
- #pragma comment(linker, "/NODEFAULTLIB:MSVCRT.LIB")
-# endif
+# pragma comment(linker, "/NODEFAULTLIB:MSVCRT.LIB")
+# endif
+
-# ifndef WIN32_LEAN_AND_MEAN
+# ifndef WIN32_LEAN_AND_MEAN
# define WIN32_LEAN_AND_MEAN 1
-# endif
+# 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
+# ifdef _G3D_INTERNAL_HIDE_WINSOCK_
+# undef _G3D_INTERNAL_HIDE_WINSOCK_
+# undef _WINSOCKAPI_
+# endif
-#endif
-# if defined(_MSC_VER) && (_MSC_VER <= 1200)
- // VC6 std:: has signed/unsigned problems
-# pragma warning (disable : 4018)
+/** @def G3D_START_AT_MAIN()
+ Defines necessary wrapper around WinMain on Windows to allow transfer of execution to main(). */
+# 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
+
+/** @def G3D_START_AT_MAIN()
+ Defines necessary wrapper around WinMain on Windows to allow transfer of execution to main(). */
+# define G3D_START_AT_MAIN()
+
+#endif // win32
+
+#ifdef __GNUC__
+
+# include <stdint.h>
+
+# if __STDC_VERSION__ < 199901
+# define restrict __restrict__
# endif
+/** @def G3D_DEPRECATED()
+ Creates deprecated warning. */
+# 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__)
+
+# ifndef __cdecl
+# define __cdecl
+# endif
+
+# ifndef __stdcall
+# define __stdcall
+# endif
+# endif // calling conventions
+
+/** @def G3D_CHECK_PRINTF_METHOD_ARGS()
+ Enables printf parameter validation on gcc. */
+# define G3D_CHECK_PRINTF_METHOD_ARGS __attribute__((__format__(__printf__, 2, 3)))
+
+/** @def G3D_CHECK_PRINTF_METHOD_ARGS()
+ Enables printf parameter validation on gcc. */
+# define G3D_CHECK_VPRINTF_METHOD_ARGS __attribute__((__format__(__printf__, 2, 0)))
+
+/** @def G3D_CHECK_PRINTF_METHOD_ARGS()
+ Enables printf parameter validation on gcc. */
+# define G3D_CHECK_PRINTF_ARGS __attribute__((__format__(__printf__, 1, 2)))
+
+/** @def G3D_CHECK_PRINTF_METHOD_ARGS()
+ Enables printf parameter validation on gcc. */
+# 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>
+ <CODE>STR(this becomes a string)\verbatim<PRE>\endverbatim evaluates the same as \verbatim<CODE>\endverbatim"this becomes a string"</CODE>
*/
#define STR(x) #x
-#undef G3D_DEPRECATED
-#define G3D_DEPRECATED
+/** @def PRAGMA(expression)
+ \#pragma may not appear inside a macro, so this uses the pragma operator
+ to create an equivalent statement.*/
+#ifdef _MSC_VER
+// Microsoft's version http://msdn.microsoft.com/en-us/library/d9x1s805.aspx
+# define PRAGMA(x) __pragma(x)
+#else
+// C99 standard http://www.delorie.com/gnu/docs/gcc/cpp_45.html
+# define PRAGMA(x) _Pragma(#x)
+#endif
-// Header guard
+/** @def G3D_BEGIN_PACKED_CLASS(byteAlign)
+ Switch to tight alignment
+ See G3D::Color3uint8 for an example.*/
+#ifdef _MSC_VER
+# define G3D_BEGIN_PACKED_CLASS(byteAlign) PRAGMA( pack(push, byteAlign) )
+#else
+# define G3D_BEGIN_PACKED_CLASS(byteAlign)
#endif
+/** @def G3D_END_PACKED_CLASS(byteAlign)
+ End switch to tight alignment
+ See G3D::Color3uint8 for an example.*/
+#ifdef _MSC_VER
+# define G3D_END_PACKED_CLASS(byteAlign) ; PRAGMA( pack(pop) )
+#elif defined(__GNUC__)
+# define G3D_END_PACKED_CLASS(byteAlign) __attribute((aligned(byteAlign))) ;
+#else
+# define G3D_END_PACKED_CLASS(byteAlign) ;
+#endif
+
+
+// Header guard
+#endif
diff --git a/dep/include/g3dlite/G3D/prompt.h b/dep/include/g3dlite/G3D/prompt.h
new file mode 100644
index 00000000000..c6df628099e
--- /dev/null
+++ b/dep/include/g3dlite/G3D/prompt.h
@@ -0,0 +1,67 @@
+/**
+ @file prompt.h
+
+ @maintainer Morgan McGuire, http://graphics.cs.williams.edu
+ @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/dep/include/g3dlite/G3D/serialize.h b/dep/include/g3dlite/G3D/serialize.h
new file mode 100644
index 00000000000..2382c0ee0fd
--- /dev/null
+++ b/dep/include/g3dlite/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/dep/include/g3dlite/G3D/splinefunc.h b/dep/include/g3dlite/G3D/splinefunc.h
new file mode 100644
index 00000000000..3f3a018c292
--- /dev/null
+++ b/dep/include/g3dlite/G3D/splinefunc.h
@@ -0,0 +1,118 @@
+/**
+ @file spline.h
+
+ @maintainer Morgan McGuire, http://graphics.cs.williams.edu
+
+ @created 2004-07-25
+ @edited 2007-05-05
+ */
+
+#ifndef G3D_SPLINEFUNC_H
+#define G3D_SPLINEFUNC_H
+
+#include "G3D/platform.h"
+#include "G3D/debug.h"
+#include "G3D/Array.h"
+#include "G3D/g3dmath.h"
+
+namespace G3D {
+
+/**
+ 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/dep/include/g3dlite/G3D/stringutils.h b/dep/include/g3dlite/G3D/stringutils.h
index 59449313bf5..e15a757a7a6 100644
--- a/dep/include/g3dlite/G3D/stringutils.h
+++ b/dep/include/g3dlite/G3D/stringutils.h
@@ -1,10 +1,10 @@
/**
@file stringutils.h
-
- @maintainer Morgan McGuire, matrix@graphics3d.com
-
+
+ @maintainer Morgan McGuire, http://graphics.cs.williams.edu
+
@author 2000-09-09
- @edited 2002-11-30
+ @edited 2008-08-05
*/
#ifndef G3D_STRINGUTILS_H
@@ -12,12 +12,22 @@
#include "G3D/platform.h"
#include "G3D/Array.h"
-#include <string>
+#include <cstring>
namespace G3D {
extern const char* NEWLINE;
+/** Separates a comma-separated line, properly escaping commas within
+ double quotes (") and super quotes ("""). This matches Microsoft Excel's
+ CSV output.
+
+ \param stripQuotes If true, strips leading and trailing " and """
+
+ \sa G3D::stringSplit, G3D::TextInput, G3D::readWholeFile
+*/
+void parseCommaSeparated(const std::string s, Array<std::string>& array, bool stripQuotes = true);
+
/**
Returns true if the test string begins with the pattern string.
*/
@@ -91,36 +101,36 @@ std::string trimWhitespace(
/** These standard C functions are renamed for clarity/naming
conventions and to return bool, not int.
*/
-inline bool isWhiteSpace(const char c) {
+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 char c) {
+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 char c) {
+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 char c) {
+inline bool isLetter(const unsigned char c) {
return isalpha(c) != 0;
}
-inline bool isSlash(const char c) {
+inline bool isSlash(const unsigned char c) {
return (c == '\\') || (c == '/');
}
-inline bool isQuote(const char c) {
+inline bool isQuote(const unsigned char c) {
return (c == '\'') || (c == '\"');
}
@@ -128,4 +138,3 @@ inline bool isQuote(const char c) {
#endif
-
diff --git a/dep/include/g3dlite/G3D/uint128.h b/dep/include/g3dlite/G3D/uint128.h
new file mode 100644
index 00000000000..da1af3ec272
--- /dev/null
+++ b/dep/include/g3dlite/G3D/uint128.h
@@ -0,0 +1,51 @@
+/**
+ @file uint128.h
+
+ @maintainer Morgan McGuire, http://graphics.cs.williams.edu
+ @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/dep/include/g3dlite/G3D/units.h b/dep/include/g3dlite/G3D/units.h
new file mode 100644
index 00000000000..2e30304dc62
--- /dev/null
+++ b/dep/include/g3dlite/G3D/units.h
@@ -0,0 +1,126 @@
+/**
+ @file units.h
+
+ @maintainer Morgan McGuire, http://graphics.cs.williams.edu
+
+ @created 2009-08-21
+ @edited 2009-08-21
+ */
+#ifndef G3D_units_h
+#define G3D_units_h
+
+#include "G3D/platform.h"
+
+namespace G3D {
+/** Use <code>using namespace G3D::units;</code> to include all units
+ into your program. The units system is specifically designed not to
+ be general but to support commonly used units efficiently and
+ clearly. See http://en.wikipedia.org/wiki/SI_prefix for interesting facts
+ about SI/metric units and full definitions.*/
+namespace units {
+
+/** 1e-9 m */
+inline float nanometers() {
+ return 1e-9f;
+}
+
+/** 1e-6 m */
+inline float micrometers() {
+ return 1e-6f;
+}
+
+/** 0.001 m */
+inline float millimeters() {
+ return 0.001f;
+}
+
+/** 0.01 m */
+inline float centimeters() {
+ return 0.01f;
+}
+
+/** SI base unit of distance measure. */
+inline float meters() {
+ return 1.0f;
+}
+
+/** 1000 m */
+inline float kilometers() {
+ return 100.0f;
+}
+
+/** 0.0254 m */
+inline float inches() {
+ return 0.0254f;
+}
+
+/** 0.3048 m */
+inline float feet() {
+ return 0.3048f;
+}
+
+/** 0.9144 m */
+inline float yards() {
+ return 0.9144f;
+}
+
+/** 1609.344 m */
+inline float miles() {
+ return 1609.344f;
+}
+
+/////////////////////////////////////////////////////////////
+
+/** SI base unit of angular measure. */
+inline float radians() {
+ return 1.0f;
+}
+
+/** pi/180 */
+inline float degrees() {
+ return 0.0174532925f;
+}
+
+//////////////////////////////////////////////////////////////
+
+/** 1e-9 s */
+inline float nanoseconds() {
+ return 1e-9f;
+}
+
+/** 1e-3 s */
+inline float milliseconds() {
+ return 1e-3f;
+}
+
+/** Base unit of time */
+inline float seconds() {
+ return 1.0;
+}
+
+/** 60 s */
+inline float minutes() {
+ return 60.0f;
+}
+
+/** 3600 s */
+inline float hours() {
+ return 3600.0f;
+}
+
+/** 86400 s */
+inline float days() {
+ return 86400.0f;
+}
+
+/** 31556926 s */
+inline float years() {
+ return 31556926.0f;
+}
+
+///////////////////////////////////////////
+
+}
+}
+
+#endif
diff --git a/dep/include/g3dlite/G3D/vectorMath.h b/dep/include/g3dlite/G3D/vectorMath.h
new file mode 100644
index 00000000000..ac6d2b32e9d
--- /dev/null
+++ b/dep/include/g3dlite/G3D/vectorMath.h
@@ -0,0 +1,235 @@
+/**
+ @file vectorMath.h
+
+ Function aliases for popular vector methods.
+
+ @maintainer Morgan McGuire, http://graphics.cs.williams.edu
+
+ @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/dep/src/bzip2/blocksort.c b/dep/src/bzip2/blocksort.c
new file mode 100644
index 00000000000..bd2dec157fa
--- /dev/null
+++ b/dep/src/bzip2/blocksort.c
@@ -0,0 +1,1094 @@
+
+/*-------------------------------------------------------------*/
+/*--- Block sorting machinery ---*/
+/*--- blocksort.c ---*/
+/*-------------------------------------------------------------*/
+
+/* ------------------------------------------------------------------
+ This file is part of bzip2/libbzip2, a program and library for
+ lossless, block-sorting data compression.
+
+ bzip2/libbzip2 version 1.0.5 of 10 December 2007
+ Copyright (C) 1996-2007 Julian Seward <jseward@bzip.org>
+
+ Please read the WARNING, DISCLAIMER and PATENTS sections in the
+ README file.
+
+ This program is released under the terms of the license contained
+ in the file LICENSE.
+ ------------------------------------------------------------------ */
+
+
+#include "bzlib_private.h"
+
+/*---------------------------------------------*/
+/*--- Fallback O(N log(N)^2) sorting ---*/
+/*--- algorithm, for repetitive blocks ---*/
+/*---------------------------------------------*/
+
+/*---------------------------------------------*/
+static
+__inline__
+void fallbackSimpleSort ( UInt32* fmap,
+ UInt32* eclass,
+ Int32 lo,
+ Int32 hi )
+{
+ Int32 i, j, tmp;
+ UInt32 ec_tmp;
+
+ if (lo == hi) return;
+
+ if (hi - lo > 3) {
+ for ( i = hi-4; i >= lo; i-- ) {
+ tmp = fmap[i];
+ ec_tmp = eclass[tmp];
+ for ( j = i+4; j <= hi && ec_tmp > eclass[fmap[j]]; j += 4 )
+ fmap[j-4] = fmap[j];
+ fmap[j-4] = tmp;
+ }
+ }
+
+ for ( i = hi-1; i >= lo; i-- ) {
+ tmp = fmap[i];
+ ec_tmp = eclass[tmp];
+ for ( j = i+1; j <= hi && ec_tmp > eclass[fmap[j]]; j++ )
+ fmap[j-1] = fmap[j];
+ fmap[j-1] = tmp;
+ }
+}
+
+
+/*---------------------------------------------*/
+#define fswap(zz1, zz2) \
+ { Int32 zztmp = zz1; zz1 = zz2; zz2 = zztmp; }
+
+#define fvswap(zzp1, zzp2, zzn) \
+{ \
+ Int32 yyp1 = (zzp1); \
+ Int32 yyp2 = (zzp2); \
+ Int32 yyn = (zzn); \
+ while (yyn > 0) { \
+ fswap(fmap[yyp1], fmap[yyp2]); \
+ yyp1++; yyp2++; yyn--; \
+ } \
+}
+
+
+#define fmin(a,b) ((a) < (b)) ? (a) : (b)
+
+#define fpush(lz,hz) { stackLo[sp] = lz; \
+ stackHi[sp] = hz; \
+ sp++; }
+
+#define fpop(lz,hz) { sp--; \
+ lz = stackLo[sp]; \
+ hz = stackHi[sp]; }
+
+#define FALLBACK_QSORT_SMALL_THRESH 10
+#define FALLBACK_QSORT_STACK_SIZE 100
+
+
+static
+void fallbackQSort3 ( UInt32* fmap,
+ UInt32* eclass,
+ Int32 loSt,
+ Int32 hiSt )
+{
+ Int32 unLo, unHi, ltLo, gtHi, n, m;
+ Int32 sp, lo, hi;
+ UInt32 med, r, r3;
+ Int32 stackLo[FALLBACK_QSORT_STACK_SIZE];
+ Int32 stackHi[FALLBACK_QSORT_STACK_SIZE];
+
+ r = 0;
+
+ sp = 0;
+ fpush ( loSt, hiSt );
+
+ while (sp > 0) {
+
+ AssertH ( sp < FALLBACK_QSORT_STACK_SIZE - 1, 1004 );
+
+ fpop ( lo, hi );
+ if (hi - lo < FALLBACK_QSORT_SMALL_THRESH) {
+ fallbackSimpleSort ( fmap, eclass, lo, hi );
+ continue;
+ }
+
+ /* Random partitioning. Median of 3 sometimes fails to
+ avoid bad cases. Median of 9 seems to help but
+ looks rather expensive. This too seems to work but
+ is cheaper. Guidance for the magic constants
+ 7621 and 32768 is taken from Sedgewick's algorithms
+ book, chapter 35.
+ */
+ r = ((r * 7621) + 1) % 32768;
+ r3 = r % 3;
+ if (r3 == 0) med = eclass[fmap[lo]]; else
+ if (r3 == 1) med = eclass[fmap[(lo+hi)>>1]]; else
+ med = eclass[fmap[hi]];
+
+ unLo = ltLo = lo;
+ unHi = gtHi = hi;
+
+ while (1) {
+ while (1) {
+ if (unLo > unHi) break;
+ n = (Int32)eclass[fmap[unLo]] - (Int32)med;
+ if (n == 0) {
+ fswap(fmap[unLo], fmap[ltLo]);
+ ltLo++; unLo++;
+ continue;
+ };
+ if (n > 0) break;
+ unLo++;
+ }
+ while (1) {
+ if (unLo > unHi) break;
+ n = (Int32)eclass[fmap[unHi]] - (Int32)med;
+ if (n == 0) {
+ fswap(fmap[unHi], fmap[gtHi]);
+ gtHi--; unHi--;
+ continue;
+ };
+ if (n < 0) break;
+ unHi--;
+ }
+ if (unLo > unHi) break;
+ fswap(fmap[unLo], fmap[unHi]); unLo++; unHi--;
+ }
+
+ AssertD ( unHi == unLo-1, "fallbackQSort3(2)" );
+
+ if (gtHi < ltLo) continue;
+
+ n = fmin(ltLo-lo, unLo-ltLo); fvswap(lo, unLo-n, n);
+ m = fmin(hi-gtHi, gtHi-unHi); fvswap(unLo, hi-m+1, m);
+
+ n = lo + unLo - ltLo - 1;
+ m = hi - (gtHi - unHi) + 1;
+
+ if (n - lo > hi - m) {
+ fpush ( lo, n );
+ fpush ( m, hi );
+ } else {
+ fpush ( m, hi );
+ fpush ( lo, n );
+ }
+ }
+}
+
+#undef fmin
+#undef fpush
+#undef fpop
+#undef fswap
+#undef fvswap
+#undef FALLBACK_QSORT_SMALL_THRESH
+#undef FALLBACK_QSORT_STACK_SIZE
+
+
+/*---------------------------------------------*/
+/* Pre:
+ nblock > 0
+ eclass exists for [0 .. nblock-1]
+ ((UChar*)eclass) [0 .. nblock-1] holds block
+ ptr exists for [0 .. nblock-1]
+
+ Post:
+ ((UChar*)eclass) [0 .. nblock-1] holds block
+ All other areas of eclass destroyed
+ fmap [0 .. nblock-1] holds sorted order
+ bhtab [ 0 .. 2+(nblock/32) ] destroyed
+*/
+
+#define SET_BH(zz) bhtab[(zz) >> 5] |= (1 << ((zz) & 31))
+#define CLEAR_BH(zz) bhtab[(zz) >> 5] &= ~(1 << ((zz) & 31))
+#define ISSET_BH(zz) (bhtab[(zz) >> 5] & (1 << ((zz) & 31)))
+#define WORD_BH(zz) bhtab[(zz) >> 5]
+#define UNALIGNED_BH(zz) ((zz) & 0x01f)
+
+static
+void fallbackSort ( UInt32* fmap,
+ UInt32* eclass,
+ UInt32* bhtab,
+ Int32 nblock,
+ Int32 verb )
+{
+ Int32 ftab[257];
+ Int32 ftabCopy[256];
+ Int32 H, i, j, k, l, r, cc, cc1;
+ Int32 nNotDone;
+ Int32 nBhtab;
+ UChar* eclass8 = (UChar*)eclass;
+
+ /*--
+ Initial 1-char radix sort to generate
+ initial fmap and initial BH bits.
+ --*/
+ if (verb >= 4)
+ VPrintf0 ( " bucket sorting ...\n" );
+ for (i = 0; i < 257; i++) ftab[i] = 0;
+ for (i = 0; i < nblock; i++) ftab[eclass8[i]]++;
+ for (i = 0; i < 256; i++) ftabCopy[i] = ftab[i];
+ for (i = 1; i < 257; i++) ftab[i] += ftab[i-1];
+
+ for (i = 0; i < nblock; i++) {
+ j = eclass8[i];
+ k = ftab[j] - 1;
+ ftab[j] = k;
+ fmap[k] = i;
+ }
+
+ nBhtab = 2 + (nblock / 32);
+ for (i = 0; i < nBhtab; i++) bhtab[i] = 0;
+ for (i = 0; i < 256; i++) SET_BH(ftab[i]);
+
+ /*--
+ Inductively refine the buckets. Kind-of an
+ "exponential radix sort" (!), inspired by the
+ Manber-Myers suffix array construction algorithm.
+ --*/
+
+ /*-- set sentinel bits for block-end detection --*/
+ for (i = 0; i < 32; i++) {
+ SET_BH(nblock + 2*i);
+ CLEAR_BH(nblock + 2*i + 1);
+ }
+
+ /*-- the log(N) loop --*/
+ H = 1;
+ while (1) {
+
+ if (verb >= 4)
+ VPrintf1 ( " depth %6d has ", H );
+
+ j = 0;
+ for (i = 0; i < nblock; i++) {
+ if (ISSET_BH(i)) j = i;
+ k = fmap[i] - H; if (k < 0) k += nblock;
+ eclass[k] = j;
+ }
+
+ nNotDone = 0;
+ r = -1;
+ while (1) {
+
+ /*-- find the next non-singleton bucket --*/
+ k = r + 1;
+ while (ISSET_BH(k) && UNALIGNED_BH(k)) k++;
+ if (ISSET_BH(k)) {
+ while (WORD_BH(k) == 0xffffffff) k += 32;
+ while (ISSET_BH(k)) k++;
+ }
+ l = k - 1;
+ if (l >= nblock) break;
+ while (!ISSET_BH(k) && UNALIGNED_BH(k)) k++;
+ if (!ISSET_BH(k)) {
+ while (WORD_BH(k) == 0x00000000) k += 32;
+ while (!ISSET_BH(k)) k++;
+ }
+ r = k - 1;
+ if (r >= nblock) break;
+
+ /*-- now [l, r] bracket current bucket --*/
+ if (r > l) {
+ nNotDone += (r - l + 1);
+ fallbackQSort3 ( fmap, eclass, l, r );
+
+ /*-- scan bucket and generate header bits-- */
+ cc = -1;
+ for (i = l; i <= r; i++) {
+ cc1 = eclass[fmap[i]];
+ if (cc != cc1) { SET_BH(i); cc = cc1; };
+ }
+ }
+ }
+
+ if (verb >= 4)
+ VPrintf1 ( "%6d unresolved strings\n", nNotDone );
+
+ H *= 2;
+ if (H > nblock || nNotDone == 0) break;
+ }
+
+ /*--
+ Reconstruct the original block in
+ eclass8 [0 .. nblock-1], since the
+ previous phase destroyed it.
+ --*/
+ if (verb >= 4)
+ VPrintf0 ( " reconstructing block ...\n" );
+ j = 0;
+ for (i = 0; i < nblock; i++) {
+ while (ftabCopy[j] == 0) j++;
+ ftabCopy[j]--;
+ eclass8[fmap[i]] = (UChar)j;
+ }
+ AssertH ( j < 256, 1005 );
+}
+
+#undef SET_BH
+#undef CLEAR_BH
+#undef ISSET_BH
+#undef WORD_BH
+#undef UNALIGNED_BH
+
+
+/*---------------------------------------------*/
+/*--- The main, O(N^2 log(N)) sorting ---*/
+/*--- algorithm. Faster for "normal" ---*/
+/*--- non-repetitive blocks. ---*/
+/*---------------------------------------------*/
+
+/*---------------------------------------------*/
+static
+__inline__
+Bool mainGtU ( UInt32 i1,
+ UInt32 i2,
+ UChar* block,
+ UInt16* quadrant,
+ UInt32 nblock,
+ Int32* budget )
+{
+ Int32 k;
+ UChar c1, c2;
+ UInt16 s1, s2;
+
+ AssertD ( i1 != i2, "mainGtU" );
+ /* 1 */
+ c1 = block[i1]; c2 = block[i2];
+ if (c1 != c2) return (c1 > c2);
+ i1++; i2++;
+ /* 2 */
+ c1 = block[i1]; c2 = block[i2];
+ if (c1 != c2) return (c1 > c2);
+ i1++; i2++;
+ /* 3 */
+ c1 = block[i1]; c2 = block[i2];
+ if (c1 != c2) return (c1 > c2);
+ i1++; i2++;
+ /* 4 */
+ c1 = block[i1]; c2 = block[i2];
+ if (c1 != c2) return (c1 > c2);
+ i1++; i2++;
+ /* 5 */
+ c1 = block[i1]; c2 = block[i2];
+ if (c1 != c2) return (c1 > c2);
+ i1++; i2++;
+ /* 6 */
+ c1 = block[i1]; c2 = block[i2];
+ if (c1 != c2) return (c1 > c2);
+ i1++; i2++;
+ /* 7 */
+ c1 = block[i1]; c2 = block[i2];
+ if (c1 != c2) return (c1 > c2);
+ i1++; i2++;
+ /* 8 */
+ c1 = block[i1]; c2 = block[i2];
+ if (c1 != c2) return (c1 > c2);
+ i1++; i2++;
+ /* 9 */
+ c1 = block[i1]; c2 = block[i2];
+ if (c1 != c2) return (c1 > c2);
+ i1++; i2++;
+ /* 10 */
+ c1 = block[i1]; c2 = block[i2];
+ if (c1 != c2) return (c1 > c2);
+ i1++; i2++;
+ /* 11 */
+ c1 = block[i1]; c2 = block[i2];
+ if (c1 != c2) return (c1 > c2);
+ i1++; i2++;
+ /* 12 */
+ c1 = block[i1]; c2 = block[i2];
+ if (c1 != c2) return (c1 > c2);
+ i1++; i2++;
+
+ k = nblock + 8;
+
+ do {
+ /* 1 */
+ c1 = block[i1]; c2 = block[i2];
+ if (c1 != c2) return (c1 > c2);
+ s1 = quadrant[i1]; s2 = quadrant[i2];
+ if (s1 != s2) return (s1 > s2);
+ i1++; i2++;
+ /* 2 */
+ c1 = block[i1]; c2 = block[i2];
+ if (c1 != c2) return (c1 > c2);
+ s1 = quadrant[i1]; s2 = quadrant[i2];
+ if (s1 != s2) return (s1 > s2);
+ i1++; i2++;
+ /* 3 */
+ c1 = block[i1]; c2 = block[i2];
+ if (c1 != c2) return (c1 > c2);
+ s1 = quadrant[i1]; s2 = quadrant[i2];
+ if (s1 != s2) return (s1 > s2);
+ i1++; i2++;
+ /* 4 */
+ c1 = block[i1]; c2 = block[i2];
+ if (c1 != c2) return (c1 > c2);
+ s1 = quadrant[i1]; s2 = quadrant[i2];
+ if (s1 != s2) return (s1 > s2);
+ i1++; i2++;
+ /* 5 */
+ c1 = block[i1]; c2 = block[i2];
+ if (c1 != c2) return (c1 > c2);
+ s1 = quadrant[i1]; s2 = quadrant[i2];
+ if (s1 != s2) return (s1 > s2);
+ i1++; i2++;
+ /* 6 */
+ c1 = block[i1]; c2 = block[i2];
+ if (c1 != c2) return (c1 > c2);
+ s1 = quadrant[i1]; s2 = quadrant[i2];
+ if (s1 != s2) return (s1 > s2);
+ i1++; i2++;
+ /* 7 */
+ c1 = block[i1]; c2 = block[i2];
+ if (c1 != c2) return (c1 > c2);
+ s1 = quadrant[i1]; s2 = quadrant[i2];
+ if (s1 != s2) return (s1 > s2);
+ i1++; i2++;
+ /* 8 */
+ c1 = block[i1]; c2 = block[i2];
+ if (c1 != c2) return (c1 > c2);
+ s1 = quadrant[i1]; s2 = quadrant[i2];
+ if (s1 != s2) return (s1 > s2);
+ i1++; i2++;
+
+ if (i1 >= nblock) i1 -= nblock;
+ if (i2 >= nblock) i2 -= nblock;
+
+ k -= 8;
+ (*budget)--;
+ }
+ while (k >= 0);
+
+ return False;
+}
+
+
+/*---------------------------------------------*/
+/*--
+ Knuth's increments seem to work better
+ than Incerpi-Sedgewick here. Possibly
+ because the number of elems to sort is
+ usually small, typically <= 20.
+--*/
+static
+Int32 incs[14] = { 1, 4, 13, 40, 121, 364, 1093, 3280,
+ 9841, 29524, 88573, 265720,
+ 797161, 2391484 };
+
+static
+void mainSimpleSort ( UInt32* ptr,
+ UChar* block,
+ UInt16* quadrant,
+ Int32 nblock,
+ Int32 lo,
+ Int32 hi,
+ Int32 d,
+ Int32* budget )
+{
+ Int32 i, j, h, bigN, hp;
+ UInt32 v;
+
+ bigN = hi - lo + 1;
+ if (bigN < 2) return;
+
+ hp = 0;
+ while (incs[hp] < bigN) hp++;
+ hp--;
+
+ for (; hp >= 0; hp--) {
+ h = incs[hp];
+
+ i = lo + h;
+ while (True) {
+
+ /*-- copy 1 --*/
+ if (i > hi) break;
+ v = ptr[i];
+ j = i;
+ while ( mainGtU (
+ ptr[j-h]+d, v+d, block, quadrant, nblock, budget
+ ) ) {
+ ptr[j] = ptr[j-h];
+ j = j - h;
+ if (j <= (lo + h - 1)) break;
+ }
+ ptr[j] = v;
+ i++;
+
+ /*-- copy 2 --*/
+ if (i > hi) break;
+ v = ptr[i];
+ j = i;
+ while ( mainGtU (
+ ptr[j-h]+d, v+d, block, quadrant, nblock, budget
+ ) ) {
+ ptr[j] = ptr[j-h];
+ j = j - h;
+ if (j <= (lo + h - 1)) break;
+ }
+ ptr[j] = v;
+ i++;
+
+ /*-- copy 3 --*/
+ if (i > hi) break;
+ v = ptr[i];
+ j = i;
+ while ( mainGtU (
+ ptr[j-h]+d, v+d, block, quadrant, nblock, budget
+ ) ) {
+ ptr[j] = ptr[j-h];
+ j = j - h;
+ if (j <= (lo + h - 1)) break;
+ }
+ ptr[j] = v;
+ i++;
+
+ if (*budget < 0) return;
+ }
+ }
+}
+
+
+/*---------------------------------------------*/
+/*--
+ The following is an implementation of
+ an elegant 3-way quicksort for strings,
+ described in a paper "Fast Algorithms for
+ Sorting and Searching Strings", by Robert
+ Sedgewick and Jon L. Bentley.
+--*/
+
+#define mswap(zz1, zz2) \
+ { Int32 zztmp = zz1; zz1 = zz2; zz2 = zztmp; }
+
+#define mvswap(zzp1, zzp2, zzn) \
+{ \
+ Int32 yyp1 = (zzp1); \
+ Int32 yyp2 = (zzp2); \
+ Int32 yyn = (zzn); \
+ while (yyn > 0) { \
+ mswap(ptr[yyp1], ptr[yyp2]); \
+ yyp1++; yyp2++; yyn--; \
+ } \
+}
+
+static
+__inline__
+UChar mmed3 ( UChar a, UChar b, UChar c )
+{
+ UChar t;
+ if (a > b) { t = a; a = b; b = t; };
+ if (b > c) {
+ b = c;
+ if (a > b) b = a;
+ }
+ return b;
+}
+
+#define mmin(a,b) ((a) < (b)) ? (a) : (b)
+
+#define mpush(lz,hz,dz) { stackLo[sp] = lz; \
+ stackHi[sp] = hz; \
+ stackD [sp] = dz; \
+ sp++; }
+
+#define mpop(lz,hz,dz) { sp--; \
+ lz = stackLo[sp]; \
+ hz = stackHi[sp]; \
+ dz = stackD [sp]; }
+
+
+#define mnextsize(az) (nextHi[az]-nextLo[az])
+
+#define mnextswap(az,bz) \
+ { Int32 tz; \
+ tz = nextLo[az]; nextLo[az] = nextLo[bz]; nextLo[bz] = tz; \
+ tz = nextHi[az]; nextHi[az] = nextHi[bz]; nextHi[bz] = tz; \
+ tz = nextD [az]; nextD [az] = nextD [bz]; nextD [bz] = tz; }
+
+
+#define MAIN_QSORT_SMALL_THRESH 20
+#define MAIN_QSORT_DEPTH_THRESH (BZ_N_RADIX + BZ_N_QSORT)
+#define MAIN_QSORT_STACK_SIZE 100
+
+static
+void mainQSort3 ( UInt32* ptr,
+ UChar* block,
+ UInt16* quadrant,
+ Int32 nblock,
+ Int32 loSt,
+ Int32 hiSt,
+ Int32 dSt,
+ Int32* budget )
+{
+ Int32 unLo, unHi, ltLo, gtHi, n, m, med;
+ Int32 sp, lo, hi, d;
+
+ Int32 stackLo[MAIN_QSORT_STACK_SIZE];
+ Int32 stackHi[MAIN_QSORT_STACK_SIZE];
+ Int32 stackD [MAIN_QSORT_STACK_SIZE];
+
+ Int32 nextLo[3];
+ Int32 nextHi[3];
+ Int32 nextD [3];
+
+ sp = 0;
+ mpush ( loSt, hiSt, dSt );
+
+ while (sp > 0) {
+
+ AssertH ( sp < MAIN_QSORT_STACK_SIZE - 2, 1001 );
+
+ mpop ( lo, hi, d );
+ if (hi - lo < MAIN_QSORT_SMALL_THRESH ||
+ d > MAIN_QSORT_DEPTH_THRESH) {
+ mainSimpleSort ( ptr, block, quadrant, nblock, lo, hi, d, budget );
+ if (*budget < 0) return;
+ continue;
+ }
+
+ med = (Int32)
+ mmed3 ( block[ptr[ lo ]+d],
+ block[ptr[ hi ]+d],
+ block[ptr[ (lo+hi)>>1 ]+d] );
+
+ unLo = ltLo = lo;
+ unHi = gtHi = hi;
+
+ while (True) {
+ while (True) {
+ if (unLo > unHi) break;
+ n = ((Int32)block[ptr[unLo]+d]) - med;
+ if (n == 0) {
+ mswap(ptr[unLo], ptr[ltLo]);
+ ltLo++; unLo++; continue;
+ };
+ if (n > 0) break;
+ unLo++;
+ }
+ while (True) {
+ if (unLo > unHi) break;
+ n = ((Int32)block[ptr[unHi]+d]) - med;
+ if (n == 0) {
+ mswap(ptr[unHi], ptr[gtHi]);
+ gtHi--; unHi--; continue;
+ };
+ if (n < 0) break;
+ unHi--;
+ }
+ if (unLo > unHi) break;
+ mswap(ptr[unLo], ptr[unHi]); unLo++; unHi--;
+ }
+
+ AssertD ( unHi == unLo-1, "mainQSort3(2)" );
+
+ if (gtHi < ltLo) {
+ mpush(lo, hi, d+1 );
+ continue;
+ }
+
+ n = mmin(ltLo-lo, unLo-ltLo); mvswap(lo, unLo-n, n);
+ m = mmin(hi-gtHi, gtHi-unHi); mvswap(unLo, hi-m+1, m);
+
+ n = lo + unLo - ltLo - 1;
+ m = hi - (gtHi - unHi) + 1;
+
+ nextLo[0] = lo; nextHi[0] = n; nextD[0] = d;
+ nextLo[1] = m; nextHi[1] = hi; nextD[1] = d;
+ nextLo[2] = n+1; nextHi[2] = m-1; nextD[2] = d+1;
+
+ if (mnextsize(0) < mnextsize(1)) mnextswap(0,1);
+ if (mnextsize(1) < mnextsize(2)) mnextswap(1,2);
+ if (mnextsize(0) < mnextsize(1)) mnextswap(0,1);
+
+ AssertD (mnextsize(0) >= mnextsize(1), "mainQSort3(8)" );
+ AssertD (mnextsize(1) >= mnextsize(2), "mainQSort3(9)" );
+
+ mpush (nextLo[0], nextHi[0], nextD[0]);
+ mpush (nextLo[1], nextHi[1], nextD[1]);
+ mpush (nextLo[2], nextHi[2], nextD[2]);
+ }
+}
+
+#undef mswap
+#undef mvswap
+#undef mpush
+#undef mpop
+#undef mmin
+#undef mnextsize
+#undef mnextswap
+#undef MAIN_QSORT_SMALL_THRESH
+#undef MAIN_QSORT_DEPTH_THRESH
+#undef MAIN_QSORT_STACK_SIZE
+
+
+/*---------------------------------------------*/
+/* Pre:
+ nblock > N_OVERSHOOT
+ block32 exists for [0 .. nblock-1 +N_OVERSHOOT]
+ ((UChar*)block32) [0 .. nblock-1] holds block
+ ptr exists for [0 .. nblock-1]
+
+ Post:
+ ((UChar*)block32) [0 .. nblock-1] holds block
+ All other areas of block32 destroyed
+ ftab [0 .. 65536 ] destroyed
+ ptr [0 .. nblock-1] holds sorted order
+ if (*budget < 0), sorting was abandoned
+*/
+
+#define BIGFREQ(b) (ftab[((b)+1) << 8] - ftab[(b) << 8])
+#define SETMASK (1 << 21)
+#define CLEARMASK (~(SETMASK))
+
+static
+void mainSort ( UInt32* ptr,
+ UChar* block,
+ UInt16* quadrant,
+ UInt32* ftab,
+ Int32 nblock,
+ Int32 verb,
+ Int32* budget )
+{
+ Int32 i, j, k, ss, sb;
+ Int32 runningOrder[256];
+ Bool bigDone[256];
+ Int32 copyStart[256];
+ Int32 copyEnd [256];
+ UChar c1;
+ Int32 numQSorted;
+ UInt16 s;
+ if (verb >= 4) VPrintf0 ( " main sort initialise ...\n" );
+
+ /*-- set up the 2-byte frequency table --*/
+ for (i = 65536; i >= 0; i--) ftab[i] = 0;
+
+ j = block[0] << 8;
+ i = nblock-1;
+ for (; i >= 3; i -= 4) {
+ quadrant[i] = 0;
+ j = (j >> 8) | ( ((UInt16)block[i]) << 8);
+ ftab[j]++;
+ quadrant[i-1] = 0;
+ j = (j >> 8) | ( ((UInt16)block[i-1]) << 8);
+ ftab[j]++;
+ quadrant[i-2] = 0;
+ j = (j >> 8) | ( ((UInt16)block[i-2]) << 8);
+ ftab[j]++;
+ quadrant[i-3] = 0;
+ j = (j >> 8) | ( ((UInt16)block[i-3]) << 8);
+ ftab[j]++;
+ }
+ for (; i >= 0; i--) {
+ quadrant[i] = 0;
+ j = (j >> 8) | ( ((UInt16)block[i]) << 8);
+ ftab[j]++;
+ }
+
+ /*-- (emphasises close relationship of block & quadrant) --*/
+ for (i = 0; i < BZ_N_OVERSHOOT; i++) {
+ block [nblock+i] = block[i];
+ quadrant[nblock+i] = 0;
+ }
+
+ if (verb >= 4) VPrintf0 ( " bucket sorting ...\n" );
+
+ /*-- Complete the initial radix sort --*/
+ for (i = 1; i <= 65536; i++) ftab[i] += ftab[i-1];
+
+ s = block[0] << 8;
+ i = nblock-1;
+ for (; i >= 3; i -= 4) {
+ s = (s >> 8) | (block[i] << 8);
+ j = ftab[s] -1;
+ ftab[s] = j;
+ ptr[j] = i;
+ s = (s >> 8) | (block[i-1] << 8);
+ j = ftab[s] -1;
+ ftab[s] = j;
+ ptr[j] = i-1;
+ s = (s >> 8) | (block[i-2] << 8);
+ j = ftab[s] -1;
+ ftab[s] = j;
+ ptr[j] = i-2;
+ s = (s >> 8) | (block[i-3] << 8);
+ j = ftab[s] -1;
+ ftab[s] = j;
+ ptr[j] = i-3;
+ }
+ for (; i >= 0; i--) {
+ s = (s >> 8) | (block[i] << 8);
+ j = ftab[s] -1;
+ ftab[s] = j;
+ ptr[j] = i;
+ }
+
+ /*--
+ Now ftab contains the first loc of every small bucket.
+ Calculate the running order, from smallest to largest
+ big bucket.
+ --*/
+ for (i = 0; i <= 255; i++) {
+ bigDone [i] = False;
+ runningOrder[i] = i;
+ }
+
+ {
+ Int32 vv;
+ Int32 h = 1;
+ do h = 3 * h + 1; while (h <= 256);
+ do {
+ h = h / 3;
+ for (i = h; i <= 255; i++) {
+ vv = runningOrder[i];
+ j = i;
+ while ( BIGFREQ(runningOrder[j-h]) > BIGFREQ(vv) ) {
+ runningOrder[j] = runningOrder[j-h];
+ j = j - h;
+ if (j <= (h - 1)) goto zero;
+ }
+ zero:
+ runningOrder[j] = vv;
+ }
+ } while (h != 1);
+ }
+
+ /*--
+ The main sorting loop.
+ --*/
+
+ numQSorted = 0;
+
+ for (i = 0; i <= 255; i++) {
+
+ /*--
+ Process big buckets, starting with the least full.
+ Basically this is a 3-step process in which we call
+ mainQSort3 to sort the small buckets [ss, j], but
+ also make a big effort to avoid the calls if we can.
+ --*/
+ ss = runningOrder[i];
+
+ /*--
+ Step 1:
+ Complete the big bucket [ss] by quicksorting
+ any unsorted small buckets [ss, j], for j != ss.
+ Hopefully previous pointer-scanning phases have already
+ completed many of the small buckets [ss, j], so
+ we don't have to sort them at all.
+ --*/
+ for (j = 0; j <= 255; j++) {
+ if (j != ss) {
+ sb = (ss << 8) + j;
+ if ( ! (ftab[sb] & SETMASK) ) {
+ Int32 lo = ftab[sb] & CLEARMASK;
+ Int32 hi = (ftab[sb+1] & CLEARMASK) - 1;
+ if (hi > lo) {
+ if (verb >= 4)
+ VPrintf4 ( " qsort [0x%x, 0x%x] "
+ "done %d this %d\n",
+ ss, j, numQSorted, hi - lo + 1 );
+ mainQSort3 (
+ ptr, block, quadrant, nblock,
+ lo, hi, BZ_N_RADIX, budget
+ );
+ numQSorted += (hi - lo + 1);
+ if (*budget < 0) return;
+ }
+ }
+ ftab[sb] |= SETMASK;
+ }
+ }
+
+ AssertH ( !bigDone[ss], 1006 );
+
+ /*--
+ Step 2:
+ Now scan this big bucket [ss] so as to synthesise the
+ sorted order for small buckets [t, ss] for all t,
+ including, magically, the bucket [ss,ss] too.
+ This will avoid doing Real Work in subsequent Step 1's.
+ --*/
+ {
+ for (j = 0; j <= 255; j++) {
+ copyStart[j] = ftab[(j << 8) + ss] & CLEARMASK;
+ copyEnd [j] = (ftab[(j << 8) + ss + 1] & CLEARMASK) - 1;
+ }
+ for (j = ftab[ss << 8] & CLEARMASK; j < copyStart[ss]; j++) {
+ k = ptr[j]-1; if (k < 0) k += nblock;
+ c1 = block[k];
+ if (!bigDone[c1])
+ ptr[ copyStart[c1]++ ] = k;
+ }
+ for (j = (ftab[(ss+1) << 8] & CLEARMASK) - 1; j > copyEnd[ss]; j--) {
+ k = ptr[j]-1; if (k < 0) k += nblock;
+ c1 = block[k];
+ if (!bigDone[c1])
+ ptr[ copyEnd[c1]-- ] = k;
+ }
+ }
+
+ AssertH ( (copyStart[ss]-1 == copyEnd[ss])
+ ||
+ /* Extremely rare case missing in bzip2-1.0.0 and 1.0.1.
+ Necessity for this case is demonstrated by compressing
+ a sequence of approximately 48.5 million of character
+ 251; 1.0.0/1.0.1 will then die here. */
+ (copyStart[ss] == 0 && copyEnd[ss] == nblock-1),
+ 1007 )
+
+ for (j = 0; j <= 255; j++) ftab[(j << 8) + ss] |= SETMASK;
+
+ /*--
+ Step 3:
+ The [ss] big bucket is now done. Record this fact,
+ and update the quadrant descriptors. Remember to
+ update quadrants in the overshoot area too, if
+ necessary. The "if (i < 255)" test merely skips
+ this updating for the last bucket processed, since
+ updating for the last bucket is pointless.
+
+ The quadrant array provides a way to incrementally
+ cache sort orderings, as they appear, so as to
+ make subsequent comparisons in fullGtU() complete
+ faster. For repetitive blocks this makes a big
+ difference (but not big enough to be able to avoid
+ the fallback sorting mechanism, exponential radix sort).
+
+ The precise meaning is: at all times:
+
+ for 0 <= i < nblock and 0 <= j <= nblock
+
+ if block[i] != block[j],
+
+ then the relative values of quadrant[i] and
+ quadrant[j] are meaningless.
+
+ else {
+ if quadrant[i] < quadrant[j]
+ then the string starting at i lexicographically
+ precedes the string starting at j
+
+ else if quadrant[i] > quadrant[j]
+ then the string starting at j lexicographically
+ precedes the string starting at i
+
+ else
+ the relative ordering of the strings starting
+ at i and j has not yet been determined.
+ }
+ --*/
+ bigDone[ss] = True;
+
+ if (i < 255) {
+ Int32 bbStart = ftab[ss << 8] & CLEARMASK;
+ Int32 bbSize = (ftab[(ss+1) << 8] & CLEARMASK) - bbStart;
+ Int32 shifts = 0;
+
+ while ((bbSize >> shifts) > 65534) shifts++;
+
+ for (j = bbSize-1; j >= 0; j--) {
+ Int32 a2update = ptr[bbStart + j];
+ UInt16 qVal = (UInt16)(j >> shifts);
+ quadrant[a2update] = qVal;
+ if (a2update < BZ_N_OVERSHOOT)
+ quadrant[a2update + nblock] = qVal;
+ }
+ AssertH ( ((bbSize-1) >> shifts) <= 65535, 1002 );
+ }
+
+ }
+
+ if (verb >= 4)
+ VPrintf3 ( " %d pointers, %d sorted, %d scanned\n",
+ nblock, numQSorted, nblock - numQSorted );
+}
+
+#undef BIGFREQ
+#undef SETMASK
+#undef CLEARMASK
+
+
+/*---------------------------------------------*/
+/* Pre:
+ nblock > 0
+ arr2 exists for [0 .. nblock-1 +N_OVERSHOOT]
+ ((UChar*)arr2) [0 .. nblock-1] holds block
+ arr1 exists for [0 .. nblock-1]
+
+ Post:
+ ((UChar*)arr2) [0 .. nblock-1] holds block
+ All other areas of block destroyed
+ ftab [ 0 .. 65536 ] destroyed
+ arr1 [0 .. nblock-1] holds sorted order
+*/
+void BZ2_blockSort ( EState* s )
+{
+ UInt32* ptr = s->ptr;
+ UChar* block = s->block;
+ UInt32* ftab = s->ftab;
+ Int32 nblock = s->nblock;
+ Int32 verb = s->verbosity;
+ Int32 wfact = s->workFactor;
+ UInt16* quadrant;
+ Int32 budget;
+ Int32 budgetInit;
+ Int32 i;
+
+ if (nblock < 10000) {
+ fallbackSort ( s->arr1, s->arr2, ftab, nblock, verb );
+ } else {
+ /* Calculate the location for quadrant, remembering to get
+ the alignment right. Assumes that &(block[0]) is at least
+ 2-byte aligned -- this should be ok since block is really
+ the first section of arr2.
+ */
+ i = nblock+BZ_N_OVERSHOOT;
+ if (i & 1) i++;
+ quadrant = (UInt16*)(&(block[i]));
+
+ /* (wfact-1) / 3 puts the default-factor-30
+ transition point at very roughly the same place as
+ with v0.1 and v0.9.0.
+ Not that it particularly matters any more, since the
+ resulting compressed stream is now the same regardless
+ of whether or not we use the main sort or fallback sort.
+ */
+ if (wfact < 1 ) wfact = 1;
+ if (wfact > 100) wfact = 100;
+ budgetInit = nblock * ((wfact-1) / 3);
+ budget = budgetInit;
+
+ mainSort ( ptr, block, quadrant, ftab, nblock, verb, &budget );
+ if (verb >= 3)
+ VPrintf3 ( " %d work, %d block, ratio %5.2f\n",
+ budgetInit - budget,
+ nblock,
+ (float)(budgetInit - budget) /
+ (float)(nblock==0 ? 1 : nblock) );
+ if (budget < 0) {
+ if (verb >= 2)
+ VPrintf0 ( " too repetitive; using fallback"
+ " sorting algorithm\n" );
+ fallbackSort ( s->arr1, s->arr2, ftab, nblock, verb );
+ }
+ }
+
+ s->origPtr = -1;
+ for (i = 0; i < s->nblock; i++)
+ if (ptr[i] == 0)
+ { s->origPtr = i; break; };
+
+ AssertH( s->origPtr != -1, 1003 );
+}
+
+
+/*-------------------------------------------------------------*/
+/*--- end blocksort.c ---*/
+/*-------------------------------------------------------------*/
diff --git a/dep/src/bzip2/bzlib.c b/dep/src/bzip2/bzlib.c
new file mode 100644
index 00000000000..ef86c91e695
--- /dev/null
+++ b/dep/src/bzip2/bzlib.c
@@ -0,0 +1,1572 @@
+
+/*-------------------------------------------------------------*/
+/*--- Library top-level functions. ---*/
+/*--- bzlib.c ---*/
+/*-------------------------------------------------------------*/
+
+/* ------------------------------------------------------------------
+ This file is part of bzip2/libbzip2, a program and library for
+ lossless, block-sorting data compression.
+
+ bzip2/libbzip2 version 1.0.5 of 10 December 2007
+ Copyright (C) 1996-2007 Julian Seward <jseward@bzip.org>
+
+ Please read the WARNING, DISCLAIMER and PATENTS sections in the
+ README file.
+
+ This program is released under the terms of the license contained
+ in the file LICENSE.
+ ------------------------------------------------------------------ */
+
+/* CHANGES
+ 0.9.0 -- original version.
+ 0.9.0a/b -- no changes in this file.
+ 0.9.0c -- made zero-length BZ_FLUSH work correctly in bzCompress().
+ fixed bzWrite/bzRead to ignore zero-length requests.
+ fixed bzread to correctly handle read requests after EOF.
+ wrong parameter order in call to bzDecompressInit in
+ bzBuffToBuffDecompress. Fixed.
+*/
+
+#include "bzlib_private.h"
+
+
+/*---------------------------------------------------*/
+/*--- Compression stuff ---*/
+/*---------------------------------------------------*/
+
+
+/*---------------------------------------------------*/
+#ifndef BZ_NO_STDIO
+void BZ2_bz__AssertH__fail ( int errcode )
+{
+ fprintf(stderr,
+ "\n\nbzip2/libbzip2: internal error number %d.\n"
+ "This is a bug in bzip2/libbzip2, %s.\n"
+ "Please report it to me at: jseward@bzip.org. If this happened\n"
+ "when you were using some program which uses libbzip2 as a\n"
+ "component, you should also report this bug to the author(s)\n"
+ "of that program. Please make an effort to report this bug;\n"
+ "timely and accurate bug reports eventually lead to higher\n"
+ "quality software. Thanks. Julian Seward, 10 December 2007.\n\n",
+ errcode,
+ BZ2_bzlibVersion()
+ );
+
+ if (errcode == 1007) {
+ fprintf(stderr,
+ "\n*** A special note about internal error number 1007 ***\n"
+ "\n"
+ "Experience suggests that a common cause of i.e. 1007\n"
+ "is unreliable memory or other hardware. The 1007 assertion\n"
+ "just happens to cross-check the results of huge numbers of\n"
+ "memory reads/writes, and so acts (unintendedly) as a stress\n"
+ "test of your memory system.\n"
+ "\n"
+ "I suggest the following: try compressing the file again,\n"
+ "possibly monitoring progress in detail with the -vv flag.\n"
+ "\n"
+ "* If the error cannot be reproduced, and/or happens at different\n"
+ " points in compression, you may have a flaky memory system.\n"
+ " Try a memory-test program. I have used Memtest86\n"
+ " (www.memtest86.com). At the time of writing it is free (GPLd).\n"
+ " Memtest86 tests memory much more thorougly than your BIOSs\n"
+ " power-on test, and may find failures that the BIOS doesn't.\n"
+ "\n"
+ "* If the error can be repeatably reproduced, this is a bug in\n"
+ " bzip2, and I would very much like to hear about it. Please\n"
+ " let me know, and, ideally, save a copy of the file causing the\n"
+ " problem -- without which I will be unable to investigate it.\n"
+ "\n"
+ );
+ }
+
+ exit(3);
+}
+#endif
+
+
+/*---------------------------------------------------*/
+static
+int bz_config_ok ( void )
+{
+ if (sizeof(int) != 4) return 0;
+ if (sizeof(short) != 2) return 0;
+ if (sizeof(char) != 1) return 0;
+ return 1;
+}
+
+
+/*---------------------------------------------------*/
+static
+void* default_bzalloc ( void* opaque, Int32 items, Int32 size )
+{
+ void* v = malloc ( items * size );
+ return v;
+}
+
+static
+void default_bzfree ( void* opaque, void* addr )
+{
+ if (addr != NULL) free ( addr );
+}
+
+
+/*---------------------------------------------------*/
+static
+void prepare_new_block ( EState* s )
+{
+ Int32 i;
+ s->nblock = 0;
+ s->numZ = 0;
+ s->state_out_pos = 0;
+ BZ_INITIALISE_CRC ( s->blockCRC );
+ for (i = 0; i < 256; i++) s->inUse[i] = False;
+ s->blockNo++;
+}
+
+
+/*---------------------------------------------------*/
+static
+void init_RL ( EState* s )
+{
+ s->state_in_ch = 256;
+ s->state_in_len = 0;
+}
+
+
+static
+Bool isempty_RL ( EState* s )
+{
+ if (s->state_in_ch < 256 && s->state_in_len > 0)
+ return False; else
+ return True;
+}
+
+
+/*---------------------------------------------------*/
+int BZ_API(BZ2_bzCompressInit)
+ ( bz_stream* strm,
+ int blockSize100k,
+ int verbosity,
+ int workFactor )
+{
+ Int32 n;
+ EState* s;
+
+ if (!bz_config_ok()) return BZ_CONFIG_ERROR;
+
+ if (strm == NULL ||
+ blockSize100k < 1 || blockSize100k > 9 ||
+ workFactor < 0 || workFactor > 250)
+ return BZ_PARAM_ERROR;
+
+ if (workFactor == 0) workFactor = 30;
+ if (strm->bzalloc == NULL) strm->bzalloc = default_bzalloc;
+ if (strm->bzfree == NULL) strm->bzfree = default_bzfree;
+
+ s = BZALLOC( sizeof(EState) );
+ if (s == NULL) return BZ_MEM_ERROR;
+ s->strm = strm;
+
+ s->arr1 = NULL;
+ s->arr2 = NULL;
+ s->ftab = NULL;
+
+ n = 100000 * blockSize100k;
+ s->arr1 = BZALLOC( n * sizeof(UInt32) );
+ s->arr2 = BZALLOC( (n+BZ_N_OVERSHOOT) * sizeof(UInt32) );
+ s->ftab = BZALLOC( 65537 * sizeof(UInt32) );
+
+ if (s->arr1 == NULL || s->arr2 == NULL || s->ftab == NULL) {
+ if (s->arr1 != NULL) BZFREE(s->arr1);
+ if (s->arr2 != NULL) BZFREE(s->arr2);
+ if (s->ftab != NULL) BZFREE(s->ftab);
+ if (s != NULL) BZFREE(s);
+ return BZ_MEM_ERROR;
+ }
+
+ s->blockNo = 0;
+ s->state = BZ_S_INPUT;
+ s->mode = BZ_M_RUNNING;
+ s->combinedCRC = 0;
+ s->blockSize100k = blockSize100k;
+ s->nblockMAX = 100000 * blockSize100k - 19;
+ s->verbosity = verbosity;
+ s->workFactor = workFactor;
+
+ s->block = (UChar*)s->arr2;
+ s->mtfv = (UInt16*)s->arr1;
+ s->zbits = NULL;
+ s->ptr = (UInt32*)s->arr1;
+
+ strm->state = s;
+ strm->total_in_lo32 = 0;
+ strm->total_in_hi32 = 0;
+ strm->total_out_lo32 = 0;
+ strm->total_out_hi32 = 0;
+ init_RL ( s );
+ prepare_new_block ( s );
+ return BZ_OK;
+}
+
+
+/*---------------------------------------------------*/
+static
+void add_pair_to_block ( EState* s )
+{
+ Int32 i;
+ UChar ch = (UChar)(s->state_in_ch);
+ for (i = 0; i < s->state_in_len; i++) {
+ BZ_UPDATE_CRC( s->blockCRC, ch );
+ }
+ s->inUse[s->state_in_ch] = True;
+ switch (s->state_in_len) {
+ case 1:
+ s->block[s->nblock] = (UChar)ch; s->nblock++;
+ break;
+ case 2:
+ s->block[s->nblock] = (UChar)ch; s->nblock++;
+ s->block[s->nblock] = (UChar)ch; s->nblock++;
+ break;
+ case 3:
+ s->block[s->nblock] = (UChar)ch; s->nblock++;
+ s->block[s->nblock] = (UChar)ch; s->nblock++;
+ s->block[s->nblock] = (UChar)ch; s->nblock++;
+ break;
+ default:
+ s->inUse[s->state_in_len-4] = True;
+ s->block[s->nblock] = (UChar)ch; s->nblock++;
+ s->block[s->nblock] = (UChar)ch; s->nblock++;
+ s->block[s->nblock] = (UChar)ch; s->nblock++;
+ s->block[s->nblock] = (UChar)ch; s->nblock++;
+ s->block[s->nblock] = ((UChar)(s->state_in_len-4));
+ s->nblock++;
+ break;
+ }
+}
+
+
+/*---------------------------------------------------*/
+static
+void flush_RL ( EState* s )
+{
+ if (s->state_in_ch < 256) add_pair_to_block ( s );
+ init_RL ( s );
+}
+
+
+/*---------------------------------------------------*/
+#define ADD_CHAR_TO_BLOCK(zs,zchh0) \
+{ \
+ UInt32 zchh = (UInt32)(zchh0); \
+ /*-- fast track the common case --*/ \
+ if (zchh != zs->state_in_ch && \
+ zs->state_in_len == 1) { \
+ UChar ch = (UChar)(zs->state_in_ch); \
+ BZ_UPDATE_CRC( zs->blockCRC, ch ); \
+ zs->inUse[zs->state_in_ch] = True; \
+ zs->block[zs->nblock] = (UChar)ch; \
+ zs->nblock++; \
+ zs->state_in_ch = zchh; \
+ } \
+ else \
+ /*-- general, uncommon cases --*/ \
+ if (zchh != zs->state_in_ch || \
+ zs->state_in_len == 255) { \
+ if (zs->state_in_ch < 256) \
+ add_pair_to_block ( zs ); \
+ zs->state_in_ch = zchh; \
+ zs->state_in_len = 1; \
+ } else { \
+ zs->state_in_len++; \
+ } \
+}
+
+
+/*---------------------------------------------------*/
+static
+Bool copy_input_until_stop ( EState* s )
+{
+ Bool progress_in = False;
+
+ if (s->mode == BZ_M_RUNNING) {
+
+ /*-- fast track the common case --*/
+ while (True) {
+ /*-- block full? --*/
+ if (s->nblock >= s->nblockMAX) break;
+ /*-- no input? --*/
+ if (s->strm->avail_in == 0) break;
+ progress_in = True;
+ ADD_CHAR_TO_BLOCK ( s, (UInt32)(*((UChar*)(s->strm->next_in))) );
+ s->strm->next_in++;
+ s->strm->avail_in--;
+ s->strm->total_in_lo32++;
+ if (s->strm->total_in_lo32 == 0) s->strm->total_in_hi32++;
+ }
+
+ } else {
+
+ /*-- general, uncommon case --*/
+ while (True) {
+ /*-- block full? --*/
+ if (s->nblock >= s->nblockMAX) break;
+ /*-- no input? --*/
+ if (s->strm->avail_in == 0) break;
+ /*-- flush/finish end? --*/
+ if (s->avail_in_expect == 0) break;
+ progress_in = True;
+ ADD_CHAR_TO_BLOCK ( s, (UInt32)(*((UChar*)(s->strm->next_in))) );
+ s->strm->next_in++;
+ s->strm->avail_in--;
+ s->strm->total_in_lo32++;
+ if (s->strm->total_in_lo32 == 0) s->strm->total_in_hi32++;
+ s->avail_in_expect--;
+ }
+ }
+ return progress_in;
+}
+
+
+/*---------------------------------------------------*/
+static
+Bool copy_output_until_stop ( EState* s )
+{
+ Bool progress_out = False;
+
+ while (True) {
+
+ /*-- no output space? --*/
+ if (s->strm->avail_out == 0) break;
+
+ /*-- block done? --*/
+ if (s->state_out_pos >= s->numZ) break;
+
+ progress_out = True;
+ *(s->strm->next_out) = s->zbits[s->state_out_pos];
+ s->state_out_pos++;
+ s->strm->avail_out--;
+ s->strm->next_out++;
+ s->strm->total_out_lo32++;
+ if (s->strm->total_out_lo32 == 0) s->strm->total_out_hi32++;
+ }
+
+ return progress_out;
+}
+
+
+/*---------------------------------------------------*/
+static
+Bool handle_compress ( bz_stream* strm )
+{
+ Bool progress_in = False;
+ Bool progress_out = False;
+ EState* s = strm->state;
+
+ while (True) {
+
+ if (s->state == BZ_S_OUTPUT) {
+ progress_out |= copy_output_until_stop ( s );
+ if (s->state_out_pos < s->numZ) break;
+ if (s->mode == BZ_M_FINISHING &&
+ s->avail_in_expect == 0 &&
+ isempty_RL(s)) break;
+ prepare_new_block ( s );
+ s->state = BZ_S_INPUT;
+ if (s->mode == BZ_M_FLUSHING &&
+ s->avail_in_expect == 0 &&
+ isempty_RL(s)) break;
+ }
+
+ if (s->state == BZ_S_INPUT) {
+ progress_in |= copy_input_until_stop ( s );
+ if (s->mode != BZ_M_RUNNING && s->avail_in_expect == 0) {
+ flush_RL ( s );
+ BZ2_compressBlock ( s, (Bool)(s->mode == BZ_M_FINISHING) );
+ s->state = BZ_S_OUTPUT;
+ }
+ else
+ if (s->nblock >= s->nblockMAX) {
+ BZ2_compressBlock ( s, False );
+ s->state = BZ_S_OUTPUT;
+ }
+ else
+ if (s->strm->avail_in == 0) {
+ break;
+ }
+ }
+
+ }
+
+ return progress_in || progress_out;
+}
+
+
+/*---------------------------------------------------*/
+int BZ_API(BZ2_bzCompress) ( bz_stream *strm, int action )
+{
+ Bool progress;
+ EState* s;
+ if (strm == NULL) return BZ_PARAM_ERROR;
+ s = strm->state;
+ if (s == NULL) return BZ_PARAM_ERROR;
+ if (s->strm != strm) return BZ_PARAM_ERROR;
+
+ preswitch:
+ switch (s->mode) {
+
+ case BZ_M_IDLE:
+ return BZ_SEQUENCE_ERROR;
+
+ case BZ_M_RUNNING:
+ if (action == BZ_RUN) {
+ progress = handle_compress ( strm );
+ return progress ? BZ_RUN_OK : BZ_PARAM_ERROR;
+ }
+ else
+ if (action == BZ_FLUSH) {
+ s->avail_in_expect = strm->avail_in;
+ s->mode = BZ_M_FLUSHING;
+ goto preswitch;
+ }
+ else
+ if (action == BZ_FINISH) {
+ s->avail_in_expect = strm->avail_in;
+ s->mode = BZ_M_FINISHING;
+ goto preswitch;
+ }
+ else
+ return BZ_PARAM_ERROR;
+
+ case BZ_M_FLUSHING:
+ if (action != BZ_FLUSH) return BZ_SEQUENCE_ERROR;
+ if (s->avail_in_expect != s->strm->avail_in)
+ return BZ_SEQUENCE_ERROR;
+ progress = handle_compress ( strm );
+ if (s->avail_in_expect > 0 || !isempty_RL(s) ||
+ s->state_out_pos < s->numZ) return BZ_FLUSH_OK;
+ s->mode = BZ_M_RUNNING;
+ return BZ_RUN_OK;
+
+ case BZ_M_FINISHING:
+ if (action != BZ_FINISH) return BZ_SEQUENCE_ERROR;
+ if (s->avail_in_expect != s->strm->avail_in)
+ return BZ_SEQUENCE_ERROR;
+ progress = handle_compress ( strm );
+ if (!progress) return BZ_SEQUENCE_ERROR;
+ if (s->avail_in_expect > 0 || !isempty_RL(s) ||
+ s->state_out_pos < s->numZ) return BZ_FINISH_OK;
+ s->mode = BZ_M_IDLE;
+ return BZ_STREAM_END;
+ }
+ return BZ_OK; /*--not reached--*/
+}
+
+
+/*---------------------------------------------------*/
+int BZ_API(BZ2_bzCompressEnd) ( bz_stream *strm )
+{
+ EState* s;
+ if (strm == NULL) return BZ_PARAM_ERROR;
+ s = strm->state;
+ if (s == NULL) return BZ_PARAM_ERROR;
+ if (s->strm != strm) return BZ_PARAM_ERROR;
+
+ if (s->arr1 != NULL) BZFREE(s->arr1);
+ if (s->arr2 != NULL) BZFREE(s->arr2);
+ if (s->ftab != NULL) BZFREE(s->ftab);
+ BZFREE(strm->state);
+
+ strm->state = NULL;
+
+ return BZ_OK;
+}
+
+
+/*---------------------------------------------------*/
+/*--- Decompression stuff ---*/
+/*---------------------------------------------------*/
+
+/*---------------------------------------------------*/
+int BZ_API(BZ2_bzDecompressInit)
+ ( bz_stream* strm,
+ int verbosity,
+ int small )
+{
+ DState* s;
+
+ if (!bz_config_ok()) return BZ_CONFIG_ERROR;
+
+ if (strm == NULL) return BZ_PARAM_ERROR;
+ if (small != 0 && small != 1) return BZ_PARAM_ERROR;
+ if (verbosity < 0 || verbosity > 4) return BZ_PARAM_ERROR;
+
+ if (strm->bzalloc == NULL) strm->bzalloc = default_bzalloc;
+ if (strm->bzfree == NULL) strm->bzfree = default_bzfree;
+
+ s = BZALLOC( sizeof(DState) );
+ if (s == NULL) return BZ_MEM_ERROR;
+ s->strm = strm;
+ strm->state = s;
+ s->state = BZ_X_MAGIC_1;
+ s->bsLive = 0;
+ s->bsBuff = 0;
+ s->calculatedCombinedCRC = 0;
+ strm->total_in_lo32 = 0;
+ strm->total_in_hi32 = 0;
+ strm->total_out_lo32 = 0;
+ strm->total_out_hi32 = 0;
+ s->smallDecompress = (Bool)small;
+ s->ll4 = NULL;
+ s->ll16 = NULL;
+ s->tt = NULL;
+ s->currBlockNo = 0;
+ s->verbosity = verbosity;
+
+ return BZ_OK;
+}
+
+
+/*---------------------------------------------------*/
+/* Return True iff data corruption is discovered.
+ Returns False if there is no problem.
+*/
+static
+Bool unRLE_obuf_to_output_FAST ( DState* s )
+{
+ UChar k1;
+
+ if (s->blockRandomised) {
+
+ while (True) {
+ /* try to finish existing run */
+ while (True) {
+ if (s->strm->avail_out == 0) return False;
+ if (s->state_out_len == 0) break;
+ *( (UChar*)(s->strm->next_out) ) = s->state_out_ch;
+ BZ_UPDATE_CRC ( s->calculatedBlockCRC, s->state_out_ch );
+ s->state_out_len--;
+ s->strm->next_out++;
+ s->strm->avail_out--;
+ s->strm->total_out_lo32++;
+ if (s->strm->total_out_lo32 == 0) s->strm->total_out_hi32++;
+ }
+
+ /* can a new run be started? */
+ if (s->nblock_used == s->save_nblock+1) return False;
+
+ /* Only caused by corrupt data stream? */
+ if (s->nblock_used > s->save_nblock+1)
+ return True;
+
+ s->state_out_len = 1;
+ s->state_out_ch = s->k0;
+ BZ_GET_FAST(k1); BZ_RAND_UPD_MASK;
+ k1 ^= BZ_RAND_MASK; s->nblock_used++;
+ if (s->nblock_used == s->save_nblock+1) continue;
+ if (k1 != s->k0) { s->k0 = k1; continue; };
+
+ s->state_out_len = 2;
+ BZ_GET_FAST(k1); BZ_RAND_UPD_MASK;
+ k1 ^= BZ_RAND_MASK; s->nblock_used++;
+ if (s->nblock_used == s->save_nblock+1) continue;
+ if (k1 != s->k0) { s->k0 = k1; continue; };
+
+ s->state_out_len = 3;
+ BZ_GET_FAST(k1); BZ_RAND_UPD_MASK;
+ k1 ^= BZ_RAND_MASK; s->nblock_used++;
+ if (s->nblock_used == s->save_nblock+1) continue;
+ if (k1 != s->k0) { s->k0 = k1; continue; };
+
+ BZ_GET_FAST(k1); BZ_RAND_UPD_MASK;
+ k1 ^= BZ_RAND_MASK; s->nblock_used++;
+ s->state_out_len = ((Int32)k1) + 4;
+ BZ_GET_FAST(s->k0); BZ_RAND_UPD_MASK;
+ s->k0 ^= BZ_RAND_MASK; s->nblock_used++;
+ }
+
+ } else {
+
+ /* restore */
+ UInt32 c_calculatedBlockCRC = s->calculatedBlockCRC;
+ UChar c_state_out_ch = s->state_out_ch;
+ Int32 c_state_out_len = s->state_out_len;
+ Int32 c_nblock_used = s->nblock_used;
+ Int32 c_k0 = s->k0;
+ UInt32* c_tt = s->tt;
+ UInt32 c_tPos = s->tPos;
+ char* cs_next_out = s->strm->next_out;
+ unsigned int cs_avail_out = s->strm->avail_out;
+ Int32 ro_blockSize100k = s->blockSize100k;
+ /* end restore */
+
+ UInt32 avail_out_INIT = cs_avail_out;
+ Int32 s_save_nblockPP = s->save_nblock+1;
+ unsigned int total_out_lo32_old;
+
+ while (True) {
+
+ /* try to finish existing run */
+ if (c_state_out_len > 0) {
+ while (True) {
+ if (cs_avail_out == 0) goto return_notr;
+ if (c_state_out_len == 1) break;
+ *( (UChar*)(cs_next_out) ) = c_state_out_ch;
+ BZ_UPDATE_CRC ( c_calculatedBlockCRC, c_state_out_ch );
+ c_state_out_len--;
+ cs_next_out++;
+ cs_avail_out--;
+ }
+ s_state_out_len_eq_one:
+ {
+ if (cs_avail_out == 0) {
+ c_state_out_len = 1; goto return_notr;
+ };
+ *( (UChar*)(cs_next_out) ) = c_state_out_ch;
+ BZ_UPDATE_CRC ( c_calculatedBlockCRC, c_state_out_ch );
+ cs_next_out++;
+ cs_avail_out--;
+ }
+ }
+ /* Only caused by corrupt data stream? */
+ if (c_nblock_used > s_save_nblockPP)
+ return True;
+
+ /* can a new run be started? */
+ if (c_nblock_used == s_save_nblockPP) {
+ c_state_out_len = 0; goto return_notr;
+ };
+ c_state_out_ch = c_k0;
+ BZ_GET_FAST_C(k1); c_nblock_used++;
+ if (k1 != c_k0) {
+ c_k0 = k1; goto s_state_out_len_eq_one;
+ };
+ if (c_nblock_used == s_save_nblockPP)
+ goto s_state_out_len_eq_one;
+
+ c_state_out_len = 2;
+ BZ_GET_FAST_C(k1); c_nblock_used++;
+ if (c_nblock_used == s_save_nblockPP) continue;
+ if (k1 != c_k0) { c_k0 = k1; continue; };
+
+ c_state_out_len = 3;
+ BZ_GET_FAST_C(k1); c_nblock_used++;
+ if (c_nblock_used == s_save_nblockPP) continue;
+ if (k1 != c_k0) { c_k0 = k1; continue; };
+
+ BZ_GET_FAST_C(k1); c_nblock_used++;
+ c_state_out_len = ((Int32)k1) + 4;
+ BZ_GET_FAST_C(c_k0); c_nblock_used++;
+ }
+
+ return_notr:
+ total_out_lo32_old = s->strm->total_out_lo32;
+ s->strm->total_out_lo32 += (avail_out_INIT - cs_avail_out);
+ if (s->strm->total_out_lo32 < total_out_lo32_old)
+ s->strm->total_out_hi32++;
+
+ /* save */
+ s->calculatedBlockCRC = c_calculatedBlockCRC;
+ s->state_out_ch = c_state_out_ch;
+ s->state_out_len = c_state_out_len;
+ s->nblock_used = c_nblock_used;
+ s->k0 = c_k0;
+ s->tt = c_tt;
+ s->tPos = c_tPos;
+ s->strm->next_out = cs_next_out;
+ s->strm->avail_out = cs_avail_out;
+ /* end save */
+ }
+ return False;
+}
+
+
+
+/*---------------------------------------------------*/
+__inline__ Int32 BZ2_indexIntoF ( Int32 indx, Int32 *cftab )
+{
+ Int32 nb, na, mid;
+ nb = 0;
+ na = 256;
+ do {
+ mid = (nb + na) >> 1;
+ if (indx >= cftab[mid]) nb = mid; else na = mid;
+ }
+ while (na - nb != 1);
+ return nb;
+}
+
+
+/*---------------------------------------------------*/
+/* Return True iff data corruption is discovered.
+ Returns False if there is no problem.
+*/
+static
+Bool unRLE_obuf_to_output_SMALL ( DState* s )
+{
+ UChar k1;
+
+ if (s->blockRandomised) {
+
+ while (True) {
+ /* try to finish existing run */
+ while (True) {
+ if (s->strm->avail_out == 0) return False;
+ if (s->state_out_len == 0) break;
+ *( (UChar*)(s->strm->next_out) ) = s->state_out_ch;
+ BZ_UPDATE_CRC ( s->calculatedBlockCRC, s->state_out_ch );
+ s->state_out_len--;
+ s->strm->next_out++;
+ s->strm->avail_out--;
+ s->strm->total_out_lo32++;
+ if (s->strm->total_out_lo32 == 0) s->strm->total_out_hi32++;
+ }
+
+ /* can a new run be started? */
+ if (s->nblock_used == s->save_nblock+1) return False;
+
+ /* Only caused by corrupt data stream? */
+ if (s->nblock_used > s->save_nblock+1)
+ return True;
+
+ s->state_out_len = 1;
+ s->state_out_ch = s->k0;
+ BZ_GET_SMALL(k1); BZ_RAND_UPD_MASK;
+ k1 ^= BZ_RAND_MASK; s->nblock_used++;
+ if (s->nblock_used == s->save_nblock+1) continue;
+ if (k1 != s->k0) { s->k0 = k1; continue; };
+
+ s->state_out_len = 2;
+ BZ_GET_SMALL(k1); BZ_RAND_UPD_MASK;
+ k1 ^= BZ_RAND_MASK; s->nblock_used++;
+ if (s->nblock_used == s->save_nblock+1) continue;
+ if (k1 != s->k0) { s->k0 = k1; continue; };
+
+ s->state_out_len = 3;
+ BZ_GET_SMALL(k1); BZ_RAND_UPD_MASK;
+ k1 ^= BZ_RAND_MASK; s->nblock_used++;
+ if (s->nblock_used == s->save_nblock+1) continue;
+ if (k1 != s->k0) { s->k0 = k1; continue; };
+
+ BZ_GET_SMALL(k1); BZ_RAND_UPD_MASK;
+ k1 ^= BZ_RAND_MASK; s->nblock_used++;
+ s->state_out_len = ((Int32)k1) + 4;
+ BZ_GET_SMALL(s->k0); BZ_RAND_UPD_MASK;
+ s->k0 ^= BZ_RAND_MASK; s->nblock_used++;
+ }
+
+ } else {
+
+ while (True) {
+ /* try to finish existing run */
+ while (True) {
+ if (s->strm->avail_out == 0) return False;
+ if (s->state_out_len == 0) break;
+ *( (UChar*)(s->strm->next_out) ) = s->state_out_ch;
+ BZ_UPDATE_CRC ( s->calculatedBlockCRC, s->state_out_ch );
+ s->state_out_len--;
+ s->strm->next_out++;
+ s->strm->avail_out--;
+ s->strm->total_out_lo32++;
+ if (s->strm->total_out_lo32 == 0) s->strm->total_out_hi32++;
+ }
+
+ /* can a new run be started? */
+ if (s->nblock_used == s->save_nblock+1) return False;
+
+ /* Only caused by corrupt data stream? */
+ if (s->nblock_used > s->save_nblock+1)
+ return True;
+
+ s->state_out_len = 1;
+ s->state_out_ch = s->k0;
+ BZ_GET_SMALL(k1); s->nblock_used++;
+ if (s->nblock_used == s->save_nblock+1) continue;
+ if (k1 != s->k0) { s->k0 = k1; continue; };
+
+ s->state_out_len = 2;
+ BZ_GET_SMALL(k1); s->nblock_used++;
+ if (s->nblock_used == s->save_nblock+1) continue;
+ if (k1 != s->k0) { s->k0 = k1; continue; };
+
+ s->state_out_len = 3;
+ BZ_GET_SMALL(k1); s->nblock_used++;
+ if (s->nblock_used == s->save_nblock+1) continue;
+ if (k1 != s->k0) { s->k0 = k1; continue; };
+
+ BZ_GET_SMALL(k1); s->nblock_used++;
+ s->state_out_len = ((Int32)k1) + 4;
+ BZ_GET_SMALL(s->k0); s->nblock_used++;
+ }
+
+ }
+}
+
+
+/*---------------------------------------------------*/
+int BZ_API(BZ2_bzDecompress) ( bz_stream *strm )
+{
+ Bool corrupt;
+ DState* s;
+ if (strm == NULL) return BZ_PARAM_ERROR;
+ s = strm->state;
+ if (s == NULL) return BZ_PARAM_ERROR;
+ if (s->strm != strm) return BZ_PARAM_ERROR;
+
+ while (True) {
+ if (s->state == BZ_X_IDLE) return BZ_SEQUENCE_ERROR;
+ if (s->state == BZ_X_OUTPUT) {
+ if (s->smallDecompress)
+ corrupt = unRLE_obuf_to_output_SMALL ( s ); else
+ corrupt = unRLE_obuf_to_output_FAST ( s );
+ if (corrupt) return BZ_DATA_ERROR;
+ if (s->nblock_used == s->save_nblock+1 && s->state_out_len == 0) {
+ BZ_FINALISE_CRC ( s->calculatedBlockCRC );
+ if (s->verbosity >= 3)
+ VPrintf2 ( " {0x%08x, 0x%08x}", s->storedBlockCRC,
+ s->calculatedBlockCRC );
+ if (s->verbosity >= 2) VPrintf0 ( "]" );
+ if (s->calculatedBlockCRC != s->storedBlockCRC)
+ return BZ_DATA_ERROR;
+ s->calculatedCombinedCRC
+ = (s->calculatedCombinedCRC << 1) |
+ (s->calculatedCombinedCRC >> 31);
+ s->calculatedCombinedCRC ^= s->calculatedBlockCRC;
+ s->state = BZ_X_BLKHDR_1;
+ } else {
+ return BZ_OK;
+ }
+ }
+ if (s->state >= BZ_X_MAGIC_1) {
+ Int32 r = BZ2_decompress ( s );
+ if (r == BZ_STREAM_END) {
+ if (s->verbosity >= 3)
+ VPrintf2 ( "\n combined CRCs: stored = 0x%08x, computed = 0x%08x",
+ s->storedCombinedCRC, s->calculatedCombinedCRC );
+ if (s->calculatedCombinedCRC != s->storedCombinedCRC)
+ return BZ_DATA_ERROR;
+ return r;
+ }
+ if (s->state != BZ_X_OUTPUT) return r;
+ }
+ }
+
+ AssertH ( 0, 6001 );
+
+ return 0; /*NOTREACHED*/
+}
+
+
+/*---------------------------------------------------*/
+int BZ_API(BZ2_bzDecompressEnd) ( bz_stream *strm )
+{
+ DState* s;
+ if (strm == NULL) return BZ_PARAM_ERROR;
+ s = strm->state;
+ if (s == NULL) return BZ_PARAM_ERROR;
+ if (s->strm != strm) return BZ_PARAM_ERROR;
+
+ if (s->tt != NULL) BZFREE(s->tt);
+ if (s->ll16 != NULL) BZFREE(s->ll16);
+ if (s->ll4 != NULL) BZFREE(s->ll4);
+
+ BZFREE(strm->state);
+ strm->state = NULL;
+
+ return BZ_OK;
+}
+
+
+#ifndef BZ_NO_STDIO
+/*---------------------------------------------------*/
+/*--- File I/O stuff ---*/
+/*---------------------------------------------------*/
+
+#define BZ_SETERR(eee) \
+{ \
+ if (bzerror != NULL) *bzerror = eee; \
+ if (bzf != NULL) bzf->lastErr = eee; \
+}
+
+typedef
+ struct {
+ FILE* handle;
+ Char buf[BZ_MAX_UNUSED];
+ Int32 bufN;
+ Bool writing;
+ bz_stream strm;
+ Int32 lastErr;
+ Bool initialisedOk;
+ }
+ bzFile;
+
+
+/*---------------------------------------------*/
+static Bool myfeof ( FILE* f )
+{
+ Int32 c = fgetc ( f );
+ if (c == EOF) return True;
+ ungetc ( c, f );
+ return False;
+}
+
+
+/*---------------------------------------------------*/
+BZFILE* BZ_API(BZ2_bzWriteOpen)
+ ( int* bzerror,
+ FILE* f,
+ int blockSize100k,
+ int verbosity,
+ int workFactor )
+{
+ Int32 ret;
+ bzFile* bzf = NULL;
+
+ BZ_SETERR(BZ_OK);
+
+ if (f == NULL ||
+ (blockSize100k < 1 || blockSize100k > 9) ||
+ (workFactor < 0 || workFactor > 250) ||
+ (verbosity < 0 || verbosity > 4))
+ { BZ_SETERR(BZ_PARAM_ERROR); return NULL; };
+
+ if (ferror(f))
+ { BZ_SETERR(BZ_IO_ERROR); return NULL; };
+
+ bzf = malloc ( sizeof(bzFile) );
+ if (bzf == NULL)
+ { BZ_SETERR(BZ_MEM_ERROR); return NULL; };
+
+ BZ_SETERR(BZ_OK);
+ bzf->initialisedOk = False;
+ bzf->bufN = 0;
+ bzf->handle = f;
+ bzf->writing = True;
+ bzf->strm.bzalloc = NULL;
+ bzf->strm.bzfree = NULL;
+ bzf->strm.opaque = NULL;
+
+ if (workFactor == 0) workFactor = 30;
+ ret = BZ2_bzCompressInit ( &(bzf->strm), blockSize100k,
+ verbosity, workFactor );
+ if (ret != BZ_OK)
+ { BZ_SETERR(ret); free(bzf); return NULL; };
+
+ bzf->strm.avail_in = 0;
+ bzf->initialisedOk = True;
+ return bzf;
+}
+
+
+
+/*---------------------------------------------------*/
+void BZ_API(BZ2_bzWrite)
+ ( int* bzerror,
+ BZFILE* b,
+ void* buf,
+ int len )
+{
+ Int32 n, n2, ret;
+ bzFile* bzf = (bzFile*)b;
+
+ BZ_SETERR(BZ_OK);
+ if (bzf == NULL || buf == NULL || len < 0)
+ { BZ_SETERR(BZ_PARAM_ERROR); return; };
+ if (!(bzf->writing))
+ { BZ_SETERR(BZ_SEQUENCE_ERROR); return; };
+ if (ferror(bzf->handle))
+ { BZ_SETERR(BZ_IO_ERROR); return; };
+
+ if (len == 0)
+ { BZ_SETERR(BZ_OK); return; };
+
+ bzf->strm.avail_in = len;
+ bzf->strm.next_in = buf;
+
+ while (True) {
+ bzf->strm.avail_out = BZ_MAX_UNUSED;
+ bzf->strm.next_out = bzf->buf;
+ ret = BZ2_bzCompress ( &(bzf->strm), BZ_RUN );
+ if (ret != BZ_RUN_OK)
+ { BZ_SETERR(ret); return; };
+
+ if (bzf->strm.avail_out < BZ_MAX_UNUSED) {
+ n = BZ_MAX_UNUSED - bzf->strm.avail_out;
+ n2 = fwrite ( (void*)(bzf->buf), sizeof(UChar),
+ n, bzf->handle );
+ if (n != n2 || ferror(bzf->handle))
+ { BZ_SETERR(BZ_IO_ERROR); return; };
+ }
+
+ if (bzf->strm.avail_in == 0)
+ { BZ_SETERR(BZ_OK); return; };
+ }
+}
+
+
+/*---------------------------------------------------*/
+void BZ_API(BZ2_bzWriteClose)
+ ( int* bzerror,
+ BZFILE* b,
+ int abandon,
+ unsigned int* nbytes_in,
+ unsigned int* nbytes_out )
+{
+ BZ2_bzWriteClose64 ( bzerror, b, abandon,
+ nbytes_in, NULL, nbytes_out, NULL );
+}
+
+
+void BZ_API(BZ2_bzWriteClose64)
+ ( int* bzerror,
+ BZFILE* b,
+ int abandon,
+ unsigned int* nbytes_in_lo32,
+ unsigned int* nbytes_in_hi32,
+ unsigned int* nbytes_out_lo32,
+ unsigned int* nbytes_out_hi32 )
+{
+ Int32 n, n2, ret;
+ bzFile* bzf = (bzFile*)b;
+
+ if (bzf == NULL)
+ { BZ_SETERR(BZ_OK); return; };
+ if (!(bzf->writing))
+ { BZ_SETERR(BZ_SEQUENCE_ERROR); return; };
+ if (ferror(bzf->handle))
+ { BZ_SETERR(BZ_IO_ERROR); return; };
+
+ if (nbytes_in_lo32 != NULL) *nbytes_in_lo32 = 0;
+ if (nbytes_in_hi32 != NULL) *nbytes_in_hi32 = 0;
+ if (nbytes_out_lo32 != NULL) *nbytes_out_lo32 = 0;
+ if (nbytes_out_hi32 != NULL) *nbytes_out_hi32 = 0;
+
+ if ((!abandon) && bzf->lastErr == BZ_OK) {
+ while (True) {
+ bzf->strm.avail_out = BZ_MAX_UNUSED;
+ bzf->strm.next_out = bzf->buf;
+ ret = BZ2_bzCompress ( &(bzf->strm), BZ_FINISH );
+ if (ret != BZ_FINISH_OK && ret != BZ_STREAM_END)
+ { BZ_SETERR(ret); return; };
+
+ if (bzf->strm.avail_out < BZ_MAX_UNUSED) {
+ n = BZ_MAX_UNUSED - bzf->strm.avail_out;
+ n2 = fwrite ( (void*)(bzf->buf), sizeof(UChar),
+ n, bzf->handle );
+ if (n != n2 || ferror(bzf->handle))
+ { BZ_SETERR(BZ_IO_ERROR); return; };
+ }
+
+ if (ret == BZ_STREAM_END) break;
+ }
+ }
+
+ if ( !abandon && !ferror ( bzf->handle ) ) {
+ fflush ( bzf->handle );
+ if (ferror(bzf->handle))
+ { BZ_SETERR(BZ_IO_ERROR); return; };
+ }
+
+ if (nbytes_in_lo32 != NULL)
+ *nbytes_in_lo32 = bzf->strm.total_in_lo32;
+ if (nbytes_in_hi32 != NULL)
+ *nbytes_in_hi32 = bzf->strm.total_in_hi32;
+ if (nbytes_out_lo32 != NULL)
+ *nbytes_out_lo32 = bzf->strm.total_out_lo32;
+ if (nbytes_out_hi32 != NULL)
+ *nbytes_out_hi32 = bzf->strm.total_out_hi32;
+
+ BZ_SETERR(BZ_OK);
+ BZ2_bzCompressEnd ( &(bzf->strm) );
+ free ( bzf );
+}
+
+
+/*---------------------------------------------------*/
+BZFILE* BZ_API(BZ2_bzReadOpen)
+ ( int* bzerror,
+ FILE* f,
+ int verbosity,
+ int small,
+ void* unused,
+ int nUnused )
+{
+ bzFile* bzf = NULL;
+ int ret;
+
+ BZ_SETERR(BZ_OK);
+
+ if (f == NULL ||
+ (small != 0 && small != 1) ||
+ (verbosity < 0 || verbosity > 4) ||
+ (unused == NULL && nUnused != 0) ||
+ (unused != NULL && (nUnused < 0 || nUnused > BZ_MAX_UNUSED)))
+ { BZ_SETERR(BZ_PARAM_ERROR); return NULL; };
+
+ if (ferror(f))
+ { BZ_SETERR(BZ_IO_ERROR); return NULL; };
+
+ bzf = malloc ( sizeof(bzFile) );
+ if (bzf == NULL)
+ { BZ_SETERR(BZ_MEM_ERROR); return NULL; };
+
+ BZ_SETERR(BZ_OK);
+
+ bzf->initialisedOk = False;
+ bzf->handle = f;
+ bzf->bufN = 0;
+ bzf->writing = False;
+ bzf->strm.bzalloc = NULL;
+ bzf->strm.bzfree = NULL;
+ bzf->strm.opaque = NULL;
+
+ while (nUnused > 0) {
+ bzf->buf[bzf->bufN] = *((UChar*)(unused)); bzf->bufN++;
+ unused = ((void*)( 1 + ((UChar*)(unused)) ));
+ nUnused--;
+ }
+
+ ret = BZ2_bzDecompressInit ( &(bzf->strm), verbosity, small );
+ if (ret != BZ_OK)
+ { BZ_SETERR(ret); free(bzf); return NULL; };
+
+ bzf->strm.avail_in = bzf->bufN;
+ bzf->strm.next_in = bzf->buf;
+
+ bzf->initialisedOk = True;
+ return bzf;
+}
+
+
+/*---------------------------------------------------*/
+void BZ_API(BZ2_bzReadClose) ( int *bzerror, BZFILE *b )
+{
+ bzFile* bzf = (bzFile*)b;
+
+ BZ_SETERR(BZ_OK);
+ if (bzf == NULL)
+ { BZ_SETERR(BZ_OK); return; };
+
+ if (bzf->writing)
+ { BZ_SETERR(BZ_SEQUENCE_ERROR); return; };
+
+ if (bzf->initialisedOk)
+ (void)BZ2_bzDecompressEnd ( &(bzf->strm) );
+ free ( bzf );
+}
+
+
+/*---------------------------------------------------*/
+int BZ_API(BZ2_bzRead)
+ ( int* bzerror,
+ BZFILE* b,
+ void* buf,
+ int len )
+{
+ Int32 n, ret;
+ bzFile* bzf = (bzFile*)b;
+
+ BZ_SETERR(BZ_OK);
+
+ if (bzf == NULL || buf == NULL || len < 0)
+ { BZ_SETERR(BZ_PARAM_ERROR); return 0; };
+
+ if (bzf->writing)
+ { BZ_SETERR(BZ_SEQUENCE_ERROR); return 0; };
+
+ if (len == 0)
+ { BZ_SETERR(BZ_OK); return 0; };
+
+ bzf->strm.avail_out = len;
+ bzf->strm.next_out = buf;
+
+ while (True) {
+
+ if (ferror(bzf->handle))
+ { BZ_SETERR(BZ_IO_ERROR); return 0; };
+
+ if (bzf->strm.avail_in == 0 && !myfeof(bzf->handle)) {
+ n = fread ( bzf->buf, sizeof(UChar),
+ BZ_MAX_UNUSED, bzf->handle );
+ if (ferror(bzf->handle))
+ { BZ_SETERR(BZ_IO_ERROR); return 0; };
+ bzf->bufN = n;
+ bzf->strm.avail_in = bzf->bufN;
+ bzf->strm.next_in = bzf->buf;
+ }
+
+ ret = BZ2_bzDecompress ( &(bzf->strm) );
+
+ if (ret != BZ_OK && ret != BZ_STREAM_END)
+ { BZ_SETERR(ret); return 0; };
+
+ if (ret == BZ_OK && myfeof(bzf->handle) &&
+ bzf->strm.avail_in == 0 && bzf->strm.avail_out > 0)
+ { BZ_SETERR(BZ_UNEXPECTED_EOF); return 0; };
+
+ if (ret == BZ_STREAM_END)
+ { BZ_SETERR(BZ_STREAM_END);
+ return len - bzf->strm.avail_out; };
+ if (bzf->strm.avail_out == 0)
+ { BZ_SETERR(BZ_OK); return len; };
+
+ }
+
+ return 0; /*not reached*/
+}
+
+
+/*---------------------------------------------------*/
+void BZ_API(BZ2_bzReadGetUnused)
+ ( int* bzerror,
+ BZFILE* b,
+ void** unused,
+ int* nUnused )
+{
+ bzFile* bzf = (bzFile*)b;
+ if (bzf == NULL)
+ { BZ_SETERR(BZ_PARAM_ERROR); return; };
+ if (bzf->lastErr != BZ_STREAM_END)
+ { BZ_SETERR(BZ_SEQUENCE_ERROR); return; };
+ if (unused == NULL || nUnused == NULL)
+ { BZ_SETERR(BZ_PARAM_ERROR); return; };
+
+ BZ_SETERR(BZ_OK);
+ *nUnused = bzf->strm.avail_in;
+ *unused = bzf->strm.next_in;
+}
+#endif
+
+
+/*---------------------------------------------------*/
+/*--- Misc convenience stuff ---*/
+/*---------------------------------------------------*/
+
+/*---------------------------------------------------*/
+int BZ_API(BZ2_bzBuffToBuffCompress)
+ ( char* dest,
+ unsigned int* destLen,
+ char* source,
+ unsigned int sourceLen,
+ int blockSize100k,
+ int verbosity,
+ int workFactor )
+{
+ bz_stream strm;
+ int ret;
+
+ if (dest == NULL || destLen == NULL ||
+ source == NULL ||
+ blockSize100k < 1 || blockSize100k > 9 ||
+ verbosity < 0 || verbosity > 4 ||
+ workFactor < 0 || workFactor > 250)
+ return BZ_PARAM_ERROR;
+
+ if (workFactor == 0) workFactor = 30;
+ strm.bzalloc = NULL;
+ strm.bzfree = NULL;
+ strm.opaque = NULL;
+ ret = BZ2_bzCompressInit ( &strm, blockSize100k,
+ verbosity, workFactor );
+ if (ret != BZ_OK) return ret;
+
+ strm.next_in = source;
+ strm.next_out = dest;
+ strm.avail_in = sourceLen;
+ strm.avail_out = *destLen;
+
+ ret = BZ2_bzCompress ( &strm, BZ_FINISH );
+ if (ret == BZ_FINISH_OK) goto output_overflow;
+ if (ret != BZ_STREAM_END) goto errhandler;
+
+ /* normal termination */
+ *destLen -= strm.avail_out;
+ BZ2_bzCompressEnd ( &strm );
+ return BZ_OK;
+
+ output_overflow:
+ BZ2_bzCompressEnd ( &strm );
+ return BZ_OUTBUFF_FULL;
+
+ errhandler:
+ BZ2_bzCompressEnd ( &strm );
+ return ret;
+}
+
+
+/*---------------------------------------------------*/
+int BZ_API(BZ2_bzBuffToBuffDecompress)
+ ( char* dest,
+ unsigned int* destLen,
+ char* source,
+ unsigned int sourceLen,
+ int small,
+ int verbosity )
+{
+ bz_stream strm;
+ int ret;
+
+ if (dest == NULL || destLen == NULL ||
+ source == NULL ||
+ (small != 0 && small != 1) ||
+ verbosity < 0 || verbosity > 4)
+ return BZ_PARAM_ERROR;
+
+ strm.bzalloc = NULL;
+ strm.bzfree = NULL;
+ strm.opaque = NULL;
+ ret = BZ2_bzDecompressInit ( &strm, verbosity, small );
+ if (ret != BZ_OK) return ret;
+
+ strm.next_in = source;
+ strm.next_out = dest;
+ strm.avail_in = sourceLen;
+ strm.avail_out = *destLen;
+
+ ret = BZ2_bzDecompress ( &strm );
+ if (ret == BZ_OK) goto output_overflow_or_eof;
+ if (ret != BZ_STREAM_END) goto errhandler;
+
+ /* normal termination */
+ *destLen -= strm.avail_out;
+ BZ2_bzDecompressEnd ( &strm );
+ return BZ_OK;
+
+ output_overflow_or_eof:
+ if (strm.avail_out > 0) {
+ BZ2_bzDecompressEnd ( &strm );
+ return BZ_UNEXPECTED_EOF;
+ } else {
+ BZ2_bzDecompressEnd ( &strm );
+ return BZ_OUTBUFF_FULL;
+ };
+
+ errhandler:
+ BZ2_bzDecompressEnd ( &strm );
+ return ret;
+}
+
+
+/*---------------------------------------------------*/
+/*--
+ Code contributed by Yoshioka Tsuneo (tsuneo@rr.iij4u.or.jp)
+ to support better zlib compatibility.
+ This code is not _officially_ part of libbzip2 (yet);
+ I haven't tested it, documented it, or considered the
+ threading-safeness of it.
+ If this code breaks, please contact both Yoshioka and me.
+--*/
+/*---------------------------------------------------*/
+
+/*---------------------------------------------------*/
+/*--
+ return version like "0.9.5d, 4-Sept-1999".
+--*/
+const char * BZ_API(BZ2_bzlibVersion)(void)
+{
+ return BZ_VERSION;
+}
+
+
+#ifndef BZ_NO_STDIO
+/*---------------------------------------------------*/
+
+#if defined(_WIN32) || defined(OS2) || defined(MSDOS)
+# include <fcntl.h>
+# include <io.h>
+# define SET_BINARY_MODE(file) setmode(fileno(file),O_BINARY)
+#else
+# define SET_BINARY_MODE(file)
+#endif
+static
+BZFILE * bzopen_or_bzdopen
+ ( const char *path, /* no use when bzdopen */
+ int fd, /* no use when bzdopen */
+ const char *mode,
+ int open_mode) /* bzopen: 0, bzdopen:1 */
+{
+ int bzerr;
+ char unused[BZ_MAX_UNUSED];
+ int blockSize100k = 9;
+ int writing = 0;
+ char mode2[10] = "";
+ FILE *fp = NULL;
+ BZFILE *bzfp = NULL;
+ int verbosity = 0;
+ int workFactor = 30;
+ int smallMode = 0;
+ int nUnused = 0;
+
+ if (mode == NULL) return NULL;
+ while (*mode) {
+ switch (*mode) {
+ case 'r':
+ writing = 0; break;
+ case 'w':
+ writing = 1; break;
+ case 's':
+ smallMode = 1; break;
+ default:
+ if (isdigit((int)(*mode))) {
+ blockSize100k = *mode-BZ_HDR_0;
+ }
+ }
+ mode++;
+ }
+ strcat(mode2, writing ? "w" : "r" );
+ strcat(mode2,"b"); /* binary mode */
+
+ if (open_mode==0) {
+ if (path==NULL || strcmp(path,"")==0) {
+ fp = (writing ? stdout : stdin);
+ SET_BINARY_MODE(fp);
+ } else {
+ fp = fopen(path,mode2);
+ }
+ } else {
+#ifdef BZ_STRICT_ANSI
+ fp = NULL;
+#else
+ fp = fdopen(fd,mode2);
+#endif
+ }
+ if (fp == NULL) return NULL;
+
+ if (writing) {
+ /* Guard against total chaos and anarchy -- JRS */
+ if (blockSize100k < 1) blockSize100k = 1;
+ if (blockSize100k > 9) blockSize100k = 9;
+ bzfp = BZ2_bzWriteOpen(&bzerr,fp,blockSize100k,
+ verbosity,workFactor);
+ } else {
+ bzfp = BZ2_bzReadOpen(&bzerr,fp,verbosity,smallMode,
+ unused,nUnused);
+ }
+ if (bzfp == NULL) {
+ if (fp != stdin && fp != stdout) fclose(fp);
+ return NULL;
+ }
+ return bzfp;
+}
+
+
+/*---------------------------------------------------*/
+/*--
+ open file for read or write.
+ ex) bzopen("file","w9")
+ case path="" or NULL => use stdin or stdout.
+--*/
+BZFILE * BZ_API(BZ2_bzopen)
+ ( const char *path,
+ const char *mode )
+{
+ return bzopen_or_bzdopen(path,-1,mode,/*bzopen*/0);
+}
+
+
+/*---------------------------------------------------*/
+BZFILE * BZ_API(BZ2_bzdopen)
+ ( int fd,
+ const char *mode )
+{
+ return bzopen_or_bzdopen(NULL,fd,mode,/*bzdopen*/1);
+}
+
+
+/*---------------------------------------------------*/
+int BZ_API(BZ2_bzread) (BZFILE* b, void* buf, int len )
+{
+ int bzerr, nread;
+ if (((bzFile*)b)->lastErr == BZ_STREAM_END) return 0;
+ nread = BZ2_bzRead(&bzerr,b,buf,len);
+ if (bzerr == BZ_OK || bzerr == BZ_STREAM_END) {
+ return nread;
+ } else {
+ return -1;
+ }
+}
+
+
+/*---------------------------------------------------*/
+int BZ_API(BZ2_bzwrite) (BZFILE* b, void* buf, int len )
+{
+ int bzerr;
+
+ BZ2_bzWrite(&bzerr,b,buf,len);
+ if(bzerr == BZ_OK){
+ return len;
+ }else{
+ return -1;
+ }
+}
+
+
+/*---------------------------------------------------*/
+int BZ_API(BZ2_bzflush) (BZFILE *b)
+{
+ /* do nothing now... */
+ return 0;
+}
+
+
+/*---------------------------------------------------*/
+void BZ_API(BZ2_bzclose) (BZFILE* b)
+{
+ int bzerr;
+ FILE *fp;
+
+ if (b==NULL) {return;}
+ fp = ((bzFile *)b)->handle;
+ if(((bzFile*)b)->writing){
+ BZ2_bzWriteClose(&bzerr,b,0,NULL,NULL);
+ if(bzerr != BZ_OK){
+ BZ2_bzWriteClose(NULL,b,1,NULL,NULL);
+ }
+ }else{
+ BZ2_bzReadClose(&bzerr,b);
+ }
+ if(fp!=stdin && fp!=stdout){
+ fclose(fp);
+ }
+}
+
+
+/*---------------------------------------------------*/
+/*--
+ return last error code
+--*/
+static const char *bzerrorstrings[] = {
+ "OK"
+ ,"SEQUENCE_ERROR"
+ ,"PARAM_ERROR"
+ ,"MEM_ERROR"
+ ,"DATA_ERROR"
+ ,"DATA_ERROR_MAGIC"
+ ,"IO_ERROR"
+ ,"UNEXPECTED_EOF"
+ ,"OUTBUFF_FULL"
+ ,"CONFIG_ERROR"
+ ,"???" /* for future */
+ ,"???" /* for future */
+ ,"???" /* for future */
+ ,"???" /* for future */
+ ,"???" /* for future */
+ ,"???" /* for future */
+};
+
+
+const char * BZ_API(BZ2_bzerror) (BZFILE *b, int *errnum)
+{
+ int err = ((bzFile *)b)->lastErr;
+
+ if(err>0) err = 0;
+ *errnum = err;
+ return bzerrorstrings[err*-1];
+}
+#endif
+
+
+/*-------------------------------------------------------------*/
+/*--- end bzlib.c ---*/
+/*-------------------------------------------------------------*/
diff --git a/dep/src/bzip2/bzlib.h b/dep/src/bzip2/bzlib.h
new file mode 100644
index 00000000000..c5b75d6d8ff
--- /dev/null
+++ b/dep/src/bzip2/bzlib.h
@@ -0,0 +1,282 @@
+
+/*-------------------------------------------------------------*/
+/*--- Public header file for the library. ---*/
+/*--- bzlib.h ---*/
+/*-------------------------------------------------------------*/
+
+/* ------------------------------------------------------------------
+ This file is part of bzip2/libbzip2, a program and library for
+ lossless, block-sorting data compression.
+
+ bzip2/libbzip2 version 1.0.5 of 10 December 2007
+ Copyright (C) 1996-2007 Julian Seward <jseward@bzip.org>
+
+ Please read the WARNING, DISCLAIMER and PATENTS sections in the
+ README file.
+
+ This program is released under the terms of the license contained
+ in the file LICENSE.
+ ------------------------------------------------------------------ */
+
+
+#ifndef _BZLIB_H
+#define _BZLIB_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define BZ_RUN 0
+#define BZ_FLUSH 1
+#define BZ_FINISH 2
+
+#define BZ_OK 0
+#define BZ_RUN_OK 1
+#define BZ_FLUSH_OK 2
+#define BZ_FINISH_OK 3
+#define BZ_STREAM_END 4
+#define BZ_SEQUENCE_ERROR (-1)
+#define BZ_PARAM_ERROR (-2)
+#define BZ_MEM_ERROR (-3)
+#define BZ_DATA_ERROR (-4)
+#define BZ_DATA_ERROR_MAGIC (-5)
+#define BZ_IO_ERROR (-6)
+#define BZ_UNEXPECTED_EOF (-7)
+#define BZ_OUTBUFF_FULL (-8)
+#define BZ_CONFIG_ERROR (-9)
+
+typedef
+ struct {
+ char *next_in;
+ unsigned int avail_in;
+ unsigned int total_in_lo32;
+ unsigned int total_in_hi32;
+
+ char *next_out;
+ unsigned int avail_out;
+ unsigned int total_out_lo32;
+ unsigned int total_out_hi32;
+
+ void *state;
+
+ void *(*bzalloc)(void *,int,int);
+ void (*bzfree)(void *,void *);
+ void *opaque;
+ }
+ bz_stream;
+
+
+#ifndef BZ_IMPORT
+#define BZ_EXPORT
+#endif
+
+#ifndef BZ_NO_STDIO
+/* Need a definitition for FILE */
+#include <stdio.h>
+#endif
+
+#ifdef _WIN32
+# include <windows.h>
+# ifdef small
+ /* windows.h define small to char */
+# undef small
+# endif
+# ifdef BZ_EXPORT
+# define BZ_API(func) WINAPI func
+# define BZ_EXTERN extern
+# else
+ /* import windows dll dynamically */
+# define BZ_API(func) (WINAPI * func)
+# define BZ_EXTERN
+# endif
+#else
+# define BZ_API(func) func
+# define BZ_EXTERN extern
+#endif
+
+
+/*-- Core (low-level) library functions --*/
+
+BZ_EXTERN int BZ_API(BZ2_bzCompressInit) (
+ bz_stream* strm,
+ int blockSize100k,
+ int verbosity,
+ int workFactor
+ );
+
+BZ_EXTERN int BZ_API(BZ2_bzCompress) (
+ bz_stream* strm,
+ int action
+ );
+
+BZ_EXTERN int BZ_API(BZ2_bzCompressEnd) (
+ bz_stream* strm
+ );
+
+BZ_EXTERN int BZ_API(BZ2_bzDecompressInit) (
+ bz_stream *strm,
+ int verbosity,
+ int small
+ );
+
+BZ_EXTERN int BZ_API(BZ2_bzDecompress) (
+ bz_stream* strm
+ );
+
+BZ_EXTERN int BZ_API(BZ2_bzDecompressEnd) (
+ bz_stream *strm
+ );
+
+
+
+/*-- High(er) level library functions --*/
+
+#ifndef BZ_NO_STDIO
+#define BZ_MAX_UNUSED 5000
+
+typedef void BZFILE;
+
+BZ_EXTERN BZFILE* BZ_API(BZ2_bzReadOpen) (
+ int* bzerror,
+ FILE* f,
+ int verbosity,
+ int small,
+ void* unused,
+ int nUnused
+ );
+
+BZ_EXTERN void BZ_API(BZ2_bzReadClose) (
+ int* bzerror,
+ BZFILE* b
+ );
+
+BZ_EXTERN void BZ_API(BZ2_bzReadGetUnused) (
+ int* bzerror,
+ BZFILE* b,
+ void** unused,
+ int* nUnused
+ );
+
+BZ_EXTERN int BZ_API(BZ2_bzRead) (
+ int* bzerror,
+ BZFILE* b,
+ void* buf,
+ int len
+ );
+
+BZ_EXTERN BZFILE* BZ_API(BZ2_bzWriteOpen) (
+ int* bzerror,
+ FILE* f,
+ int blockSize100k,
+ int verbosity,
+ int workFactor
+ );
+
+BZ_EXTERN void BZ_API(BZ2_bzWrite) (
+ int* bzerror,
+ BZFILE* b,
+ void* buf,
+ int len
+ );
+
+BZ_EXTERN void BZ_API(BZ2_bzWriteClose) (
+ int* bzerror,
+ BZFILE* b,
+ int abandon,
+ unsigned int* nbytes_in,
+ unsigned int* nbytes_out
+ );
+
+BZ_EXTERN void BZ_API(BZ2_bzWriteClose64) (
+ int* bzerror,
+ BZFILE* b,
+ int abandon,
+ unsigned int* nbytes_in_lo32,
+ unsigned int* nbytes_in_hi32,
+ unsigned int* nbytes_out_lo32,
+ unsigned int* nbytes_out_hi32
+ );
+#endif
+
+
+/*-- Utility functions --*/
+
+BZ_EXTERN int BZ_API(BZ2_bzBuffToBuffCompress) (
+ char* dest,
+ unsigned int* destLen,
+ char* source,
+ unsigned int sourceLen,
+ int blockSize100k,
+ int verbosity,
+ int workFactor
+ );
+
+BZ_EXTERN int BZ_API(BZ2_bzBuffToBuffDecompress) (
+ char* dest,
+ unsigned int* destLen,
+ char* source,
+ unsigned int sourceLen,
+ int small,
+ int verbosity
+ );
+
+
+/*--
+ Code contributed by Yoshioka Tsuneo (tsuneo@rr.iij4u.or.jp)
+ to support better zlib compatibility.
+ This code is not _officially_ part of libbzip2 (yet);
+ I haven't tested it, documented it, or considered the
+ threading-safeness of it.
+ If this code breaks, please contact both Yoshioka and me.
+--*/
+
+BZ_EXTERN const char * BZ_API(BZ2_bzlibVersion) (
+ void
+ );
+
+#ifndef BZ_NO_STDIO
+BZ_EXTERN BZFILE * BZ_API(BZ2_bzopen) (
+ const char *path,
+ const char *mode
+ );
+
+BZ_EXTERN BZFILE * BZ_API(BZ2_bzdopen) (
+ int fd,
+ const char *mode
+ );
+
+BZ_EXTERN int BZ_API(BZ2_bzread) (
+ BZFILE* b,
+ void* buf,
+ int len
+ );
+
+BZ_EXTERN int BZ_API(BZ2_bzwrite) (
+ BZFILE* b,
+ void* buf,
+ int len
+ );
+
+BZ_EXTERN int BZ_API(BZ2_bzflush) (
+ BZFILE* b
+ );
+
+BZ_EXTERN void BZ_API(BZ2_bzclose) (
+ BZFILE* b
+ );
+
+BZ_EXTERN const char * BZ_API(BZ2_bzerror) (
+ BZFILE *b,
+ int *errnum
+ );
+#endif
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
+
+/*-------------------------------------------------------------*/
+/*--- end bzlib.h ---*/
+/*-------------------------------------------------------------*/
diff --git a/dep/src/bzip2/bzlib_private.h b/dep/src/bzip2/bzlib_private.h
new file mode 100644
index 00000000000..23427879b18
--- /dev/null
+++ b/dep/src/bzip2/bzlib_private.h
@@ -0,0 +1,509 @@
+
+/*-------------------------------------------------------------*/
+/*--- Private header file for the library. ---*/
+/*--- bzlib_private.h ---*/
+/*-------------------------------------------------------------*/
+
+/* ------------------------------------------------------------------
+ This file is part of bzip2/libbzip2, a program and library for
+ lossless, block-sorting data compression.
+
+ bzip2/libbzip2 version 1.0.5 of 10 December 2007
+ Copyright (C) 1996-2007 Julian Seward <jseward@bzip.org>
+
+ Please read the WARNING, DISCLAIMER and PATENTS sections in the
+ README file.
+
+ This program is released under the terms of the license contained
+ in the file LICENSE.
+ ------------------------------------------------------------------ */
+
+
+#ifndef _BZLIB_PRIVATE_H
+#define _BZLIB_PRIVATE_H
+
+#include <stdlib.h>
+
+#ifndef BZ_NO_STDIO
+#include <stdio.h>
+#include <ctype.h>
+#include <string.h>
+#endif
+
+#include "bzlib.h"
+
+
+
+/*-- General stuff. --*/
+
+#define BZ_VERSION "1.0.5, 10-Dec-2007"
+
+typedef char Char;
+typedef unsigned char Bool;
+typedef unsigned char UChar;
+typedef int Int32;
+typedef unsigned int UInt32;
+typedef short Int16;
+typedef unsigned short UInt16;
+
+#define True ((Bool)1)
+#define False ((Bool)0)
+
+#ifndef __GNUC__
+#define __inline__ /* */
+#endif
+
+#ifndef BZ_NO_STDIO
+
+extern void BZ2_bz__AssertH__fail ( int errcode );
+#define AssertH(cond,errcode) \
+ { if (!(cond)) BZ2_bz__AssertH__fail ( errcode ); }
+
+#if BZ_DEBUG
+#define AssertD(cond,msg) \
+ { if (!(cond)) { \
+ fprintf ( stderr, \
+ "\n\nlibbzip2(debug build): internal error\n\t%s\n", msg );\
+ exit(1); \
+ }}
+#else
+#define AssertD(cond,msg) /* */
+#endif
+
+#define VPrintf0(zf) \
+ fprintf(stderr,zf)
+#define VPrintf1(zf,za1) \
+ fprintf(stderr,zf,za1)
+#define VPrintf2(zf,za1,za2) \
+ fprintf(stderr,zf,za1,za2)
+#define VPrintf3(zf,za1,za2,za3) \
+ fprintf(stderr,zf,za1,za2,za3)
+#define VPrintf4(zf,za1,za2,za3,za4) \
+ fprintf(stderr,zf,za1,za2,za3,za4)
+#define VPrintf5(zf,za1,za2,za3,za4,za5) \
+ fprintf(stderr,zf,za1,za2,za3,za4,za5)
+
+#else
+
+extern void bz_internal_error ( int errcode );
+#define AssertH(cond,errcode) \
+ { if (!(cond)) bz_internal_error ( errcode ); }
+#define AssertD(cond,msg) do { } while (0)
+#define VPrintf0(zf) do { } while (0)
+#define VPrintf1(zf,za1) do { } while (0)
+#define VPrintf2(zf,za1,za2) do { } while (0)
+#define VPrintf3(zf,za1,za2,za3) do { } while (0)
+#define VPrintf4(zf,za1,za2,za3,za4) do { } while (0)
+#define VPrintf5(zf,za1,za2,za3,za4,za5) do { } while (0)
+
+#endif
+
+
+#define BZALLOC(nnn) (strm->bzalloc)(strm->opaque,(nnn),1)
+#define BZFREE(ppp) (strm->bzfree)(strm->opaque,(ppp))
+
+
+/*-- Header bytes. --*/
+
+#define BZ_HDR_B 0x42 /* 'B' */
+#define BZ_HDR_Z 0x5a /* 'Z' */
+#define BZ_HDR_h 0x68 /* 'h' */
+#define BZ_HDR_0 0x30 /* '0' */
+
+/*-- Constants for the back end. --*/
+
+#define BZ_MAX_ALPHA_SIZE 258
+#define BZ_MAX_CODE_LEN 23
+
+#define BZ_RUNA 0
+#define BZ_RUNB 1
+
+#define BZ_N_GROUPS 6
+#define BZ_G_SIZE 50
+#define BZ_N_ITERS 4
+
+#define BZ_MAX_SELECTORS (2 + (900000 / BZ_G_SIZE))
+
+
+
+/*-- Stuff for randomising repetitive blocks. --*/
+
+extern Int32 BZ2_rNums[512];
+
+#define BZ_RAND_DECLS \
+ Int32 rNToGo; \
+ Int32 rTPos \
+
+#define BZ_RAND_INIT_MASK \
+ s->rNToGo = 0; \
+ s->rTPos = 0 \
+
+#define BZ_RAND_MASK ((s->rNToGo == 1) ? 1 : 0)
+
+#define BZ_RAND_UPD_MASK \
+ if (s->rNToGo == 0) { \
+ s->rNToGo = BZ2_rNums[s->rTPos]; \
+ s->rTPos++; \
+ if (s->rTPos == 512) s->rTPos = 0; \
+ } \
+ s->rNToGo--;
+
+
+
+/*-- Stuff for doing CRCs. --*/
+
+extern UInt32 BZ2_crc32Table[256];
+
+#define BZ_INITIALISE_CRC(crcVar) \
+{ \
+ crcVar = 0xffffffffL; \
+}
+
+#define BZ_FINALISE_CRC(crcVar) \
+{ \
+ crcVar = ~(crcVar); \
+}
+
+#define BZ_UPDATE_CRC(crcVar,cha) \
+{ \
+ crcVar = (crcVar << 8) ^ \
+ BZ2_crc32Table[(crcVar >> 24) ^ \
+ ((UChar)cha)]; \
+}
+
+
+
+/*-- States and modes for compression. --*/
+
+#define BZ_M_IDLE 1
+#define BZ_M_RUNNING 2
+#define BZ_M_FLUSHING 3
+#define BZ_M_FINISHING 4
+
+#define BZ_S_OUTPUT 1
+#define BZ_S_INPUT 2
+
+#define BZ_N_RADIX 2
+#define BZ_N_QSORT 12
+#define BZ_N_SHELL 18
+#define BZ_N_OVERSHOOT (BZ_N_RADIX + BZ_N_QSORT + BZ_N_SHELL + 2)
+
+
+
+
+/*-- Structure holding all the compression-side stuff. --*/
+
+typedef
+ struct {
+ /* pointer back to the struct bz_stream */
+ bz_stream* strm;
+
+ /* mode this stream is in, and whether inputting */
+ /* or outputting data */
+ Int32 mode;
+ Int32 state;
+
+ /* remembers avail_in when flush/finish requested */
+ UInt32 avail_in_expect;
+
+ /* for doing the block sorting */
+ UInt32* arr1;
+ UInt32* arr2;
+ UInt32* ftab;
+ Int32 origPtr;
+
+ /* aliases for arr1 and arr2 */
+ UInt32* ptr;
+ UChar* block;
+ UInt16* mtfv;
+ UChar* zbits;
+
+ /* for deciding when to use the fallback sorting algorithm */
+ Int32 workFactor;
+
+ /* run-length-encoding of the input */
+ UInt32 state_in_ch;
+ Int32 state_in_len;
+ BZ_RAND_DECLS;
+
+ /* input and output limits and current posns */
+ Int32 nblock;
+ Int32 nblockMAX;
+ Int32 numZ;
+ Int32 state_out_pos;
+
+ /* map of bytes used in block */
+ Int32 nInUse;
+ Bool inUse[256];
+ UChar unseqToSeq[256];
+
+ /* the buffer for bit stream creation */
+ UInt32 bsBuff;
+ Int32 bsLive;
+
+ /* block and combined CRCs */
+ UInt32 blockCRC;
+ UInt32 combinedCRC;
+
+ /* misc administratium */
+ Int32 verbosity;
+ Int32 blockNo;
+ Int32 blockSize100k;
+
+ /* stuff for coding the MTF values */
+ Int32 nMTF;
+ Int32 mtfFreq [BZ_MAX_ALPHA_SIZE];
+ UChar selector [BZ_MAX_SELECTORS];
+ UChar selectorMtf[BZ_MAX_SELECTORS];
+
+ UChar len [BZ_N_GROUPS][BZ_MAX_ALPHA_SIZE];
+ Int32 code [BZ_N_GROUPS][BZ_MAX_ALPHA_SIZE];
+ Int32 rfreq [BZ_N_GROUPS][BZ_MAX_ALPHA_SIZE];
+ /* second dimension: only 3 needed; 4 makes index calculations faster */
+ UInt32 len_pack[BZ_MAX_ALPHA_SIZE][4];
+
+ }
+ EState;
+
+
+
+/*-- externs for compression. --*/
+
+extern void
+BZ2_blockSort ( EState* );
+
+extern void
+BZ2_compressBlock ( EState*, Bool );
+
+extern void
+BZ2_bsInitWrite ( EState* );
+
+extern void
+BZ2_hbAssignCodes ( Int32*, UChar*, Int32, Int32, Int32 );
+
+extern void
+BZ2_hbMakeCodeLengths ( UChar*, Int32*, Int32, Int32 );
+
+
+
+/*-- states for decompression. --*/
+
+#define BZ_X_IDLE 1
+#define BZ_X_OUTPUT 2
+
+#define BZ_X_MAGIC_1 10
+#define BZ_X_MAGIC_2 11
+#define BZ_X_MAGIC_3 12
+#define BZ_X_MAGIC_4 13
+#define BZ_X_BLKHDR_1 14
+#define BZ_X_BLKHDR_2 15
+#define BZ_X_BLKHDR_3 16
+#define BZ_X_BLKHDR_4 17
+#define BZ_X_BLKHDR_5 18
+#define BZ_X_BLKHDR_6 19
+#define BZ_X_BCRC_1 20
+#define BZ_X_BCRC_2 21
+#define BZ_X_BCRC_3 22
+#define BZ_X_BCRC_4 23
+#define BZ_X_RANDBIT 24
+#define BZ_X_ORIGPTR_1 25
+#define BZ_X_ORIGPTR_2 26
+#define BZ_X_ORIGPTR_3 27
+#define BZ_X_MAPPING_1 28
+#define BZ_X_MAPPING_2 29
+#define BZ_X_SELECTOR_1 30
+#define BZ_X_SELECTOR_2 31
+#define BZ_X_SELECTOR_3 32
+#define BZ_X_CODING_1 33
+#define BZ_X_CODING_2 34
+#define BZ_X_CODING_3 35
+#define BZ_X_MTF_1 36
+#define BZ_X_MTF_2 37
+#define BZ_X_MTF_3 38
+#define BZ_X_MTF_4 39
+#define BZ_X_MTF_5 40
+#define BZ_X_MTF_6 41
+#define BZ_X_ENDHDR_2 42
+#define BZ_X_ENDHDR_3 43
+#define BZ_X_ENDHDR_4 44
+#define BZ_X_ENDHDR_5 45
+#define BZ_X_ENDHDR_6 46
+#define BZ_X_CCRC_1 47
+#define BZ_X_CCRC_2 48
+#define BZ_X_CCRC_3 49
+#define BZ_X_CCRC_4 50
+
+
+
+/*-- Constants for the fast MTF decoder. --*/
+
+#define MTFA_SIZE 4096
+#define MTFL_SIZE 16
+
+
+
+/*-- Structure holding all the decompression-side stuff. --*/
+
+typedef
+ struct {
+ /* pointer back to the struct bz_stream */
+ bz_stream* strm;
+
+ /* state indicator for this stream */
+ Int32 state;
+
+ /* for doing the final run-length decoding */
+ UChar state_out_ch;
+ Int32 state_out_len;
+ Bool blockRandomised;
+ BZ_RAND_DECLS;
+
+ /* the buffer for bit stream reading */
+ UInt32 bsBuff;
+ Int32 bsLive;
+
+ /* misc administratium */
+ Int32 blockSize100k;
+ Bool smallDecompress;
+ Int32 currBlockNo;
+ Int32 verbosity;
+
+ /* for undoing the Burrows-Wheeler transform */
+ Int32 origPtr;
+ UInt32 tPos;
+ Int32 k0;
+ Int32 unzftab[256];
+ Int32 nblock_used;
+ Int32 cftab[257];
+ Int32 cftabCopy[257];
+
+ /* for undoing the Burrows-Wheeler transform (FAST) */
+ UInt32 *tt;
+
+ /* for undoing the Burrows-Wheeler transform (SMALL) */
+ UInt16 *ll16;
+ UChar *ll4;
+
+ /* stored and calculated CRCs */
+ UInt32 storedBlockCRC;
+ UInt32 storedCombinedCRC;
+ UInt32 calculatedBlockCRC;
+ UInt32 calculatedCombinedCRC;
+
+ /* map of bytes used in block */
+ Int32 nInUse;
+ Bool inUse[256];
+ Bool inUse16[16];
+ UChar seqToUnseq[256];
+
+ /* for decoding the MTF values */
+ UChar mtfa [MTFA_SIZE];
+ Int32 mtfbase[256 / MTFL_SIZE];
+ UChar selector [BZ_MAX_SELECTORS];
+ UChar selectorMtf[BZ_MAX_SELECTORS];
+ UChar len [BZ_N_GROUPS][BZ_MAX_ALPHA_SIZE];
+
+ Int32 limit [BZ_N_GROUPS][BZ_MAX_ALPHA_SIZE];
+ Int32 base [BZ_N_GROUPS][BZ_MAX_ALPHA_SIZE];
+ Int32 perm [BZ_N_GROUPS][BZ_MAX_ALPHA_SIZE];
+ Int32 minLens[BZ_N_GROUPS];
+
+ /* save area for scalars in the main decompress code */
+ Int32 save_i;
+ Int32 save_j;
+ Int32 save_t;
+ Int32 save_alphaSize;
+ Int32 save_nGroups;
+ Int32 save_nSelectors;
+ Int32 save_EOB;
+ Int32 save_groupNo;
+ Int32 save_groupPos;
+ Int32 save_nextSym;
+ Int32 save_nblockMAX;
+ Int32 save_nblock;
+ Int32 save_es;
+ Int32 save_N;
+ Int32 save_curr;
+ Int32 save_zt;
+ Int32 save_zn;
+ Int32 save_zvec;
+ Int32 save_zj;
+ Int32 save_gSel;
+ Int32 save_gMinlen;
+ Int32* save_gLimit;
+ Int32* save_gBase;
+ Int32* save_gPerm;
+
+ }
+ DState;
+
+
+
+/*-- Macros for decompression. --*/
+
+#define BZ_GET_FAST(cccc) \
+ /* c_tPos is unsigned, hence test < 0 is pointless. */ \
+ if (s->tPos >= (UInt32)100000 * (UInt32)s->blockSize100k) return True; \
+ s->tPos = s->tt[s->tPos]; \
+ cccc = (UChar)(s->tPos & 0xff); \
+ s->tPos >>= 8;
+
+#define BZ_GET_FAST_C(cccc) \
+ /* c_tPos is unsigned, hence test < 0 is pointless. */ \
+ if (c_tPos >= (UInt32)100000 * (UInt32)ro_blockSize100k) return True; \
+ c_tPos = c_tt[c_tPos]; \
+ cccc = (UChar)(c_tPos & 0xff); \
+ c_tPos >>= 8;
+
+#define SET_LL4(i,n) \
+ { if (((i) & 0x1) == 0) \
+ s->ll4[(i) >> 1] = (s->ll4[(i) >> 1] & 0xf0) | (n); else \
+ s->ll4[(i) >> 1] = (s->ll4[(i) >> 1] & 0x0f) | ((n) << 4); \
+ }
+
+#define GET_LL4(i) \
+ ((((UInt32)(s->ll4[(i) >> 1])) >> (((i) << 2) & 0x4)) & 0xF)
+
+#define SET_LL(i,n) \
+ { s->ll16[i] = (UInt16)(n & 0x0000ffff); \
+ SET_LL4(i, n >> 16); \
+ }
+
+#define GET_LL(i) \
+ (((UInt32)s->ll16[i]) | (GET_LL4(i) << 16))
+
+#define BZ_GET_SMALL(cccc) \
+ /* c_tPos is unsigned, hence test < 0 is pointless. */ \
+ if (s->tPos >= (UInt32)100000 * (UInt32)s->blockSize100k) return True; \
+ cccc = BZ2_indexIntoF ( s->tPos, s->cftab ); \
+ s->tPos = GET_LL(s->tPos);
+
+
+/*-- externs for decompression. --*/
+
+extern Int32
+BZ2_indexIntoF ( Int32, Int32* );
+
+extern Int32
+BZ2_decompress ( DState* );
+
+extern void
+BZ2_hbCreateDecodeTables ( Int32*, Int32*, Int32*, UChar*,
+ Int32, Int32, Int32 );
+
+
+#endif
+
+
+/*-- BZ_NO_STDIO seems to make NULL disappear on some platforms. --*/
+
+#ifdef BZ_NO_STDIO
+#ifndef NULL
+#define NULL 0
+#endif
+#endif
+
+
+/*-------------------------------------------------------------*/
+/*--- end bzlib_private.h ---*/
+/*-------------------------------------------------------------*/
diff --git a/dep/src/bzip2/compress.c b/dep/src/bzip2/compress.c
new file mode 100644
index 00000000000..8c80a079700
--- /dev/null
+++ b/dep/src/bzip2/compress.c
@@ -0,0 +1,672 @@
+
+/*-------------------------------------------------------------*/
+/*--- Compression machinery (not incl block sorting) ---*/
+/*--- compress.c ---*/
+/*-------------------------------------------------------------*/
+
+/* ------------------------------------------------------------------
+ This file is part of bzip2/libbzip2, a program and library for
+ lossless, block-sorting data compression.
+
+ bzip2/libbzip2 version 1.0.5 of 10 December 2007
+ Copyright (C) 1996-2007 Julian Seward <jseward@bzip.org>
+
+ Please read the WARNING, DISCLAIMER and PATENTS sections in the
+ README file.
+
+ This program is released under the terms of the license contained
+ in the file LICENSE.
+ ------------------------------------------------------------------ */
+
+
+/* CHANGES
+ 0.9.0 -- original version.
+ 0.9.0a/b -- no changes in this file.
+ 0.9.0c -- changed setting of nGroups in sendMTFValues()
+ so as to do a bit better on small files
+*/
+
+#include "bzlib_private.h"
+
+
+/*---------------------------------------------------*/
+/*--- Bit stream I/O ---*/
+/*---------------------------------------------------*/
+
+/*---------------------------------------------------*/
+void BZ2_bsInitWrite ( EState* s )
+{
+ s->bsLive = 0;
+ s->bsBuff = 0;
+}
+
+
+/*---------------------------------------------------*/
+static
+void bsFinishWrite ( EState* s )
+{
+ while (s->bsLive > 0) {
+ s->zbits[s->numZ] = (UChar)(s->bsBuff >> 24);
+ s->numZ++;
+ s->bsBuff <<= 8;
+ s->bsLive -= 8;
+ }
+}
+
+
+/*---------------------------------------------------*/
+#define bsNEEDW(nz) \
+{ \
+ while (s->bsLive >= 8) { \
+ s->zbits[s->numZ] \
+ = (UChar)(s->bsBuff >> 24); \
+ s->numZ++; \
+ s->bsBuff <<= 8; \
+ s->bsLive -= 8; \
+ } \
+}
+
+
+/*---------------------------------------------------*/
+static
+__inline__
+void bsW ( EState* s, Int32 n, UInt32 v )
+{
+ bsNEEDW ( n );
+ s->bsBuff |= (v << (32 - s->bsLive - n));
+ s->bsLive += n;
+}
+
+
+/*---------------------------------------------------*/
+static
+void bsPutUInt32 ( EState* s, UInt32 u )
+{
+ bsW ( s, 8, (u >> 24) & 0xffL );
+ bsW ( s, 8, (u >> 16) & 0xffL );
+ bsW ( s, 8, (u >> 8) & 0xffL );
+ bsW ( s, 8, u & 0xffL );
+}
+
+
+/*---------------------------------------------------*/
+static
+void bsPutUChar ( EState* s, UChar c )
+{
+ bsW( s, 8, (UInt32)c );
+}
+
+
+/*---------------------------------------------------*/
+/*--- The back end proper ---*/
+/*---------------------------------------------------*/
+
+/*---------------------------------------------------*/
+static
+void makeMaps_e ( EState* s )
+{
+ Int32 i;
+ s->nInUse = 0;
+ for (i = 0; i < 256; i++)
+ if (s->inUse[i]) {
+ s->unseqToSeq[i] = s->nInUse;
+ s->nInUse++;
+ }
+}
+
+
+/*---------------------------------------------------*/
+static
+void generateMTFValues ( EState* s )
+{
+ UChar yy[256];
+ Int32 i, j;
+ Int32 zPend;
+ Int32 wr;
+ Int32 EOB;
+
+ /*
+ After sorting (eg, here),
+ s->arr1 [ 0 .. s->nblock-1 ] holds sorted order,
+ and
+ ((UChar*)s->arr2) [ 0 .. s->nblock-1 ]
+ holds the original block data.
+
+ The first thing to do is generate the MTF values,
+ and put them in
+ ((UInt16*)s->arr1) [ 0 .. s->nblock-1 ].
+ Because there are strictly fewer or equal MTF values
+ than block values, ptr values in this area are overwritten
+ with MTF values only when they are no longer needed.
+
+ The final compressed bitstream is generated into the
+ area starting at
+ (UChar*) (&((UChar*)s->arr2)[s->nblock])
+
+ These storage aliases are set up in bzCompressInit(),
+ except for the last one, which is arranged in
+ compressBlock().
+ */
+ UInt32* ptr = s->ptr;
+ UChar* block = s->block;
+ UInt16* mtfv = s->mtfv;
+
+ makeMaps_e ( s );
+ EOB = s->nInUse+1;
+
+ for (i = 0; i <= EOB; i++) s->mtfFreq[i] = 0;
+
+ wr = 0;
+ zPend = 0;
+ for (i = 0; i < s->nInUse; i++) yy[i] = (UChar) i;
+
+ for (i = 0; i < s->nblock; i++) {
+ UChar ll_i;
+ AssertD ( wr <= i, "generateMTFValues(1)" );
+ j = ptr[i]-1; if (j < 0) j += s->nblock;
+ ll_i = s->unseqToSeq[block[j]];
+ AssertD ( ll_i < s->nInUse, "generateMTFValues(2a)" );
+
+ if (yy[0] == ll_i) {
+ zPend++;
+ } else {
+
+ if (zPend > 0) {
+ zPend--;
+ while (True) {
+ if (zPend & 1) {
+ mtfv[wr] = BZ_RUNB; wr++;
+ s->mtfFreq[BZ_RUNB]++;
+ } else {
+ mtfv[wr] = BZ_RUNA; wr++;
+ s->mtfFreq[BZ_RUNA]++;
+ }
+ if (zPend < 2) break;
+ zPend = (zPend - 2) / 2;
+ };
+ zPend = 0;
+ }
+ {
+ register UChar rtmp;
+ register UChar* ryy_j;
+ register UChar rll_i;
+ rtmp = yy[1];
+ yy[1] = yy[0];
+ ryy_j = &(yy[1]);
+ rll_i = ll_i;
+ while ( rll_i != rtmp ) {
+ register UChar rtmp2;
+ ryy_j++;
+ rtmp2 = rtmp;
+ rtmp = *ryy_j;
+ *ryy_j = rtmp2;
+ };
+ yy[0] = rtmp;
+ j = ryy_j - &(yy[0]);
+ mtfv[wr] = j+1; wr++; s->mtfFreq[j+1]++;
+ }
+
+ }
+ }
+
+ if (zPend > 0) {
+ zPend--;
+ while (True) {
+ if (zPend & 1) {
+ mtfv[wr] = BZ_RUNB; wr++;
+ s->mtfFreq[BZ_RUNB]++;
+ } else {
+ mtfv[wr] = BZ_RUNA; wr++;
+ s->mtfFreq[BZ_RUNA]++;
+ }
+ if (zPend < 2) break;
+ zPend = (zPend - 2) / 2;
+ };
+ zPend = 0;
+ }
+
+ mtfv[wr] = EOB; wr++; s->mtfFreq[EOB]++;
+
+ s->nMTF = wr;
+}
+
+
+/*---------------------------------------------------*/
+#define BZ_LESSER_ICOST 0
+#define BZ_GREATER_ICOST 15
+
+static
+void sendMTFValues ( EState* s )
+{
+ Int32 v, t, i, j, gs, ge, totc, bt, bc, iter;
+ Int32 nSelectors, alphaSize, minLen, maxLen, selCtr;
+ Int32 nGroups, nBytes;
+
+ /*--
+ UChar len [BZ_N_GROUPS][BZ_MAX_ALPHA_SIZE];
+ is a global since the decoder also needs it.
+
+ Int32 code[BZ_N_GROUPS][BZ_MAX_ALPHA_SIZE];
+ Int32 rfreq[BZ_N_GROUPS][BZ_MAX_ALPHA_SIZE];
+ are also globals only used in this proc.
+ Made global to keep stack frame size small.
+ --*/
+
+
+ UInt16 cost[BZ_N_GROUPS];
+ Int32 fave[BZ_N_GROUPS];
+
+ UInt16* mtfv = s->mtfv;
+
+ if (s->verbosity >= 3)
+ VPrintf3( " %d in block, %d after MTF & 1-2 coding, "
+ "%d+2 syms in use\n",
+ s->nblock, s->nMTF, s->nInUse );
+
+ alphaSize = s->nInUse+2;
+ for (t = 0; t < BZ_N_GROUPS; t++)
+ for (v = 0; v < alphaSize; v++)
+ s->len[t][v] = BZ_GREATER_ICOST;
+
+ /*--- Decide how many coding tables to use ---*/
+ AssertH ( s->nMTF > 0, 3001 );
+ if (s->nMTF < 200) nGroups = 2; else
+ if (s->nMTF < 600) nGroups = 3; else
+ if (s->nMTF < 1200) nGroups = 4; else
+ if (s->nMTF < 2400) nGroups = 5; else
+ nGroups = 6;
+
+ /*--- Generate an initial set of coding tables ---*/
+ {
+ Int32 nPart, remF, tFreq, aFreq;
+
+ nPart = nGroups;
+ remF = s->nMTF;
+ gs = 0;
+ while (nPart > 0) {
+ tFreq = remF / nPart;
+ ge = gs-1;
+ aFreq = 0;
+ while (aFreq < tFreq && ge < alphaSize-1) {
+ ge++;
+ aFreq += s->mtfFreq[ge];
+ }
+
+ if (ge > gs
+ && nPart != nGroups && nPart != 1
+ && ((nGroups-nPart) % 2 == 1)) {
+ aFreq -= s->mtfFreq[ge];
+ ge--;
+ }
+
+ if (s->verbosity >= 3)
+ VPrintf5( " initial group %d, [%d .. %d], "
+ "has %d syms (%4.1f%%)\n",
+ nPart, gs, ge, aFreq,
+ (100.0 * (float)aFreq) / (float)(s->nMTF) );
+
+ for (v = 0; v < alphaSize; v++)
+ if (v >= gs && v <= ge)
+ s->len[nPart-1][v] = BZ_LESSER_ICOST; else
+ s->len[nPart-1][v] = BZ_GREATER_ICOST;
+
+ nPart--;
+ gs = ge+1;
+ remF -= aFreq;
+ }
+ }
+
+ /*---
+ Iterate up to BZ_N_ITERS times to improve the tables.
+ ---*/
+ for (iter = 0; iter < BZ_N_ITERS; iter++) {
+
+ for (t = 0; t < nGroups; t++) fave[t] = 0;
+
+ for (t = 0; t < nGroups; t++)
+ for (v = 0; v < alphaSize; v++)
+ s->rfreq[t][v] = 0;
+
+ /*---
+ Set up an auxiliary length table which is used to fast-track
+ the common case (nGroups == 6).
+ ---*/
+ if (nGroups == 6) {
+ for (v = 0; v < alphaSize; v++) {
+ s->len_pack[v][0] = (s->len[1][v] << 16) | s->len[0][v];
+ s->len_pack[v][1] = (s->len[3][v] << 16) | s->len[2][v];
+ s->len_pack[v][2] = (s->len[5][v] << 16) | s->len[4][v];
+ }
+ }
+
+ nSelectors = 0;
+ totc = 0;
+ gs = 0;
+ while (True) {
+
+ /*--- Set group start & end marks. --*/
+ if (gs >= s->nMTF) break;
+ ge = gs + BZ_G_SIZE - 1;
+ if (ge >= s->nMTF) ge = s->nMTF-1;
+
+ /*--
+ Calculate the cost of this group as coded
+ by each of the coding tables.
+ --*/
+ for (t = 0; t < nGroups; t++) cost[t] = 0;
+
+ if (nGroups == 6 && 50 == ge-gs+1) {
+ /*--- fast track the common case ---*/
+ register UInt32 cost01, cost23, cost45;
+ register UInt16 icv;
+ cost01 = cost23 = cost45 = 0;
+
+# define BZ_ITER(nn) \
+ icv = mtfv[gs+(nn)]; \
+ cost01 += s->len_pack[icv][0]; \
+ cost23 += s->len_pack[icv][1]; \
+ cost45 += s->len_pack[icv][2]; \
+
+ BZ_ITER(0); BZ_ITER(1); BZ_ITER(2); BZ_ITER(3); BZ_ITER(4);
+ BZ_ITER(5); BZ_ITER(6); BZ_ITER(7); BZ_ITER(8); BZ_ITER(9);
+ BZ_ITER(10); BZ_ITER(11); BZ_ITER(12); BZ_ITER(13); BZ_ITER(14);
+ BZ_ITER(15); BZ_ITER(16); BZ_ITER(17); BZ_ITER(18); BZ_ITER(19);
+ BZ_ITER(20); BZ_ITER(21); BZ_ITER(22); BZ_ITER(23); BZ_ITER(24);
+ BZ_ITER(25); BZ_ITER(26); BZ_ITER(27); BZ_ITER(28); BZ_ITER(29);
+ BZ_ITER(30); BZ_ITER(31); BZ_ITER(32); BZ_ITER(33); BZ_ITER(34);
+ BZ_ITER(35); BZ_ITER(36); BZ_ITER(37); BZ_ITER(38); BZ_ITER(39);
+ BZ_ITER(40); BZ_ITER(41); BZ_ITER(42); BZ_ITER(43); BZ_ITER(44);
+ BZ_ITER(45); BZ_ITER(46); BZ_ITER(47); BZ_ITER(48); BZ_ITER(49);
+
+# undef BZ_ITER
+
+ cost[0] = cost01 & 0xffff; cost[1] = cost01 >> 16;
+ cost[2] = cost23 & 0xffff; cost[3] = cost23 >> 16;
+ cost[4] = cost45 & 0xffff; cost[5] = cost45 >> 16;
+
+ } else {
+ /*--- slow version which correctly handles all situations ---*/
+ for (i = gs; i <= ge; i++) {
+ UInt16 icv = mtfv[i];
+ for (t = 0; t < nGroups; t++) cost[t] += s->len[t][icv];
+ }
+ }
+
+ /*--
+ Find the coding table which is best for this group,
+ and record its identity in the selector table.
+ --*/
+ bc = 999999999; bt = -1;
+ for (t = 0; t < nGroups; t++)
+ if (cost[t] < bc) { bc = cost[t]; bt = t; };
+ totc += bc;
+ fave[bt]++;
+ s->selector[nSelectors] = bt;
+ nSelectors++;
+
+ /*--
+ Increment the symbol frequencies for the selected table.
+ --*/
+ if (nGroups == 6 && 50 == ge-gs+1) {
+ /*--- fast track the common case ---*/
+
+# define BZ_ITUR(nn) s->rfreq[bt][ mtfv[gs+(nn)] ]++
+
+ BZ_ITUR(0); BZ_ITUR(1); BZ_ITUR(2); BZ_ITUR(3); BZ_ITUR(4);
+ BZ_ITUR(5); BZ_ITUR(6); BZ_ITUR(7); BZ_ITUR(8); BZ_ITUR(9);
+ BZ_ITUR(10); BZ_ITUR(11); BZ_ITUR(12); BZ_ITUR(13); BZ_ITUR(14);
+ BZ_ITUR(15); BZ_ITUR(16); BZ_ITUR(17); BZ_ITUR(18); BZ_ITUR(19);
+ BZ_ITUR(20); BZ_ITUR(21); BZ_ITUR(22); BZ_ITUR(23); BZ_ITUR(24);
+ BZ_ITUR(25); BZ_ITUR(26); BZ_ITUR(27); BZ_ITUR(28); BZ_ITUR(29);
+ BZ_ITUR(30); BZ_ITUR(31); BZ_ITUR(32); BZ_ITUR(33); BZ_ITUR(34);
+ BZ_ITUR(35); BZ_ITUR(36); BZ_ITUR(37); BZ_ITUR(38); BZ_ITUR(39);
+ BZ_ITUR(40); BZ_ITUR(41); BZ_ITUR(42); BZ_ITUR(43); BZ_ITUR(44);
+ BZ_ITUR(45); BZ_ITUR(46); BZ_ITUR(47); BZ_ITUR(48); BZ_ITUR(49);
+
+# undef BZ_ITUR
+
+ } else {
+ /*--- slow version which correctly handles all situations ---*/
+ for (i = gs; i <= ge; i++)
+ s->rfreq[bt][ mtfv[i] ]++;
+ }
+
+ gs = ge+1;
+ }
+ if (s->verbosity >= 3) {
+ VPrintf2 ( " pass %d: size is %d, grp uses are ",
+ iter+1, totc/8 );
+ for (t = 0; t < nGroups; t++)
+ VPrintf1 ( "%d ", fave[t] );
+ VPrintf0 ( "\n" );
+ }
+
+ /*--
+ Recompute the tables based on the accumulated frequencies.
+ --*/
+ /* maxLen was changed from 20 to 17 in bzip2-1.0.3. See
+ comment in huffman.c for details. */
+ for (t = 0; t < nGroups; t++)
+ BZ2_hbMakeCodeLengths ( &(s->len[t][0]), &(s->rfreq[t][0]),
+ alphaSize, 17 /*20*/ );
+ }
+
+
+ AssertH( nGroups < 8, 3002 );
+ AssertH( nSelectors < 32768 &&
+ nSelectors <= (2 + (900000 / BZ_G_SIZE)),
+ 3003 );
+
+
+ /*--- Compute MTF values for the selectors. ---*/
+ {
+ UChar pos[BZ_N_GROUPS], ll_i, tmp2, tmp;
+ for (i = 0; i < nGroups; i++) pos[i] = i;
+ for (i = 0; i < nSelectors; i++) {
+ ll_i = s->selector[i];
+ j = 0;
+ tmp = pos[j];
+ while ( ll_i != tmp ) {
+ j++;
+ tmp2 = tmp;
+ tmp = pos[j];
+ pos[j] = tmp2;
+ };
+ pos[0] = tmp;
+ s->selectorMtf[i] = j;
+ }
+ };
+
+ /*--- Assign actual codes for the tables. --*/
+ for (t = 0; t < nGroups; t++) {
+ minLen = 32;
+ maxLen = 0;
+ for (i = 0; i < alphaSize; i++) {
+ if (s->len[t][i] > maxLen) maxLen = s->len[t][i];
+ if (s->len[t][i] < minLen) minLen = s->len[t][i];
+ }
+ AssertH ( !(maxLen > 17 /*20*/ ), 3004 );
+ AssertH ( !(minLen < 1), 3005 );
+ BZ2_hbAssignCodes ( &(s->code[t][0]), &(s->len[t][0]),
+ minLen, maxLen, alphaSize );
+ }
+
+ /*--- Transmit the mapping table. ---*/
+ {
+ Bool inUse16[16];
+ for (i = 0; i < 16; i++) {
+ inUse16[i] = False;
+ for (j = 0; j < 16; j++)
+ if (s->inUse[i * 16 + j]) inUse16[i] = True;
+ }
+
+ nBytes = s->numZ;
+ for (i = 0; i < 16; i++)
+ if (inUse16[i]) bsW(s,1,1); else bsW(s,1,0);
+
+ for (i = 0; i < 16; i++)
+ if (inUse16[i])
+ for (j = 0; j < 16; j++) {
+ if (s->inUse[i * 16 + j]) bsW(s,1,1); else bsW(s,1,0);
+ }
+
+ if (s->verbosity >= 3)
+ VPrintf1( " bytes: mapping %d, ", s->numZ-nBytes );
+ }
+
+ /*--- Now the selectors. ---*/
+ nBytes = s->numZ;
+ bsW ( s, 3, nGroups );
+ bsW ( s, 15, nSelectors );
+ for (i = 0; i < nSelectors; i++) {
+ for (j = 0; j < s->selectorMtf[i]; j++) bsW(s,1,1);
+ bsW(s,1,0);
+ }
+ if (s->verbosity >= 3)
+ VPrintf1( "selectors %d, ", s->numZ-nBytes );
+
+ /*--- Now the coding tables. ---*/
+ nBytes = s->numZ;
+
+ for (t = 0; t < nGroups; t++) {
+ Int32 curr = s->len[t][0];
+ bsW ( s, 5, curr );
+ for (i = 0; i < alphaSize; i++) {
+ while (curr < s->len[t][i]) { bsW(s,2,2); curr++; /* 10 */ };
+ while (curr > s->len[t][i]) { bsW(s,2,3); curr--; /* 11 */ };
+ bsW ( s, 1, 0 );
+ }
+ }
+
+ if (s->verbosity >= 3)
+ VPrintf1 ( "code lengths %d, ", s->numZ-nBytes );
+
+ /*--- And finally, the block data proper ---*/
+ nBytes = s->numZ;
+ selCtr = 0;
+ gs = 0;
+ while (True) {
+ if (gs >= s->nMTF) break;
+ ge = gs + BZ_G_SIZE - 1;
+ if (ge >= s->nMTF) ge = s->nMTF-1;
+ AssertH ( s->selector[selCtr] < nGroups, 3006 );
+
+ if (nGroups == 6 && 50 == ge-gs+1) {
+ /*--- fast track the common case ---*/
+ UInt16 mtfv_i;
+ UChar* s_len_sel_selCtr
+ = &(s->len[s->selector[selCtr]][0]);
+ Int32* s_code_sel_selCtr
+ = &(s->code[s->selector[selCtr]][0]);
+
+# define BZ_ITAH(nn) \
+ mtfv_i = mtfv[gs+(nn)]; \
+ bsW ( s, \
+ s_len_sel_selCtr[mtfv_i], \
+ s_code_sel_selCtr[mtfv_i] )
+
+ BZ_ITAH(0); BZ_ITAH(1); BZ_ITAH(2); BZ_ITAH(3); BZ_ITAH(4);
+ BZ_ITAH(5); BZ_ITAH(6); BZ_ITAH(7); BZ_ITAH(8); BZ_ITAH(9);
+ BZ_ITAH(10); BZ_ITAH(11); BZ_ITAH(12); BZ_ITAH(13); BZ_ITAH(14);
+ BZ_ITAH(15); BZ_ITAH(16); BZ_ITAH(17); BZ_ITAH(18); BZ_ITAH(19);
+ BZ_ITAH(20); BZ_ITAH(21); BZ_ITAH(22); BZ_ITAH(23); BZ_ITAH(24);
+ BZ_ITAH(25); BZ_ITAH(26); BZ_ITAH(27); BZ_ITAH(28); BZ_ITAH(29);
+ BZ_ITAH(30); BZ_ITAH(31); BZ_ITAH(32); BZ_ITAH(33); BZ_ITAH(34);
+ BZ_ITAH(35); BZ_ITAH(36); BZ_ITAH(37); BZ_ITAH(38); BZ_ITAH(39);
+ BZ_ITAH(40); BZ_ITAH(41); BZ_ITAH(42); BZ_ITAH(43); BZ_ITAH(44);
+ BZ_ITAH(45); BZ_ITAH(46); BZ_ITAH(47); BZ_ITAH(48); BZ_ITAH(49);
+
+# undef BZ_ITAH
+
+ } else {
+ /*--- slow version which correctly handles all situations ---*/
+ for (i = gs; i <= ge; i++) {
+ bsW ( s,
+ s->len [s->selector[selCtr]] [mtfv[i]],
+ s->code [s->selector[selCtr]] [mtfv[i]] );
+ }
+ }
+
+
+ gs = ge+1;
+ selCtr++;
+ }
+ AssertH( selCtr == nSelectors, 3007 );
+
+ if (s->verbosity >= 3)
+ VPrintf1( "codes %d\n", s->numZ-nBytes );
+}
+
+
+/*---------------------------------------------------*/
+void BZ2_compressBlock ( EState* s, Bool is_last_block )
+{
+ if (s->nblock > 0) {
+
+ BZ_FINALISE_CRC ( s->blockCRC );
+ s->combinedCRC = (s->combinedCRC << 1) | (s->combinedCRC >> 31);
+ s->combinedCRC ^= s->blockCRC;
+ if (s->blockNo > 1) s->numZ = 0;
+
+ if (s->verbosity >= 2)
+ VPrintf4( " block %d: crc = 0x%08x, "
+ "combined CRC = 0x%08x, size = %d\n",
+ s->blockNo, s->blockCRC, s->combinedCRC, s->nblock );
+
+ BZ2_blockSort ( s );
+ }
+
+ s->zbits = (UChar*) (&((UChar*)s->arr2)[s->nblock]);
+
+ /*-- If this is the first block, create the stream header. --*/
+ if (s->blockNo == 1) {
+ BZ2_bsInitWrite ( s );
+ bsPutUChar ( s, BZ_HDR_B );
+ bsPutUChar ( s, BZ_HDR_Z );
+ bsPutUChar ( s, BZ_HDR_h );
+ bsPutUChar ( s, (UChar)(BZ_HDR_0 + s->blockSize100k) );
+ }
+
+ if (s->nblock > 0) {
+
+ bsPutUChar ( s, 0x31 ); bsPutUChar ( s, 0x41 );
+ bsPutUChar ( s, 0x59 ); bsPutUChar ( s, 0x26 );
+ bsPutUChar ( s, 0x53 ); bsPutUChar ( s, 0x59 );
+
+ /*-- Now the block's CRC, so it is in a known place. --*/
+ bsPutUInt32 ( s, s->blockCRC );
+
+ /*--
+ Now a single bit indicating (non-)randomisation.
+ As of version 0.9.5, we use a better sorting algorithm
+ which makes randomisation unnecessary. So always set
+ the randomised bit to 'no'. Of course, the decoder
+ still needs to be able to handle randomised blocks
+ so as to maintain backwards compatibility with
+ older versions of bzip2.
+ --*/
+ bsW(s,1,0);
+
+ bsW ( s, 24, s->origPtr );
+ generateMTFValues ( s );
+ sendMTFValues ( s );
+ }
+
+
+ /*-- If this is the last block, add the stream trailer. --*/
+ if (is_last_block) {
+
+ bsPutUChar ( s, 0x17 ); bsPutUChar ( s, 0x72 );
+ bsPutUChar ( s, 0x45 ); bsPutUChar ( s, 0x38 );
+ bsPutUChar ( s, 0x50 ); bsPutUChar ( s, 0x90 );
+ bsPutUInt32 ( s, s->combinedCRC );
+ if (s->verbosity >= 2)
+ VPrintf1( " final combined CRC = 0x%08x\n ", s->combinedCRC );
+ bsFinishWrite ( s );
+ }
+}
+
+
+/*-------------------------------------------------------------*/
+/*--- end compress.c ---*/
+/*-------------------------------------------------------------*/
diff --git a/dep/src/bzip2/crctable.c b/dep/src/bzip2/crctable.c
new file mode 100644
index 00000000000..215687b2c05
--- /dev/null
+++ b/dep/src/bzip2/crctable.c
@@ -0,0 +1,104 @@
+
+/*-------------------------------------------------------------*/
+/*--- Table for doing CRCs ---*/
+/*--- crctable.c ---*/
+/*-------------------------------------------------------------*/
+
+/* ------------------------------------------------------------------
+ This file is part of bzip2/libbzip2, a program and library for
+ lossless, block-sorting data compression.
+
+ bzip2/libbzip2 version 1.0.5 of 10 December 2007
+ Copyright (C) 1996-2007 Julian Seward <jseward@bzip.org>
+
+ Please read the WARNING, DISCLAIMER and PATENTS sections in the
+ README file.
+
+ This program is released under the terms of the license contained
+ in the file LICENSE.
+ ------------------------------------------------------------------ */
+
+
+#include "bzlib_private.h"
+
+/*--
+ I think this is an implementation of the AUTODIN-II,
+ Ethernet & FDDI 32-bit CRC standard. Vaguely derived
+ from code by Rob Warnock, in Section 51 of the
+ comp.compression FAQ.
+--*/
+
+UInt32 BZ2_crc32Table[256] = {
+
+ /*-- Ugly, innit? --*/
+
+ 0x00000000L, 0x04c11db7L, 0x09823b6eL, 0x0d4326d9L,
+ 0x130476dcL, 0x17c56b6bL, 0x1a864db2L, 0x1e475005L,
+ 0x2608edb8L, 0x22c9f00fL, 0x2f8ad6d6L, 0x2b4bcb61L,
+ 0x350c9b64L, 0x31cd86d3L, 0x3c8ea00aL, 0x384fbdbdL,
+ 0x4c11db70L, 0x48d0c6c7L, 0x4593e01eL, 0x4152fda9L,
+ 0x5f15adacL, 0x5bd4b01bL, 0x569796c2L, 0x52568b75L,
+ 0x6a1936c8L, 0x6ed82b7fL, 0x639b0da6L, 0x675a1011L,
+ 0x791d4014L, 0x7ddc5da3L, 0x709f7b7aL, 0x745e66cdL,
+ 0x9823b6e0L, 0x9ce2ab57L, 0x91a18d8eL, 0x95609039L,
+ 0x8b27c03cL, 0x8fe6dd8bL, 0x82a5fb52L, 0x8664e6e5L,
+ 0xbe2b5b58L, 0xbaea46efL, 0xb7a96036L, 0xb3687d81L,
+ 0xad2f2d84L, 0xa9ee3033L, 0xa4ad16eaL, 0xa06c0b5dL,
+ 0xd4326d90L, 0xd0f37027L, 0xddb056feL, 0xd9714b49L,
+ 0xc7361b4cL, 0xc3f706fbL, 0xceb42022L, 0xca753d95L,
+ 0xf23a8028L, 0xf6fb9d9fL, 0xfbb8bb46L, 0xff79a6f1L,
+ 0xe13ef6f4L, 0xe5ffeb43L, 0xe8bccd9aL, 0xec7dd02dL,
+ 0x34867077L, 0x30476dc0L, 0x3d044b19L, 0x39c556aeL,
+ 0x278206abL, 0x23431b1cL, 0x2e003dc5L, 0x2ac12072L,
+ 0x128e9dcfL, 0x164f8078L, 0x1b0ca6a1L, 0x1fcdbb16L,
+ 0x018aeb13L, 0x054bf6a4L, 0x0808d07dL, 0x0cc9cdcaL,
+ 0x7897ab07L, 0x7c56b6b0L, 0x71159069L, 0x75d48ddeL,
+ 0x6b93dddbL, 0x6f52c06cL, 0x6211e6b5L, 0x66d0fb02L,
+ 0x5e9f46bfL, 0x5a5e5b08L, 0x571d7dd1L, 0x53dc6066L,
+ 0x4d9b3063L, 0x495a2dd4L, 0x44190b0dL, 0x40d816baL,
+ 0xaca5c697L, 0xa864db20L, 0xa527fdf9L, 0xa1e6e04eL,
+ 0xbfa1b04bL, 0xbb60adfcL, 0xb6238b25L, 0xb2e29692L,
+ 0x8aad2b2fL, 0x8e6c3698L, 0x832f1041L, 0x87ee0df6L,
+ 0x99a95df3L, 0x9d684044L, 0x902b669dL, 0x94ea7b2aL,
+ 0xe0b41de7L, 0xe4750050L, 0xe9362689L, 0xedf73b3eL,
+ 0xf3b06b3bL, 0xf771768cL, 0xfa325055L, 0xfef34de2L,
+ 0xc6bcf05fL, 0xc27dede8L, 0xcf3ecb31L, 0xcbffd686L,
+ 0xd5b88683L, 0xd1799b34L, 0xdc3abdedL, 0xd8fba05aL,
+ 0x690ce0eeL, 0x6dcdfd59L, 0x608edb80L, 0x644fc637L,
+ 0x7a089632L, 0x7ec98b85L, 0x738aad5cL, 0x774bb0ebL,
+ 0x4f040d56L, 0x4bc510e1L, 0x46863638L, 0x42472b8fL,
+ 0x5c007b8aL, 0x58c1663dL, 0x558240e4L, 0x51435d53L,
+ 0x251d3b9eL, 0x21dc2629L, 0x2c9f00f0L, 0x285e1d47L,
+ 0x36194d42L, 0x32d850f5L, 0x3f9b762cL, 0x3b5a6b9bL,
+ 0x0315d626L, 0x07d4cb91L, 0x0a97ed48L, 0x0e56f0ffL,
+ 0x1011a0faL, 0x14d0bd4dL, 0x19939b94L, 0x1d528623L,
+ 0xf12f560eL, 0xf5ee4bb9L, 0xf8ad6d60L, 0xfc6c70d7L,
+ 0xe22b20d2L, 0xe6ea3d65L, 0xeba91bbcL, 0xef68060bL,
+ 0xd727bbb6L, 0xd3e6a601L, 0xdea580d8L, 0xda649d6fL,
+ 0xc423cd6aL, 0xc0e2d0ddL, 0xcda1f604L, 0xc960ebb3L,
+ 0xbd3e8d7eL, 0xb9ff90c9L, 0xb4bcb610L, 0xb07daba7L,
+ 0xae3afba2L, 0xaafbe615L, 0xa7b8c0ccL, 0xa379dd7bL,
+ 0x9b3660c6L, 0x9ff77d71L, 0x92b45ba8L, 0x9675461fL,
+ 0x8832161aL, 0x8cf30badL, 0x81b02d74L, 0x857130c3L,
+ 0x5d8a9099L, 0x594b8d2eL, 0x5408abf7L, 0x50c9b640L,
+ 0x4e8ee645L, 0x4a4ffbf2L, 0x470cdd2bL, 0x43cdc09cL,
+ 0x7b827d21L, 0x7f436096L, 0x7200464fL, 0x76c15bf8L,
+ 0x68860bfdL, 0x6c47164aL, 0x61043093L, 0x65c52d24L,
+ 0x119b4be9L, 0x155a565eL, 0x18197087L, 0x1cd86d30L,
+ 0x029f3d35L, 0x065e2082L, 0x0b1d065bL, 0x0fdc1becL,
+ 0x3793a651L, 0x3352bbe6L, 0x3e119d3fL, 0x3ad08088L,
+ 0x2497d08dL, 0x2056cd3aL, 0x2d15ebe3L, 0x29d4f654L,
+ 0xc5a92679L, 0xc1683bceL, 0xcc2b1d17L, 0xc8ea00a0L,
+ 0xd6ad50a5L, 0xd26c4d12L, 0xdf2f6bcbL, 0xdbee767cL,
+ 0xe3a1cbc1L, 0xe760d676L, 0xea23f0afL, 0xeee2ed18L,
+ 0xf0a5bd1dL, 0xf464a0aaL, 0xf9278673L, 0xfde69bc4L,
+ 0x89b8fd09L, 0x8d79e0beL, 0x803ac667L, 0x84fbdbd0L,
+ 0x9abc8bd5L, 0x9e7d9662L, 0x933eb0bbL, 0x97ffad0cL,
+ 0xafb010b1L, 0xab710d06L, 0xa6322bdfL, 0xa2f33668L,
+ 0xbcb4666dL, 0xb8757bdaL, 0xb5365d03L, 0xb1f740b4L
+};
+
+
+/*-------------------------------------------------------------*/
+/*--- end crctable.c ---*/
+/*-------------------------------------------------------------*/
diff --git a/dep/src/bzip2/decompress.c b/dep/src/bzip2/decompress.c
new file mode 100644
index 00000000000..bba5e0fa36d
--- /dev/null
+++ b/dep/src/bzip2/decompress.c
@@ -0,0 +1,626 @@
+
+/*-------------------------------------------------------------*/
+/*--- Decompression machinery ---*/
+/*--- decompress.c ---*/
+/*-------------------------------------------------------------*/
+
+/* ------------------------------------------------------------------
+ This file is part of bzip2/libbzip2, a program and library for
+ lossless, block-sorting data compression.
+
+ bzip2/libbzip2 version 1.0.5 of 10 December 2007
+ Copyright (C) 1996-2007 Julian Seward <jseward@bzip.org>
+
+ Please read the WARNING, DISCLAIMER and PATENTS sections in the
+ README file.
+
+ This program is released under the terms of the license contained
+ in the file LICENSE.
+ ------------------------------------------------------------------ */
+
+
+#include "bzlib_private.h"
+
+
+/*---------------------------------------------------*/
+static
+void makeMaps_d ( DState* s )
+{
+ Int32 i;
+ s->nInUse = 0;
+ for (i = 0; i < 256; i++)
+ if (s->inUse[i]) {
+ s->seqToUnseq[s->nInUse] = i;
+ s->nInUse++;
+ }
+}
+
+
+/*---------------------------------------------------*/
+#define RETURN(rrr) \
+ { retVal = rrr; goto save_state_and_return; };
+
+#define GET_BITS(lll,vvv,nnn) \
+ case lll: s->state = lll; \
+ while (True) { \
+ if (s->bsLive >= nnn) { \
+ UInt32 v; \
+ v = (s->bsBuff >> \
+ (s->bsLive-nnn)) & ((1 << nnn)-1); \
+ s->bsLive -= nnn; \
+ vvv = v; \
+ break; \
+ } \
+ if (s->strm->avail_in == 0) RETURN(BZ_OK); \
+ s->bsBuff \
+ = (s->bsBuff << 8) | \
+ ((UInt32) \
+ (*((UChar*)(s->strm->next_in)))); \
+ s->bsLive += 8; \
+ s->strm->next_in++; \
+ s->strm->avail_in--; \
+ s->strm->total_in_lo32++; \
+ if (s->strm->total_in_lo32 == 0) \
+ s->strm->total_in_hi32++; \
+ }
+
+#define GET_UCHAR(lll,uuu) \
+ GET_BITS(lll,uuu,8)
+
+#define GET_BIT(lll,uuu) \
+ GET_BITS(lll,uuu,1)
+
+/*---------------------------------------------------*/
+#define GET_MTF_VAL(label1,label2,lval) \
+{ \
+ if (groupPos == 0) { \
+ groupNo++; \
+ if (groupNo >= nSelectors) \
+ RETURN(BZ_DATA_ERROR); \
+ groupPos = BZ_G_SIZE; \
+ gSel = s->selector[groupNo]; \
+ gMinlen = s->minLens[gSel]; \
+ gLimit = &(s->limit[gSel][0]); \
+ gPerm = &(s->perm[gSel][0]); \
+ gBase = &(s->base[gSel][0]); \
+ } \
+ groupPos--; \
+ zn = gMinlen; \
+ GET_BITS(label1, zvec, zn); \
+ while (1) { \
+ if (zn > 20 /* the longest code */) \
+ RETURN(BZ_DATA_ERROR); \
+ if (zvec <= gLimit[zn]) break; \
+ zn++; \
+ GET_BIT(label2, zj); \
+ zvec = (zvec << 1) | zj; \
+ }; \
+ if (zvec - gBase[zn] < 0 \
+ || zvec - gBase[zn] >= BZ_MAX_ALPHA_SIZE) \
+ RETURN(BZ_DATA_ERROR); \
+ lval = gPerm[zvec - gBase[zn]]; \
+}
+
+
+/*---------------------------------------------------*/
+Int32 BZ2_decompress ( DState* s )
+{
+ UChar uc;
+ Int32 retVal;
+ Int32 minLen, maxLen;
+ bz_stream* strm = s->strm;
+
+ /* stuff that needs to be saved/restored */
+ Int32 i;
+ Int32 j;
+ Int32 t;
+ Int32 alphaSize;
+ Int32 nGroups;
+ Int32 nSelectors;
+ Int32 EOB;
+ Int32 groupNo;
+ Int32 groupPos;
+ Int32 nextSym;
+ Int32 nblockMAX;
+ Int32 nblock;
+ Int32 es;
+ Int32 N;
+ Int32 curr;
+ Int32 zt;
+ Int32 zn;
+ Int32 zvec;
+ Int32 zj;
+ Int32 gSel;
+ Int32 gMinlen;
+ Int32* gLimit;
+ Int32* gBase;
+ Int32* gPerm;
+
+ if (s->state == BZ_X_MAGIC_1) {
+ /*initialise the save area*/
+ s->save_i = 0;
+ s->save_j = 0;
+ s->save_t = 0;
+ s->save_alphaSize = 0;
+ s->save_nGroups = 0;
+ s->save_nSelectors = 0;
+ s->save_EOB = 0;
+ s->save_groupNo = 0;
+ s->save_groupPos = 0;
+ s->save_nextSym = 0;
+ s->save_nblockMAX = 0;
+ s->save_nblock = 0;
+ s->save_es = 0;
+ s->save_N = 0;
+ s->save_curr = 0;
+ s->save_zt = 0;
+ s->save_zn = 0;
+ s->save_zvec = 0;
+ s->save_zj = 0;
+ s->save_gSel = 0;
+ s->save_gMinlen = 0;
+ s->save_gLimit = NULL;
+ s->save_gBase = NULL;
+ s->save_gPerm = NULL;
+ }
+
+ /*restore from the save area*/
+ i = s->save_i;
+ j = s->save_j;
+ t = s->save_t;
+ alphaSize = s->save_alphaSize;
+ nGroups = s->save_nGroups;
+ nSelectors = s->save_nSelectors;
+ EOB = s->save_EOB;
+ groupNo = s->save_groupNo;
+ groupPos = s->save_groupPos;
+ nextSym = s->save_nextSym;
+ nblockMAX = s->save_nblockMAX;
+ nblock = s->save_nblock;
+ es = s->save_es;
+ N = s->save_N;
+ curr = s->save_curr;
+ zt = s->save_zt;
+ zn = s->save_zn;
+ zvec = s->save_zvec;
+ zj = s->save_zj;
+ gSel = s->save_gSel;
+ gMinlen = s->save_gMinlen;
+ gLimit = s->save_gLimit;
+ gBase = s->save_gBase;
+ gPerm = s->save_gPerm;
+
+ retVal = BZ_OK;
+
+ switch (s->state) {
+
+ GET_UCHAR(BZ_X_MAGIC_1, uc);
+ if (uc != BZ_HDR_B) RETURN(BZ_DATA_ERROR_MAGIC);
+
+ GET_UCHAR(BZ_X_MAGIC_2, uc);
+ if (uc != BZ_HDR_Z) RETURN(BZ_DATA_ERROR_MAGIC);
+
+ GET_UCHAR(BZ_X_MAGIC_3, uc)
+ if (uc != BZ_HDR_h) RETURN(BZ_DATA_ERROR_MAGIC);
+
+ GET_BITS(BZ_X_MAGIC_4, s->blockSize100k, 8)
+ if (s->blockSize100k < (BZ_HDR_0 + 1) ||
+ s->blockSize100k > (BZ_HDR_0 + 9)) RETURN(BZ_DATA_ERROR_MAGIC);
+ s->blockSize100k -= BZ_HDR_0;
+
+ if (s->smallDecompress) {
+ s->ll16 = BZALLOC( s->blockSize100k * 100000 * sizeof(UInt16) );
+ s->ll4 = BZALLOC(
+ ((1 + s->blockSize100k * 100000) >> 1) * sizeof(UChar)
+ );
+ if (s->ll16 == NULL || s->ll4 == NULL) RETURN(BZ_MEM_ERROR);
+ } else {
+ s->tt = BZALLOC( s->blockSize100k * 100000 * sizeof(Int32) );
+ if (s->tt == NULL) RETURN(BZ_MEM_ERROR);
+ }
+
+ GET_UCHAR(BZ_X_BLKHDR_1, uc);
+
+ if (uc == 0x17) goto endhdr_2;
+ if (uc != 0x31) RETURN(BZ_DATA_ERROR);
+ GET_UCHAR(BZ_X_BLKHDR_2, uc);
+ if (uc != 0x41) RETURN(BZ_DATA_ERROR);
+ GET_UCHAR(BZ_X_BLKHDR_3, uc);
+ if (uc != 0x59) RETURN(BZ_DATA_ERROR);
+ GET_UCHAR(BZ_X_BLKHDR_4, uc);
+ if (uc != 0x26) RETURN(BZ_DATA_ERROR);
+ GET_UCHAR(BZ_X_BLKHDR_5, uc);
+ if (uc != 0x53) RETURN(BZ_DATA_ERROR);
+ GET_UCHAR(BZ_X_BLKHDR_6, uc);
+ if (uc != 0x59) RETURN(BZ_DATA_ERROR);
+
+ s->currBlockNo++;
+ if (s->verbosity >= 2)
+ VPrintf1 ( "\n [%d: huff+mtf ", s->currBlockNo );
+
+ s->storedBlockCRC = 0;
+ GET_UCHAR(BZ_X_BCRC_1, uc);
+ s->storedBlockCRC = (s->storedBlockCRC << 8) | ((UInt32)uc);
+ GET_UCHAR(BZ_X_BCRC_2, uc);
+ s->storedBlockCRC = (s->storedBlockCRC << 8) | ((UInt32)uc);
+ GET_UCHAR(BZ_X_BCRC_3, uc);
+ s->storedBlockCRC = (s->storedBlockCRC << 8) | ((UInt32)uc);
+ GET_UCHAR(BZ_X_BCRC_4, uc);
+ s->storedBlockCRC = (s->storedBlockCRC << 8) | ((UInt32)uc);
+
+ GET_BITS(BZ_X_RANDBIT, s->blockRandomised, 1);
+
+ s->origPtr = 0;
+ GET_UCHAR(BZ_X_ORIGPTR_1, uc);
+ s->origPtr = (s->origPtr << 8) | ((Int32)uc);
+ GET_UCHAR(BZ_X_ORIGPTR_2, uc);
+ s->origPtr = (s->origPtr << 8) | ((Int32)uc);
+ GET_UCHAR(BZ_X_ORIGPTR_3, uc);
+ s->origPtr = (s->origPtr << 8) | ((Int32)uc);
+
+ if (s->origPtr < 0)
+ RETURN(BZ_DATA_ERROR);
+ if (s->origPtr > 10 + 100000*s->blockSize100k)
+ RETURN(BZ_DATA_ERROR);
+
+ /*--- Receive the mapping table ---*/
+ for (i = 0; i < 16; i++) {
+ GET_BIT(BZ_X_MAPPING_1, uc);
+ if (uc == 1)
+ s->inUse16[i] = True; else
+ s->inUse16[i] = False;
+ }
+
+ for (i = 0; i < 256; i++) s->inUse[i] = False;
+
+ for (i = 0; i < 16; i++)
+ if (s->inUse16[i])
+ for (j = 0; j < 16; j++) {
+ GET_BIT(BZ_X_MAPPING_2, uc);
+ if (uc == 1) s->inUse[i * 16 + j] = True;
+ }
+ makeMaps_d ( s );
+ if (s->nInUse == 0) RETURN(BZ_DATA_ERROR);
+ alphaSize = s->nInUse+2;
+
+ /*--- Now the selectors ---*/
+ GET_BITS(BZ_X_SELECTOR_1, nGroups, 3);
+ if (nGroups < 2 || nGroups > 6) RETURN(BZ_DATA_ERROR);
+ GET_BITS(BZ_X_SELECTOR_2, nSelectors, 15);
+ if (nSelectors < 1) RETURN(BZ_DATA_ERROR);
+ for (i = 0; i < nSelectors; i++) {
+ j = 0;
+ while (True) {
+ GET_BIT(BZ_X_SELECTOR_3, uc);
+ if (uc == 0) break;
+ j++;
+ if (j >= nGroups) RETURN(BZ_DATA_ERROR);
+ }
+ s->selectorMtf[i] = j;
+ }
+
+ /*--- Undo the MTF values for the selectors. ---*/
+ {
+ UChar pos[BZ_N_GROUPS], tmp, v;
+ for (v = 0; v < nGroups; v++) pos[v] = v;
+
+ for (i = 0; i < nSelectors; i++) {
+ v = s->selectorMtf[i];
+ tmp = pos[v];
+ while (v > 0) { pos[v] = pos[v-1]; v--; }
+ pos[0] = tmp;
+ s->selector[i] = tmp;
+ }
+ }
+
+ /*--- Now the coding tables ---*/
+ for (t = 0; t < nGroups; t++) {
+ GET_BITS(BZ_X_CODING_1, curr, 5);
+ for (i = 0; i < alphaSize; i++) {
+ while (True) {
+ if (curr < 1 || curr > 20) RETURN(BZ_DATA_ERROR);
+ GET_BIT(BZ_X_CODING_2, uc);
+ if (uc == 0) break;
+ GET_BIT(BZ_X_CODING_3, uc);
+ if (uc == 0) curr++; else curr--;
+ }
+ s->len[t][i] = curr;
+ }
+ }
+
+ /*--- Create the Huffman decoding tables ---*/
+ for (t = 0; t < nGroups; t++) {
+ minLen = 32;
+ maxLen = 0;
+ for (i = 0; i < alphaSize; i++) {
+ if (s->len[t][i] > maxLen) maxLen = s->len[t][i];
+ if (s->len[t][i] < minLen) minLen = s->len[t][i];
+ }
+ BZ2_hbCreateDecodeTables (
+ &(s->limit[t][0]),
+ &(s->base[t][0]),
+ &(s->perm[t][0]),
+ &(s->len[t][0]),
+ minLen, maxLen, alphaSize
+ );
+ s->minLens[t] = minLen;
+ }
+
+ /*--- Now the MTF values ---*/
+
+ EOB = s->nInUse+1;
+ nblockMAX = 100000 * s->blockSize100k;
+ groupNo = -1;
+ groupPos = 0;
+
+ for (i = 0; i <= 255; i++) s->unzftab[i] = 0;
+
+ /*-- MTF init --*/
+ {
+ Int32 ii, jj, kk;
+ kk = MTFA_SIZE-1;
+ for (ii = 256 / MTFL_SIZE - 1; ii >= 0; ii--) {
+ for (jj = MTFL_SIZE-1; jj >= 0; jj--) {
+ s->mtfa[kk] = (UChar)(ii * MTFL_SIZE + jj);
+ kk--;
+ }
+ s->mtfbase[ii] = kk + 1;
+ }
+ }
+ /*-- end MTF init --*/
+
+ nblock = 0;
+ GET_MTF_VAL(BZ_X_MTF_1, BZ_X_MTF_2, nextSym);
+
+ while (True) {
+
+ if (nextSym == EOB) break;
+
+ if (nextSym == BZ_RUNA || nextSym == BZ_RUNB) {
+
+ es = -1;
+ N = 1;
+ do {
+ if (nextSym == BZ_RUNA) es = es + (0+1) * N; else
+ if (nextSym == BZ_RUNB) es = es + (1+1) * N;
+ N = N * 2;
+ GET_MTF_VAL(BZ_X_MTF_3, BZ_X_MTF_4, nextSym);
+ }
+ while (nextSym == BZ_RUNA || nextSym == BZ_RUNB);
+
+ es++;
+ uc = s->seqToUnseq[ s->mtfa[s->mtfbase[0]] ];
+ s->unzftab[uc] += es;
+
+ if (s->smallDecompress)
+ while (es > 0) {
+ if (nblock >= nblockMAX) RETURN(BZ_DATA_ERROR);
+ s->ll16[nblock] = (UInt16)uc;
+ nblock++;
+ es--;
+ }
+ else
+ while (es > 0) {
+ if (nblock >= nblockMAX) RETURN(BZ_DATA_ERROR);
+ s->tt[nblock] = (UInt32)uc;
+ nblock++;
+ es--;
+ };
+
+ continue;
+
+ } else {
+
+ if (nblock >= nblockMAX) RETURN(BZ_DATA_ERROR);
+
+ /*-- uc = MTF ( nextSym-1 ) --*/
+ {
+ Int32 ii, jj, kk, pp, lno, off;
+ UInt32 nn;
+ nn = (UInt32)(nextSym - 1);
+
+ if (nn < MTFL_SIZE) {
+ /* avoid general-case expense */
+ pp = s->mtfbase[0];
+ uc = s->mtfa[pp+nn];
+ while (nn > 3) {
+ Int32 z = pp+nn;
+ s->mtfa[(z) ] = s->mtfa[(z)-1];
+ s->mtfa[(z)-1] = s->mtfa[(z)-2];
+ s->mtfa[(z)-2] = s->mtfa[(z)-3];
+ s->mtfa[(z)-3] = s->mtfa[(z)-4];
+ nn -= 4;
+ }
+ while (nn > 0) {
+ s->mtfa[(pp+nn)] = s->mtfa[(pp+nn)-1]; nn--;
+ };
+ s->mtfa[pp] = uc;
+ } else {
+ /* general case */
+ lno = nn / MTFL_SIZE;
+ off = nn % MTFL_SIZE;
+ pp = s->mtfbase[lno] + off;
+ uc = s->mtfa[pp];
+ while (pp > s->mtfbase[lno]) {
+ s->mtfa[pp] = s->mtfa[pp-1]; pp--;
+ };
+ s->mtfbase[lno]++;
+ while (lno > 0) {
+ s->mtfbase[lno]--;
+ s->mtfa[s->mtfbase[lno]]
+ = s->mtfa[s->mtfbase[lno-1] + MTFL_SIZE - 1];
+ lno--;
+ }
+ s->mtfbase[0]--;
+ s->mtfa[s->mtfbase[0]] = uc;
+ if (s->mtfbase[0] == 0) {
+ kk = MTFA_SIZE-1;
+ for (ii = 256 / MTFL_SIZE-1; ii >= 0; ii--) {
+ for (jj = MTFL_SIZE-1; jj >= 0; jj--) {
+ s->mtfa[kk] = s->mtfa[s->mtfbase[ii] + jj];
+ kk--;
+ }
+ s->mtfbase[ii] = kk + 1;
+ }
+ }
+ }
+ }
+ /*-- end uc = MTF ( nextSym-1 ) --*/
+
+ s->unzftab[s->seqToUnseq[uc]]++;
+ if (s->smallDecompress)
+ s->ll16[nblock] = (UInt16)(s->seqToUnseq[uc]); else
+ s->tt[nblock] = (UInt32)(s->seqToUnseq[uc]);
+ nblock++;
+
+ GET_MTF_VAL(BZ_X_MTF_5, BZ_X_MTF_6, nextSym);
+ continue;
+ }
+ }
+
+ /* Now we know what nblock is, we can do a better sanity
+ check on s->origPtr.
+ */
+ if (s->origPtr < 0 || s->origPtr >= nblock)
+ RETURN(BZ_DATA_ERROR);
+
+ /*-- Set up cftab to facilitate generation of T^(-1) --*/
+ s->cftab[0] = 0;
+ for (i = 1; i <= 256; i++) s->cftab[i] = s->unzftab[i-1];
+ for (i = 1; i <= 256; i++) s->cftab[i] += s->cftab[i-1];
+ for (i = 0; i <= 256; i++) {
+ if (s->cftab[i] < 0 || s->cftab[i] > nblock) {
+ /* s->cftab[i] can legitimately be == nblock */
+ RETURN(BZ_DATA_ERROR);
+ }
+ }
+
+ s->state_out_len = 0;
+ s->state_out_ch = 0;
+ BZ_INITIALISE_CRC ( s->calculatedBlockCRC );
+ s->state = BZ_X_OUTPUT;
+ if (s->verbosity >= 2) VPrintf0 ( "rt+rld" );
+
+ if (s->smallDecompress) {
+
+ /*-- Make a copy of cftab, used in generation of T --*/
+ for (i = 0; i <= 256; i++) s->cftabCopy[i] = s->cftab[i];
+
+ /*-- compute the T vector --*/
+ for (i = 0; i < nblock; i++) {
+ uc = (UChar)(s->ll16[i]);
+ SET_LL(i, s->cftabCopy[uc]);
+ s->cftabCopy[uc]++;
+ }
+
+ /*-- Compute T^(-1) by pointer reversal on T --*/
+ i = s->origPtr;
+ j = GET_LL(i);
+ do {
+ Int32 tmp = GET_LL(j);
+ SET_LL(j, i);
+ i = j;
+ j = tmp;
+ }
+ while (i != s->origPtr);
+
+ s->tPos = s->origPtr;
+ s->nblock_used = 0;
+ if (s->blockRandomised) {
+ BZ_RAND_INIT_MASK;
+ BZ_GET_SMALL(s->k0); s->nblock_used++;
+ BZ_RAND_UPD_MASK; s->k0 ^= BZ_RAND_MASK;
+ } else {
+ BZ_GET_SMALL(s->k0); s->nblock_used++;
+ }
+
+ } else {
+
+ /*-- compute the T^(-1) vector --*/
+ for (i = 0; i < nblock; i++) {
+ uc = (UChar)(s->tt[i] & 0xff);
+ s->tt[s->cftab[uc]] |= (i << 8);
+ s->cftab[uc]++;
+ }
+
+ s->tPos = s->tt[s->origPtr] >> 8;
+ s->nblock_used = 0;
+ if (s->blockRandomised) {
+ BZ_RAND_INIT_MASK;
+ BZ_GET_FAST(s->k0); s->nblock_used++;
+ BZ_RAND_UPD_MASK; s->k0 ^= BZ_RAND_MASK;
+ } else {
+ BZ_GET_FAST(s->k0); s->nblock_used++;
+ }
+
+ }
+
+ RETURN(BZ_OK);
+
+
+
+ endhdr_2:
+
+ GET_UCHAR(BZ_X_ENDHDR_2, uc);
+ if (uc != 0x72) RETURN(BZ_DATA_ERROR);
+ GET_UCHAR(BZ_X_ENDHDR_3, uc);
+ if (uc != 0x45) RETURN(BZ_DATA_ERROR);
+ GET_UCHAR(BZ_X_ENDHDR_4, uc);
+ if (uc != 0x38) RETURN(BZ_DATA_ERROR);
+ GET_UCHAR(BZ_X_ENDHDR_5, uc);
+ if (uc != 0x50) RETURN(BZ_DATA_ERROR);
+ GET_UCHAR(BZ_X_ENDHDR_6, uc);
+ if (uc != 0x90) RETURN(BZ_DATA_ERROR);
+
+ s->storedCombinedCRC = 0;
+ GET_UCHAR(BZ_X_CCRC_1, uc);
+ s->storedCombinedCRC = (s->storedCombinedCRC << 8) | ((UInt32)uc);
+ GET_UCHAR(BZ_X_CCRC_2, uc);
+ s->storedCombinedCRC = (s->storedCombinedCRC << 8) | ((UInt32)uc);
+ GET_UCHAR(BZ_X_CCRC_3, uc);
+ s->storedCombinedCRC = (s->storedCombinedCRC << 8) | ((UInt32)uc);
+ GET_UCHAR(BZ_X_CCRC_4, uc);
+ s->storedCombinedCRC = (s->storedCombinedCRC << 8) | ((UInt32)uc);
+
+ s->state = BZ_X_IDLE;
+ RETURN(BZ_STREAM_END);
+
+ default: AssertH ( False, 4001 );
+ }
+
+ AssertH ( False, 4002 );
+
+ save_state_and_return:
+
+ s->save_i = i;
+ s->save_j = j;
+ s->save_t = t;
+ s->save_alphaSize = alphaSize;
+ s->save_nGroups = nGroups;
+ s->save_nSelectors = nSelectors;
+ s->save_EOB = EOB;
+ s->save_groupNo = groupNo;
+ s->save_groupPos = groupPos;
+ s->save_nextSym = nextSym;
+ s->save_nblockMAX = nblockMAX;
+ s->save_nblock = nblock;
+ s->save_es = es;
+ s->save_N = N;
+ s->save_curr = curr;
+ s->save_zt = zt;
+ s->save_zn = zn;
+ s->save_zvec = zvec;
+ s->save_zj = zj;
+ s->save_gSel = gSel;
+ s->save_gMinlen = gMinlen;
+ s->save_gLimit = gLimit;
+ s->save_gBase = gBase;
+ s->save_gPerm = gPerm;
+
+ return retVal;
+}
+
+
+/*-------------------------------------------------------------*/
+/*--- end decompress.c ---*/
+/*-------------------------------------------------------------*/
diff --git a/dep/src/bzip2/huffman.c b/dep/src/bzip2/huffman.c
new file mode 100644
index 00000000000..87e79e38af0
--- /dev/null
+++ b/dep/src/bzip2/huffman.c
@@ -0,0 +1,205 @@
+
+/*-------------------------------------------------------------*/
+/*--- Huffman coding low-level stuff ---*/
+/*--- huffman.c ---*/
+/*-------------------------------------------------------------*/
+
+/* ------------------------------------------------------------------
+ This file is part of bzip2/libbzip2, a program and library for
+ lossless, block-sorting data compression.
+
+ bzip2/libbzip2 version 1.0.5 of 10 December 2007
+ Copyright (C) 1996-2007 Julian Seward <jseward@bzip.org>
+
+ Please read the WARNING, DISCLAIMER and PATENTS sections in the
+ README file.
+
+ This program is released under the terms of the license contained
+ in the file LICENSE.
+ ------------------------------------------------------------------ */
+
+
+#include "bzlib_private.h"
+
+/*---------------------------------------------------*/
+#define WEIGHTOF(zz0) ((zz0) & 0xffffff00)
+#define DEPTHOF(zz1) ((zz1) & 0x000000ff)
+#define MYMAX(zz2,zz3) ((zz2) > (zz3) ? (zz2) : (zz3))
+
+#define ADDWEIGHTS(zw1,zw2) \
+ (WEIGHTOF(zw1)+WEIGHTOF(zw2)) | \
+ (1 + MYMAX(DEPTHOF(zw1),DEPTHOF(zw2)))
+
+#define UPHEAP(z) \
+{ \
+ Int32 zz, tmp; \
+ zz = z; tmp = heap[zz]; \
+ while (weight[tmp] < weight[heap[zz >> 1]]) { \
+ heap[zz] = heap[zz >> 1]; \
+ zz >>= 1; \
+ } \
+ heap[zz] = tmp; \
+}
+
+#define DOWNHEAP(z) \
+{ \
+ Int32 zz, yy, tmp; \
+ zz = z; tmp = heap[zz]; \
+ while (True) { \
+ yy = zz << 1; \
+ if (yy > nHeap) break; \
+ if (yy < nHeap && \
+ weight[heap[yy+1]] < weight[heap[yy]]) \
+ yy++; \
+ if (weight[tmp] < weight[heap[yy]]) break; \
+ heap[zz] = heap[yy]; \
+ zz = yy; \
+ } \
+ heap[zz] = tmp; \
+}
+
+
+/*---------------------------------------------------*/
+void BZ2_hbMakeCodeLengths ( UChar *len,
+ Int32 *freq,
+ Int32 alphaSize,
+ Int32 maxLen )
+{
+ /*--
+ Nodes and heap entries run from 1. Entry 0
+ for both the heap and nodes is a sentinel.
+ --*/
+ Int32 nNodes, nHeap, n1, n2, i, j, k;
+ Bool tooLong;
+
+ Int32 heap [ BZ_MAX_ALPHA_SIZE + 2 ];
+ Int32 weight [ BZ_MAX_ALPHA_SIZE * 2 ];
+ Int32 parent [ BZ_MAX_ALPHA_SIZE * 2 ];
+
+ for (i = 0; i < alphaSize; i++)
+ weight[i+1] = (freq[i] == 0 ? 1 : freq[i]) << 8;
+
+ while (True) {
+
+ nNodes = alphaSize;
+ nHeap = 0;
+
+ heap[0] = 0;
+ weight[0] = 0;
+ parent[0] = -2;
+
+ for (i = 1; i <= alphaSize; i++) {
+ parent[i] = -1;
+ nHeap++;
+ heap[nHeap] = i;
+ UPHEAP(nHeap);
+ }
+
+ AssertH( nHeap < (BZ_MAX_ALPHA_SIZE+2), 2001 );
+
+ while (nHeap > 1) {
+ n1 = heap[1]; heap[1] = heap[nHeap]; nHeap--; DOWNHEAP(1);
+ n2 = heap[1]; heap[1] = heap[nHeap]; nHeap--; DOWNHEAP(1);
+ nNodes++;
+ parent[n1] = parent[n2] = nNodes;
+ weight[nNodes] = ADDWEIGHTS(weight[n1], weight[n2]);
+ parent[nNodes] = -1;
+ nHeap++;
+ heap[nHeap] = nNodes;
+ UPHEAP(nHeap);
+ }
+
+ AssertH( nNodes < (BZ_MAX_ALPHA_SIZE * 2), 2002 );
+
+ tooLong = False;
+ for (i = 1; i <= alphaSize; i++) {
+ j = 0;
+ k = i;
+ while (parent[k] >= 0) { k = parent[k]; j++; }
+ len[i-1] = j;
+ if (j > maxLen) tooLong = True;
+ }
+
+ if (! tooLong) break;
+
+ /* 17 Oct 04: keep-going condition for the following loop used
+ to be 'i < alphaSize', which missed the last element,
+ theoretically leading to the possibility of the compressor
+ looping. However, this count-scaling step is only needed if
+ one of the generated Huffman code words is longer than
+ maxLen, which up to and including version 1.0.2 was 20 bits,
+ which is extremely unlikely. In version 1.0.3 maxLen was
+ changed to 17 bits, which has minimal effect on compression
+ ratio, but does mean this scaling step is used from time to
+ time, enough to verify that it works.
+
+ This means that bzip2-1.0.3 and later will only produce
+ Huffman codes with a maximum length of 17 bits. However, in
+ order to preserve backwards compatibility with bitstreams
+ produced by versions pre-1.0.3, the decompressor must still
+ handle lengths of up to 20. */
+
+ for (i = 1; i <= alphaSize; i++) {
+ j = weight[i] >> 8;
+ j = 1 + (j / 2);
+ weight[i] = j << 8;
+ }
+ }
+}
+
+
+/*---------------------------------------------------*/
+void BZ2_hbAssignCodes ( Int32 *code,
+ UChar *length,
+ Int32 minLen,
+ Int32 maxLen,
+ Int32 alphaSize )
+{
+ Int32 n, vec, i;
+
+ vec = 0;
+ for (n = minLen; n <= maxLen; n++) {
+ for (i = 0; i < alphaSize; i++)
+ if (length[i] == n) { code[i] = vec; vec++; };
+ vec <<= 1;
+ }
+}
+
+
+/*---------------------------------------------------*/
+void BZ2_hbCreateDecodeTables ( Int32 *limit,
+ Int32 *base,
+ Int32 *perm,
+ UChar *length,
+ Int32 minLen,
+ Int32 maxLen,
+ Int32 alphaSize )
+{
+ Int32 pp, i, j, vec;
+
+ pp = 0;
+ for (i = minLen; i <= maxLen; i++)
+ for (j = 0; j < alphaSize; j++)
+ if (length[j] == i) { perm[pp] = j; pp++; };
+
+ for (i = 0; i < BZ_MAX_CODE_LEN; i++) base[i] = 0;
+ for (i = 0; i < alphaSize; i++) base[length[i]+1]++;
+
+ for (i = 1; i < BZ_MAX_CODE_LEN; i++) base[i] += base[i-1];
+
+ for (i = 0; i < BZ_MAX_CODE_LEN; i++) limit[i] = 0;
+ vec = 0;
+
+ for (i = minLen; i <= maxLen; i++) {
+ vec += (base[i+1] - base[i]);
+ limit[i] = vec-1;
+ vec <<= 1;
+ }
+ for (i = minLen + 1; i <= maxLen; i++)
+ base[i] = ((limit[i-1] + 1) << 1) - base[i];
+}
+
+
+/*-------------------------------------------------------------*/
+/*--- end huffman.c ---*/
+/*-------------------------------------------------------------*/
diff --git a/dep/src/bzip2/randtable.c b/dep/src/bzip2/randtable.c
new file mode 100644
index 00000000000..068b76367bc
--- /dev/null
+++ b/dep/src/bzip2/randtable.c
@@ -0,0 +1,84 @@
+
+/*-------------------------------------------------------------*/
+/*--- Table for randomising repetitive blocks ---*/
+/*--- randtable.c ---*/
+/*-------------------------------------------------------------*/
+
+/* ------------------------------------------------------------------
+ This file is part of bzip2/libbzip2, a program and library for
+ lossless, block-sorting data compression.
+
+ bzip2/libbzip2 version 1.0.5 of 10 December 2007
+ Copyright (C) 1996-2007 Julian Seward <jseward@bzip.org>
+
+ Please read the WARNING, DISCLAIMER and PATENTS sections in the
+ README file.
+
+ This program is released under the terms of the license contained
+ in the file LICENSE.
+ ------------------------------------------------------------------ */
+
+
+#include "bzlib_private.h"
+
+
+/*---------------------------------------------*/
+Int32 BZ2_rNums[512] = {
+ 619, 720, 127, 481, 931, 816, 813, 233, 566, 247,
+ 985, 724, 205, 454, 863, 491, 741, 242, 949, 214,
+ 733, 859, 335, 708, 621, 574, 73, 654, 730, 472,
+ 419, 436, 278, 496, 867, 210, 399, 680, 480, 51,
+ 878, 465, 811, 169, 869, 675, 611, 697, 867, 561,
+ 862, 687, 507, 283, 482, 129, 807, 591, 733, 623,
+ 150, 238, 59, 379, 684, 877, 625, 169, 643, 105,
+ 170, 607, 520, 932, 727, 476, 693, 425, 174, 647,
+ 73, 122, 335, 530, 442, 853, 695, 249, 445, 515,
+ 909, 545, 703, 919, 874, 474, 882, 500, 594, 612,
+ 641, 801, 220, 162, 819, 984, 589, 513, 495, 799,
+ 161, 604, 958, 533, 221, 400, 386, 867, 600, 782,
+ 382, 596, 414, 171, 516, 375, 682, 485, 911, 276,
+ 98, 553, 163, 354, 666, 933, 424, 341, 533, 870,
+ 227, 730, 475, 186, 263, 647, 537, 686, 600, 224,
+ 469, 68, 770, 919, 190, 373, 294, 822, 808, 206,
+ 184, 943, 795, 384, 383, 461, 404, 758, 839, 887,
+ 715, 67, 618, 276, 204, 918, 873, 777, 604, 560,
+ 951, 160, 578, 722, 79, 804, 96, 409, 713, 940,
+ 652, 934, 970, 447, 318, 353, 859, 672, 112, 785,
+ 645, 863, 803, 350, 139, 93, 354, 99, 820, 908,
+ 609, 772, 154, 274, 580, 184, 79, 626, 630, 742,
+ 653, 282, 762, 623, 680, 81, 927, 626, 789, 125,
+ 411, 521, 938, 300, 821, 78, 343, 175, 128, 250,
+ 170, 774, 972, 275, 999, 639, 495, 78, 352, 126,
+ 857, 956, 358, 619, 580, 124, 737, 594, 701, 612,
+ 669, 112, 134, 694, 363, 992, 809, 743, 168, 974,
+ 944, 375, 748, 52, 600, 747, 642, 182, 862, 81,
+ 344, 805, 988, 739, 511, 655, 814, 334, 249, 515,
+ 897, 955, 664, 981, 649, 113, 974, 459, 893, 228,
+ 433, 837, 553, 268, 926, 240, 102, 654, 459, 51,
+ 686, 754, 806, 760, 493, 403, 415, 394, 687, 700,
+ 946, 670, 656, 610, 738, 392, 760, 799, 887, 653,
+ 978, 321, 576, 617, 626, 502, 894, 679, 243, 440,
+ 680, 879, 194, 572, 640, 724, 926, 56, 204, 700,
+ 707, 151, 457, 449, 797, 195, 791, 558, 945, 679,
+ 297, 59, 87, 824, 713, 663, 412, 693, 342, 606,
+ 134, 108, 571, 364, 631, 212, 174, 643, 304, 329,
+ 343, 97, 430, 751, 497, 314, 983, 374, 822, 928,
+ 140, 206, 73, 263, 980, 736, 876, 478, 430, 305,
+ 170, 514, 364, 692, 829, 82, 855, 953, 676, 246,
+ 369, 970, 294, 750, 807, 827, 150, 790, 288, 923,
+ 804, 378, 215, 828, 592, 281, 565, 555, 710, 82,
+ 896, 831, 547, 261, 524, 462, 293, 465, 502, 56,
+ 661, 821, 976, 991, 658, 869, 905, 758, 745, 193,
+ 768, 550, 608, 933, 378, 286, 215, 979, 792, 961,
+ 61, 688, 793, 644, 986, 403, 106, 366, 905, 644,
+ 372, 567, 466, 434, 645, 210, 389, 550, 919, 135,
+ 780, 773, 635, 389, 707, 100, 626, 958, 165, 504,
+ 920, 176, 193, 713, 857, 265, 203, 50, 668, 108,
+ 645, 990, 626, 197, 510, 357, 358, 850, 858, 364,
+ 936, 638
+};
+
+
+/*-------------------------------------------------------------*/
+/*--- end randtable.c ---*/
+/*-------------------------------------------------------------*/
diff --git a/dep/src/g3dlite/AABox.cpp b/dep/src/g3dlite/AABox.cpp
index 2279e9a51f0..035497aa3c4 100644
--- a/dep/src/g3dlite/AABox.cpp
+++ b/dep/src/g3dlite/AABox.cpp
@@ -1,30 +1,61 @@
/**
@file AABox.cpp
- @maintainer Morgan McGuire, matrix@graphics3d.com
+ @maintainer Morgan McGuire, http://graphics.cs.williams.edu
@created 2004-01-10
@edited 2006-01-11
*/
#include "G3D/platform.h"
-# if defined(_MSC_VER) && (_MSC_VER <= 1200)
- // VC6 std:: has signed/unsigned problems
-# pragma warning (disable : 4018)
-# endif
-
-#include <assert.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 {
-Box AABox::toBox() const {
- return Box(lo, hi);
+const AABox& AABox::maxFinite() {
+ static const AABox b = AABox(Vector3::minFinite(),
+ Vector3::maxFinite());
+ return b;
+}
+
+
+const AABox& AABox::large() {
+ static const AABox b = AABox(Vector3::minFinite() * 0.5f,
+ Vector3::maxFinite() * 0.5f);
+ return b;
+}
+
+
+const AABox& AABox::inf() {
+ static const AABox b = AABox(-Vector3::inf(), Vector3::inf());
+ return b;
+}
+
+
+const AABox& AABox::zero() {
+ static const AABox b = AABox(Vector3::zero(), Vector3::zero());
+ return b;
+}
+
+
+void AABox::serialize(class BinaryOutput& b) const {
+ b.writeVector3(lo);
+ b.writeVector3(hi);
+}
+
+
+void AABox::deserialize(class BinaryInput& b) {
+ lo = b.readVector3();
+ hi = b.readVector3();
}
+
void AABox::split(const Vector3::Axis& axis, float location, AABox& low, AABox& high) const {
// Low, medium, and high along the chosen axis
float L = G3D::min(location, lo[axis]);
@@ -41,51 +72,52 @@ void AABox::split(const Vector3::Axis& axis, float location, AABox& low, AABox&
high.hi[axis] = H;
}
-#if 0
+
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)random(0, aXY + aYZ + aZX);
+ float r = (float)uniformRandom(0.0f, aXY + aYZ + aZX);
// Choose evenly between positive and negative face planes
- float d = ((float)random(0, 1) < 0.5f) ? 0.0f : 1.0f;
+ 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 +
+ return
+ lo +
Vector3(
- (float)random(0, extent.x),
- (float)random(0, extent.y),
+ (float)uniformRandom(0.0f, extent.x),
+ (float)uniformRandom(0.0f, extent.y),
d * extent.z);
} else if (r < aYZ) {
- return
- lo +
+ return
+ lo +
Vector3(
d * extent.x,
- (float)random(0, extent.y),
- (float)random(0, extent.z));
+ (float)uniformRandom(0, extent.y),
+ (float)uniformRandom(0, extent.z));
} else {
- return
- lo +
+ return
+ lo +
Vector3(
- (float)random(0, extent.x),
+ (float)uniformRandom(0, extent.x),
d * extent.y,
- (float)random(0, extent.z));
+ (float)uniformRandom(0, extent.z));
}
}
+
Vector3 AABox::randomInteriorPoint() const {
return Vector3(
- (float)random(lo.x, hi.x),
- (float)random(lo.y, hi.y),
- (float)random(lo.z, hi.z));
+ (float)uniformRandom(lo.x, hi.x),
+ (float)uniformRandom(lo.y, hi.y),
+ (float)uniformRandom(lo.z, hi.z));
}
-#endif
+
bool AABox::intersects(const AABox& other) const {
// Must be overlap along all three axes.
@@ -105,53 +137,35 @@ bool AABox::intersects(const AABox& other) const {
return true;
}
-bool AABox::culledBy(
- const Array<Plane>& plane,
- int& cullingPlaneIndex,
- const uint32 inMask,
- uint32& outMask) const {
-
- return culledBy(plane.getCArray(), plane.size(), cullingPlaneIndex, inMask, outMask);
-}
-
-bool AABox::culledBy(
- const Array<Plane>& plane,
- int& cullingPlaneIndex,
- const uint32 inMask) const {
-
- return culledBy(plane.getCArray(), plane.size(), cullingPlaneIndex, inMask);
-}
-
int AABox::dummy = 0;
bool AABox::culledBy(
- const class Plane* plane,
- int numPlanes,
- int& cullingPlane,
- const uint32 _inMask,
+ const Array<Plane>& plane,
+ int& cullingPlane,
+ const uint32 _inMask,
uint32& childMask) const {
uint32 inMask = _inMask;
- assert(numPlanes < 31);
+ 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());
+ const bool finite =
+ (abs(lo.x) < G3D::finf()) &&
+ (abs(hi.x) < G3D::finf()) &&
+ (abs(lo.y) < G3D::finf()) &&
+ (abs(hi.y) < G3D::finf()) &&
+ (abs(lo.z) < G3D::finf()) &&
+ (abs(hi.z) < G3D::finf());
// See if there is one plane for which all of the
- // vertices are in the negative half space.
- for (int p = 0; p < numPlanes; p++) {
-
- // Only test planes that are not masked
- if ((inMask & 1) != 0) {
+ // vertices are in the negative half space.
+ for (int p = 0; p < plane.size(); ++p) {
- Vector3 corner;
+ // Only test planes that are not masked
+ if ((inMask & 1) != 0) {
+
+ Vector3 corner;
int numContained = 0;
int v = 0;
@@ -159,13 +173,13 @@ bool AABox::culledBy(
// 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) {
+ 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;
-
+ 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;
@@ -175,101 +189,102 @@ bool AABox::culledBy(
++numContained;
}
}
- }
+ }
- if (numContained == 0) {
- // Plane p culled the box
- cullingPlane = p;
+ 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;
+ 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;
+ inMask = inMask >> 1;
}
// None of the planes could cull this box
- cullingPlane = -1;
+ cullingPlane = -1;
return false;
}
-bool AABox::culledBy(
- const class Plane* plane,
- int numPlanes,
- int& cullingPlane,
- const uint32 _inMask) const {
-
- uint32 inMask = _inMask;
- assert(numPlanes < 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());
+bool AABox::culledBy(
+ const Array<Plane>& plane,
+ int& cullingPlane,
+ const uint32 _inMask) const {
+
+ uint32 inMask = _inMask;
+ assert(plane.size() < 31);
+
+ const bool finite =
+ (abs(lo.x) < G3D::finf()) &&
+ (abs(hi.x) < G3D::finf()) &&
+ (abs(lo.y) < G3D::finf()) &&
+ (abs(hi.y) < G3D::finf()) &&
+ (abs(lo.z) < G3D::finf()) &&
+ (abs(hi.z) < G3D::finf());
// See if there is one plane for which all of the
- // vertices are in the negative half space.
- for (int p = 0; p < numPlanes; p++) {
-
- // Only test planes that are not masked
- if ((inMask & 1) != 0) {
+ // vertices are in the negative half space.
+ for (int p = 0; p < plane.size(); ++p) {
- bool culled = true;
- Vector3 corner;
+ // 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
+ // 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) {
+ 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;
-
+ 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;
+ if (culled) {
+ // Plane p culled the box
+ cullingPlane = p;
- return true;
+ return true;
}
- }
+ }
// Move on to the next bit.
- inMask = inMask >> 1;
+ inMask = inMask >> 1;
}
// None of the planes could cull this box
- cullingPlane = -1;
+ cullingPlane = -1;
return false;
}
+
bool AABox::intersects(const class Sphere& sphere) const {
- double d = 0;
+ double d = 0;
//find the square of the distance
//from the sphere to the box
@@ -284,5 +299,68 @@ bool AABox::intersects(const class Sphere& sphere) const {
return d <= square(sphere.radius);
}
-} // namespace
+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/dep/src/g3dlite/Any.cpp b/dep/src/g3dlite/Any.cpp
new file mode 100644
index 00000000000..de4d32e83ea
--- /dev/null
+++ b/dep/src/g3dlite/Any.cpp
@@ -0,0 +1,1237 @@
+/**
+ @file Any.cpp
+
+ @author Morgan McGuire
+ @author Shawn Yarbrough
+
+ @created 2006-06-11
+ @edited 2009-11-15
+
+ Copyright 2000-2009, Morgan McGuire.
+ All rights reserved.
+ */
+
+#include "G3D/Any.h"
+#include "G3D/TextOutput.h"
+#include "G3D/TextInput.h"
+#include "G3D/stringutils.h"
+#include <deque>
+#include <iostream>
+
+namespace G3D {
+
+void Any::beforeRead() const {
+ if (isPlaceholder()) {
+ // Tried to read from a placeholder--throw an exception as if
+ // the original operator[] had failed.
+ KeyNotFound e;
+ alwaysAssertM(m_data, "Corrupt placeholder");
+
+ e.filename = m_data->source.filename;
+ e.line = m_data->source.line;
+ e.character = m_data->source.character;
+ e.key = m_placeholderName;
+ e.message =
+ "This exception may have been thrown later than "
+ "the actual operator[] invocation.";
+
+ throw e;
+ }
+}
+
+
+Any::Data* Any::Data::create(const Data* d) {
+ Data* p = create(d->type);
+
+ p->comment = d->comment;
+ p->name = d->name;
+
+ switch (d->type) {
+ case NONE:
+ case BOOLEAN:
+ case NUMBER:
+ // No clone needed
+ break;
+
+ case STRING:
+ *(p->value.s) = *(d->value.s);
+ break;
+
+ case ARRAY:
+ *(p->value.a) = *(d->value.a);
+ break;
+
+ case TABLE:
+ *(p->value.t) = *(d->value.t);
+ // Note that placeholders may be copied; that is ok--they are still
+ // just placeholders.
+ break;
+ }
+
+ return p;
+}
+
+
+Any::Data* Any::Data::create(Any::Type t) {
+ size_t s = sizeof(Data);
+
+ switch (t) {
+ case NONE:
+ case BOOLEAN:
+ case NUMBER:
+ // No extra space needed
+ break;
+
+ case STRING:
+ s += sizeof(std::string);
+ break;
+
+ case ARRAY:
+ s += sizeof(AnyArray);
+ break;
+
+ case TABLE:
+ s += sizeof(AnyTable);
+ break;
+ }
+
+ // Allocate the data object
+ Data* p = new (MemoryManager::create()->alloc(s)) Data(t);
+
+ // Create the (empyt) value object at the end of the Data object
+ switch (t) {
+ case NONE:
+ case BOOLEAN:
+ case NUMBER:
+ // No value
+ break;
+
+ case STRING:
+ p->value.s = new (p + 1) std::string();
+ break;
+
+ case ARRAY:
+ p->value.a = new (p + 1) AnyArray();
+ break;
+
+ case TABLE:
+ p->value.t = new (p + 1) AnyTable();
+ break;
+ }
+
+ return p;
+}
+
+
+void Any::Data::destroy(Data* d) {
+ if (d != NULL) {
+ d->~Data();
+ MemoryManager::create()->free(d);
+ }
+}
+
+
+Any::Data::~Data() {
+ debugAssertM(referenceCount.value() <= 0, "Deleted while still referenced.");
+
+ // Destruct but do not deallocate children
+ switch (type) {
+ case STRING:
+ debugAssert(value.s != NULL);
+ value.s->~basic_string();
+ break;
+
+ case ARRAY:
+ debugAssert(value.a != NULL);
+ value.a->~Array();
+ break;
+
+ case TABLE:
+ debugAssert(value.t != NULL);
+ value.t->~Table();
+ break;
+
+ default:
+ // All other types should have a NULL value pointer (i.e., they were used just for name and comment fields)
+ debugAssertM(value.s == NULL, "Corrupt Any::Data::Value");
+ }
+
+ value.s = NULL;
+}
+
+
+//////////////////////////////////////////////////////////////
+
+bool Any::containsKey(const std::string& x) const {
+ beforeRead();
+ verifyType(TABLE);
+
+ Any* a = m_data->value.t->getPointer(x);
+
+ // Don't return true for placeholder objects
+ return (a != NULL) && (! a->isPlaceholder());
+}
+
+
+void Any::dropReference() {
+ if (m_data && m_data->referenceCount.decrement() <= 0) {
+ // This was the last reference to the shared data
+ Data::destroy(m_data);
+ }
+ m_data = NULL;
+}
+
+
+void Any::ensureMutable() {
+ if (m_data && (m_data->referenceCount.value() >= 1)) {
+ // Copy the data. We must do this before dropping the reference
+ // to avoid a race condition
+ Data* d = Data::create(m_data);
+ dropReference();
+ m_data = d;
+ }
+}
+
+
+Any::Any() : m_type(NONE), m_data(NULL) {
+}
+
+
+Any::Any(TextInput& t) : m_type(NONE), m_data(NULL) {
+ deserialize(t);
+}
+
+
+Any::Any(const Any& x) : m_type(NONE), m_data(NULL) {
+ x.beforeRead();
+ *this = x;
+}
+
+
+Any::Any(double x) : m_type(NUMBER), m_simpleValue(x), m_data(NULL) {
+}
+
+
+#ifdef G3D_32BIT
+Any::Any(int64 x) : m_type(NUMBER), m_simpleValue((double)x), m_data(NULL) {
+}
+#endif // G3D_32BIT
+
+
+Any::Any(long x) : m_type(NUMBER), m_simpleValue((double)x), m_data(NULL) {
+}
+
+
+Any::Any(int x) : m_type(NUMBER), m_simpleValue((double)x), m_data(NULL) {
+}
+
+
+Any::Any(short x) : m_type(NUMBER), m_simpleValue((double)x), m_data(NULL) {
+}
+
+
+Any::Any(bool x) : m_type(BOOLEAN), m_simpleValue(x), m_data(NULL) {
+}
+
+
+Any::Any(const std::string& s) : m_type(STRING), m_data(Data::create(STRING)) {
+ *(m_data->value.s) = s;
+}
+
+
+Any::Any(const char* s) : m_type(STRING), m_data(NULL) {
+ if (s == NULL) {
+ m_type = NONE;
+ } else {
+ ensureData();
+ *(m_data->value.s) = s;
+ }
+}
+
+
+Any::Any(Type t, const std::string& name) : m_type(t), m_data(NULL) {
+ alwaysAssertM(t == ARRAY || t == TABLE, "Can only create ARRAY or TABLE from Type enum.");
+
+ ensureData();
+ if (name != "") {
+ m_data->name = name;
+ }
+}
+
+
+Any::~Any() {
+ dropReference();
+}
+
+
+void Any::beforeWrite() {
+ if (isPlaceholder()) {
+ // This is no longer a placeholder
+ m_placeholderName = "";
+ }
+}
+
+Any& Any::operator=(const Any& x) {
+ x.beforeRead();
+
+ if (this == &x) {
+ return *this;
+ }
+
+ beforeWrite();
+
+ dropReference();
+
+ m_type = x.m_type;
+ m_simpleValue = x.m_simpleValue;
+
+ if (x.m_data != NULL) {
+ x.m_data->referenceCount.increment();
+ m_data = x.m_data;
+ }
+
+ return *this;
+}
+
+
+Any& Any::operator=(double x) {
+ *this = Any(x);
+ return *this;
+}
+
+
+Any& Any::operator=(int x) {
+ return (*this = Any(x));
+}
+
+
+Any& Any::operator=(bool x) {
+ *this = Any(x);
+ return *this;
+}
+
+
+Any& Any::operator=(const std::string& x) {
+ *this = Any(x);
+ return *this;
+}
+
+
+Any& Any::operator=(const char* x) {
+ *this = Any(x);
+ return *this;
+}
+
+
+Any& Any::operator=(Type t) {
+ switch (t) {
+ case NONE:
+ *this = Any();
+ break;
+
+ case TABLE:
+ case ARRAY:
+ *this = Any(t);
+ break;
+
+ default:
+ alwaysAssertM(false, "Can only assign NONE, TABLE, or ARRAY Type enum.");
+ }
+
+ return *this;
+}
+
+
+Any::Type Any::type() const {
+ beforeRead();
+ return m_type;
+}
+
+
+const std::string& Any::comment() const {
+ beforeRead();
+
+ static const std::string blank;
+ if (m_data != NULL) {
+ return m_data->comment;
+ } else {
+ return blank;
+ }
+}
+
+
+void Any::setComment(const std::string& c) {
+ beforeRead();
+ ensureData();
+ m_data->comment = c;
+}
+
+
+bool Any::isNone() const {
+ beforeRead();
+ return (m_type == NONE);
+}
+
+
+double Any::number() const {
+ beforeRead();
+ verifyType(NUMBER);
+ return m_simpleValue.n;
+}
+
+
+const std::string& Any::string() const {
+ beforeRead();
+ verifyType(STRING);
+ return *(m_data->value.s);
+}
+
+
+bool Any::boolean() const {
+ beforeRead();
+ verifyType(BOOLEAN);
+ return m_simpleValue.b;
+}
+
+
+const std::string& Any::name() const {
+ beforeRead();
+ static const std::string blank;
+ if (m_data != NULL) {
+ return m_data->name;
+ } else {
+ return blank;
+ }
+}
+
+
+void Any::setName(const std::string& n) {
+ beforeRead();
+ ensureData();
+ m_data->name = n;
+}
+
+
+int Any::size() const {
+ beforeRead();
+ verifyType(ARRAY, TABLE);
+ switch (m_type) {
+ case TABLE:
+ return m_data->value.t->size();
+
+ case ARRAY:
+ return m_data->value.a->size();
+
+ default:;
+ return 0;
+ } // switch (m_type)
+}
+
+
+int Any::length() const {
+ beforeRead();
+ return size();
+}
+
+
+void Any::resize(int n) {
+ beforeRead();
+ alwaysAssertM(n >= 0, "Cannot resize less than 0.");
+ verifyType(ARRAY);
+ m_data->value.a->resize(n);
+}
+
+
+void Any::clear() {
+ beforeRead();
+ verifyType(ARRAY, TABLE);
+ switch (m_type) {
+ case ARRAY:
+ m_data->value.a->clear();
+ break;
+
+ case TABLE:
+ m_data->value.t->clear();
+ break;
+
+ default:;
+ }
+}
+
+
+const Any& Any::operator[](int i) const {
+ beforeRead();
+ verifyType(ARRAY);
+ debugAssert(m_data != NULL);
+ Array<Any>& array = *(m_data->value.a);
+ return array[i];
+}
+
+
+Any& Any::next() {
+ beforeRead();
+ verifyType(ARRAY);
+ int n = size();
+ resize(n + 1);
+ return (*this)[n];
+}
+
+
+Any& Any::operator[](int i) {
+ beforeRead();
+ verifyType(ARRAY);
+ debugAssert(m_data != NULL);
+ Array<Any>& array = *(m_data->value.a);
+ return array[i];
+}
+
+
+const Array<Any>& Any::array() const {
+ beforeRead();
+ verifyType(ARRAY);
+ debugAssert(m_data != NULL);
+ return *(m_data->value.a);
+}
+
+
+void Any::append(const Any& x0) {
+ beforeRead();
+ verifyType(ARRAY);
+ debugAssert(m_data != NULL);
+ m_data->value.a->append(x0);
+}
+
+
+void Any::append(const Any& x0, const Any& x1) {
+ beforeRead();
+ append(x0);
+ append(x1);
+}
+
+
+void Any::append(const Any& x0, const Any& x1, const Any& x2) {
+ beforeRead();
+ append(x0);
+ append(x1);
+ append(x2);
+}
+
+
+void Any::append(const Any& x0, const Any& x1, const Any& x2, const Any& x3) {
+ beforeRead();
+ append(x0);
+ append(x1);
+ append(x2);
+ append(x3);
+}
+
+
+const Table<std::string, Any>& Any::table() const {
+ beforeRead();
+ verifyType(TABLE);
+ debugAssert(m_data != NULL);
+ return *(m_data->value.t);
+}
+
+
+const Any& Any::operator[](const std::string& x) const {
+ beforeRead();
+ verifyType(TABLE);
+ debugAssert(m_data != NULL);
+ const Table<std::string, Any>& table = *(m_data->value.t);
+ Any* value = table.getPointer(x);
+ if (value == NULL) {
+ KeyNotFound e;
+ if (m_data) {
+ e.filename = m_data->source.filename;
+ e.line = m_data->source.line;
+ e.character = m_data->source.character;
+ }
+ e.key = x;
+ throw e;
+ }
+ return *value;
+}
+
+
+Any& Any::operator[](const std::string& key) {
+ beforeRead();
+ verifyType(TABLE);
+
+ bool created = false;
+ Any& value = m_data->value.t->getCreate(key, created);
+
+ if (created) {
+ // The entry was created by this method; do not allow it to be
+ // read before it is written.
+ value.m_placeholderName = key;
+
+ // Write source data for the value
+ value.ensureData();
+ value.m_data->source = source();
+ }
+
+ return value;
+}
+
+
+void Any::set(const std::string& k, const Any& v) {
+ beforeRead();
+ v.beforeRead();
+ verifyType(TABLE);
+ debugAssert(m_data != NULL);
+ Table<std::string, Any>& table = *(m_data->value.t);
+ table.set(k, v);
+}
+
+
+const Any& Any::get(const std::string& x, const Any& defaultVal) const {
+ beforeRead();
+ defaultVal.beforeRead();
+ try {
+ return operator[](x);
+ } catch(KeyNotFound) {
+ return defaultVal;
+ }
+}
+
+
+bool Any::operator==(const Any& x) const {
+ beforeRead();
+ x.beforeRead();
+ if (m_type != x.m_type) {
+ return false;
+ }
+
+ switch (m_type) {
+ case NONE:
+ return true;
+
+ case BOOLEAN:
+ return (m_simpleValue.b == x.m_simpleValue.b);
+
+ case NUMBER:
+ return (m_simpleValue.n == x.m_simpleValue.n);
+
+ case STRING:
+ debugAssert(m_data != NULL);
+ return (*(m_data->value.s) == *(x.m_data->value.s));
+
+ case TABLE: {
+ if (size() != x.size()) {
+ return false;
+ }
+ debugAssert(m_data != NULL);
+ if (m_data->name != x.m_data->name) {
+ return false;
+ }
+ Table<std::string, Any>& cmptable = *( m_data->value.t);
+ Table<std::string, Any>& xcmptable = *(x.m_data->value.t);
+ for (Table<std::string,Any>::Iterator it1 = cmptable.begin(), it2 = xcmptable.begin();
+ it1 != cmptable.end() && it2 != xcmptable.end();
+ ++it1, ++it2) {
+ if (*it1 != *it2) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ case ARRAY: {
+ if (size() != x.size()) {
+ return false;
+ }
+ debugAssert(m_data != NULL);
+ if (m_data->name != x.m_data->name) {
+ return false;
+ }
+
+ Array<Any>& cmparray = *( m_data->value.a);
+ Array<Any>& xcmparray = *(x.m_data->value.a);
+
+ for (int ii = 0; ii < size(); ++ii) {
+ if (cmparray[ii] != xcmparray[ii]) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ default:
+ alwaysAssertM(false, "Unknown type.");
+ return false;
+ } // switch (m_type)
+
+}
+
+
+bool Any::operator!=(const Any& x) const {
+ beforeRead();
+ x.beforeRead();
+ return !operator==(x);
+}
+
+
+static void getDeserializeSettings(TextInput::Settings& settings) {
+ settings.cppBlockComments = true;
+ settings.cppLineComments = true;
+ settings.otherLineComments = true;
+ settings.otherCommentCharacter = '#';
+ settings.generateCommentTokens = true;
+ settings.singleQuotedStrings = false;
+ settings.msvcSpecials = false;
+ settings.caseSensitive = false;
+}
+
+
+std::string Any::unparse() const {
+ beforeRead();
+ TextOutput::Settings settings;
+ TextOutput to(settings);
+ serialize(to);
+ return to.commitString();
+}
+
+
+void Any::parse(const std::string& src) {
+ beforeRead();
+ TextInput::Settings settings;
+ getDeserializeSettings(settings);
+
+ TextInput ti(TextInput::FROM_STRING, src, settings);
+ deserialize(ti);
+}
+
+
+void Any::load(const std::string& filename) {
+ beforeRead();
+ TextInput::Settings settings;
+ getDeserializeSettings(settings);
+
+ TextInput ti(filename, settings);
+ deserialize(ti);
+}
+
+
+void Any::save(const std::string& filename) const {
+ beforeRead();
+ TextOutput::Settings settings;
+ settings.wordWrap = TextOutput::Settings::WRAP_NONE;
+
+ TextOutput to(filename,settings);
+ serialize(to);
+ to.commit();
+}
+
+
+static bool needsQuotes(const std::string& s) {
+ if (! isLetter(s[0]) && (s[0] != '_')) {
+ return true;
+ }
+
+ for (int i = 0; i < (int)s.length(); ++i) {
+ char c = s[i];
+
+ // peek character
+ char p = (i == (int)s.length() - 1) ? '_' : s[i + 1];
+
+ // Identify separators
+ if ((c == '-' && p == '>') ||
+ (c == ':' && p == ':')) {
+ // Skip over this symbol
+ ++i;
+ continue;
+ }
+
+ if (! isDigit(c) && ! isLetter(c) & (c != '.')) {
+ // This is an illegal character for an identifier, so we need quotes
+ return true;
+ }
+ }
+
+ return false;
+}
+
+
+// TODO: if the output will fit on one line, compress tables and arrays into a single line
+void Any::serialize(TextOutput& to) const {
+ beforeRead();
+ if (m_data && ! m_data->comment.empty()) {
+ to.printf("\n/* %s */\n", m_data->comment.c_str());
+ }
+
+ switch (m_type) {
+ case NONE:
+ to.writeSymbol("NONE");
+ break;
+
+ case BOOLEAN:
+ to.writeBoolean(m_simpleValue.b);
+ break;
+
+ case NUMBER:
+ to.writeNumber(m_simpleValue.n);
+ break;
+
+ case STRING:
+ debugAssert(m_data != NULL);
+ to.writeString(*(m_data->value.s));
+ break;
+
+ case TABLE: {
+ debugAssert(m_data != NULL);
+ if (! m_data->name.empty()) {
+ if (needsQuotes(m_data->name)) {
+ to.writeString(m_data->name);
+ } else {
+ to.writeSymbol(m_data->name);
+ }
+ }
+ to.writeSymbol("{");
+ to.writeNewline();
+ to.pushIndent();
+ AnyTable& table = *(m_data->value.t);
+ Array<std::string> keys;
+ table.getKeys(keys);
+ keys.sort();
+
+ for (int i = 0; i < keys.size(); ++i) {
+
+ to.writeSymbol(keys[i]);
+ to.writeSymbol("=");
+ table[keys[i]].serialize(to);
+
+ if (i < keys.size() - 1) {
+ to.writeSymbol(",");
+ }
+ to.writeNewline();
+
+ // Skip a line between table entries
+ to.writeNewline();
+ }
+
+ to.popIndent();
+ to.writeSymbol("}");
+ break;
+ }
+
+ case ARRAY: {
+ debugAssert(m_data != NULL);
+ if (! m_data->name.empty()) {
+ // For arrays, leave no trailing space between the name and the paren
+ to.writeSymbol(format("%s(", m_data->name.c_str()));
+ } else {
+ to.writeSymbol("(");
+ }
+ to.writeNewline();
+ to.pushIndent();
+ Array<Any>& array = *(m_data->value.a);
+ for (int ii = 0; ii < size(); ++ii) {
+ array[ii].serialize(to);
+ if (ii < size() - 1) {
+ to.writeSymbol(",");
+ to.writeNewline();
+ }
+
+ // Put the close paren on an array right behind the last element
+ }
+ to.popIndent();
+ to.writeSymbol(")");
+ break;
+ }
+ }
+}
+
+
+void Any::deserializeComment(TextInput& ti, Token& token, std::string& comment) {
+ // Parse comments
+ while (token.type() == Token::COMMENT) {
+ comment += trimWhitespace(token.string()) + "\n";
+
+ // Allow comments to contain newlines.
+ do {
+ token = ti.read();
+ comment += "\n";
+ } while (token.type() == Token::NEWLINE);
+ }
+
+ comment = trimWhitespace(comment);
+}
+
+/** True if \a c is an open paren of some form */
+static bool isOpen(const char c) {
+ return c == '(' || c == '[' || c == '{';
+}
+
+
+/** True if \a c is an open paren of some form */
+static bool isClose(const char c) {
+ return c == ')' || c == ']' || c == '}';
+}
+
+
+/** True if \a s is a C++ name operator */
+static bool isNameOperator(const std::string& s) {
+ return s == "." || s == "::" || s == "->";
+}
+
+
+void Any::deserializeName(TextInput& ti, Token& token, std::string& name) {
+ debugAssert(token.type() == Token::SYMBOL);
+ std::string s = token.string();
+ while (! isOpen(s[0])) {
+ name += s;
+
+ // Skip newlines and comments
+ token = ti.readSignificant();
+
+ if (token.type() != Token::SYMBOL) {
+ throw ParseError(ti.filename(), token.line(), token.character(),
+ "Expected symbol while parsing Any");
+ }
+ s = token.string();
+ }
+}
+
+
+void Any::deserialize(TextInput& ti) {
+ beforeRead();
+ Token token = ti.read();
+ deserialize(ti, token);
+ // Restore the last token
+ ti.push(token);
+}
+
+
+void Any::deserialize(TextInput& ti, Token& token) {
+ // Deallocate old data
+ dropReference();
+ m_type = NONE;
+ m_simpleValue.b = false;
+
+ // Skip leading newlines
+ while (token.type() == Token::NEWLINE) {
+ token = ti.read();
+ }
+
+ std::string comment;
+ if (token.type() == Token::COMMENT) {
+ deserializeComment(ti, token, comment);
+ }
+
+ if (token.type() == Token::END) {
+ // There should never be a comment without an Any following it; even
+ // if the file ends with some commented out stuff,
+ // that should not happen after a comma, so we'd never read that
+ // far in a proper file.
+ throw ParseError(ti.filename(), token.line(), token.character(),
+ "File ended without a properly formed Any");
+ }
+
+ switch (token.type()) {
+ case Token::STRING:
+ m_type = STRING;
+ ensureData();
+ *(m_data->value.s) = token.string();
+ m_data->source.set(ti, token);
+ break;
+
+ case Token::NUMBER:
+ m_type = NUMBER;
+ m_simpleValue.n = token.number();
+ ensureData();
+ m_data->source.set(ti, token);
+ break;
+
+ case Token::BOOLEAN:
+ m_type = BOOLEAN;
+ m_simpleValue.b = token.boolean();
+ ensureData();
+ m_data->source.set(ti, token);
+ break;
+
+ case Token::SYMBOL:
+ // Named Array, Named Table, Array, Table, or NONE
+ if (toUpper(token.string()) == "NONE") {
+ // Nothing left to do; we initialized to NONE originally
+ ensureData();
+ m_data->source.set(ti, token);
+ } else {
+ // Array or Table
+
+ // Parse the name
+
+ // s must have at least one element or this would not have
+ // been parsed as a symbol
+ std::string name;
+ deserializeName(ti, token, name);
+ if (token.type() != Token::SYMBOL) {
+ throw ParseError(ti.filename(), token.line(), token.character(),
+ "Malformed Any TABLE or ARRAY; must start with [, (, or {");
+ }
+
+ if (isOpen(token.string()[0])) {
+ // Array or table
+ deserializeBody(ti, token);
+ } else {
+ throw ParseError(ti.filename(), token.line(), token.character(),
+ "Malformed Any TABLE or ARRAY; must start with [, (, or {");
+ }
+
+ if (! name.empty()) {
+ ensureData();
+ m_data->name = name;
+ }
+ } // if NONE
+ break;
+
+ default:
+ throw ParseError(ti.filename(), token.line(), token.character(),
+ "Unexpected token");
+
+ } // switch
+
+ if (! comment.empty()) {
+ ensureData();
+ m_data->comment = comment;
+ }
+
+ if (m_type != ARRAY && m_type != TABLE) {
+ // Array and table already consumed their last token
+ token = ti.read();
+ }
+}
+
+
+void Any::ensureData() {
+ if (m_data == NULL) {
+ m_data = Data::create(m_type);
+ }
+}
+
+
+static bool isSeparator(char c) {
+ return c == ',' || c == ';';
+}
+
+
+void Any::readUntilCommaOrClose(TextInput& ti, Token& token) {
+ while (! ((token.type() == Token::SYMBOL) &&
+ (isClose(token.string()[0])) ||
+ isSeparator(token.string()[0]))) {
+ switch (token.type()) {
+ case Token::NEWLINE:
+ case Token::COMMENT:
+ // Consume
+ token = ti.read();
+ break;
+
+ default:
+ throw ParseError(ti.filename(), token.line(), token.character(),
+ "Expected a comma or close paren");
+ }
+ }
+}
+
+
+void Any::deserializeBody(TextInput& ti, Token& token) {
+ char closeSymbol = '}';
+ m_type = TABLE;
+
+ const char c = token.string()[0];
+
+ if (c != '{') {
+ m_type = ARRAY;
+ // Chose the appropriate close symbol
+ closeSymbol = (c == '(') ? ')' : ']';
+ }
+
+ // Allocate the underlying data structure
+ ensureData();
+ m_data->source.set(ti, token);
+
+ // Consume the open token
+ token = ti.read();
+
+ while (! ((token.type() == Token::SYMBOL) && (token.string()[0] == closeSymbol))) {
+
+ // Read any leading comment. This must be done here (and not in the recursive deserialize
+ // call) in case the body contains only a comment.
+ std::string comment;
+ deserializeComment(ti, token, comment);
+
+ if ((token.type() == Token::SYMBOL) && (token.string()[0] == closeSymbol)) {
+ // We're done; this catches the case where the array is empty
+ break;
+ }
+
+ // Pointer the value being read
+ Any a = NULL;
+ std::string key;
+
+ if (m_type == TABLE) {
+ // Read the key
+ if (token.type() != Token::SYMBOL && token.type() != Token::STRING) {
+ throw ParseError(ti.filename(), token.line(), token.character(), "Expected a name");
+ }
+
+ key = token.string();
+ // Consume everything up to the = sign
+ token = ti.readSignificant();
+
+ if ((token.type() != Token::SYMBOL) || (token.string() != "=")) {
+ throw ParseError(ti.filename(), token.line(), token.character(), "Expected =");
+ } else {
+ // Consume (don't consume comments--we want the value pointed to by a to get those).
+ token = ti.read();
+ }
+ }
+ a.deserialize(ti, token);
+
+ if (! comment.empty()) {
+ // Prepend the comment we read earlier
+ a.ensureData();
+ a.m_data->comment = trimWhitespace(comment + "\n" + a.m_data->comment);
+ }
+
+ if (m_type == TABLE) {
+ set(key, a);
+ } else {
+ append(a);
+ }
+
+ // Read until the comma or close paren, discarding trailing comments and newlines
+ readUntilCommaOrClose(ti, token);
+
+ // Consume the comma
+ if (isSeparator(token.string()[0])) {
+ token = ti.read();
+ }
+ }
+
+ // Consume the close paren (to match other deserialize methods)
+ token = ti.read();
+}
+
+
+Any::operator int() const {
+ beforeRead();
+ return iRound(number());
+}
+
+
+Any::operator float() const {
+ beforeRead();
+ return float(number());
+}
+
+
+Any::operator double() const {
+ beforeRead();
+ return number();
+}
+
+
+Any::operator bool() const {
+ beforeRead();
+ return boolean();
+}
+
+
+Any::operator std::string() const {
+ beforeRead();
+ return string();
+}
+
+
+const Any::Source& Any::source() const {
+ static Source s;
+ if (m_data) {
+ return m_data->source;
+ } else {
+ return s;
+ }
+}
+
+
+void Any::verify(bool value, const std::string& message) const {
+ beforeRead();
+ if (! value) {
+ ParseError p;
+ if (m_data) {
+ p.filename = m_data->source.filename;
+ p.line = m_data->source.line;
+ p.character = m_data->source.character;
+ }
+
+ if (name().empty()) {
+ p.message = "Parse error";
+ } else {
+ p.message = "Parse error while reading the contents of " + name();
+ }
+
+ if (! message.empty()) {
+ p.message = p.message + ": " + message;
+ }
+
+ throw p;
+ }
+}
+
+
+void Any::verifyName(const std::string& n) const {
+ beforeRead();
+ verify(beginsWith(toUpper(name()), toUpper(n)), "Name must begin with " + n);
+}
+
+
+void Any::verifyType(Type t) const {
+ beforeRead();
+ if (type() != t) {
+ verify(false, "Must have type " + toString(t));
+ }
+}
+
+
+void Any::verifyType(Type t0, Type t1) const {
+ beforeRead();
+ if (type() != t0 && type() != t1) {
+ verify(false, "Must have type " + toString(t0) + " or " + toString(t1));
+ }
+}
+
+
+void Any::verifySize(int low, int high) const {
+ beforeRead();
+ verifyType(ARRAY, TABLE);
+ if (size() < low || size() > high) {
+ verify(false, format("Size must be between %d and %d", low, high));
+ }
+}
+
+
+void Any::verifySize(int s) const {
+ beforeRead();
+ verifyType(ARRAY, TABLE);
+ if (size() != s) {
+ verify(false, format("Size must be %d", s));
+ }
+}
+
+
+std::string Any::toString(Type t) {
+ switch(t) {
+ case NONE: return "NONE";
+ case BOOLEAN: return "BOOLEAN";
+ case NUMBER: return "NUMBER";
+ case STRING: return "STRING";
+ case ARRAY: return "ARRAY";
+ case TABLE: return "TABLE";
+ default:
+ alwaysAssertM(false, "Illegal Any::Type");
+ return "";
+ }
+}
+
+} // namespace G3D
+
diff --git a/dep/src/g3dlite/AnyVal.cpp b/dep/src/g3dlite/AnyVal.cpp
new file mode 100644
index 00000000000..7b98486523a
--- /dev/null
+++ b/dep/src/g3dlite/AnyVal.cpp
@@ -0,0 +1,1379 @@
+/**
+ @file AnyVal.cpp
+ @author Morgan McGuire
+ @maintainer Morgan McGuire
+ @created 2006-06-11
+ @edited 2008-07-14
+ */
+
+#include "G3D/AnyVal.h"
+#include "G3D/Array.h"
+#include "G3D/stringutils.h"
+#include "G3D/Table.h"
+#include "G3D/Vector2.h"
+#include "G3D/Vector3.h"
+#include "G3D/Vector4.h"
+#include "G3D/Color1.h"
+#include "G3D/Color3.h"
+#include "G3D/Color4.h"
+#include "G3D/Matrix2.h"
+#include "G3D/Matrix3.h"
+#include "G3D/Matrix4.h"
+#include "G3D/Rect2D.h"
+#include "G3D/AABox.h"
+#include "G3D/CoordinateFrame.h"
+#include "G3D/Quat.h"
+#include "G3D/TextInput.h"
+#include "G3D/TextOutput.h"
+#include "G3D/BinaryInput.h"
+#include "G3D/BinaryOutput.h"
+
+namespace G3D {
+
+AnyVal AnyVal::fromFile(const std::string& filename) {
+ TextInput t(filename);
+ return AnyVal(t);
+}
+
+
+void AnyVal::load(const std::string& filename) {
+ *this = fromFile(filename);
+}
+
+
+void AnyVal::save(const std::string& filename) const {
+ TextOutput t(filename);
+ serialize(t);
+ t.commit();
+}
+
+
+AnyVal::AnyVal() : m_type(NIL), m_value(NULL), m_referenceCount(NULL) {
+}
+
+
+AnyVal::AnyVal(bool b) : m_type(BOOLEAN), m_value(new bool(b)), m_referenceCount(NULL) {
+}
+
+
+AnyVal::AnyVal(G3D::TextInput& t) : m_type(NIL), m_value(NULL), m_referenceCount(NULL) {
+ deserialize(t);
+}
+
+
+/*AnyVal::AnyVal(G3D::BinaryInput& b) {
+ deserialize(b);
+}
+*/
+
+AnyVal::AnyVal(double v) : m_type(NUMBER), m_referenceCount(NULL) {
+ m_value = new double(v);
+}
+
+
+AnyVal::AnyVal(int v) : m_type(NUMBER), m_referenceCount(NULL) {
+ m_value = new double(v);
+}
+
+
+AnyVal::AnyVal(const Rect2D& v) : m_type(RECT2D), m_referenceCount(NULL) {
+ m_value = new Rect2D(v);
+}
+
+
+AnyVal::AnyVal(const AABox& v) : m_type(AABOX), m_referenceCount(NULL) {
+ m_value = new AABox(v);
+}
+
+
+AnyVal::AnyVal(const Vector2& v) : m_type(VECTOR2), m_referenceCount(NULL) {
+ m_value = new Vector2(v);
+}
+
+
+AnyVal::AnyVal(const Vector3& v) : m_type(VECTOR3), m_referenceCount(NULL) {
+ m_value = new Vector3(v);
+}
+
+
+AnyVal::AnyVal(const Vector4& v) : m_type(VECTOR4), m_referenceCount(NULL) {
+ m_value = new Vector4(v);
+}
+
+
+AnyVal::AnyVal(const Color1& v) : m_type(COLOR1), m_referenceCount(NULL) {
+ m_value = new Color1(v);
+}
+
+
+AnyVal::AnyVal(const Color3& v) : m_type(COLOR3), m_referenceCount(NULL) {
+ m_value = new Color3(v);
+}
+
+
+AnyVal::AnyVal(const Color4& v) : m_type(COLOR4), m_referenceCount(NULL) {
+ m_value = new Color4(v);
+}
+
+
+AnyVal::AnyVal(const std::string& v) : m_type(STRING), m_referenceCount(NULL) {
+ m_value = new std::string(v);
+}
+
+
+AnyVal::AnyVal(const char* v) : m_type(STRING), m_referenceCount(NULL) {
+ m_value = new std::string(v);
+}
+
+
+AnyVal::AnyVal(const Quat& v) : m_type(QUAT), m_referenceCount(NULL) {
+ m_value = new Quat(v);
+}
+
+
+AnyVal::AnyVal(const CoordinateFrame& v) : m_type(COORDINATEFRAME), m_referenceCount(NULL) {
+ m_value = new CoordinateFrame(v);
+}
+
+
+AnyVal::AnyVal(const Matrix2& v) : m_type(MATRIX2), m_referenceCount(NULL) {
+ m_value = new Matrix2(v);
+}
+
+AnyVal::AnyVal(const Matrix3& v) : m_type(MATRIX3), m_referenceCount(NULL) {
+ m_value = new Matrix3(v);
+}
+
+
+AnyVal::AnyVal(const Matrix4& v) : m_type(MATRIX4), m_referenceCount(NULL) {
+ m_value = new Matrix4(v);
+}
+
+
+AnyVal::AnyVal(const AnyVal& c) : m_type(NIL), m_value(NULL), m_referenceCount(NULL) {
+ *this = c;
+}
+
+
+AnyVal::AnyVal(Type arrayOrTable) : m_type(NIL), m_value(NULL), m_referenceCount(new int(1)) {
+ // TODO: make AnyVal::createArray()
+ switch (arrayOrTable) {
+ case ARRAY:
+ m_type = ARRAY;
+ m_value = new Array<AnyVal>();
+ break;
+
+ case TABLE:
+ m_type = TABLE;
+ m_value = new Table<std::string, AnyVal>();
+ break;
+
+ default:
+ debugAssertM(false, "Cannot construct AnyVal from constants except ARRAY or TABLE.");
+ }
+}
+
+
+AnyVal::~AnyVal() {
+ deleteValue();
+}
+
+
+void AnyVal::deleteValue() {
+ if (m_referenceCount) {
+ --(*m_referenceCount);
+ if (*m_referenceCount <= 0) {
+ delete m_referenceCount;
+ m_referenceCount = NULL;
+ // Pass through and delete the real object now
+ } else {
+ // Someone else is holding a reference, so we can't delete
+ // the object.
+ m_referenceCount = NULL;
+ return;
+ }
+ }
+
+ switch (m_type) {
+ case NIL:
+ // Nothing to do
+ break;
+
+ case NUMBER:
+ delete (double*)m_value;
+ break;
+
+ case BOOLEAN:
+ delete (bool*)m_value;
+ break;
+
+ case STRING:
+ delete (std::string*)m_value;
+ break;
+
+ case RECT2D:
+ delete (Rect2D*)m_value;
+ break;
+
+ case AABOX:
+ delete (AABox*)m_value;
+ break;
+
+ case VECTOR2:
+ delete (Vector2*)m_value;
+ break;
+
+ case VECTOR3:
+ delete (Vector3*)m_value;
+ break;
+
+ case VECTOR4:
+ delete (Vector4*)m_value;
+ break;
+
+ case MATRIX2:
+ delete (Matrix2*)m_value;
+ break;
+
+ case MATRIX3:
+ delete (Matrix3*)m_value;
+ break;
+
+ case MATRIX4:
+ delete (Matrix4*)m_value;
+ break;
+
+ case QUAT:
+ delete (Quat*)m_value;
+ break;
+
+ case COORDINATEFRAME:
+ delete (CoordinateFrame*)m_value;
+ break;
+
+ case COLOR1:
+ delete (Color1*)m_value;
+ break;
+
+ case COLOR3:
+ delete (Color3*)m_value;
+ break;
+
+ case COLOR4:
+ delete (Color4*)m_value;
+ break;
+
+ case ARRAY:
+ delete (Array<AnyVal>*)m_value;
+ break;
+
+ case TABLE:
+ delete (Table<std::string, AnyVal>*)m_value;
+ break;
+
+ default:
+ debugAssertM(false, "Internal error: no destructor for this type.");
+ }
+
+ m_value = NULL;
+}
+
+
+AnyVal& AnyVal::operator=(const AnyVal& v) {
+ deleteValue();
+
+ m_type = v.m_type;
+
+ m_referenceCount = v.m_referenceCount;
+
+ if (isSharedType()) {
+ ++(*m_referenceCount);
+ m_value = v.m_value;
+ } else {
+ m_value = v.copyValue();
+ }
+
+ return *this;
+}
+
+
+void* AnyVal::copyValue() const {
+ switch (m_type) {
+ case NIL:
+ return NULL;
+
+ case NUMBER:
+ return new double(*(double*)m_value);
+
+ case BOOLEAN:
+ return new bool(*(bool*)m_value);
+
+ case STRING:
+ return new std::string(*(std::string*)m_value);
+
+ case RECT2D:
+ return new Rect2D(*(Rect2D*)m_value);
+
+ case AABOX:
+ return new AABox(*(AABox*)m_value);
+
+ case VECTOR2:
+ return new Vector2(*(Vector2*)m_value);
+
+ case VECTOR3:
+ return new Vector3(*(Vector3*)m_value);
+
+ case VECTOR4:
+ return new Vector4(*(Vector4*)m_value);
+
+ case MATRIX2:
+ return new Matrix2(*(Matrix2*)m_value);
+
+ case MATRIX3:
+ return new Matrix3(*(Matrix3*)m_value);
+
+ case MATRIX4:
+ return new Matrix4(*(Matrix4*)m_value);
+
+ case QUAT:
+ return new Quat(*(Quat*)m_value);
+
+ case COORDINATEFRAME:
+ return new CoordinateFrame(*(CoordinateFrame*)m_value);
+
+ case COLOR1:
+ return new Color1(*(Color1*)m_value);
+
+ case COLOR3:
+ return new Color3(*(Color3*)m_value);
+
+ case COLOR4:
+ return new Color4(*(Color4*)m_value);
+
+ case ARRAY:
+ return new Array<AnyVal>(*(Array<AnyVal>*)m_value);
+
+ case TABLE:
+ return new Table<std::string, AnyVal>(*(Table<std::string, AnyVal>*)m_value);
+
+ default:
+ debugAssertM(false, "Internal error: no assignment operator for this type.");
+ return NULL;
+ }
+}
+
+AnyVal::Type AnyVal::type() const {
+ return m_type;
+}
+
+
+static bool legalIdentifier(const std::string& s) {
+ if (s.size() == 0) {
+ return false;
+ }
+
+ if (! isLetter(s[0]) || (s[0] == '_')) {
+ return false;
+ }
+
+ bool ok = true;
+
+ for (unsigned int i = 1; i < s.size(); ++i) {
+ ok &= isDigit(s[i]) || isLetter(s[i]) || (s[i] == '_');
+ }
+
+ return ok;
+}
+
+
+void AnyVal::serialize(G3D::TextOutput& t) const {
+ switch (m_type) {
+ case NIL:
+ t.writeSymbol("Nil");
+ break;
+
+ case NUMBER:
+ t.printf("%g", *(double*)m_value);
+ break;
+
+ case BOOLEAN:
+ t.writeBoolean(*(bool*)m_value);
+ break;
+
+ case STRING:
+ t.writeString(*(std::string*)m_value);
+ break;
+
+ case RECT2D:
+ t.printf("R(%g, %g, %g, %g)", ((Rect2D*)m_value)->x0(), ((Rect2D*)m_value)->y0(),
+ ((Rect2D*)m_value)->width(), ((Rect2D*)m_value)->height());
+ break;
+
+ case AABOX:
+ t.printf("AAB(V3(%g, %g, %g), V3(%g, %g, %g))",
+ aabox().low().x,
+ aabox().low().y,
+ aabox().low().z,
+ aabox().high().x,
+ aabox().high().y,
+ aabox().high().z);
+ break;
+
+ case VECTOR2:
+ t.printf("V2(%g, %g)", ((Vector2*)m_value)->x, ((Vector2*)m_value)->y);
+ break;
+
+ case VECTOR3:
+ t.printf("V3(%g, %g, %g)", ((Vector3*)m_value)->x, ((Vector3*)m_value)->y, ((Vector3*)m_value)->z);
+ break;
+
+ case VECTOR4:
+ t.printf("V4(%g, %g, %g, %g)", ((Vector4*)m_value)->x, ((Vector4*)m_value)->y, ((Vector4*)m_value)->z, ((Vector4*)m_value)->w);
+ break;
+
+ case MATRIX2:
+ {
+ const Matrix2& m = *(Matrix2*)m_value;
+ t.printf("M2(\n");
+ t.pushIndent();
+ t.printf("%10.5f, %10.5f,\n%10.5f, %10.5f)",
+ m[0][0], m[0][1],
+ m[1][0], m[1][1]);
+ t.popIndent();
+ }
+ break;
+
+ case MATRIX3:
+ {
+ const Matrix3& m = *(Matrix3*)m_value;
+ t.printf("M3(\n");
+ t.pushIndent();
+ t.printf("%10.5f, %10.5f, %10.5f,\n%10.5f, %10.5f, %10.5f,\n%10.5f, %10.5f, %10.5f)",
+ m[0][0], m[0][1], m[0][2],
+ m[1][0], m[1][1], m[1][2],
+ m[2][0], m[2][1], m[2][2]);
+ t.popIndent();
+ }
+ break;
+
+ case MATRIX4:
+ {
+ const Matrix4& m = *(Matrix4*)m_value;
+ t.printf("M4(\n");
+ t.pushIndent();
+ t.printf(
+ "%10.5f, %10.5f, %10.5f, %10.5f,\n"
+ "%10.5f, %10.5f, %10.5f, %10.5f,\n"
+ "%10.5f, %10.5f, %10.5f, %10.5f,\n"
+ "%10.5f, %10.5f, %10.5f, %10.5f)",
+ m[0][0], m[0][1], m[0][2], m[0][3],
+ m[1][0], m[1][1], m[1][2], m[1][3],
+ m[2][0], m[2][1], m[2][2], m[2][3],
+ m[3][0], m[3][1], m[3][2], m[3][3]);
+ t.popIndent();
+ }
+ break;
+
+ case QUAT:
+ t.printf("Q(%g, %g, %g, %g)", ((Quat*)m_value)->x, ((Quat*)m_value)->y, ((Quat*)m_value)->z, ((Quat*)m_value)->w);
+ break;
+
+ case COORDINATEFRAME:
+ {
+ const CoordinateFrame& c = *(CoordinateFrame*)m_value;
+ float x,y,z,yaw,pitch,roll;
+ c.getXYZYPRDegrees(x,y,z,yaw,pitch,roll);
+ t.printf("CF(V3(%g,%g,%g), %g, %g, %g)", x, y, z, yaw, pitch, roll);
+ /*
+ t.pushIndent();
+ t.printf(
+ "CF(\n%10.5f, %10.5f, %10.5f, %10.5f,\n"
+ "%10.5f, %10.5f, %10.5f, %10.5f,\n"
+ "%10.5f, %10.5f, %10.5f, %10.5f)",
+ c.rotation[0][0], c.rotation[0][1], c.rotation[0][2], c.translation.x,
+ c.rotation[1][0], c.rotation[1][1], c.rotation[1][2], c.translation.y,
+ c.rotation[2][0], c.rotation[2][1], c.rotation[2][2], c.translation.z);
+ t.popIndent();
+ */
+ }
+ break;
+
+ case COLOR1:
+ t.printf("C1(%g)", ((Color1*)m_value)->value);
+ break;
+
+ case COLOR3:
+ t.printf("C3(%g, %g, %g)", ((Color3*)m_value)->r, ((Color3*)m_value)->g, ((Color3*)m_value)->b);
+ break;
+
+ case COLOR4:
+ t.printf("C4(%g, %g, %g, %g)", ((Color4*)m_value)->r, ((Color4*)m_value)->g, ((Color4*)m_value)->b, ((Color4*)m_value)->a);
+ break;
+
+ case ARRAY:
+ {
+ const Array<AnyVal>& a = *(Array<AnyVal>*)m_value;
+ t.printf("[\n");
+ t.pushIndent();
+ for (int i = 0; i < a.size(); ++i) {
+ a[i].serialize(t);
+ if (i != a.size() - 1) {
+ t.printf(", \n");
+ }
+ }
+ t.printf("]");
+ t.popIndent();
+ }
+ break;
+
+ case TABLE:
+ {
+ const Table<std::string, AnyVal>& a = *(Table<std::string, AnyVal>*)m_value;
+ t.printf("{\n");
+ t.pushIndent();
+ Table<std::string, AnyVal>::Iterator i = a.begin();
+ const Table<std::string, AnyVal>::Iterator end = a.end();
+ while (i != end) {
+ // Quote names that are not legal C++ identifiers
+ if (! legalIdentifier(i->key)) {
+ t.printf("'%s' ", i->key.c_str());
+ } else {
+ t.writeSymbol(i->key);
+ }
+ t.printf("= ");
+
+ i->value.serialize(t);
+
+ if (i != end) {
+ t.printf("\n");
+ }
+ ++i;
+ }
+ t.popIndent();
+ t.printf("}");
+ }
+ break;
+
+ default:
+ debugAssertM(false, "Internal error: no serialize method for this type.");
+ }
+}
+
+
+std::string AnyVal::toString() const {
+ TextOutput t;
+ serialize(t);
+ std::string s;
+ t.commitString(s);
+ return s;
+}
+
+void AnyVal::deserialize(G3D::TextInput& t) {
+ deleteValue();
+ m_type = NIL;
+ m_value = NULL;
+
+ if (! t.hasMore()) {
+ return;
+ }
+
+ switch (t.peek().type()) {
+ case Token::END:
+ // should never get here because of the hasMore check above
+ return;
+ break;
+
+ case Token::NUMBER:
+ m_type = NUMBER;
+ m_value = new double(t.readNumber());
+ break;
+
+ case Token::STRING:
+ m_type = STRING;
+ m_value = new std::string(t.readString());
+ break;
+
+ case Token::NEWLINE:
+ m_type = STRING;
+ m_value = new std::string(t.readNewline());
+ break;
+
+ case Token::COMMENT:
+ m_type = STRING;
+ m_value = new std::string(t.readComment());
+ break;
+
+ case Token::BOOLEAN:
+ m_type = BOOLEAN;
+ m_value = new bool(t.readBoolean());
+ break;
+
+ case Token::SYMBOL:
+ {
+ std::string s = t.readSymbol();
+ if (s == "NIL") {
+ break;
+
+ } else if (s == "true") {
+
+ m_type = BOOLEAN;
+ m_value = new bool(true);
+
+ } else if (s == "false") {
+
+ m_type = BOOLEAN;
+ m_value = new bool(false);
+
+ } else if (s == "R") {
+
+ m_type = RECT2D;
+ t.readSymbol("(");
+ float x,y,w,h;
+ x = (float)t.readNumber();
+ t.readSymbol(",");
+ y = (float)t.readNumber();
+ t.readSymbol(",");
+ w = (float)t.readNumber();
+ t.readSymbol(",");
+ h = (float)t.readNumber();
+ t.readSymbol(")");
+ m_value = new Rect2D(Rect2D::xywh(x, y, w, h));
+
+ } else if (s == "AAB") {
+
+ m_type = AABOX;
+ Vector3 v[2];
+ t.readSymbol("(");
+ for (int i = 0; i < 2; ++i) {
+ t.readSymbols("V3", "(");
+ v[i].x = (float)t.readNumber();
+ t.readSymbol(",");
+ v[i].y = (float)t.readNumber();
+ t.readSymbol(",");
+ v[i].z = (float)t.readNumber();
+ t.readSymbol(",");
+ if (i == 0) {
+ t.readSymbol(",");
+ }
+ }
+ t.readSymbol(")");
+ m_value = new AABox(v[0], v[1]);
+
+ } else if (s == "V2") {
+
+ t.readSymbol("(");
+ Vector2 v;
+ v.x = (float)t.readNumber();
+ t.readSymbol(",");
+ v.y = (float)t.readNumber();
+ t.readSymbol(")");
+ m_value = new Vector2(v);
+ m_type = VECTOR2;
+
+ } else if (s == "V3") {
+
+ t.readSymbol("(");
+ Vector3 v;
+ v.x = (float)t.readNumber();
+ t.readSymbol(",");
+ v.y = (float)t.readNumber();
+ t.readSymbol(",");
+ v.z = (float)t.readNumber();
+ t.readSymbol(")");
+ m_value = new Vector3(v);
+ m_type = VECTOR3;
+
+ } else if (s == "V4") {
+
+ t.readSymbol("(");
+ Vector4 v;
+ v.x = (float)t.readNumber();
+ t.readSymbol(",");
+ v.y = (float)t.readNumber();
+ t.readSymbol(",");
+ v.z = (float)t.readNumber();
+ t.readSymbol(",");
+ v.w = (float)t.readNumber();
+ t.readSymbol(")");
+ m_value = new Vector4(v);
+ m_type = VECTOR4;
+
+ } else if (s == "M2") {
+
+ t.readSymbol("(");
+ Matrix2 m;
+ for (int r = 0; r < 2; ++r) {
+ for (int c = 0; c < 2; ++c) {
+ m[r][c] = (float)t.readNumber();
+ if ((c != 1) || (r != 1)) {
+ t.readSymbol(",");
+ }
+ }
+ }
+ t.readSymbol(")");
+ m_value = new Matrix2(m);
+ m_type = MATRIX2;
+
+ } else if (s == "M3") {
+
+ t.readSymbol("(");
+ Matrix3 m;
+ for (int r = 0; r < 3; ++r) {
+ for (int c = 0; c < 3; ++c) {
+ m[r][c] = (float)t.readNumber();
+ if ((c != 2) || (r != 2)) {
+ t.readSymbol(",");
+ }
+ }
+ }
+ t.readSymbol(")");
+ m_value = new Matrix3(m);
+ m_type = MATRIX3;
+
+ } else if (s == "M4") {
+
+ t.readSymbol("(");
+ Matrix4 m;
+ for (int r = 0; r < 4; ++r) {
+ for (int c = 0; c < 4; ++c) {
+ m[r][c] = (float)t.readNumber();
+ if ((c != 3) || (r != 3)) {
+ t.readSymbol(",");
+ }
+ }
+ }
+ t.readSymbol(")");
+ m_value = new Matrix4(m);
+ m_type = MATRIX4;
+
+ } else if (s == "Q") {
+
+ t.readSymbol("(");
+ Quat q;
+ q.x = (float)t.readNumber();
+ t.readSymbol(",");
+ q.y = (float)t.readNumber();
+ t.readSymbol(",");
+ q.z = (float)t.readNumber();
+ t.readSymbol(",");
+ q.w = (float)t.readNumber();
+ t.readSymbol(")");
+ m_value = new Quat(q);
+ m_type = QUAT;
+
+ } else if (s == "CF") {
+
+ t.readSymbol("(");
+ CoordinateFrame m;
+ if (t.peek().type() == Token::SYMBOL) {
+ // Angle format
+ float x, y, z, yaw, roll, pitch;
+ t.readSymbols("V3", "(");
+ x = (float)t.readNumber();
+ t.readSymbol(",");
+ y = (float)t.readNumber();
+ t.readSymbol(",");
+ z = (float)t.readNumber();
+ t.readSymbols(")", ",");
+ yaw = (float)t.readNumber();
+ t.readSymbol(",");
+ pitch = (float)t.readNumber();
+ roll = 0;
+ if (t.peek().string() == ",") {
+ t.readSymbol(",");
+ roll = (float)t.readNumber();
+ }
+ m = CoordinateFrame::fromXYZYPRDegrees(x, y, z, yaw, pitch, roll);
+ } else {
+ // Matrix format
+ for (int r = 0; r < 3; ++r) {
+ for (int c = 0; c < 3; ++c) {
+ m.rotation[r][c] = (float)t.readNumber();
+ }
+ m.translation[r] = (float)t.readNumber();
+ if (r != 2) {
+ t.readSymbol(",");
+ }
+ }
+ }
+ t.readSymbol(")");
+ m_value = new CoordinateFrame(m);
+ m_type = COORDINATEFRAME;
+
+ } else if (s == "C1") {
+
+ t.readSymbol("(");
+ float v = (float)t.readNumber();
+ t.readSymbol(")");
+ m_value = new Color1(v);
+ m_type = COLOR1;
+
+ } else if (s == "C3") {
+
+ t.readSymbol("(");
+ Color3 c;
+ c.r = (float)t.readNumber();
+ t.readSymbol(",");
+ c.g = (float)t.readNumber();
+ t.readSymbol(",");
+ c.b = (float)t.readNumber();
+ t.readSymbol(")");
+ m_value = new Color3(c);
+ m_type = COLOR3;
+
+ } else if (s == "C4") {
+
+ t.readSymbol("(");
+ Color4 c;
+ c.r = (float)t.readNumber();
+ t.readSymbol(",");
+ c.g = (float)t.readNumber();
+ t.readSymbol(",");
+ c.b = (float)t.readNumber();
+ t.readSymbol(",");
+ c.a = (float)t.readNumber();
+ t.readSymbol(")");
+ m_value = new Color4(c);
+ m_type = COLOR4;
+
+ } else if (s == "[") {
+
+ // Array
+ m_type = ARRAY;
+ m_value = new Array<AnyVal>();
+ m_referenceCount = new int(1);
+ Array<AnyVal>& a = *(Array<AnyVal>*)m_value;
+
+ Token peek = t.peek();
+ while ((peek.type() != Token::SYMBOL) || (peek.string() != "]")) {
+ // Avoid copying large objects
+ a.next().deserialize(t);
+
+ peek = t.peek();
+ if (peek.type() != Token::SYMBOL) {
+ throw CorruptText("Expected ',' or ']'", peek);
+ } else if (peek.string() == ",") {
+ t.readSymbol(",");
+ } else if (peek.string() != "]") {
+ throw CorruptText("Missing ']'", peek);
+ }
+ }
+ t.readSymbol("]");
+
+ } else if (s == "{") {
+
+ // Table
+ m_type = TABLE;
+ m_value = new Table<std::string, AnyVal>();
+ m_referenceCount = new int(1);
+ Table<std::string, AnyVal>& a = *(Table<std::string, AnyVal>*)m_value;
+
+ Token peek = t.peek();
+ while ((peek.type() != Token::SYMBOL) || (peek.string() != "}")) {
+
+ std::string key;
+ // Get the name
+ if (peek.type() == Token::SYMBOL) {
+ key = t.readSymbol();
+ } else if (peek.extendedType() == Token::SINGLE_QUOTED_TYPE) {
+ key = t.readString();
+ } else {
+ throw CorruptText("Expected name inside table", peek);
+ }
+
+ t.readSymbol("=");
+
+ // Avoid copying large values
+ a.set(key, AnyVal());
+ a[key].deserialize(t);
+
+ peek = t.peek();
+ if ((peek.type() != Token::SYMBOL) && (peek.extendedType() != Token::SINGLE_QUOTED_TYPE)) {
+ throw CorruptText("Missing expected name or '}'", peek);
+ }
+ }
+ t.readSymbol("}");
+
+ } else {
+ throw CorruptText("Invalid value type.", t.peek());
+ } // dispatch on symbol type
+ } // scope
+ break;
+ }
+}
+
+
+AnyVal& AnyVal::operator[](const char* key) {
+ return this->operator[]((std::string)key);
+}
+
+
+const AnyVal& AnyVal::operator[](const char* key) const {
+ return this->operator[]((std::string)key);
+}
+
+
+AnyVal& AnyVal::operator[](const std::string& key) {
+ if (m_type != TABLE) {
+ throw WrongType(TABLE, m_type);
+ }
+
+ makeMutable();
+
+ Table<std::string, AnyVal>& t = *(Table<std::string, AnyVal>*)m_value;
+
+ if (! t.containsKey(key)) {
+ t.set(key, AnyVal());
+ }
+
+ return t[key];
+}
+
+
+const AnyVal& AnyVal::operator[](const std::string& key) const {
+ if (m_type != TABLE) {
+ throw WrongType(TABLE, m_type);
+ }
+
+ const Table<std::string, AnyVal>& t = *(const Table<std::string, AnyVal>*)m_value;
+
+ if (! t.containsKey(key)) {
+ throw KeyNotFound(key);
+ }
+
+ return t[key];
+}
+
+
+void AnyVal::append(const AnyVal& v) {
+ if (m_type != ARRAY) {
+ throw WrongType(ARRAY, m_type);
+ }
+ makeMutable();
+
+ Array<AnyVal>& a = *(Array<AnyVal>*)m_value;
+ a.append(v);
+}
+
+
+void AnyVal::getKeys(Array<std::string>& keys) const {
+ if (m_type != TABLE) {
+ throw WrongType(TABLE, m_type);
+ }
+
+ const Table<std::string, AnyVal>& t = *(const Table<std::string, AnyVal>*)m_value;
+ t.getKeys(keys);
+}
+
+
+int AnyVal::size() const {
+ switch (m_type) {
+ case TABLE:
+ {
+ const Table<std::string, AnyVal>& t = *(const Table<std::string, AnyVal>*)m_value;
+ return t.size();
+ }
+
+ case ARRAY:
+ {
+ const Array<AnyVal>& a = *(Array<AnyVal>*)m_value;
+ return a.size();
+ }
+
+ default:
+ throw WrongType(ARRAY, m_type);
+ }
+}
+
+
+AnyVal& AnyVal::operator[](int i) {
+ if (m_type != ARRAY) {
+ throw WrongType(ARRAY, m_type);
+ }
+ makeMutable();
+
+ Array<AnyVal>& a = *(Array<AnyVal>*)m_value;
+
+ if (i < 0) {
+ throw IndexOutOfBounds(i, a.size());
+ }
+
+ if (a.size() <= i) {
+ a.resize(i + 1);
+ }
+
+ return a[i];
+}
+
+
+const AnyVal& AnyVal::operator[](int i) const {
+ if (m_type != ARRAY) {
+ throw WrongType(ARRAY, m_type);
+ }
+
+ const Array<AnyVal>& a = *(Array<AnyVal>*)m_value;
+
+ if (a.size() <= i || i < 0) {
+ throw IndexOutOfBounds(i, a.size());
+ }
+
+ return a[i];
+}
+
+
+void AnyVal::makeMutable() {
+ if (*m_referenceCount > 1) {
+ // This is a shared instance
+ --(*m_referenceCount);
+ m_referenceCount = new int(1);
+ m_value = copyValue();
+ }
+}
+
+bool AnyVal::boolean() const {
+ if (m_type != BOOLEAN) {
+ throw WrongType(BOOLEAN, m_type);
+ }
+
+ return *(bool*)m_value;
+}
+
+
+bool AnyVal::boolean(bool defaultVal) const {
+ if (m_type != BOOLEAN) {
+ return defaultVal;
+ }
+
+ return *(bool*)m_value;
+}
+
+
+const std::string& AnyVal::string() const {
+ if (m_type != STRING) {
+ throw WrongType(STRING, m_type);
+ }
+
+ return *(std::string*)m_value;
+}
+
+
+const std::string& AnyVal::string(const std::string& defaultVal) const {
+ if (m_type != STRING) {
+ return defaultVal;
+ } else {
+ return *(std::string*)m_value;
+ }
+}
+
+
+double AnyVal::number() const {
+ if (m_type != NUMBER) {
+ throw WrongType(NUMBER, m_type);
+ }
+
+ return *(double*)m_value;
+}
+
+
+double AnyVal::number(double defaultVal) const {
+ if (m_type != NUMBER) {
+ return defaultVal;
+ } else {
+ return *(double*)m_value;
+ }
+}
+
+
+const Rect2D& AnyVal::rect2D() const {
+ if (m_type != RECT2D) {
+ throw WrongType(RECT2D, m_type);
+ }
+
+ return *(Rect2D*)m_value;
+}
+
+
+const Rect2D& AnyVal::rect2D(const Rect2D& defaultVal) const {
+ if (m_type != RECT2D) {
+ return defaultVal;
+ } else {
+ return *(Rect2D*)m_value;
+ }
+}
+
+
+const AABox& AnyVal::aabox() const {
+ if (m_type != AABOX) {
+ throw WrongType(AABOX, m_type);
+ }
+
+ return *(AABox*)m_value;
+}
+
+
+const AABox& AnyVal::aabox(const AABox& defaultVal) const {
+ if (m_type != AABOX) {
+ return defaultVal;
+ } else {
+ return *(AABox*)m_value;
+ }
+}
+
+
+const Color1& AnyVal::color1() const {
+ if (m_type != COLOR1) {
+ throw WrongType(COLOR1, m_type);
+ }
+
+ return *(Color1*)m_value;
+}
+
+
+const Color1& AnyVal::color1(const Color1& defaultVal) const {
+ if (m_type != COLOR1) {
+ return defaultVal;
+ } else {
+ return *(Color1*)m_value;
+ }
+}
+
+
+const Color3& AnyVal::color3() const {
+ if (m_type != COLOR3) {
+ throw WrongType(COLOR3, m_type);
+ }
+
+ return *(Color3*)m_value;
+}
+
+
+const Color3& AnyVal::color3(const Color3& defaultVal) const {
+ if (m_type != COLOR3) {
+ return defaultVal;
+ } else {
+ return *(Color3*)m_value;
+ }
+}
+
+
+const Color4& AnyVal::color4() const {
+ if (m_type != COLOR4) {
+ throw WrongType(COLOR4, m_type);
+ }
+
+ return *(Color4*)m_value;
+}
+
+
+const Color4& AnyVal::color4(const Color4& defaultVal) const {
+ if (m_type != COLOR4) {
+ return defaultVal;
+ } else {
+ return *(Color4*)m_value;
+ }
+}
+
+
+const Vector2& AnyVal::vector2() const {
+ if (m_type != VECTOR2) {
+ throw WrongType(VECTOR2, m_type);
+ }
+
+ return *(Vector2*)m_value;
+}
+
+
+const Vector2& AnyVal::vector2(const Vector2& defaultVal) const {
+ if (m_type != VECTOR2) {
+ return defaultVal;
+ } else {
+ return *(Vector2*)m_value;
+ }
+}
+
+
+const Vector3& AnyVal::vector3() const {
+ if (m_type != VECTOR3) {
+ throw WrongType(VECTOR3, m_type);
+ }
+
+ return *(Vector3*)m_value;
+}
+
+
+const Vector3& AnyVal::vector3(const Vector3& defaultVal) const {
+ if (m_type != VECTOR3) {
+ return defaultVal;
+ } else {
+ return *(Vector3*)m_value;
+ }
+}
+
+
+const Vector4& AnyVal::vector4() const {
+ if (m_type != VECTOR4) {
+ throw WrongType(VECTOR4, m_type);
+ }
+
+ return *(Vector4*)m_value;
+}
+
+
+const Vector4& AnyVal::vector4(const Vector4& defaultVal) const {
+ if (m_type != VECTOR4) {
+ return defaultVal;
+ } else {
+ return *(Vector4*)m_value;
+ }
+}
+
+
+const CoordinateFrame& AnyVal::coordinateFrame() const {
+ if (m_type != COORDINATEFRAME) {
+ throw WrongType(COORDINATEFRAME, m_type);
+ }
+
+ return *(CoordinateFrame*)m_value;
+}
+
+
+const CoordinateFrame& AnyVal::coordinateFrame(const CoordinateFrame& defaultVal) const {
+ if (m_type != COORDINATEFRAME) {
+ return defaultVal;
+ } else {
+ return *(CoordinateFrame*)m_value;
+ }
+}
+
+const Matrix2& AnyVal::matrix2(const Matrix2& defaultVal) const {
+ if (m_type != MATRIX2) {
+ return defaultVal;
+ } else {
+ return *(Matrix2*)m_value;
+ }
+}
+
+
+const Matrix2& AnyVal::matrix2() const {
+ if (m_type != MATRIX2) {
+ throw WrongType(MATRIX2, m_type);
+ }
+
+ return *(Matrix2*)m_value;
+}
+
+
+const Matrix3& AnyVal::matrix3(const Matrix3& defaultVal) const {
+ if (m_type != MATRIX3) {
+ return defaultVal;
+ } else {
+ return *(Matrix3*)m_value;
+ }
+}
+
+
+const Matrix3& AnyVal::matrix3() const {
+ if (m_type != MATRIX3) {
+ throw WrongType(MATRIX3, m_type);
+ }
+
+ return *(Matrix3*)m_value;
+}
+
+
+const Matrix4& AnyVal::matrix4(const Matrix4& defaultVal) const {
+ if (m_type != MATRIX4) {
+ return defaultVal;
+ } else {
+ return *(Matrix4*)m_value;
+ }
+}
+
+
+const Matrix4& AnyVal::matrix4() const {
+ if (m_type != MATRIX4) {
+ throw WrongType(MATRIX4, m_type);
+ }
+
+ return *(Matrix4*)m_value;
+}
+
+
+const Quat& AnyVal::quat(const Quat& defaultVal) const {
+ if (m_type != QUAT) {
+ return defaultVal;
+ } else {
+ return *(Quat*)m_value;
+ }
+}
+
+
+const Quat& AnyVal::quat() const {
+ if (m_type != QUAT) {
+ throw WrongType(QUAT, m_type);
+ }
+
+ return *(Quat*)m_value;
+}
+
+
+const AnyVal& AnyVal::get(const std::string& key, const AnyVal& defaultVal) const {
+ if (m_type != TABLE) {
+ return defaultVal;
+ }
+
+ const Table<std::string, AnyVal>& t = *(const Table<std::string, AnyVal>*)m_value;
+
+ if (t.containsKey(key)) {
+ return t[key];
+ } else {
+ return defaultVal;
+ }
+}
+
+
+const AnyVal& AnyVal::get(const std::string& key) const {
+ if (m_type != TABLE) {
+ throw WrongType(TABLE, m_type);
+ }
+
+ const Table<std::string, AnyVal>& t = *(const Table<std::string, AnyVal>*)m_value;
+
+ if (t.containsKey(key)) {
+ return t[key];
+ } else {
+ throw KeyNotFound(key);
+ }
+}
+
+
+const AnyVal& AnyVal::get(int i, const AnyVal& defaultVal) const {
+ if (m_type != ARRAY) {
+ return defaultVal;
+ }
+
+ const Array<AnyVal>& a = *(const Array<AnyVal>*)m_value;
+
+ if ((i >= 0) && (i < a.size())) {
+ return a[i];
+ } else {
+ return defaultVal;
+ }
+}
+
+
+const AnyVal& AnyVal::get(int i) const {
+ if (m_type != ARRAY) {
+ throw WrongType(ARRAY, m_type);
+ }
+
+ const Array<AnyVal>& a = *(const Array<AnyVal>*)m_value;
+
+ if ((i >= 0) && (i < a.size())) {
+ return a[i];
+ } else {
+ throw IndexOutOfBounds(i, a.size());
+ }
+}
+
+}
diff --git a/dep/src/g3dlite/AreaMemoryManager.cpp b/dep/src/g3dlite/AreaMemoryManager.cpp
new file mode 100644
index 00000000000..00cb33dc91f
--- /dev/null
+++ b/dep/src/g3dlite/AreaMemoryManager.cpp
@@ -0,0 +1,87 @@
+/**
+ @file AreaMemoryManager.cpp
+
+ @maintainer Morgan McGuire, http://graphics.cs.williams.edu
+
+ @created 2009-01-20
+ @edited 2009-01-20
+
+ Copyright 2000-2009, Morgan McGuire.
+ All rights reserved.
+ */
+
+#include "G3D/AreaMemoryManager.h"
+#include "G3D/System.h"
+
+namespace G3D {
+
+AreaMemoryManager::Buffer::Buffer(size_t size) : m_size(size), m_used(0) {
+ // Allocate space for a lot of buffers.
+ m_first = (uint8*)::malloc(m_size);
+}
+
+
+AreaMemoryManager::Buffer::~Buffer() {
+ ::free(m_first);
+}
+
+
+void* AreaMemoryManager::Buffer::alloc(size_t s) {
+ if (s + m_used > m_size) {
+ return NULL;
+ } else {
+ void* old = m_first + m_used;
+ m_used += s;
+ return old;
+ }
+}
+
+
+bool AreaMemoryManager::isThreadsafe() const {
+ return false;
+}
+
+
+AreaMemoryManager::Ref AreaMemoryManager::create(size_t sizeHint) {
+ return new AreaMemoryManager(sizeHint);
+}
+
+
+AreaMemoryManager::AreaMemoryManager(size_t sizeHint) : m_sizeHint(sizeHint) {
+ debugAssert(sizeHint > 0);
+}
+
+
+AreaMemoryManager::~AreaMemoryManager() {
+ deallocateAll();
+}
+
+
+size_t AreaMemoryManager::bytesAllocated() const {
+ return m_sizeHint * m_bufferArray.size();
+}
+
+
+void* AreaMemoryManager::alloc(size_t s) {
+ void* n = (m_bufferArray.size() > 0) ? m_bufferArray.last()->alloc(s) : NULL;
+ if (n == NULL) {
+ // This buffer is full
+ m_bufferArray.append(new Buffer(max(s, m_sizeHint)));
+ return m_bufferArray.last()->alloc(s);
+ } else {
+ return n;
+ }
+}
+
+
+void AreaMemoryManager::free(void* x) {
+ // Intentionally empty; we block deallocate
+}
+
+
+void AreaMemoryManager::deallocateAll() {
+ m_bufferArray.deleteAll();
+ m_bufferArray.clear();
+}
+
+}
diff --git a/dep/src/g3dlite/BinaryFormat.cpp b/dep/src/g3dlite/BinaryFormat.cpp
new file mode 100644
index 00000000000..d3991378f45
--- /dev/null
+++ b/dep/src/g3dlite/BinaryFormat.cpp
@@ -0,0 +1,81 @@
+/**
+ @file BinaryFormat.cpp
+ @maintainer Morgan McGuire, http://graphics.cs.williams.edu
+
+ @created 2005-06-10
+ @edited 2005-06-10
+ */
+
+#include "G3D/BinaryFormat.h"
+
+namespace G3D {
+
+int32 byteSize(BinaryFormat f) {
+ switch (f) {
+ case BOOL8_BINFMT:
+ case UINT8_BINFMT:
+ case INT8_BINFMT:
+ return 1;
+
+ case UINT16_BINFMT:
+ case INT16_BINFMT:
+ return 2;
+
+ case FLOAT16_BINFMT:
+ return 2;
+
+ case UINT32_BINFMT:
+ case INT32_BINFMT:
+ case FLOAT32_BINFMT:
+ return 4;
+
+ case FLOAT64_BINFMT:
+ case UINT64_BINFMT:
+ case INT64_BINFMT:
+ return 8;
+
+ case INT128_BINFMT:
+ case UINT128_BINFMT:
+ return 16;
+
+ case VECTOR2_BINFMT:
+ return 2 * 4;
+
+ case VECTOR2INT16_BINFMT:
+ return 2 * 2;
+
+ case VECTOR3_BINFMT:
+ return 3 * 4;
+
+ case VECTOR3INT16_BINFMT:
+ return 3 * 2;
+
+ case VECTOR4_BINFMT:
+ return 4 * 4;
+
+ case VECTOR4INT16_BINFMT:
+ return 4 * 4;
+
+ case COLOR3_BINFMT:
+ return 3 * 4;
+
+ case COLOR3UINT8_BINFMT:
+ return 3 * 1;
+
+ case COLOR3INT16_BINFMT:
+ return 3 * 2;
+
+ case COLOR4_BINFMT:
+ return 4 * 4;
+
+ case COLOR4UINT8_BINFMT:
+ return 4 * 1;
+
+ case COLOR4INT16_BINFMT:
+ return 4 * 2;
+
+ default:
+ return -1;
+ }
+}
+}
diff --git a/dep/src/g3dlite/BinaryInput.cpp b/dep/src/g3dlite/BinaryInput.cpp
new file mode 100644
index 00000000000..65a9976fe04
--- /dev/null
+++ b/dep/src/g3dlite/BinaryInput.cpp
@@ -0,0 +1,568 @@
+/**
+ @file BinaryInput.cpp
+
+ @author Morgan McGuire, graphics3d.com
+ Copyright 2001-2007, Morgan McGuire. All rights reserved.
+
+ @created 2001-08-09
+ @edited 2005-02-24
+
+
+ <PRE>
+ {
+ BinaryOutput b("c:/tmp/test.b", BinaryOutput::LITTLE_ENDIAN);
+
+ float f = 3.1415926;
+ int i = 1027221;
+ std::string s = "Hello World!";
+
+ b.writeFloat32(f);
+ b.writeInt32(i);
+ b.writeString(s);
+ b.commit();
+
+
+ BinaryInput in("c:/tmp/test.b", BinaryInput::LITTLE_ENDIAN);
+
+ debugAssert(f == in.readFloat32());
+ int ii = in.readInt32();
+ debugAssert(i == ii);
+ debugAssert(s == in.readString());
+ }
+ </PRE>
+ */
+
+#include "G3D/platform.h"
+#include "G3D/BinaryInput.h"
+#include "G3D/Array.h"
+#include "G3D/fileutils.h"
+#include "G3D/Log.h"
+#include <zlib.h>
+
+#include <cstring>
+
+namespace G3D {
+
+void BinaryInput::readBool8(std::vector<bool>& out, int64 n) {
+ out.resize((int)n);
+ // std::vector optimizes bool in a way that prevents fast reading
+ for (int64 i = 0; i < n ; ++i) {
+ out[i] = readBool8();
+ }
+}
+
+
+void BinaryInput::readBool8(Array<bool>& out, int64 n) {
+ out.resize(n);
+ readBool8(out.begin(), n);
+}
+
+
+#define IMPLEMENT_READER(ucase, lcase)\
+void BinaryInput::read##ucase(std::vector<lcase>& out, int64 n) {\
+ out.resize(n);\
+ read##ucase(&out[0], n);\
+}\
+\
+\
+void BinaryInput::read##ucase(Array<lcase>& out, int64 n) {\
+ out.resize(n);\
+ read##ucase(out.begin(), n);\
+}
+
+
+IMPLEMENT_READER(UInt8, uint8)
+IMPLEMENT_READER(Int8, int8)
+IMPLEMENT_READER(UInt16, uint16)
+IMPLEMENT_READER(Int16, int16)
+IMPLEMENT_READER(UInt32, uint32)
+IMPLEMENT_READER(Int32, int32)
+IMPLEMENT_READER(UInt64, uint64)
+IMPLEMENT_READER(Int64, int64)
+IMPLEMENT_READER(Float32, float32)
+IMPLEMENT_READER(Float64, float64)
+
+#undef IMPLEMENT_READER
+
+// Data structures that are one byte per element can be
+// directly copied, regardles of endian-ness.
+#define IMPLEMENT_READER(ucase, lcase)\
+void BinaryInput::read##ucase(lcase* out, int64 n) {\
+ if (sizeof(lcase) == 1) {\
+ readBytes(out, n);\
+ } else {\
+ for (int64 i = 0; i < n ; ++i) {\
+ out[i] = read##ucase();\
+ }\
+ }\
+}
+
+IMPLEMENT_READER(Bool8, bool)
+IMPLEMENT_READER(UInt8, uint8)
+IMPLEMENT_READER(Int8, int8)
+
+#undef IMPLEMENT_READER
+
+
+#define IMPLEMENT_READER(ucase, lcase)\
+void BinaryInput::read##ucase(lcase* out, int64 n) {\
+ if (m_swapBytes) {\
+ for (int64 i = 0; i < n; ++i) {\
+ out[i] = read##ucase();\
+ }\
+ } else {\
+ readBytes(out, sizeof(lcase) * n);\
+ }\
+}
+
+
+IMPLEMENT_READER(UInt16, uint16)
+IMPLEMENT_READER(Int16, int16)
+IMPLEMENT_READER(UInt32, uint32)
+IMPLEMENT_READER(Int32, int32)
+IMPLEMENT_READER(UInt64, uint64)
+IMPLEMENT_READER(Int64, int64)
+IMPLEMENT_READER(Float32, float32)
+IMPLEMENT_READER(Float64, float64)
+
+#undef IMPLEMENT_READER
+
+void BinaryInput::loadIntoMemory(int64 startPosition, int64 minLength) {
+ // Load the next section of the file
+ debugAssertM(m_filename != "<memory>", "Read past end of file.");
+
+ int64 absPos = m_alreadyRead + m_pos;
+
+ if (m_bufferLength < minLength) {
+ // The current buffer isn't big enough to hold the chunk we want to read.
+ // This happens if there was little memory available during the initial constructor
+ // read but more memory has since been freed.
+ m_bufferLength = minLength;
+ debugAssert(m_freeBuffer);
+ m_buffer = (uint8*)System::realloc(m_buffer, m_bufferLength);
+ if (m_buffer == NULL) {
+ throw "Tried to read a larger memory chunk than could fit in memory. (2)";
+ }
+ }
+
+ m_alreadyRead = startPosition;
+
+# ifdef G3D_WIN32
+ FILE* file = fopen(m_filename.c_str(), "rb");
+ debugAssert(file);
+ int ret = fseek(file, (off_t)m_alreadyRead, SEEK_SET);
+ debugAssert(ret == 0);
+ size_t toRead = (size_t)G3D::min(m_bufferLength, m_length - m_alreadyRead);
+ ret = fread(m_buffer, 1, toRead, file);
+ debugAssert(ret == toRead);
+ fclose(file);
+ file = NULL;
+
+# else
+ FILE* file = fopen(m_filename.c_str(), "rb");
+ debugAssert(file);
+ int ret = fseeko(file, (off_t)m_alreadyRead, SEEK_SET);
+ debugAssert(ret == 0);
+ size_t toRead = (size_t)G3D::min<int64>(m_bufferLength, m_length - m_alreadyRead);
+ ret = fread(m_buffer, 1, toRead, file);
+ debugAssert((size_t)ret == (size_t)toRead);
+ fclose(file);
+ file = NULL;
+# endif
+
+ m_pos = absPos - m_alreadyRead;
+ debugAssert(m_pos >= 0);
+}
+
+
+
+const bool BinaryInput::NO_COPY = false;
+
+static bool needSwapBytes(G3DEndian fileEndian) {
+ return (fileEndian != System::machineEndian());
+}
+
+
+/** Helper used by the constructors for decompression */
+static uint32 readUInt32(const uint8* data, bool swapBytes) {
+ if (swapBytes) {
+ uint8 out[4];
+ out[0] = data[3];
+ out[1] = data[2];
+ out[2] = data[1];
+ out[3] = data[0];
+ return *((uint32*)out);
+ } else {
+ return *((uint32*)data);
+ }
+}
+
+
+void BinaryInput::setEndian(G3DEndian e) {
+ m_fileEndian = e;
+ m_swapBytes = needSwapBytes(m_fileEndian);
+}
+
+
+BinaryInput::BinaryInput(
+ const uint8* data,
+ int64 dataLen,
+ G3DEndian dataEndian,
+ bool compressed,
+ bool copyMemory) :
+ m_filename("<memory>"),
+ m_bitPos(0),
+ m_bitString(0),
+ m_beginEndBits(0),
+ m_alreadyRead(0),
+ m_bufferLength(0),
+ m_pos(0) {
+
+ m_freeBuffer = copyMemory || compressed;
+
+ setEndian(dataEndian);
+
+ if (compressed) {
+ // Read the decompressed size from the first 4 bytes
+ m_length = G3D::readUInt32(data, m_swapBytes);
+
+ debugAssert(m_freeBuffer);
+ m_buffer = (uint8*)System::alignedMalloc(m_length, 16);
+
+ unsigned long L = m_length;
+ // Decompress with zlib
+ int64 result = uncompress(m_buffer, (unsigned long*)&L, data + 4, dataLen - 4);
+ m_length = L;
+ m_bufferLength = L;
+ debugAssert(result == Z_OK); (void)result;
+
+ } else {
+ m_length = dataLen;
+ m_bufferLength = m_length;
+ if (! copyMemory) {
+ debugAssert(!m_freeBuffer);
+ m_buffer = const_cast<uint8*>(data);
+ } else {
+ debugAssert(m_freeBuffer);
+ m_buffer = (uint8*)System::alignedMalloc(m_length, 16);
+ System::memcpy(m_buffer, data, dataLen);
+ }
+ }
+}
+
+
+BinaryInput::BinaryInput(
+ const std::string& filename,
+ G3DEndian fileEndian,
+ bool compressed) :
+ m_filename(filename),
+ m_bitPos(0),
+ m_bitString(0),
+ m_beginEndBits(0),
+ m_alreadyRead(0),
+ m_length(0),
+ m_bufferLength(0),
+ m_buffer(NULL),
+ m_pos(0),
+ m_freeBuffer(true) {
+
+ setEndian(fileEndian);
+
+ // Update global file tracker
+ _internal::currentFilesUsed.insert(m_filename);
+
+
+ if (! fileExists(m_filename, false)) {
+ std::string zipfile;
+ std::string internalfile;
+ if (zipfileExists(m_filename, zipfile, internalfile)) {
+ // Load from zipfile
+ void* v;
+ size_t s;
+ zipRead(filename, v, s);
+ m_buffer = reinterpret_cast<uint8*>(v);
+ m_bufferLength = m_length = s;
+ if (compressed) {
+ decompress();
+ }
+ m_freeBuffer = true;
+ } else {
+ Log::common()->printf("Warning: File not found: %s\n", m_filename.c_str());
+ }
+ return;
+ }
+
+ // Figure out how big the file is and verify that it exists.
+ m_length = fileLength(m_filename);
+
+ // Read the file into memory
+ FILE* file = fopen(m_filename.c_str(), "rb");
+
+ if (! file || (m_length == -1)) {
+ throw format("File not found: \"%s\"", m_filename.c_str());
+ return;
+ }
+
+ if (! compressed && (m_length > INITIAL_BUFFER_LENGTH)) {
+ // Read only a subset of the file so we don't consume
+ // all available memory.
+ m_bufferLength = INITIAL_BUFFER_LENGTH;
+ } else {
+ // Either the length is fine or the file is compressed
+ // and requires us to read the whole thing for zlib.
+ m_bufferLength = m_length;
+ }
+
+ debugAssert(m_freeBuffer);
+ m_buffer = (uint8*)System::alignedMalloc(m_bufferLength, 16);
+ if (m_buffer == NULL) {
+ if (compressed) {
+ throw "Not enough memory to load compressed file. (1)";
+ }
+
+ // Try to allocate a small array; not much memory is available.
+ // Give up if we can't allocate even 1k.
+ while ((m_buffer == NULL) && (m_bufferLength > 1024)) {
+ m_bufferLength /= 2;
+ m_buffer = (uint8*)System::alignedMalloc(m_bufferLength, 16);
+ }
+ }
+ debugAssert(m_buffer);
+
+ fread(m_buffer, m_bufferLength, sizeof(int8), file);
+ fclose(file);
+ file = NULL;
+
+ if (compressed) {
+ if (m_bufferLength != m_length) {
+ throw "Not enough memory to load compressed file. (2)";
+ }
+
+ decompress();
+ }
+}
+
+void BinaryInput::decompress() {
+ // Decompress
+ // Use the existing buffer as the source, allocate
+ // a new buffer to use as the destination.
+
+ int64 tempLength = m_length;
+ m_length = G3D::readUInt32(m_buffer, m_swapBytes);
+
+ // The file couldn't have better than 500:1 compression
+ alwaysAssertM(m_length < m_bufferLength * 500, "Compressed file header is corrupted");
+
+ uint8* tempBuffer = m_buffer;
+ m_buffer = (uint8*)System::alignedMalloc(m_length, 16);
+
+ debugAssert(m_buffer);
+ debugAssert(isValidHeapPointer(tempBuffer));
+ debugAssert(isValidHeapPointer(m_buffer));
+
+ unsigned long L = m_length;
+ int64 result = uncompress(m_buffer, &L, tempBuffer + 4, tempLength - 4);
+ m_length = L;
+ m_bufferLength = m_length;
+
+ debugAssertM(result == Z_OK, "BinaryInput/zlib detected corruption in " + m_filename);
+ (void)result;
+
+ System::alignedFree(tempBuffer);
+}
+
+
+void BinaryInput::readBytes(void* bytes, int64 n) {
+ prepareToRead(n);
+ debugAssert(isValidPointer(bytes));
+
+ memcpy(bytes, m_buffer + m_pos, n);
+ m_pos += n;
+}
+
+
+BinaryInput::~BinaryInput() {
+
+ if (m_freeBuffer) {
+ System::alignedFree(m_buffer);
+ }
+ m_buffer = NULL;
+}
+
+
+uint64 BinaryInput::readUInt64() {
+ prepareToRead(8);
+ uint8 out[8];
+
+ if (m_swapBytes) {
+ out[0] = m_buffer[m_pos + 7];
+ out[1] = m_buffer[m_pos + 6];
+ out[2] = m_buffer[m_pos + 5];
+ out[3] = m_buffer[m_pos + 4];
+ out[4] = m_buffer[m_pos + 3];
+ out[5] = m_buffer[m_pos + 2];
+ out[6] = m_buffer[m_pos + 1];
+ out[7] = m_buffer[m_pos + 0];
+ } else {
+ *(uint64*)out = *(uint64*)(m_buffer + m_pos);
+ }
+
+ m_pos += 8;
+ return *(uint64*)out;
+}
+
+
+std::string BinaryInput::readString(int64 n) {
+ prepareToRead(n);
+ debugAssertM((m_pos + n) <= m_length, "Read past end of file");
+
+ char *s = (char*)System::alignedMalloc(n + 1, 16);
+ assert(s != NULL);
+
+ memcpy(s, m_buffer + m_pos, n);
+ // There may not be a null, so make sure
+ // we add one.
+ s[n] = '\0';
+
+ std::string out = s;
+ System::alignedFree(s);
+ s = NULL;
+
+ m_pos += n;
+
+ return out;
+
+}
+
+
+std::string BinaryInput::readString() {
+ int64 n = 0;
+
+ if ((m_pos + m_alreadyRead + n) < (m_length - 1)) {
+ prepareToRead(1);
+ }
+
+ if ( ((m_pos + m_alreadyRead + n) < (m_length - 1)) &&
+ (m_buffer[m_pos + n] != '\0')) {
+
+ ++n;
+ while ( ((m_pos + m_alreadyRead + n) < (m_length - 1)) &&
+ (m_buffer[m_pos + n] != '\0')) {
+
+ prepareToRead(1);
+ ++n;
+ }
+ }
+
+ // Consume NULL
+ ++n;
+
+ return readString(n);
+}
+
+
+std::string BinaryInput::readStringEven() {
+ std::string x = readString();
+ if (hasMore() && (G3D::isOdd(x.length() + 1))) {
+ skip(1);
+ }
+ return x;
+}
+
+
+std::string BinaryInput::readString32() {
+ int len = readUInt32();
+ return readString(len);
+}
+
+
+Vector4 BinaryInput::readVector4() {
+ float x = readFloat32();
+ float y = readFloat32();
+ float z = readFloat32();
+ float w = readFloat32();
+ return Vector4(x, y, z, w);
+}
+
+
+Vector3 BinaryInput::readVector3() {
+ float x = readFloat32();
+ float y = readFloat32();
+ float z = readFloat32();
+ return Vector3(x, y, z);
+}
+
+
+Vector2 BinaryInput::readVector2() {
+ float x = readFloat32();
+ float y = readFloat32();
+ return Vector2(x, y);
+}
+
+
+Color4 BinaryInput::readColor4() {
+ float r = readFloat32();
+ float g = readFloat32();
+ float b = readFloat32();
+ float a = readFloat32();
+ return Color4(r, g, b, a);
+}
+
+
+Color3 BinaryInput::readColor3() {
+ float r = readFloat32();
+ float g = readFloat32();
+ float b = readFloat32();
+ return Color3(r, g, b);
+}
+
+
+void BinaryInput::beginBits() {
+ debugAssert(m_beginEndBits == 0);
+ m_beginEndBits = 1;
+ m_bitPos = 0;
+
+ debugAssertM(hasMore(), "Can't call beginBits when at the end of a file");
+ m_bitString = readUInt8();
+}
+
+
+uint32 BinaryInput::readBits(int numBits) {
+ debugAssert(m_beginEndBits == 1);
+
+ uint32 out = 0;
+
+ const int total = numBits;
+ while (numBits > 0) {
+ if (m_bitPos > 7) {
+ // Consume a new byte for reading. We do this at the beginning
+ // of the loop so that we don't try to read past the end of the file.
+ m_bitPos = 0;
+ m_bitString = readUInt8();
+ }
+
+ // Slide the lowest bit of the bitString into
+ // the correct position.
+ out |= (m_bitString & 1) << (total - numBits);
+
+ // Shift over to the next bit
+ m_bitString = m_bitString >> 1;
+ ++m_bitPos;
+ --numBits;
+ }
+
+ return out;
+}
+
+
+void BinaryInput::endBits() {
+ debugAssert(m_beginEndBits == 1);
+ if (m_bitPos == 0) {
+ // Put back the last byte we read
+ --m_pos;
+ }
+ m_beginEndBits = 0;
+ m_bitPos = 0;
+}
+
+}
diff --git a/dep/src/g3dlite/BinaryOutput.cpp b/dep/src/g3dlite/BinaryOutput.cpp
new file mode 100644
index 00000000000..2de46c6d4bb
--- /dev/null
+++ b/dep/src/g3dlite/BinaryOutput.cpp
@@ -0,0 +1,522 @@
+/**
+ @file BinaryOutput.cpp
+
+ @author Morgan McGuire, graphics3d.com
+ Copyright 2002-2007, Morgan McGuire, All rights reserved.
+
+ @created 2002-02-20
+ @edited 2008-01-07
+ */
+
+#include "G3D/platform.h"
+#include "G3D/BinaryOutput.h"
+#include "G3D/fileutils.h"
+#include "G3D/stringutils.h"
+#include "G3D/Array.h"
+#include <zlib.h>
+
+#include <cstring>
+
+// Largest memory buffer that the system will use for writing to
+// disk. After this (or if the system runs out of memory)
+// chunks of the file will be dumped to disk.
+//
+// Currently 400 MB
+#define MAX_BINARYOUTPUT_BUFFER_SIZE 400000000
+
+namespace G3D {
+
+void BinaryOutput::writeBool8(const std::vector<bool>& out, int n) {
+ for (int i = 0; i < n; ++i) {
+ writeBool8(out[i]);
+ }
+}
+
+
+void BinaryOutput::writeBool8(const Array<bool>& out, int n) {
+ writeBool8(out.getCArray(), n);
+}
+
+#define IMPLEMENT_WRITER(ucase, lcase)\
+void BinaryOutput::write##ucase(const std::vector<lcase>& out, int n) {\
+ write##ucase(&out[0], n);\
+}\
+\
+\
+void BinaryOutput::write##ucase(const Array<lcase>& out, int n) {\
+ write##ucase(out.getCArray(), n);\
+}
+
+
+IMPLEMENT_WRITER(UInt8, uint8)
+IMPLEMENT_WRITER(Int8, int8)
+IMPLEMENT_WRITER(UInt16, uint16)
+IMPLEMENT_WRITER(Int16, int16)
+IMPLEMENT_WRITER(UInt32, uint32)
+IMPLEMENT_WRITER(Int32, int32)
+IMPLEMENT_WRITER(UInt64, uint64)
+IMPLEMENT_WRITER(Int64, int64)
+IMPLEMENT_WRITER(Float32, float32)
+IMPLEMENT_WRITER(Float64, float64)
+
+#undef IMPLEMENT_WRITER
+
+// Data structures that are one byte per element can be
+// directly copied, regardles of endian-ness.
+#define IMPLEMENT_WRITER(ucase, lcase)\
+void BinaryOutput::write##ucase(const lcase* out, int n) {\
+ if (sizeof(lcase) == 1) {\
+ writeBytes((void*)out, n);\
+ } else {\
+ for (int i = 0; i < n ; ++i) {\
+ write##ucase(out[i]);\
+ }\
+ }\
+}
+
+IMPLEMENT_WRITER(Bool8, bool)
+IMPLEMENT_WRITER(UInt8, uint8)
+IMPLEMENT_WRITER(Int8, int8)
+
+#undef IMPLEMENT_WRITER
+
+
+#define IMPLEMENT_WRITER(ucase, lcase)\
+void BinaryOutput::write##ucase(const lcase* out, int n) {\
+ if (m_swapBytes) {\
+ for (int i = 0; i < n; ++i) {\
+ write##ucase(out[i]);\
+ }\
+ } else {\
+ writeBytes((const void*)out, sizeof(lcase) * n);\
+ }\
+}
+
+
+IMPLEMENT_WRITER(UInt16, uint16)
+IMPLEMENT_WRITER(Int16, int16)
+IMPLEMENT_WRITER(UInt32, uint32)
+IMPLEMENT_WRITER(Int32, int32)
+IMPLEMENT_WRITER(UInt64, uint64)
+IMPLEMENT_WRITER(Int64, int64)
+IMPLEMENT_WRITER(Float32, float32)
+IMPLEMENT_WRITER(Float64, float64)
+
+#undef IMPLEMENT_WRITER
+
+
+void BinaryOutput::reallocBuffer(size_t bytes, size_t oldBufferLen) {
+ //debugPrintf("reallocBuffer(%d, %d)\n", bytes, oldBufferLen);
+
+ size_t newBufferLen = (int)(m_bufferLen * 1.5) + 100;
+ uint8* newBuffer = NULL;
+
+ if ((m_filename == "<memory>") || (newBufferLen < MAX_BINARYOUTPUT_BUFFER_SIZE)) {
+ // We're either writing to memory (in which case we *have* to try and allocate)
+ // or we've been asked to allocate a reasonable size buffer.
+
+ //debugPrintf(" realloc(%d)\n", newBufferLen);
+ newBuffer = (uint8*)System::realloc(m_buffer, newBufferLen);
+ if (newBuffer != NULL) {
+ m_maxBufferLen = newBufferLen;
+ }
+ }
+
+ if ((newBuffer == NULL) && (bytes > 0)) {
+ // Realloc failed; we're probably out of memory. Back out
+ // the entire call and try to dump some data to disk.
+ m_bufferLen = oldBufferLen;
+ reserveBytesWhenOutOfMemory(bytes);
+ } else {
+ m_buffer = newBuffer;
+ debugAssert(isValidHeapPointer(m_buffer));
+ }
+}
+
+
+void BinaryOutput::reserveBytesWhenOutOfMemory(size_t bytes) {
+ if (m_filename == "<memory>") {
+ throw "Out of memory while writing to memory in BinaryOutput (no RAM left).";
+ } else if ((int)bytes > (int)m_maxBufferLen) {
+ throw "Out of memory while writing to disk in BinaryOutput (could not create a large enough buffer).";
+ } else {
+
+ // Dump the contents to disk. In order to enable seeking backwards,
+ // we keep the last 10 MB in memory.
+ int writeBytes = m_bufferLen - 10 * 1024 * 1024;
+
+ if (writeBytes < m_bufferLen / 3) {
+ // We're going to write less than 1/3 of the file;
+ // give up and just write the whole thing.
+ writeBytes = m_bufferLen;
+ }
+ debugAssert(writeBytes > 0);
+
+ //debugPrintf("Writing %d bytes to disk\n", writeBytes);
+
+ const char* mode = (m_alreadyWritten > 0) ? "ab" : "wb";
+ FILE* file = fopen(m_filename.c_str(), mode);
+ debugAssert(file);
+
+ size_t count = fwrite(m_buffer, 1, writeBytes, file);
+ debugAssert((int)count == writeBytes); (void)count;
+
+ fclose(file);
+ file = NULL;
+
+ // Record that we saved this data.
+ m_alreadyWritten += writeBytes;
+ m_bufferLen -= writeBytes;
+ m_pos -= writeBytes;
+
+ debugAssert(m_bufferLen < m_maxBufferLen);
+ debugAssert(m_bufferLen >= 0);
+ debugAssert(m_pos >= 0);
+ debugAssert(m_pos <= m_bufferLen);
+
+ // Shift the unwritten data back appropriately in the buffer.
+ debugAssert(isValidHeapPointer(m_buffer));
+ System::memcpy(m_buffer, m_buffer + writeBytes, m_bufferLen);
+ debugAssert(isValidHeapPointer(m_buffer));
+
+ // *now* we allocate bytes (there should presumably be enough
+ // space in the buffer; if not, we'll come back through this
+ // code and dump the last 10MB to disk as well. Note that the
+ // bytes > maxBufferLen case above would already have triggered
+ // if this call couldn't succeed.
+ reserveBytes(bytes);
+ }
+}
+
+
+BinaryOutput::BinaryOutput() {
+ m_alreadyWritten = 0;
+ m_swapBytes = false;
+ m_pos = 0;
+ m_filename = "<memory>";
+ m_buffer = NULL;
+ m_bufferLen = 0;
+ m_maxBufferLen = 0;
+ m_beginEndBits = 0;
+ m_bitString = 0;
+ m_bitPos = 0;
+ m_ok = true;
+ m_committed = false;
+}
+
+
+BinaryOutput::BinaryOutput(
+ const std::string& filename,
+ G3DEndian fileEndian) {
+
+ m_pos = 0;
+ m_alreadyWritten = 0;
+ setEndian(fileEndian);
+ m_filename = filename;
+ m_buffer = NULL;
+ m_bufferLen = 0;
+ m_maxBufferLen = 0;
+ m_beginEndBits = 0;
+ m_bitString = 0;
+ m_bitPos = 0;
+ m_committed = false;
+
+ m_ok = true;
+ /** Verify ability to write to disk */
+ commit(false);
+ m_committed = false;
+}
+
+
+void BinaryOutput::reset() {
+ debugAssert(m_beginEndBits == 0);
+ alwaysAssertM(m_filename == "<memory>",
+ "Can only reset a BinaryOutput that writes to memory.");
+
+ // Do not reallocate, just clear the size of the buffer.
+ m_pos = 0;
+ m_alreadyWritten = 0;
+ m_bufferLen = 0;
+ m_beginEndBits = 0;
+ m_bitString = 0;
+ m_bitPos = 0;
+ m_committed = false;
+}
+
+
+BinaryOutput::~BinaryOutput() {
+ debugAssert((m_buffer == NULL) || isValidHeapPointer(m_buffer));
+ System::free(m_buffer);
+ m_buffer = NULL;
+ m_bufferLen = 0;
+ m_maxBufferLen = 0;
+}
+
+
+void BinaryOutput::setEndian(G3DEndian fileEndian) {
+ m_fileEndian = fileEndian;
+ m_swapBytes = (fileEndian != System::machineEndian());
+}
+
+
+bool BinaryOutput::ok() const {
+ return m_ok;
+}
+
+
+void BinaryOutput::compress() {
+ if (m_alreadyWritten > 0) {
+ throw "Cannot compress huge files (part of this file has already been written to disk).";
+ }
+
+ // Old buffer size
+ int L = m_bufferLen;
+ uint8* convert = (uint8*)&L;
+
+ // Zlib requires the output buffer to be this big
+ unsigned long newSize = iCeil(m_bufferLen * 1.01) + 12;
+ uint8* temp = (uint8*)System::malloc(newSize);
+ int result = compress2(temp, &newSize, m_buffer, m_bufferLen, 9);
+
+ debugAssert(result == Z_OK); (void)result;
+
+ // Write the header
+ if (m_swapBytes) {
+ m_buffer[0] = convert[3];
+ m_buffer[1] = convert[2];
+ m_buffer[2] = convert[1];
+ m_buffer[3] = convert[0];
+ } else {
+ m_buffer[0] = convert[0];
+ m_buffer[1] = convert[1];
+ m_buffer[2] = convert[2];
+ m_buffer[3] = convert[3];
+ }
+
+ // Write the data
+ if ((int64)newSize + 4 > (int64)m_maxBufferLen) {
+ m_maxBufferLen = newSize + 4;
+ m_buffer = (uint8*)System::realloc(m_buffer, m_maxBufferLen);
+ }
+ m_bufferLen = newSize + 4;
+ System::memcpy(m_buffer + 4, temp, newSize);
+ m_pos = m_bufferLen;
+
+ System::free(temp);
+}
+
+
+void BinaryOutput::commit(bool flush) {
+ debugAssertM(! m_committed, "Cannot commit twice");
+ m_committed = true;
+ debugAssertM(m_beginEndBits == 0, "Missing endBits before commit");
+
+ // Make sure the directory exists.
+ std::string root, base, ext, path;
+ Array<std::string> pathArray;
+ parseFilename(m_filename, root, pathArray, base, ext);
+
+ path = root + stringJoin(pathArray, '/');
+ if (! fileExists(path, false)) {
+ createDirectory(path);
+ }
+
+ const char* mode = (m_alreadyWritten > 0) ? "ab" : "wb";
+
+ FILE* file = fopen(m_filename.c_str(), mode);
+
+ m_ok = (file != NULL) && m_ok;
+
+ if (m_ok) {
+ debugAssertM(file, std::string("Could not open '") + m_filename + "'");
+
+ if (m_buffer != NULL) {
+ m_alreadyWritten += m_bufferLen;
+
+ int success = fwrite(m_buffer, m_bufferLen, 1, file);
+ (void)success;
+ debugAssertM(success == 1, std::string("Could not write to '") + m_filename + "'");
+ }
+ if (flush) {
+ fflush(file);
+ }
+ fclose(file);
+ file = NULL;
+ }
+}
+
+
+void BinaryOutput::commit(
+ uint8* out) {
+ debugAssertM(! m_committed, "Cannot commit twice");
+ m_committed = true;
+
+ System::memcpy(out, m_buffer, m_bufferLen);
+}
+
+
+void BinaryOutput::writeUInt16(uint16 u) {
+ reserveBytes(2);
+
+ uint8* convert = (uint8*)&u;
+
+ if (m_swapBytes) {
+ m_buffer[m_pos] = convert[1];
+ m_buffer[m_pos + 1] = convert[0];
+ } else {
+ *(uint16*)(m_buffer + m_pos) = u;
+ }
+
+ m_pos += 2;
+}
+
+
+void BinaryOutput::writeUInt32(uint32 u) {
+ reserveBytes(4);
+
+ uint8* convert = (uint8*)&u;
+
+ debugAssert(m_beginEndBits == 0);
+
+ if (m_swapBytes) {
+ m_buffer[m_pos] = convert[3];
+ m_buffer[m_pos + 1] = convert[2];
+ m_buffer[m_pos + 2] = convert[1];
+ m_buffer[m_pos + 3] = convert[0];
+ } else {
+ *(uint32*)(m_buffer + m_pos) = u;
+ }
+
+ m_pos += 4;
+}
+
+
+void BinaryOutput::writeUInt64(uint64 u) {
+ reserveBytes(8);
+
+ uint8* convert = (uint8*)&u;
+
+ if (m_swapBytes) {
+ m_buffer[m_pos] = convert[7];
+ m_buffer[m_pos + 1] = convert[6];
+ m_buffer[m_pos + 2] = convert[5];
+ m_buffer[m_pos + 3] = convert[4];
+ m_buffer[m_pos + 4] = convert[3];
+ m_buffer[m_pos + 5] = convert[2];
+ m_buffer[m_pos + 6] = convert[1];
+ m_buffer[m_pos + 7] = convert[0];
+ } else {
+ *(uint64*)(m_buffer + m_pos) = u;
+ }
+
+ m_pos += 8;
+}
+
+
+void BinaryOutput::writeString(const char* s) {
+ // +1 is because strlen doesn't count the null
+ int len = strlen(s) + 1;
+
+ debugAssert(m_beginEndBits == 0);
+ reserveBytes(len);
+ System::memcpy(m_buffer + m_pos, s, len);
+ m_pos += len;
+}
+
+
+void BinaryOutput::writeStringEven(const char* s) {
+ // +1 is because strlen doesn't count the null
+ int len = strlen(s) + 1;
+
+ reserveBytes(len);
+ System::memcpy(m_buffer + m_pos, s, len);
+ m_pos += len;
+
+ // Pad with another NULL
+ if ((len % 2) == 1) {
+ writeUInt8(0);
+ }
+}
+
+
+void BinaryOutput::writeString32(const char* s) {
+ writeUInt32(strlen(s) + 1);
+ writeString(s);
+}
+
+
+void BinaryOutput::writeVector4(const Vector4& v) {
+ writeFloat32(v.x);
+ writeFloat32(v.y);
+ writeFloat32(v.z);
+ writeFloat32(v.w);
+}
+
+
+void BinaryOutput::writeVector3(const Vector3& v) {
+ writeFloat32(v.x);
+ writeFloat32(v.y);
+ writeFloat32(v.z);
+}
+
+
+void BinaryOutput::writeVector2(const Vector2& v) {
+ writeFloat32(v.x);
+ writeFloat32(v.y);
+}
+
+
+void BinaryOutput::writeColor4(const Color4& v) {
+ writeFloat32(v.r);
+ writeFloat32(v.g);
+ writeFloat32(v.b);
+ writeFloat32(v.a);
+}
+
+
+void BinaryOutput::writeColor3(const Color3& v) {
+ writeFloat32(v.r);
+ writeFloat32(v.g);
+ writeFloat32(v.b);
+}
+
+
+void BinaryOutput::beginBits() {
+ debugAssertM(m_beginEndBits == 0, "Already in beginBits...endBits");
+ m_bitString = 0x00;
+ m_bitPos = 0;
+ m_beginEndBits = 1;
+}
+
+
+void BinaryOutput::writeBits(uint32 value, int numBits) {
+
+ while (numBits > 0) {
+ // Extract the current bit of value and
+ // insert it into the current byte
+ m_bitString |= (value & 1) << m_bitPos;
+ ++m_bitPos;
+ value = value >> 1;
+ --numBits;
+
+ if (m_bitPos > 7) {
+ // We've reached the end of this byte
+ writeUInt8(m_bitString);
+ m_bitString = 0x00;
+ m_bitPos = 0;
+ }
+ }
+}
+
+
+void BinaryOutput::endBits() {
+ debugAssertM(m_beginEndBits == 1, "Not in beginBits...endBits");
+ if (m_bitPos > 0) {
+ writeUInt8(m_bitString);
+ }
+ m_bitString = 0;
+ m_bitPos = 0;
+ m_beginEndBits = 0;
+}
+
+}
diff --git a/dep/src/g3dlite/Box.cpp b/dep/src/g3dlite/Box.cpp
index 725a7e95c3a..f7c112ae3a5 100644
--- a/dep/src/g3dlite/Box.cpp
+++ b/dep/src/g3dlite/Box.cpp
@@ -2,7 +2,7 @@
@file Box.cpp
Box class
- @maintainer Morgan McGuire, matrix@graphics3d.com
+ @maintainer Morgan McGuire, http://graphics.cs.williams.edu
@created 2001-06-02
@edited 2006-02-05
@@ -27,10 +27,56 @@ namespace G3D {
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) {
@@ -43,6 +89,11 @@ 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);
@@ -58,23 +109,40 @@ void Box::init(
_axis[1] = Vector3::unitY();
_axis[2] = Vector3::unitZ();
- _volume = _extent.x * _extent.y * _extent.z;
- _area = 2 *
+ if (_extent.isFinite()) {
+ _volume = _extent.x * _extent.y * _extent.z;
+ } else {
+ _volume = G3D::finf();
+ }
+
+ debugAssert(! isNaN(_extent.x));
+
+ _area = 2 *
(_extent.x * _extent.y +
_extent.y * _extent.z +
_extent.z * _extent.x);
- _center = (max + min) / 2;
+ _center = (max + min) * 0.5f;
+
+ // If the extent is infinite along an axis, make the center zero to avoid NaNs
+ for (int i = 0; i < 3; ++i) {
+ if (! G3D::isFinite(_extent[i])) {
+ _center[i] = 0.0f;
+ }
+ }
}
+
float Box::volume() const {
return _volume;
}
-float Box::surfaceArea() const {
+
+float Box::area() const {
return _area;
}
+
void Box::getLocalFrame(CoordinateFrame& frame) const {
frame.rotation = Matrix3(
@@ -85,12 +153,14 @@ void Box::getLocalFrame(CoordinateFrame& frame) const {
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:
@@ -122,45 +192,29 @@ void Box::getFaceCorners(int f, Vector3& v0, Vector3& v1, Vector3& v2, Vector3&
}
}
-bool Box::culledBy(
- const Array<Plane>& plane,
- int& cullingPlaneIndex,
- const uint32 inMask,
- uint32& outMask) const {
-
- return culledBy(plane.getCArray(), plane.size(), cullingPlaneIndex, inMask, outMask);
-}
-
-bool Box::culledBy(
- const Array<Plane>& plane,
- int& cullingPlaneIndex,
- const uint32 inMask) const {
- return culledBy(plane.getCArray(), plane.size(), cullingPlaneIndex, inMask);
-}
-int32 Box::dummy = 0;
+int Box::dummy = 0;
bool Box::culledBy(
- const class Plane* plane,
- int numPlanes,
+ const Array<Plane>& plane,
int& cullingPlane,
const uint32 _inMask,
uint32& childMask) const {
uint32 inMask = _inMask;
- assert(numPlanes < 31);
+ 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 < numPlanes; p++) {
-
- // Only test planes that are not masked
- if ((inMask & 1) != 0) {
+ // vertices are in the negative half space.
+ for (int p = 0; p < plane.size(); ++p) {
- Vector3 corner;
+ // Only test planes that are not masked
+ if ((inMask & 1) != 0) {
+
+ Vector3 corner;
int numContained = 0;
int v = 0;
@@ -168,83 +222,84 @@ bool Box::culledBy(
// 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(getCorner(v))) {
+ 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;
+ 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;
+ 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;
+ inMask = inMask >> 1;
}
// None of the planes could cull this box
- cullingPlane = -1;
+ cullingPlane = -1;
return false;
}
+
bool Box::culledBy(
- const class Plane* plane,
- int numPlanes,
- int& cullingPlane,
- const uint32 _inMask) const {
+ const Array<Plane>& plane,
+ int& cullingPlane,
+ const uint32 _inMask) const {
- uint32 inMask = _inMask;
- assert(numPlanes < 31);
+ 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 < numPlanes; p++) {
+ // 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;
+ // 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
+ // 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(getCorner(v));
- }
+ for (v = 0; (v < 8) && culled; ++v) {
+ culled = ! plane[p].halfSpaceContains(corner(v));
+ }
- if (culled) {
- // Plane p culled the box
- cullingPlane = p;
+ if (culled) {
+ // Plane p culled the box
+ cullingPlane = p;
- return true;
+ return true;
}
- }
+ }
// Move on to the next bit.
- inMask = inMask >> 1;
+ inMask = inMask >> 1;
}
// None of the planes could cull this box
- cullingPlane = -1;
+ cullingPlane = -1;
return false;
}
+
bool Box::contains(
const Vector3& point) const {
@@ -264,7 +319,7 @@ bool Box::contains(
Vector3 osPoint = M.inverse() * (point - _corner[0]);
return
- (osPoint.x >= 0) &&
+ (osPoint.x >= 0) &&
(osPoint.y >= 0) &&
(osPoint.z >= 0) &&
(osPoint.x <= 1) &&
@@ -274,47 +329,51 @@ bool Box::contains(
#undef setMany
-#if 0
+
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)random(0, aXY + aYZ + aZX);
+ float r = (float)uniformRandom(0, aXY + aYZ + aZX);
// Choose evenly between positive and negative face planes
- float d = (random(0, 1) < 0.5f) ? -1.0f : 1.0f;
+ 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)random(-0.5, 0.5) * _extent.x +
- _axis[1] * (float)random(-0.5, 0.5) * _extent.y +
+ 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)random(-0.5, 0.5) * _extent.y +
- _axis[2] * (float)random(-0.5, 0.5) * _extent.z +
+ 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)random(-0.5, 0.5) * _extent.z +
- _axis[0] *(float) random(-0.5, 0.5) * _extent.x +
+ 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)random(-0.5, 0.5) * _extent[a];
+ sum += _axis[a] * (float)uniformRandom(-0.5, 0.5) * _extent[a];
}
return sum;
}
-#endif
+
+Box Box::inf() {
+ return Box(-Vector3::inf(), Vector3::inf());
+}
void Box::getBounds(class AABox& aabb) const {
@@ -330,5 +389,5 @@ void Box::getBounds(class AABox& aabb) const {
aabb = AABox(lo, hi);
}
-} // namespace
+} // namespace
diff --git a/dep/src/g3dlite/Box2D.cpp b/dep/src/g3dlite/Box2D.cpp
new file mode 100644
index 00000000000..ea5a47af1a9
--- /dev/null
+++ b/dep/src/g3dlite/Box2D.cpp
@@ -0,0 +1,113 @@
+/**
+ @file Box.cpp
+ Box class
+
+ @maintainer Morgan McGuire, http://graphics.cs.williams.edu
+
+ @created 2001-06-02
+ @edited 2008-12-27
+*/
+
+#include "G3D/Box2D.h"
+#include "G3D/CoordinateFrame.h"
+#include "G3D/Rect2D.h"
+
+namespace G3D {
+
+bool Box2D::overlaps1Way(const Box2D& other) const {
+ for (int a = 0; a < 2; ++a) {
+
+ float t = other.m_corner[0].dot(m_axisin[a]);
+
+ // Find the extent of box 2 on m_axisin a
+ float tMin = t;
+ float tMax = t;
+
+ for (int c = 1; c < 4; ++c) {
+ t = other.m_corner[c].dot(m_axisin[a]);
+
+ if (t < tMin) {
+ tMin = t;
+ } else if (t > tMax) {
+ tMax = t;
+ }
+ }
+
+ // We have to subtract off the origin
+
+ // See if [tMin, tMax] intersects [0, 1]
+ if ((tMin > 1 + origin[a]) || (tMax < origin[a])) {
+ // There was no intersection along this dimension;
+ // the boxes cannot possibly overlap.
+ return false;
+ }
+ }
+
+ // There was no dimension along which there is no intersection.
+ // Therefore the boxes overlap.
+ return true;
+}
+
+
+void Box2D::computeAxes() {
+ m_axis[0] = m_corner[1] - m_corner[0];
+ m_axis[1] = m_corner[3] - m_corner[0];
+
+ // Make the length of each m_axisin = 1/edge length so we know any
+ // dot product must be less than 1 to fall within the edge.
+ float len[2];
+ for (int a = 0; a < 2; ++a) {
+ float lenSq = m_axis[a].squaredLength();
+ m_axisin[a] = m_axis[a] / lenSq;
+ origin[a] = m_corner[0].dot(m_axisin[a]);
+ len[a] = sqrt(lenSq);
+ m_axis[a] /= len[a];
+ }
+
+ // w * h
+ m_area = len[0] * len[1];
+
+
+ m_center = (m_corner[0] + m_corner[2]) * 0.5f;
+}
+
+
+Box2D::Box2D(const Vector2& center, float w, float h, float angle) {
+ Vector2 X( cos(angle), sin(angle));
+ Vector2 Y(-sin(angle), cos(angle));
+
+ X *= w / 2;
+ Y *= h / 2;
+
+ m_corner[0] = center - X - Y;
+ m_corner[1] = center + X - Y;
+ m_corner[2] = center + X + Y;
+ m_corner[3] = center - X + Y;
+
+ computeAxes();
+}
+
+
+Box2D::Box2D(const AABox2D& b) {
+ for (int i = 0; i < 4; ++i) {
+ m_corner[i] = b.corner(i);
+ }
+
+ computeAxes();
+}
+
+
+Box2D::Box2D(const Vector2& min, const Vector2& max) {
+ *this = Box2D(Rect2D::xyxy(min, max));
+}
+
+
+Box2D::Box2D(const CFrame& frame, Box2D& b) {
+ for (int i = 0; i < 4; ++i) {
+ m_corner[i] = frame.pointToWorldSpace(Vector3(b.corner(i), 0)).xy();
+ }
+ computeAxes();
+}
+
+
+} // G3D
diff --git a/dep/src/g3dlite/BumpMapPreprocess.cpp b/dep/src/g3dlite/BumpMapPreprocess.cpp
new file mode 100644
index 00000000000..20281caf8cb
--- /dev/null
+++ b/dep/src/g3dlite/BumpMapPreprocess.cpp
@@ -0,0 +1,43 @@
+/**
+ \file BumpMapPreprocess.cpp
+
+ \maintainer Morgan McGuire, http://graphics.cs.williams.edu
+
+ \created 2010-01-28
+ \edited 2010-01-28
+
+ Copyright 2000-2010, Morgan McGuire.
+ All rights reserved.
+ */
+#include "G3D/BumpMapPreprocess.h"
+#include "G3D/Any.h"
+#include "G3D/stringutils.h"
+
+namespace G3D {
+
+BumpMapPreprocess::BumpMapPreprocess(const Any& any) {
+ *this = BumpMapPreprocess();
+ for (Any::AnyTable::Iterator it = any.table().begin(); it.hasMore(); ++it) {
+ const std::string& key = toLower(it->key);
+ if (key == "lowpassfilter") {
+ lowPassFilter = it->value;
+ } else if (key == "zextentpixels") {
+ zExtentPixels = it->value;
+ } else if (key == "scalezbynz") {
+ scaleZByNz = it->value;
+ } else {
+ any.verify(false, "Illegal key: " + it->key);
+ }
+ }
+}
+
+
+BumpMapPreprocess::operator Any() const {
+ Any any(Any::TABLE, "BumpMapPreprocess");
+ any["lowPassFilter"] = lowPassFilter;
+ any["zExtentPixels"] = zExtentPixels;
+ any["scaleZByNz"] = scaleZByNz;
+ return any;
+}
+
+}
diff --git a/dep/src/g3dlite/CMakeLists.txt b/dep/src/g3dlite/CMakeLists.txt
index 8f0a6191623..0a548e44f8b 100644
--- a/dep/src/g3dlite/CMakeLists.txt
+++ b/dep/src/g3dlite/CMakeLists.txt
@@ -2,19 +2,48 @@
########### next target ###############
SET(g3dlite_STAT_SRCS
- AABox.cpp
- Box.cpp
- Crypto.cpp
- format.cpp
- Matrix3.cpp
- Plane.cpp
- System.cpp
- Triangle.cpp
- Vector3.cpp
- Vector4.cpp
+ AABox.cpp
+ Box.cpp
+ Crypto.cpp
+ format.cpp
+ Matrix3.cpp
+ Plane.cpp
+ System.cpp
+ Triangle.cpp
+ Vector3.cpp
+ Vector4.cpp
+ debugAssert.cpp
+ fileutils.cpp
+ g3dmath.cpp
+ g3dfnmatch.cpp
+ prompt.cpp
+ stringutils.cpp
+ Any.cpp
+ BinaryFormat.cpp
+ BinaryInput.cpp
+ BinaryOutput.cpp
+ Capsule.cpp
+ CollisionDetection.cpp
+ CoordinateFrame.cpp
+ Cylinder.cpp
+ Line.cpp
+ LineSegment.cpp
+ Log.cpp
+ Matrix4.cpp
+ MemoryManager.cpp
+ Quat.cpp
+ Random.cpp
+ Ray.cpp
+ ReferenceCount.cpp
+ Sphere.cpp
+ TextInput.cpp
+ TextOutput.cpp
+ UprightFrame.cpp
+ Vector2.cpp
)
include_directories(
+ ${CMAKE_SOURCE_DIR}/dep/include
${CMAKE_SOURCE_DIR}/dep/include/g3dlite
)
diff --git a/dep/src/g3dlite/Capsule.cpp b/dep/src/g3dlite/Capsule.cpp
new file mode 100644
index 00000000000..2ad3891c960
--- /dev/null
+++ b/dep/src/g3dlite/Capsule.cpp
@@ -0,0 +1,179 @@
+/**
+ @file Capsule.cpp
+
+ @maintainer Morgan McGuire, http://graphics.cs.williams.edu
+
+ @created 2003-02-07
+ @edited 2005-08-18
+
+ Copyright 2000-2009, Morgan McGuire.
+ All rights reserved.
+ */
+
+#include "G3D/Capsule.h"
+#include "G3D/BinaryInput.h"
+#include "G3D/BinaryOutput.h"
+#include "G3D/LineSegment.h"
+#include "G3D/Sphere.h"
+#include "G3D/CoordinateFrame.h"
+#include "G3D/Line.h"
+#include "G3D/AABox.h"
+
+namespace G3D {
+
+Capsule::Capsule(class BinaryInput& b) {
+ deserialize(b);
+}
+
+
+Capsule::Capsule() {
+}
+
+
+Capsule::Capsule(const Vector3& _p1, const Vector3& _p2, float _r)
+ : p1(_p1), p2(_p2), _radius(_r) {
+}
+
+
+void Capsule::serialize(class BinaryOutput& b) const {
+ p1.serialize(b);
+ p2.serialize(b);
+ b.writeFloat64(_radius);
+}
+
+
+void Capsule::deserialize(class BinaryInput& b) {
+ p1.deserialize(b);
+ p2.deserialize(b);
+ _radius = b.readFloat64();
+}
+
+
+Line Capsule::axis() const {
+ return Line::fromTwoPoints(p1, p2);
+}
+
+
+float Capsule::volume() const {
+ return
+ // Sphere volume
+ pow(_radius, 3) * pi() * 4 / 3 +
+
+ // Cylinder volume
+ pow(_radius, 2) * (p1 - p2).magnitude();
+}
+
+
+float Capsule::area() const {
+
+ return
+ // Sphere area
+ pow(_radius, 2) * 4 * pi() +
+
+ // Cylinder area
+ twoPi() * _radius * (p1 - p2).magnitude();
+}
+
+
+void Capsule::getBounds(AABox& out) const {
+ Vector3 min = p1.min(p2) - (Vector3(1, 1, 1) * _radius);
+ Vector3 max = p1.max(p2) + (Vector3(1, 1, 1) * _radius);
+
+ out = AABox(min, max);
+}
+
+
+bool Capsule::contains(const Vector3& p) const {
+ return LineSegment::fromTwoPoints(p1, p2).distanceSquared(p) <= square(radius());
+}
+
+
+void Capsule::getRandomSurfacePoint(Vector3& p, Vector3& N) const {
+ float h = height();
+ float r = radius();
+
+ // Create a random point on a standard capsule and then rotate to the global frame.
+
+ // Relative areas
+ float capRelArea = sqrt(r) / 2.0f;
+ float sideRelArea = r * h;
+
+ float r1 = uniformRandom(0, capRelArea * 2 + sideRelArea);
+
+ if (r1 < capRelArea * 2) {
+
+ // Select a point uniformly at random on a sphere
+ N = Sphere(Vector3::zero(), 1).randomSurfacePoint();
+ p = N * r;
+ p.y += sign(p.y) * h / 2.0f;
+ } else {
+ // Side
+ float a = uniformRandom(0, (float)twoPi());
+ N.x = cos(a);
+ N.y = 0;
+ N.z = sin(a);
+ p.x = N.x * r;
+ p.z = N.y * r;
+ p.y = uniformRandom(-h / 2.0f, h / 2.0f);
+ }
+
+ // Transform to world space
+ CoordinateFrame cframe;
+ getReferenceFrame(cframe);
+
+ p = cframe.pointToWorldSpace(p);
+ N = cframe.normalToWorldSpace(N);
+}
+
+
+void Capsule::getReferenceFrame(CoordinateFrame& cframe) const {
+ cframe.translation = center();
+
+ Vector3 Y = (p1 - p2).direction();
+ Vector3 X = (abs(Y.dot(Vector3::unitX())) > 0.9) ? Vector3::unitY() : Vector3::unitX();
+ Vector3 Z = X.cross(Y).direction();
+ X = Y.cross(Z);
+ cframe.rotation.setColumn(0, X);
+ cframe.rotation.setColumn(1, Y);
+ cframe.rotation.setColumn(2, Z);
+}
+
+
+Vector3 Capsule::randomInteriorPoint() const {
+ float h = height();
+ float r = radius();
+
+ // Create a random point in a standard capsule and then rotate to the global frame.
+
+ Vector3 p;
+
+ float hemiVolume = pi() * (r*r*r) * 4 / 6.0;
+ float cylVolume = pi() * square(r) * h;
+
+ float r1 = uniformRandom(0, 2.0 * hemiVolume + cylVolume);
+
+ if (r1 < 2.0 * hemiVolume) {
+
+ p = Sphere(Vector3::zero(), r).randomInteriorPoint();
+
+ p.y += sign(p.y) * h / 2.0f;
+
+ } else {
+
+ // Select a point uniformly at random on a disk
+ float a = uniformRandom(0, (float)twoPi());
+ float r2 = sqrt(uniformRandom(0, 1)) * r;
+
+ p = Vector3(cos(a) * r2,
+ uniformRandom(-h / 2.0f, h / 2.0f),
+ sin(a) * r2);
+ }
+
+ // Transform to world space
+ CoordinateFrame cframe;
+ getReferenceFrame(cframe);
+
+ return cframe.pointToWorldSpace(p);
+}
+
+} // namespace
diff --git a/dep/src/g3dlite/CollisionDetection.cpp b/dep/src/g3dlite/CollisionDetection.cpp
new file mode 100644
index 00000000000..77eef0a5500
--- /dev/null
+++ b/dep/src/g3dlite/CollisionDetection.cpp
@@ -0,0 +1,2455 @@
+/**
+ @file CollisionDetection.cpp
+
+ @maintainer Morgan McGuire, http://graphics.cs.williams.edu
+
+ @cite Bounce direction based on Paul Nettle's ftp://ftp.3dmaileffects.com/pub/FluidStudios/CollisionDetection/Fluid_Studios_Generic_Collision_Detection_for_Games_Using_Ellipsoids.pdf and comments by Max McGuire. Ray-sphere code by Eric Haines.
+
+ @created 2001-11-24
+ @edited 2008-12-29
+ */
+
+#include "G3D/CoordinateFrame.h"
+#include "G3D/platform.h"
+#include "G3D/CollisionDetection.h"
+#include "G3D/debugAssert.h"
+#include "G3D/vectorMath.h"
+#include "G3D/Capsule.h"
+#include "G3D/Plane.h"
+#include "G3D/Line.h"
+#include "G3D/LineSegment.h"
+#include "G3D/Sphere.h"
+#include "G3D/Box.h"
+#include "G3D/Triangle.h"
+#include "G3D/Vector3.h"
+#include "G3D/AABox.h"
+
+#ifdef _MSC_VER
+// Turn on fast floating-point optimizations
+#pragma float_control( push )
+#pragma fp_contract( on )
+#pragma fenv_access( off )
+#pragma float_control( except, off )
+#pragma float_control( precise, off )
+#endif
+
+
+namespace G3D {
+
+bool CollisionDetection::ignoreBool;
+Vector3 CollisionDetection::ignore;
+Array<Vector3> CollisionDetection::ignoreArray;
+
+
+
+Vector3 CollisionDetection::separatingAxisForSolidBoxSolidBox(
+ const int separatingAxisIndex,
+ const Box & box1,
+ const Box & box2) {
+ debugAssert(separatingAxisIndex >= 0);
+ debugAssert(separatingAxisIndex < 15);
+ Vector3 axis;
+ if (separatingAxisIndex < 3) {
+ axis = box1.axis(separatingAxisIndex);
+ } else if (separatingAxisIndex < 6) {
+ axis = box2.axis(separatingAxisIndex - 3);
+ } else {
+ int box1Index = (separatingAxisIndex - 6) / 3;
+ int box2Index = (separatingAxisIndex - 6) % 3;
+ axis = cross(box1.axis(box1Index), box2.axis(box2Index));
+ }
+ return axis;
+}
+
+#ifdef _MSC_VER
+# pragma warning (push)
+# pragma warning (disable : 4244)
+#endif
+
+float CollisionDetection::projectedDistanceForSolidBoxSolidBox(
+ const int separatingAxisIndex,
+ const Vector3 & a,
+ const Vector3 & b,
+ const Vector3 & D,
+ const double* c,
+ const double* ca,
+ const double* ad,
+ const double* bd)
+{
+ (void)D;
+
+ float R0 = 0.0f;
+ float R1 = 0.0f;
+ float R = 0.0f;
+ switch (separatingAxisIndex) {
+ case 0:
+ // A0
+ R0 = a[0];
+ R1 = b[0] * ca[0] + b[1] * ca[1] + b[2] * ca[2];
+ R = fabs(ad[0]);
+ break;
+ case 1:
+ // A1
+ R0 = a[1];
+ R1 = b[0] * ca[3] + b[1] * ca[4] + b[2] * ca[5];
+ R = fabs(ad[1]);
+ break;
+ case 2:
+ // A2
+ R0 = a[2];
+ R1 = b[0] * ca[6] + b[1] * ca[7] + b[2] * ca[8];
+ R = fabs(ad[2]);
+ break;
+ case 3:
+ // B0
+ R0 = a[0] * ca[0] + a[1] * ca[3] + a[2] * ca[6];
+ R1 = b[0];
+ R = fabs(bd[0]);
+ break;
+ case 4:
+ // B1
+ R0 = a[0] * ca[1] + a[1] * ca[4] + a[2] * ca[7];
+ R1 = b[1];
+ R = fabs(bd[1]);
+ break;
+ case 5:
+ // B2
+ R0 = a[0] * ca[2] + a[1] * ca[5] + a[2] * ca[8];
+ R1 = b[2];
+ R = fabs(bd[2]);
+ break;
+ case 6:
+ // A0 x B0
+ R0 = a[1] * ca[6] + a[2] * ca[3];
+ R1 = b[1] * ca[2] + b[2] * ca[1];
+ R = fabs(c[3] * ad[2] - c[6] * ad[1]);
+ break;
+ case 7:
+ // A0 x B1
+ R0 = a[1] * ca[7] + a[2] * ca[4];
+ R1 = b[0] * ca[2] + b[2] * ca[0];
+ R = fabs(c[4] * ad[2] - c[7] * ad[1]);
+ break;
+ case 8:
+ // A0 x B2
+ R0 = a[1] * ca[8] + a[2] * ca[5];
+ R1 = b[0] * ca[1] + b[1] * ca[0];
+ R = fabs(c[5] * ad[2] - c[8] * ad[1]);
+ break;
+ case 9:
+ // A1 x B0
+ R0 = a[0] * ca[6] + a[2] * ca[0];
+ R1 = b[1] * ca[5] + b[2] * ca[4];
+ R = fabs(c[6] * ad[0] - c[0] * ad[2]);
+ break;
+ case 10:
+ // A1 x B1
+ R0 = a[0] * ca[7] + a[2] * ca[1];
+ R1 = b[0] * ca[5] + b[2] * ca[3];
+ R = fabs(c[7] * ad[0] - c[1] * ad[2]);
+ break;
+ case 11:
+ // A1 x B2
+ R0 = a[0] * ca[8] + a[2] * ca[2];
+ R1 = b[0] * ca[4] + b[1] * ca[3];
+ R = fabs(c[8] * ad[0] - c[2] * ad[2]);
+ break;
+ case 12:
+ // A2 x B0
+ R0 = a[0] * ca[3] + a[1] * ca[0];
+ R1 = b[1] * ca[8] + b[2] * ca[7];
+ R = fabs(c[0] * ad[1] - c[3] * ad[0]);
+ break;
+ case 13:
+ // A2 x B1
+ R0 = a[0] * ca[4] + a[1] * ca[1];
+ R1 = b[0] * ca[8] + b[2] * ca[6];
+ R = fabs(c[1] * ad[1] - c[4] * ad[0]);
+ break;
+ case 14:
+ // A2 x B2
+ R0 = a[0] * ca[5] + a[1] * ca[2];
+ R1 = b[0] * ca[7] + b[1] * ca[6];
+ R = fabs(c[2] * ad[1] - c[5] * ad[0]);
+ break;
+ default:
+ debugAssertM(false, "fell through switch statement");
+ }
+
+ return (R - (R0 + R1));
+}
+
+
+bool CollisionDetection::parallelAxisForSolidBoxSolidBox(
+ const double* ca,
+ const double epsilon,
+ int & axis1,
+ int & axis2) {
+ const double parallelDot = 1.0 - epsilon;
+ for (int i = 0; i < 9; i++) {
+ if (ca[i] >= parallelDot) {
+ axis1 = i / 3;
+ axis2 = i % 3;
+ return true;
+ }
+ }
+ return false;
+}
+
+
+
+
+void CollisionDetection::fillSolidBoxSolidBoxInfo(
+ const Box & box1,
+ const Box & box2,
+ Vector3 & a,
+ Vector3 & b,
+ Vector3 & D,
+ double* c,
+ double* ca,
+ double* ad,
+ double* bd) {
+ // length between center and each side of box1 and box2
+ a = box1.extent() * 0.5;
+ b = box2.extent() * 0.5;
+
+ // difference between centers of box1 and box2
+ D = box2.center() - box1.center();
+
+ // store the value of all possible dot products between the
+ // axes of box1 and box2, c_{row, col} in the Eberly paper
+ // corresponds to c[row * 3 + col] for this 9 element array.
+ //
+ // c[] holds signed values, ca[] hold absolute values
+ for (int i = 0; i < 9; i++) {
+ c[i] = dot(box1.axis(i / 3), box2.axis(i % 3));
+ ca[i] = fabs(c[i]);
+ }
+
+ // store all possible dot products between the axes of box1 and D,
+ // as well as the axes of box2 and D
+ for (int i = 0; i < 3; i++) {
+ ad[i] = dot(box1.axis(i), D);
+ bd[i] = dot(box2.axis(i), D);
+ }
+}
+
+
+
+bool CollisionDetection::conservativeBoxBoxTest(
+ const Vector3 & a, const Vector3 & b, const Vector3 & D) {
+ // do a quick bounding sphere test because it is relatively
+ // cheap, (three dot products, two sqrts, and a few others)
+ double boxRadius1 = a.magnitude();
+ double boxRadius2 = b.magnitude();
+ return (D.squaredMagnitude() < square(boxRadius1 + boxRadius2));
+}
+
+
+
+
+bool CollisionDetection::fixedSolidBoxIntersectsFixedSolidBox(
+ const Box& box1,
+ const Box& box2,
+ const int lastSeparatingAxis) {
+ // for explanations of the variable please refer to the
+ // paper and fillSolidBoxSolidBoxInfo()
+ Vector3 a;
+ Vector3 b;
+ Vector3 D;
+ double c[9];
+ double ca[9];
+ double ad[3];
+ double bd[3];
+
+ fillSolidBoxSolidBoxInfo(box1, box2, a, b, D, c, ca, ad, bd);
+
+ int dummy1, dummy2;
+ bool parallelAxes = parallelAxisForSolidBoxSolidBox(ca, 0.00001,
+ dummy1, dummy2);
+
+ // check the separating axis from the last time step
+ if (lastSeparatingAxis != -1 &&
+ (lastSeparatingAxis < 6 || !parallelAxes)) {
+ double projectedDistance = projectedDistanceForSolidBoxSolidBox(
+ lastSeparatingAxis, a, b, D, c, ca, ad, bd);
+
+ // the separating axis from the last time step is still
+ // valid, the boxes do not intersect
+ if (projectedDistance > 0.0) {
+ return false;
+ }
+ }
+
+ // test if the boxes can be separated by a plane normal to
+ // any of the three axes of box1, any of the three axes of box2,
+ // or any of the 9 possible cross products of axes from box1
+ // and box2
+ for (int i = 0; i < 15; i++) {
+ // do not need to check edge-edge cases if any two of
+ // the axes are parallel
+ if (parallelAxes && i == 6) {
+ return true;
+ }
+
+ double projectedDistance =
+ projectedDistanceForSolidBoxSolidBox(i, a, b, D, c, ca, ad, bd);
+
+ // found a separating axis, the boxes do not intersect
+ if (projectedDistance > 0.0) {
+ return false;
+ }
+ }
+
+ return true;
+}
+
+
+
+void CollisionDetection::closestPointsBetweenLineAndLine(
+ const Line & line1,
+ const Line & line2,
+ Vector3 & closest1,
+ Vector3 & closest2) {
+ // TODO make accessors for Line that don't make a copy of data
+ Vector3 P0 = line1.point();
+ Vector3 u = line1.direction();
+ Vector3 Q0 = line2.point();
+ Vector3 v = line2.direction();
+ Vector3 w0 = P0 - Q0;
+
+ // a = 1.0, c = 1.0
+ double b = dot(u, v);
+ double d = dot(u, w0);
+ double e = dot(v, w0);
+ double D = 1.0 - b * b;
+ double sc, tc;
+
+ static const double epsilon = 0.00001;
+
+ if (D < epsilon) {
+ // lines are parallel, choose P0 as one point, find the point
+ // on line2 that is closest to P0
+ sc = 0.0;
+ tc = (b > 1.0) ? (d / b) : (e / 1.0);
+ } else {
+ // lines are not parallel
+ sc = (b * e - 1.0 * d) / D;
+ tc = (1.0 * e - b * d) / D;
+ }
+
+ closest1 = P0 + (sc * u);
+ closest2 = Q0 + (tc * v);
+}
+
+
+
+float CollisionDetection::penetrationDepthForFixedBoxFixedBox(
+ const Box& box1,
+ const Box& box2,
+ Array<Vector3>& contactPoints,
+ Array<Vector3>& contactNormals,
+ const int lastSeparatingAxis) {
+
+ contactPoints.resize(0, DONT_SHRINK_UNDERLYING_ARRAY);
+ contactNormals.resize(0, DONT_SHRINK_UNDERLYING_ARRAY);
+
+ Vector3 a;
+ Vector3 b;
+ Vector3 D;
+ double c[9];
+ double ca[9];
+ double ad[3];
+ double bd[3];
+
+ debugAssert(lastSeparatingAxis >= -1);
+ debugAssert(lastSeparatingAxis < 15);
+
+ fillSolidBoxSolidBoxInfo(box1, box2, a, b, D, c, ca, ad, bd);
+
+ int axis1, axis2;
+ bool parallelAxes = parallelAxisForSolidBoxSolidBox(ca, 0.00001,
+ axis1, axis2);
+
+
+ // check the separating axis from the last time step
+ if (lastSeparatingAxis != -1 &&
+ (lastSeparatingAxis < 6 || !parallelAxes)) {
+ float projectedDistance = projectedDistanceForSolidBoxSolidBox(
+ lastSeparatingAxis, a, b, D, c, ca, ad, bd);
+
+ // the separating axis from the last time step is still
+ // valid, the boxes do not intersect
+ if (projectedDistance > 0.0) {
+ return -projectedDistance;
+ }
+ }
+
+ // test if the boxes can be separated by a plane normal to
+ // any of the three axes of box1, any of the three axes of box2,
+ // (test 9 possible cross products later)
+ float penetration = -finf();
+ int penetrationAxisIndex = -1;
+
+ for (int i = 0; i < 6; i++) {
+ float projectedDistance =
+ projectedDistanceForSolidBoxSolidBox(i, a, b, D, c, ca, ad, bd);
+
+ // found a separating axis, the boxes do not intersect
+ if (projectedDistance > 0.0) {
+ return -projectedDistance;
+ }
+
+ // keep track of the axis that is least violated
+ if (projectedDistance > penetration) {
+ penetration = projectedDistance;
+ penetrationAxisIndex = i;
+ }
+ }
+
+
+ // for each edge-edge case we have to adjust the magnitude of
+ // penetration since we did not include the dot(L, L) denominator
+ // that can be smaller than 1.0 for the edge-edge cases.
+ if (!parallelAxes) {
+ double edgeDistances[9];
+
+ // run through edge-edge cases to see if we can find a separating axis
+ for (int i = 6; i < 15; i++) {
+ float projectedDistance =
+ projectedDistanceForSolidBoxSolidBox(i, a, b, D, c, ca, ad, bd);
+
+ // found a separating axis, the boxes do not intersect,
+ // correct magnitude and return projected distance
+ if (projectedDistance > 0.0) {
+ Vector3 L = separatingAxisForSolidBoxSolidBox(i, box1, box2);
+ projectedDistance /= dot(L, L);
+ return -projectedDistance;
+ }
+
+ edgeDistances[i - 6] = projectedDistance;
+ }
+
+ // no separating axis found, the boxes do intersect,
+ // correct the magnitudes of the projectedDistance values
+ for (int i = 6; i < 15; i++) {
+ // find the negative penetration value with the smallest magnitude,
+ // the adjustment done for the edge-edge cases only increases
+ // magnitude by dividing by a number smaller than 1 and greater than 0
+ float projectedDistance = (float)edgeDistances[i - 6];
+ if (projectedDistance > penetration) {
+ Vector3 L = separatingAxisForSolidBoxSolidBox(i, box1, box2);
+ projectedDistance /= dot(L, L);
+ if (projectedDistance > penetration) {
+ penetration = projectedDistance;
+ penetrationAxisIndex = i;
+ }
+ }
+ }
+ }
+
+ // get final separating axis vector
+ Vector3 L = separatingAxisForSolidBoxSolidBox(penetrationAxisIndex,
+ box1, box2);
+
+ // set L to be the normal that faces away from box1
+ if (dot(L, D) < 0) {
+ L = -L;
+ }
+
+ Vector3 contactPoint;
+
+ if (penetrationAxisIndex < 6) {
+ // vertex to face collision, find deepest colliding vertex
+ const Box* vertexBox;
+ const Box* faceBox;
+ Vector3 faceNormal = L;
+
+ // L will be the outward facing normal for the faceBox
+ if (penetrationAxisIndex < 3) {
+ faceBox = & box1;
+ vertexBox = & box2;
+ if (dot(L, D) < 0) {
+ faceNormal = -L;
+ }
+ } else {
+ faceBox = & box2;
+ vertexBox = & box1;
+ if (dot(L, D) > 0) {
+ faceNormal = -L;
+ }
+ }
+
+ // find the vertex that is farthest away in the direction
+ // face normal direction
+ int deepestPointIndex = 0;
+ float deepestPointDot = dot(faceNormal, vertexBox->corner(0));
+ for (int i = 1; i < 8; i++) {
+ float dotProduct = dot(faceNormal, vertexBox->corner(i));
+ if (dotProduct < deepestPointDot) {
+ deepestPointDot = dotProduct;
+ deepestPointIndex = i;
+ }
+ }
+
+ // return the point half way between the deepest point and the
+ // contacting face
+ contactPoint = vertexBox->corner(deepestPointIndex) +
+ (-penetration * 0.5 * faceNormal);
+ } else {
+ // edge-edge case, find the two ege lines
+ int edge1 = (penetrationAxisIndex - 6) / 3;
+ int edge2 = (penetrationAxisIndex - 6) % 3;
+ Vector3 linePoint1 = box1.center();
+ Vector3 linePoint2 = box2.center();
+ Vector3 lineDir1;
+ Vector3 lineDir2;
+
+ // find edge line by finding the edge axis, and the
+ // other two axes that are closest to the other box
+ for (int i = 0; i < 3; i++ ) {
+ if (i == edge1) {
+ lineDir1 = box1.axis(i);
+ } else {
+ Vector3 axis = box1.axis(i);
+ if (dot(axis, L) < 0) {
+ axis = -axis;
+ }
+ linePoint1 += axis * a[i];
+ }
+
+ if (i == edge2) {
+ lineDir2 = box2.axis(i);
+ } else {
+ Vector3 axis = box2.axis(i);
+ if (dot(axis, L) > 0) {
+ axis = -axis;
+ }
+ linePoint2 += axis * b[i];
+ }
+ }
+
+ // make lines from the two closest edges, and find
+ // the points that on each line that are closest to the other
+ Line line1 = Line::fromPointAndDirection(linePoint1, lineDir1);
+ Line line2 = Line::fromPointAndDirection(linePoint2, lineDir2);
+ Vector3 closest1;
+ Vector3 closest2;
+
+ closestPointsBetweenLineAndLine(line1, line2, closest1, closest2);
+
+ // take the average of the two closest edge points for the final
+ // contact point
+ contactPoint = (closest1 + closest2) * 0.5;
+ }
+
+ contactPoints.push(contactPoint);
+ contactNormals.push(L);
+
+ return -penetration;
+
+}
+
+
+
+
+float CollisionDetection::penetrationDepthForFixedSphereFixedBox(
+ const Sphere& sphere,
+ const Box& box,
+ Array<Vector3>& contactPoints,
+ Array<Vector3>& contactNormals) {
+
+ contactPoints.resize(0, DONT_SHRINK_UNDERLYING_ARRAY);
+ contactNormals.resize(0, DONT_SHRINK_UNDERLYING_ARRAY);
+
+ // In its local coordinate frame, the box measures
+ // 2 * halfExtent[a] along dimesion a.
+ Vector3 halfExtent(box.extent(0), box.extent(1), box.extent(2));
+ halfExtent *= 0.5f;
+
+ CoordinateFrame boxFrame;
+ box.getLocalFrame(boxFrame);
+
+ // Transform the sphere to the box's coordinate frame.
+ Vector3 center = boxFrame.pointToObjectSpace(sphere.center);
+
+ // Find the square of the distance from the sphere to the box
+
+
+ // Distance along each axis from the closest side of the box
+ // to the sphere center. Negative values are *inside* the box.
+ Vector3 distOutsideBox;
+
+ // Divide space up into the 27 regions corresponding
+ // to {+|-|0}X, {+|-|0}Y, {+|-|0}Z and classify the
+ // sphere center into one of them.
+ Vector3 centerRegion;
+
+ // In the edge collision case, the edge is between vertices
+ // (constant + variable) and (constant - variable).
+ Vector3 constant, variable;
+
+ int numNonZero = 0;
+
+ // Iterate over axes
+ for (int a = 0; a < 3; ++a) {
+ // For each (box side), see which direction the sphere
+ // is outside the box (positive or negative). Add the
+ // square of that distance to the total distance from
+ // the box.
+
+ float distanceFromLow = -halfExtent[a] - center[a];
+ float distanceFromHigh = center[a] - halfExtent[a];
+
+ if (fabsf(distanceFromLow) < fabsf(distanceFromHigh)) {
+ distOutsideBox[a] = distanceFromLow;
+ } else {
+ distOutsideBox[a] = distanceFromHigh;
+ }
+
+ if (distanceFromLow < 0.0) {
+ if (distanceFromHigh < 0.0) {
+ // Inside the box
+ centerRegion[a] = 0.0;
+ variable[a] = 1.0;
+ } else {
+ // Off the high side
+ centerRegion[a] = 1.0;
+ constant[a] = halfExtent[a];
+ ++numNonZero;
+ }
+ } else if (distanceFromHigh < 0.0) {
+ // Off the low side
+ centerRegion[a] = -1.0;
+ constant[a] = -halfExtent[a];
+ ++numNonZero;
+ } else {
+ debugAssertM(false,
+ "distanceFromLow and distanceFromHigh cannot both be positive");
+ }
+ }
+
+ // Squared distance between the outside of the box and the
+ // sphere center.
+ float d2 = Vector3::zero().max(distOutsideBox).squaredMagnitude();
+
+ if (d2 > square(sphere.radius)) {
+ // There is no penetration because the distance is greater
+ // than the radius of the sphere. This is the common case
+ // and we quickly exit.
+ return -1;
+ }
+
+ // We know there is some penetration but need to classify it.
+ //
+ // Examine the region that contains the center of the sphere. If
+ // there is exactly one non-zero axis, the collision is with a
+ // plane. If there are exactly two non-zero axes, the collision
+ // is with an edge. If all three axes are non-zero, the collision is
+ // with a vertex. If there are no non-zero axes, the center is inside
+ // the box.
+
+ double depth = -1;
+ switch (numNonZero) {
+ case 3: // Vertex collision
+ // The collision point is the vertex at constant, the normal
+ // is the vector from there to the sphere center.
+ contactNormals.append(boxFrame.normalToWorldSpace(constant - center));
+ contactPoints.append(boxFrame.pointToWorldSpace(constant));
+ depth = sphere.radius - sqrt(d2);
+ break;
+
+ case 2: // Edge collision
+ {
+ // TODO: unwrapping the edge constructor and closest point
+ // code will probably make it faster.
+
+ // Determine the edge
+ Line line = Line::fromPointAndDirection(constant, variable);
+
+ // Penetration depth:
+ depth = sphere.radius - sqrt(d2);
+
+ // The contact point is the closes point to the sphere on the line
+ Vector3 X = line.closestPoint(center);
+ contactNormals.append(boxFrame.normalToWorldSpace(X - center).direction());
+ contactPoints.append(boxFrame.pointToWorldSpace(X));
+ }
+ break;
+
+ case 1: // Plane collision
+ {
+ // The plane normal is the centerRegion vector,
+ // so the sphere normal is the negative. Take
+ // it to world space from box-space.
+
+ // Center region doesn't need to be normalized because
+ // it is known to contain only one non-zero value
+ // and that value is +/- 1.
+ Vector3 N = boxFrame.normalToWorldSpace(-centerRegion);
+ contactNormals.append(N);
+
+ // Penetration depth:
+ depth = sphere.radius - sqrtf(d2);
+
+ // Compute the contact point from the penetration depth
+ contactPoints.append(sphere.center + N * (sphere.radius - depth));
+ }
+ break;
+
+ case 0: // Volume collision
+
+ // The sphere center is inside the box. This is an easy case
+ // to handle. Note that all axes of distOutsideBox must
+ // be negative.
+
+ // Arbitratily choose the sphere center as a contact point
+ contactPoints.append(sphere.center);
+
+ // Find the least-negative penetration axis.
+ //
+ // We could have computed this during the loop over the axes,
+ // but since volume collisions are rare (they only occur with
+ // large time steps), this case will seldom be executed and
+ // should not be optimized at the expense of the others.
+ if (distOutsideBox.x > distOutsideBox.y) {
+ if (distOutsideBox.x > distOutsideBox.z) {
+ // Smallest penetration on x-axis
+ // Chose normal based on which side we're closest to.
+ // Keep in mind that this is a normal to the sphere,
+ // so it is the inverse of the box normal.
+ if (center.x > 0) {
+ contactNormals.append(boxFrame.normalToWorldSpace(-Vector3::unitX()));
+ } else {
+ contactNormals.append(boxFrame.normalToWorldSpace(Vector3::unitX()));
+ }
+ depth = -distOutsideBox.x;
+ } else {
+ // Smallest penetration on z-axis
+ goto ZAXIS;
+ }
+ } else if (distOutsideBox.y > distOutsideBox.z) {
+ // Smallest penetration on y-axis
+ // Chose normal based on which side we're closest to.
+ // Keep in mind that this is a normal to the sphere,
+ // so it is the inverse of the box normal.
+ if (center.y > 0) {
+ contactNormals.append(boxFrame.normalToWorldSpace(-Vector3::unitY()));
+ } else {
+ contactNormals.append(boxFrame.normalToWorldSpace(Vector3::unitY()));
+ }
+ depth = -distOutsideBox.y;
+ } else {
+ // Smallest on z-axis
+ZAXIS:
+ // Chose normal based on which side we're closest to.
+ // Keep in mind that this is a normal to the sphere,
+ // so it is the inverse of the box normal.
+ if (center.z > 0) {
+ contactNormals.append(boxFrame.normalToWorldSpace(-Vector3::unitZ()));
+ } else {
+ contactNormals.append(boxFrame.normalToWorldSpace(Vector3::unitZ()));
+ }
+ depth = -distOutsideBox.z;
+ }
+ break;
+
+ default:
+ debugAssertM(false, "Fell through switch");
+ break;
+ }
+
+ return depth;
+}
+
+
+float CollisionDetection::penetrationDepthForFixedSphereFixedSphere(
+ const Sphere& sphereA,
+ const Sphere& sphereB,
+ Array<Vector3>& contactPoints,
+ Array<Vector3>& contactNormals) {
+
+ Vector3 axis = sphereB.center - sphereA.center;
+ double radius = sphereA.radius + sphereB.radius;
+ double mag = axis.magnitude();
+ axis /= mag;
+ double depth = -(mag - radius);
+
+ contactPoints.resize(0, DONT_SHRINK_UNDERLYING_ARRAY);
+ contactNormals.resize(0, DONT_SHRINK_UNDERLYING_ARRAY);
+
+ if (depth >= 0) {
+ contactPoints.append(sphereA.center + axis * (sphereA.radius - depth / 2));
+ contactNormals.append(axis);
+ }
+
+ return depth;
+}
+
+
+float CollisionDetection::penetrationDepthForFixedSphereFixedPlane(
+ const Sphere& sphereA,
+ const Plane& planeB,
+ Array<Vector3>& contactPoints,
+ Array<Vector3>& contactNormals) {
+
+ Vector3 N;
+ double d;
+
+ planeB.getEquation(N, d);
+
+ double depth = -(sphereA.center.dot(N) + d - sphereA.radius);
+
+ contactPoints.resize(0, DONT_SHRINK_UNDERLYING_ARRAY);
+ contactNormals.resize(0, DONT_SHRINK_UNDERLYING_ARRAY);
+
+ if (depth >= 0) {
+ contactPoints.append(N * (depth - sphereA.radius) + sphereA.center);
+ contactNormals.append(N);
+ }
+
+ return depth;
+}
+
+
+float CollisionDetection::penetrationDepthForFixedBoxFixedPlane(
+ const Box& box,
+ const Plane& plane,
+ Array<Vector3>& contactPoints,
+ Array<Vector3>& contactNormals) {
+
+ Vector3 N;
+ double d;
+
+ plane.getEquation(N, d);
+
+ contactPoints.resize(0, DONT_SHRINK_UNDERLYING_ARRAY);
+ contactNormals.resize(0, DONT_SHRINK_UNDERLYING_ARRAY);
+
+ float lowest = finf();
+ for (int i = 0; i < 8; ++i) {
+ const Vector3 vertex = box.corner(i);
+
+ float x = vertex.dot(N) + (float)d;
+
+ if (x <= 0) {
+ // All vertices below the plane should be contact points.
+ contactPoints.append(vertex);
+ contactNormals.append(-N);
+ }
+
+ lowest = min(lowest, x);
+ }
+
+ // Depth should be a positive number
+ return -lowest;
+}
+
+
+float CollisionDetection::collisionTimeForMovingPointFixedPlane(
+ const Vector3& point,
+ const Vector3& velocity,
+ const Plane& plane,
+ Vector3& location,
+ Vector3& outNormal) {
+
+ // Solve for the time at which normal.dot(point + velocity) + d == 0.
+ double d;
+ Vector3 normal;
+ plane.getEquation(normal, d);
+
+ float vdotN = velocity.dot(normal);
+ float pdotN = point.dot(normal);
+
+ if (fuzzyEq(pdotN + d, 0)) {
+ // The point is *in* the plane.
+ location = point;
+ outNormal = normal;
+ return 0;
+ }
+
+ if (vdotN >= 0) {
+ // no collision will occur
+ location = Vector3::inf();
+ return finf();
+ }
+
+ float t = -(pdotN + d) / vdotN;
+ if (t < 0) {
+ location = Vector3::inf();
+ return finf();
+ } else {
+ location = point + velocity * t;
+ outNormal = normal;
+ return t;
+ }
+}
+
+bool __fastcall CollisionDetection::rayAABox(
+ const Ray& ray,
+ const Vector3& invDir,
+ const AABox& box,
+ const Vector3& boxCenter,
+ float boundingRadiusSquared,
+ Vector3& location,
+ bool& inside) {
+
+ debugAssertM(fabs(ray.direction().squaredLength() - 1.0f) < 0.01f, format("Length = %f", ray.direction().length()));
+ {
+ // Pre-emptive partial bounding sphere test
+ const Vector3 L(boxCenter - ray.origin());
+ float d = L.dot(ray.direction());
+
+ float L2 = L.dot(L);
+ float D2 = square(d);
+ float M2 = L2 - D2;
+
+ if (((d < 0) && (L2 > boundingRadiusSquared)) || (M2 > boundingRadiusSquared)) {
+ inside = false;
+ return false;
+ }
+ // Passing here does not mean that the ray hits the bounding sphere;
+ // we would still have to perform more expensive tests to determine
+ // that.
+ }
+
+ inside = true;
+ const Vector3& MinB = box.low();
+ const Vector3& MaxB = box.high();
+ Vector3 MaxT(-1.0f, -1.0f, -1.0f);
+
+ // Find candidate planes.
+ for (int i = 0; i < 3; ++i) {
+ if (ray.origin()[i] < MinB[i]) {
+ location[i] = MinB[i];
+ inside = false;
+
+ // Calculate T distances to candidate planes
+ if (ray.direction()[i] != 0) {
+ MaxT[i] = (MinB[i] - ray.origin()[i]) * invDir[i];
+ }
+ } else if (ray.origin()[i] > MaxB[i]) {
+ location[i] = MaxB[i];
+ inside = false;
+
+ // Calculate T distances to candidate planes
+ if (ray.direction()[i] != 0) {
+ MaxT[i] = (MaxB[i] - ray.origin()[i]) * invDir[i];
+ }
+ }
+ }
+
+ if (inside) {
+ // Ray origin inside bounding box
+ location = ray.origin();
+ return true;
+ }
+
+ // Get largest of the maxT's for final choice of intersection
+ int WhichPlane = 0;
+ if (MaxT[1] > MaxT[WhichPlane]) {
+ WhichPlane = 1;
+ }
+
+ if (MaxT[2] > MaxT[WhichPlane]) {
+ WhichPlane = 2;
+ }
+
+ // Check final candidate actually inside box
+ if (MaxT[WhichPlane] < 0.0f) {
+ // Miss the box
+ return false;
+ }
+
+ for (int i = 0; i < 3; ++i) {
+ if (i != WhichPlane) {
+ location[i] = ray.origin()[i] + MaxT[WhichPlane] * ray.direction()[i];
+ if ((location[i] < MinB[i]) ||
+ (location[i] > MaxB[i])) {
+ // On this plane we're outside the box extents, so
+ // we miss the box
+ return false;
+ }
+ }
+ }
+
+ return true;
+}
+
+float CollisionDetection::collisionTimeForMovingPointFixedSphere(
+ const Vector3& point,
+ const Vector3& velocity,
+ const Sphere& sphere,
+ Vector3& location,
+ Vector3& outNormal,
+ bool solid) {
+
+ if (solid && sphere.contains(point)) {
+ location = point;
+ outNormal = (point - sphere.center).direction();
+ return 0.0f;
+ }
+
+ float speed = velocity.magnitude();
+ const Vector3& direction = velocity / speed;
+
+ // length of the axis between the start and the sphere
+ const Vector3& L = sphere.center - point;
+ float d = L.dot(direction);
+
+ float L2 = L.dot(L);
+ float R2 = square(sphere.radius);
+ float D2 = square(d);
+
+ if ((d < 0.0f) && (L2 > R2)) {
+ location = Vector3::inf();
+ return finf();
+ }
+
+ const float M2 = L2 - D2;
+
+ if (M2 > R2) {
+ location = Vector3::inf();
+ return finf();
+ }
+
+ float q = sqrt(R2 - M2);
+ float time;
+
+ if (L2 > R2) {
+ time = d - q;
+ } else {
+ time = d + q;
+ }
+
+ time /= speed;
+
+ location = point + velocity * time;
+ outNormal = (location - sphere.center).direction();
+
+ return time;
+}
+
+
+float CollisionDetection::collisionTimeForMovingSphereFixedSphere(
+ const Sphere& movingSphere,
+ const Vector3& velocity,
+ const Sphere& fixedSphere,
+ Vector3& location,
+ Vector3& outNormal) {
+
+ const Vector3& sep = (fixedSphere.center - movingSphere.center);
+ float sepLen = sep.squaredLength();
+ if (sepLen < square(movingSphere.radius + fixedSphere.radius)) {
+ // Interpenetrating
+ outNormal = sep.directionOrZero();
+ location = fixedSphere.center - outNormal * fixedSphere.radius;
+ return 0;
+ }
+
+ float time = collisionTimeForMovingPointFixedSphere
+ (movingSphere.center, velocity,
+ Sphere(fixedSphere.center, fixedSphere.radius + movingSphere.radius),
+ location, outNormal);
+
+ if (time < finf()) {
+ // Location is now the center of the moving sphere at the collision time.
+ // Adjust for the size of the moving sphere. Two spheres always collide
+ // along a line between their centers.
+ location += (location - fixedSphere.center) * movingSphere.radius / fixedSphere.radius;
+ }
+
+ return time;
+}
+
+
+/*
+float CollisionDetection::collisionTimeForMovingPointFixedTriangle(
+ const Vector3& point,
+ const Vector3& velocity,
+ const Triangle& triangle,
+ Vector3& outLocation,
+ Vector3& outNormal) {
+
+ double time = collisionTimeForMovingPointFixedPlane(point, velocity, triangle.plane(), outLocation, outNormal);
+
+ if (time == finf()) {
+ // No collision with the plane of the triangle.
+ return finf();
+ }
+
+ if (isPointInsideTriangle(triangle.vertex(0), triangle.vertex(1), triangle.vertex(2), triangle.normal(), outLocation, triangle.primaryAxis())) {
+ // Collision occured inside the triangle
+ return time;
+ } else {
+ // Missed the triangle
+ outLocation = Vector3::inf();
+ return finf();
+ }
+}*/
+
+/*
+float CollisionDetection::collisionTimeForMovingPointFixedTriangle(
+ const Vector3& orig,
+ const Vector3& dir,
+ const Vector3& vert0,
+ const Vector3& vert1,
+ const Vector3& vert2) {
+
+ // Barycenteric coords
+ double u, v;
+ #define EPSILON 0.000001
+ #define CROSS(dest,v1,v2) \
+ dest[0]=v1[1]*v2[2]-v1[2]*v2[1]; \
+ dest[1]=v1[2]*v2[0]-v1[0]*v2[2]; \
+ dest[2]=v1[0]*v2[1]-v1[1]*v2[0];
+
+ #define DOT(v1,v2) (v1[0]*v2[0]+v1[1]*v2[1]+v1[2]*v2[2])
+
+ #define SUB(dest,v1,v2) \
+ dest[0]=v1[0]-v2[0]; \
+ dest[1]=v1[1]-v2[1]; \
+ dest[2]=v1[2]-v2[2];
+
+ double edge1[3], edge2[3], tvec[3], pvec[3], qvec[3];
+
+ // find vectors for two edges sharing vert0
+ SUB(edge1, vert1, vert0);
+ SUB(edge2, vert2, vert0);
+
+ // begin calculating determinant - also used to calculate U parameter
+ CROSS(pvec, dir, edge2);
+
+ // if determinant is near zero, ray lies in plane of triangle
+ const double det = DOT(edge1, pvec);
+
+ if (det < EPSILON) {
+ return finf();
+ }
+
+ // calculate distance from vert0 to ray origin
+ SUB(tvec, orig, vert0);
+
+ // calculate U parameter and test bounds
+ u = DOT(tvec, pvec);
+ if ((u < 0.0) || (u > det)) {
+ // Hit the plane outside the triangle
+ return finf();
+ }
+
+ // prepare to test V parameter
+ CROSS(qvec, tvec, edge1);
+
+ // calculate V parameter and test bounds
+ v = DOT(dir, qvec);
+ if ((v < 0.0) || (u + v > det)) {
+ // Hit the plane outside the triangle
+ return finf();
+ }
+
+ // calculate t, scale parameters, ray intersects triangle
+ // If we want u,v, we can compute this
+ // double t = DOT(edge2, qvec);
+ //const double inv_det = 1.0 / det;
+ //t *= inv_det;
+ //u *= inv_det;
+ //v *= inv_det;
+ // return t;
+
+ // Case where we don't need correct (u, v):
+
+ const double t = DOT(edge2, qvec);
+
+ if (t >= 0) {
+ // Note that det must be positive
+ return t / det;
+ } else {
+ // We had to travel backwards in time to intersect
+ return finf();
+ }
+
+ #undef EPSILON
+ #undef CROSS
+ #undef DOT
+ #undef SUB
+}
+*/
+
+float CollisionDetection::collisionTimeForMovingPointFixedBox(
+ const Vector3& point,
+ const Vector3& velocity,
+ const Box& box,
+ Vector3& location,
+ Vector3& outNormal) {
+
+ double bestTime;
+
+ Vector3 normal;
+ Vector3 v[4];
+
+ // Prime the loop
+ int f = 0;
+ box.getFaceCorners(f, v[0], v[1], v[2], v[3]);
+ bestTime = collisionTimeForMovingPointFixedRectangle(point, velocity, v[0], v[1], v[2], v[3], location, normal);
+ outNormal = normal;
+
+ // Check other faces
+ for (f = 1; f < 6; ++f) {
+ Vector3 pos;
+ box.getFaceCorners(f, v[0], v[1], v[2], v[3]);
+ float time = collisionTimeForMovingPointFixedRectangle(point, velocity, v[0], v[1], v[2], v[3], pos, normal);
+ if (time < bestTime) {
+ bestTime = time;
+ outNormal = normal;
+ location = pos;
+ }
+ }
+
+ return bestTime;
+}
+
+
+float CollisionDetection::collisionTimeForMovingPointFixedAABox(
+ const Vector3& origin,
+ const Vector3& dir,
+ const AABox& box,
+ Vector3& location,
+ bool& Inside,
+ Vector3& normal) {
+
+ if (collisionLocationForMovingPointFixedAABox(origin, dir, box, location, Inside, normal)) {
+ return (location - origin).magnitude();
+ } else {
+ return (float)finf();
+ }
+}
+
+
+bool CollisionDetection::collisionLocationForMovingPointFixedAABox(
+ const Vector3& origin,
+ const Vector3& dir,
+ const AABox& box,
+ Vector3& location,
+ bool& Inside,
+ Vector3& normal) {
+
+ // Integer representation of a floating-point value.
+ #define IR(x) ((uint32&)x)
+
+ Inside = true;
+ const Vector3& MinB = box.low();
+ const Vector3& MaxB = box.high();
+ Vector3 MaxT(-1.0f, -1.0f, -1.0f);
+
+ // Find candidate planes.
+ for (int i = 0; i < 3; ++i) {
+ if (origin[i] < MinB[i]) {
+ location[i] = MinB[i];
+ Inside = false;
+
+ // Calculate T distances to candidate planes
+ if (IR(dir[i])) {
+ MaxT[i] = (MinB[i] - origin[i]) / dir[i];
+ }
+ } else if (origin[i] > MaxB[i]) {
+ location[i] = MaxB[i];
+ Inside = false;
+
+ // Calculate T distances to candidate planes
+ if (IR(dir[i])) {
+ MaxT[i] = (MaxB[i] - origin[i]) / dir[i];
+ }
+ }
+ }
+
+ if (Inside) {
+ // Ray origin inside bounding box
+ location = origin;
+ return false;
+ }
+
+ // Get largest of the maxT's for final choice of intersection
+ int WhichPlane = 0;
+ if (MaxT[1] > MaxT[WhichPlane]) {
+ WhichPlane = 1;
+ }
+
+ if (MaxT[2] > MaxT[WhichPlane]) {
+ WhichPlane = 2;
+ }
+
+ // Check final candidate actually inside box
+ if (IR(MaxT[WhichPlane]) & 0x80000000) {
+ // Miss the box
+ return false;
+ }
+
+ for (int i = 0; i < 3; ++i) {
+ if (i != WhichPlane) {
+ location[i] = origin[i] + MaxT[WhichPlane] * dir[i];
+ if ((location[i] < MinB[i]) ||
+ (location[i] > MaxB[i])) {
+ // On this plane we're outside the box extents, so
+ // we miss the box
+ return false;
+ }
+ }
+ }
+
+ // Choose the normal to be the plane normal facing into the ray
+ normal = Vector3::zero();
+ normal[WhichPlane] = (dir[WhichPlane] > 0) ? -1.0 : 1.0;
+
+ return true;
+
+ #undef IR
+}
+
+
+
+float CollisionDetection::collisionTimeForMovingPointFixedRectangle(
+ const Vector3& point,
+ const Vector3& velocity,
+ const Vector3& v0,
+ const Vector3& v1,
+ const Vector3& v2,
+ const Vector3& v3,
+ Vector3& location,
+ Vector3& outNormal) {
+
+ Plane plane = Plane(v0, v1, v2);
+
+ float time = collisionTimeForMovingPointFixedPlane(point, velocity, plane, location, outNormal);
+
+ if (time == finf()) {
+ // No collision is ever going to happen
+ return time;
+ }
+
+ if (isPointInsideRectangle(v0, v1, v2, v3, plane.normal(), location)) {
+ // The intersection point is inside the rectangle; that is the location where
+ // the point hits the rectangle.
+ return time;
+ } else {
+ return finf();
+ }
+}
+
+/** Used by findRayCapsuleIntersection.
+ @cite From magic software http://www.magic-software.com/Source/Intersection3D/MgcIntr3DLinCap.cpp */
+static int findRayCapsuleIntersectionAux(
+ const Vector3& rkOrigin,
+ const Vector3& rkDirection,
+ const Capsule& rkCapsule,
+ double afT[2]) {
+
+ Vector3 capsuleDirection = rkCapsule.point(1) - rkCapsule.point(0);
+
+ // set up quadratic Q(t) = a*t^2 + 2*b*t + c
+ Vector3 kU, kV, kW = capsuleDirection;
+ float fWLength = kW.unitize();
+ Vector3::generateOrthonormalBasis(kU, kV, kW);
+ Vector3 kD(kU.dot(rkDirection), kV.dot(rkDirection), kW.dot(rkDirection));
+ float fDLength = kD.unitize();
+
+ float fEpsilon = 1e-6f;
+
+ float fInvDLength = 1.0f/fDLength;
+ Vector3 kDiff = rkOrigin - rkCapsule.point(0);
+ Vector3 kP(kU.dot(kDiff),kV.dot(kDiff),kW.dot(kDiff));
+ float fRadiusSqr = square(rkCapsule.radius());
+
+ float fInv, fA, fB, fC, fDiscr, fRoot, fT, fTmp;
+
+ // Is the velocity parallel to the capsule direction? (or zero)
+ if ((abs(kD.z) >= 1.0f - fEpsilon) || (fDLength < fEpsilon)) {
+
+ float fAxisDir = rkDirection.dot(capsuleDirection);
+
+ fDiscr = fRadiusSqr - kP.x*kP.x - kP.y*kP.y;
+ if ((fAxisDir < 0) && (fDiscr >= 0.0f)) {
+ // Velocity anti-parallel to the capsule direction
+ fRoot = sqrt(fDiscr);
+ afT[0] = (kP.z + fRoot)*fInvDLength;
+ afT[1] = -(fWLength - kP.z + fRoot)*fInvDLength;
+ return 2;
+ } else if ((fAxisDir > 0) && (fDiscr >= 0.0f)) {
+ // Velocity parallel to the capsule direction
+ fRoot = sqrt(fDiscr);
+ afT[0] = -(kP.z + fRoot)*fInvDLength;
+ afT[1] = (fWLength - kP.z + fRoot)*fInvDLength;
+ return 2;
+ } else {
+ // sphere heading wrong direction, or no velocity at all
+ return 0;
+ }
+ }
+
+ // test intersection with infinite cylinder
+ fA = kD.x*kD.x + kD.y*kD.y;
+ fB = kP.x*kD.x + kP.y*kD.y;
+ fC = kP.x*kP.x + kP.y*kP.y - fRadiusSqr;
+ fDiscr = fB*fB - fA*fC;
+ if (fDiscr < 0.0f) {
+ // line does not intersect infinite cylinder
+ return 0;
+ }
+
+ int iQuantity = 0;
+
+ if (fDiscr > 0.0f) {
+ // line intersects infinite cylinder in two places
+ fRoot = sqrt(fDiscr);
+ fInv = 1.0f/fA;
+ fT = (-fB - fRoot)*fInv;
+ fTmp = kP.z + fT*kD.z;
+ if ((0.0f <= fTmp) && (fTmp <= fWLength)) {
+ afT[iQuantity] = fT * fInvDLength;
+ iQuantity++;
+ }
+
+ fT = (-fB + fRoot)*fInv;
+ fTmp = kP.z + fT*kD.z;
+
+ if ((0.0f <= fTmp) && (fTmp <= fWLength)) {
+ afT[iQuantity++] = fT*fInvDLength;
+ }
+
+ if (iQuantity == 2) {
+ // line intersects capsule wall in two places
+ return 2;
+ }
+ } else {
+ // line is tangent to infinite cylinder
+ fT = -fB/fA;
+ fTmp = kP.z + fT*kD.z;
+ if ((0.0f <= fTmp) && (fTmp <= fWLength)) {
+ afT[0] = fT*fInvDLength;
+ return 1;
+ }
+ }
+
+ // test intersection with bottom hemisphere
+ // fA = 1
+ fB += kP.z*kD.z;
+ fC += kP.z*kP.z;
+ fDiscr = fB*fB - fC;
+ if (fDiscr > 0.0f) {
+ fRoot = sqrt(fDiscr);
+ fT = -fB - fRoot;
+ fTmp = kP.z + fT*kD.z;
+ if (fTmp <= 0.0f) {
+ afT[iQuantity++] = fT*fInvDLength;
+ if (iQuantity == 2) {
+ return 2;
+ }
+ }
+
+ fT = -fB + fRoot;
+ fTmp = kP.z + fT*kD.z;
+ if (fTmp <= 0.0f) {
+ afT[iQuantity++] = fT*fInvDLength;
+ if (iQuantity == 2) {
+ return 2;
+ }
+ }
+ } else if (fDiscr == 0.0f) {
+ fT = -fB;
+ fTmp = kP.z + fT*kD.z;
+ if (fTmp <= 0.0f) {
+ afT[iQuantity++] = fT*fInvDLength;
+ if (iQuantity == 2) {
+ return 2;
+ }
+ }
+ }
+
+ // test intersection with top hemisphere
+ // fA = 1
+ fB -= kD.z*fWLength;
+ fC += fWLength*(fWLength - 2.0f*kP.z);
+
+ fDiscr = fB*fB - fC;
+ if (fDiscr > 0.0f) {
+ fRoot = sqrt(fDiscr);
+ fT = -fB - fRoot;
+ fTmp = kP.z + fT*kD.z;
+ if (fTmp >= fWLength) {
+ afT[iQuantity++] = fT*fInvDLength;
+ if (iQuantity == 2) {
+ return 2;
+ }
+ }
+
+ fT = -fB + fRoot;
+ fTmp = kP.z + fT*kD.z;
+ if (fTmp >= fWLength) {
+ afT[iQuantity++] = fT*fInvDLength;
+ if (iQuantity == 2) {
+ return 2;
+ }
+ }
+ } else if (fDiscr == 0.0f) {
+ fT = -fB;
+ fTmp = kP.z + fT*kD.z;
+ if (fTmp >= fWLength) {
+ afT[iQuantity++] = fT*fInvDLength;
+ if (iQuantity == 2) {
+ return 2;
+ }
+ }
+ }
+
+ return iQuantity;
+}
+
+
+/** Used by collisionTimeForMovingPointFixedCapsule.
+ @cite From magic software http://www.magic-software.com/Source/Intersection3D/MgcIntr3DLinCap.cpp
+
+ @param rkRay The ray
+ @param rkCapsule The capsule
+ @param riQuantity The number of intersections found
+ @param akPoint The intersections found
+ @return True if there is at least one intersection
+ */
+static bool findRayCapsuleIntersection(
+ const Ray& rkRay,
+ const Capsule& rkCapsule,
+ int& riQuantity,
+ Vector3 akPoint[2]) {
+
+ double afT[2];
+ riQuantity = findRayCapsuleIntersectionAux(rkRay.origin(), rkRay.direction(), rkCapsule, afT);
+
+ // Only return intersections that occur in the future
+ int iClipQuantity = 0;
+ int i;
+ for (i = 0; i < riQuantity; ++i) {
+ if (afT[i] >= 0.0f) {
+ akPoint[iClipQuantity] = rkRay.origin() + afT[i] * rkRay.direction();
+ ++iClipQuantity;
+ }
+ }
+
+ riQuantity = iClipQuantity;
+ return (riQuantity > 0);
+}
+
+float CollisionDetection::collisionTimeForMovingPointFixedCapsule(
+ const Vector3& _point,
+ const Vector3& velocity,
+ const Capsule& capsule,
+ Vector3& location,
+ Vector3& outNormal) {
+
+ float timeScale = velocity.magnitude();
+
+ if (timeScale == 0.0f) {
+ timeScale = 1;
+ }
+
+ Vector3 direction = velocity / timeScale;
+ int numIntersections;
+ Vector3 intersection[2];
+ findRayCapsuleIntersection(Ray::fromOriginAndDirection(_point, direction), capsule, numIntersections, intersection);
+
+ if (numIntersections == 2) {
+ // A collision can only occur if there are two intersections. If there is one
+ // intersection, that one is exiting the capsule.
+
+ // Find the entering intersection (the first one that occurs).
+ float d0 = (intersection[0] - _point).squaredMagnitude();
+ float d1 = (intersection[1] - _point).squaredMagnitude();
+
+ // Compute the surface normal (if we aren't ignoring the result)
+ if (&outNormal != &ignore) {
+ Vector3 p2 = LineSegment::fromTwoPoints(capsule.point(0), capsule.point(1)).closestPoint(_point);
+ outNormal = (_point - p2).direction();
+ }
+
+ if (d0 > d1) {
+ location = intersection[1];
+ return sqrt(d1) / timeScale;
+ } else {
+ location = intersection[0];
+ return sqrt(d0) / timeScale;
+ }
+ } else {
+ // No entering intersection discovered; return no intersection.
+ location = Vector3::inf();
+ return finf();
+ }
+}
+
+
+float CollisionDetection::collisionTimeForMovingSphereFixedPlane(
+ const Sphere& sphere,
+ const Vector3& velocity,
+ const Plane& plane,
+ Vector3& location,
+ Vector3& outNormal) {
+
+ if (sphere.radius == 0) {
+ // Optimization for zero radius sphere
+ return collisionTimeForMovingPointFixedPlane(sphere.center, velocity, plane, location, outNormal);
+ }
+
+ // The collision point on the sphere will be the point at
+ // center - (radius * normal). Collisions only occur when
+ // the sphere is travelling into the plane.
+
+ double d;
+ plane.getEquation(outNormal, d);
+
+ double vdotN = velocity.dot(outNormal);
+
+ if (fuzzyGt(vdotN, 0)) {
+ // No collision when the sphere is moving towards a backface.
+ location = Vector3::inf();
+ return (float)finf();
+ }
+
+ float cdotN = sphere.center.dot(outNormal);
+
+ // Distance from the center to the plane
+ float distance = cdotN + (float)d;
+
+ // Where is the collision on the sphere?
+ Vector3 point = sphere.center - (sphere.radius * outNormal);
+
+ if (fuzzyLe(G3D::abs(distance), sphere.radius)) {
+ // Already interpenetrating
+ location = sphere.center - distance * outNormal;
+ return 0;
+ } else {
+ return collisionTimeForMovingPointFixedPlane(point, velocity, plane, location, outNormal);
+ }
+
+}
+
+
+float CollisionDetection::collisionTimeForMovingSphereFixedTriangle(
+ const class Sphere& sphere,
+ const Vector3& velocity,
+ const Triangle& triangle,
+ Vector3& outLocation,
+ float b[3]) {
+
+ Vector3 dummy;
+ float time = collisionTimeForMovingSphereFixedPlane(sphere, velocity, triangle.plane(),
+ outLocation, dummy);
+
+ if (time == finf()) {
+ // No collision is ever going to happen
+ return time;
+ }
+
+ // We will hit the plane of the triangle at *time*. See if
+ // the intersection point actually is within the triangle.
+
+ if (isPointInsideTriangle(triangle.vertex(0), triangle.vertex(1), triangle.vertex(2), triangle.normal(),
+ outLocation, b, triangle.primaryAxis())) {
+
+ // The intersection point is inside the triangle; that is the location where
+ // the sphere hits the triangle.
+
+# ifdef G3D_DEBUG
+ {
+ // Internal consistency checks
+ debugAssertM(b[0] >= 0.0 && b[0] <= 1.0f, "Intersection is outside triangle.");
+ debugAssertM(b[1] >= 0.0 && b[1] <= 1.0f, "Intersection is outside triangle.");
+ debugAssertM(b[2] >= 0.0 && b[2] <= 1.0f, "Intersection is outside triangle.");
+ Vector3 blend =
+ b[0] * triangle.vertex(0) +
+ b[1] * triangle.vertex(1) +
+ b[2] * triangle.vertex(2);
+ debugAssertM(blend.fuzzyEq(outLocation), "Barycentric coords don't match intersection.");
+ // Call again so that we can debug the problem
+ // isPointInsideTriangle(triangle.vertex(0), triangle.vertex(1), triangle.vertex(2), triangle.normal(),
+ // outLocation, b, triangle.primaryAxis());
+ }
+# endif
+
+ return time;
+ }
+
+ // The collision (if it exists) is with a point on the triangle perimeter.
+ // Switch over to moving the triangle towards a fixed sphere and see at what time
+ // they will hit.
+
+ // Closest point on the triangle to the sphere intersection with the plane.
+ int edgeIndex;
+ const Vector3& point = closestPointOnTrianglePerimeter(triangle._vertex, triangle.edgeDirection,
+ triangle.edgeMagnitude, outLocation, edgeIndex);
+
+ float t = 0;
+ if (! sphere.contains(point)) {
+ // The point is outside the sphere--see when it will hit
+ t = collisionTimeForMovingPointFixedSphere(point, -velocity, sphere, dummy, dummy);
+ }
+
+ if (t < finf()) {
+ outLocation = point;
+ // Compute Barycentric coords
+
+ // Index of the next vertex
+ static const int next[] = {1, 2, 0};
+
+ // Project along the edge in question.
+ // Avoid sqrt by taking advantage of the existing edgeDirection unit vector.
+ b[next[edgeIndex]] = (outLocation - triangle._vertex[edgeIndex]).dot
+ (triangle.edgeDirection[edgeIndex]) / triangle.edgeMagnitude[edgeIndex];
+
+ b[edgeIndex] = 1.0f - b[next[edgeIndex]];
+
+ b[next[next[edgeIndex]]] = 0.0f;
+
+# ifdef G3D_DEBUG
+ {
+ // Internal consistency checks
+ for (int i = 0; i < 3; ++i) {
+ debugAssertM(fuzzyGe(b[i], 0.0f) && fuzzyLe(b[i], 1.0f), "Intersection is outside triangle.");
+ }
+ Vector3 blend =
+ b[0] * triangle.vertex(0) +
+ b[1] * triangle.vertex(1) +
+ b[2] * triangle.vertex(2);
+ debugAssertM(blend.fuzzyEq(outLocation),
+ format("Barycentric coords don't match intersection. %s != %s",
+ blend.toString().c_str(),
+ outLocation.toString().c_str()));
+
+ // Call again so that we can debug the problem
+ collisionTimeForMovingPointFixedSphere(point, -velocity, sphere, dummy, dummy);
+ }
+# endif
+
+ // Due to tiny roundoffs, these values might be slightly out of bounds.
+ // Ensure that they are legal. Note that the above debugging code
+ // verifies that we are not clamping truly illegal values.
+ for (int i = 0; i < 3; ++i) {
+ b[i] = clamp(b[i], 0.0f, 1.0f);
+ }
+ }
+
+ // The collision occured at the point, if it occured. The normal
+ // was the plane normal, computed above.
+
+ return t;
+}
+
+
+float CollisionDetection::collisionTimeForMovingSphereFixedRectangle(
+ const Sphere& sphere,
+ const Vector3& velocity,
+ const Vector3& v0,
+ const Vector3& v1,
+ const Vector3& v2,
+ const Vector3& v3,
+ Vector3& location,
+ Vector3& outNormal) {
+
+ Plane plane(v0, v1, v2);
+
+ float time = collisionTimeForMovingSphereFixedPlane(sphere, velocity, plane, location, outNormal);
+
+ if (time == finf()) {
+ // No collision is ever going to happen
+ return time;
+ }
+
+ if (isPointInsideRectangle(v0, v1, v2, v3, plane.normal(), location)) {
+ // The intersection point is inside the rectangle; that is the location where
+ // the sphere hits the rectangle.
+ return time;
+ }
+
+ // Switch over to moving the rectangle towards a fixed sphere and see at what time
+ // they will hit.
+
+ Vector3 point = closestPointToRectanglePerimeter(v0, v1, v2, v3, sphere.center);
+
+ Vector3 dummy;
+ double t = collisionTimeForMovingPointFixedSphere(point, -velocity, sphere, location, dummy);
+
+ // Normal is the plane normal, location is the original location of the point.
+ location = point;
+
+ return t;
+}
+
+
+float CollisionDetection::collisionTimeForMovingSphereFixedBox(
+ const Sphere& sphere,
+ const Vector3& velocity,
+ const Box& box,
+ Vector3& location,
+ Vector3& outNormal) {
+
+ if (fixedSolidSphereIntersectsFixedSolidBox(sphere, box)) {
+ // TODO: Compute more useful location and normal?
+ location = sphere.center;
+ outNormal = Vector3::zero();
+ return 0;
+ }
+
+ float bestTime;
+
+ Vector3 v[4];
+ int f = 0;
+ box.getFaceCorners(f, v[0], v[1], v[2], v[3]);
+ bestTime = collisionTimeForMovingSphereFixedRectangle(sphere, velocity, v[0], v[1], v[2], v[3], location, outNormal);
+
+ for (f = 1; f < 6; ++f) {
+ Vector3 pos, normal;
+ box.getFaceCorners(f, v[0], v[1], v[2], v[3]);
+ float time = collisionTimeForMovingSphereFixedRectangle(sphere, velocity, v[0], v[1], v[2], v[3], pos, normal);
+ if (time < bestTime) {
+ bestTime = time;
+ location = pos;
+ outNormal = normal;
+ }
+ }
+
+ return bestTime;
+}
+
+
+float CollisionDetection::collisionTimeForMovingSphereFixedCapsule(
+ const Sphere& sphere,
+ const Vector3& velocity,
+ const Capsule& capsule,
+ Vector3& location,
+ Vector3& outNormal) {
+
+ (void)outNormal;
+
+ Capsule _capsule(capsule.point(0), capsule.point(1), capsule.radius() + sphere.radius);
+
+ Vector3 normal;
+ double time = collisionTimeForMovingPointFixedCapsule(sphere.center, velocity, _capsule, location, normal);
+
+ if (time < finf()) {
+ // Location is now the position of the center of the sphere at the time of collision.
+ // We have to adjust the collision location for the size of the sphere.
+ location -= sphere.radius * normal;
+ }
+
+ return time;
+}
+
+
+Vector3 CollisionDetection::bounceDirection(
+ const Sphere& sphere,
+ const Vector3& velocity,
+ const float collisionTime,
+ const Vector3& collisionLocation,
+ const Vector3& collisionNormal) {
+
+ // Location when the collision occurs
+ Vector3 sphereLocation = sphere.center + velocity * collisionTime;
+
+ Vector3 normal = (sphereLocation - collisionLocation);
+ if (fuzzyEq(normal.squaredMagnitude(), 0)) {
+ normal = collisionNormal;
+ } else {
+ normal.unitize();
+ }
+
+ Vector3 direction = velocity.direction();
+
+ // Reflect direction about the normal
+ return direction - 2.0 * normal * normal.dot(direction);
+}
+
+
+Vector3 CollisionDetection::slideDirection(
+ const Sphere& sphere,
+ const Vector3& velocity,
+ const float collisionTime,
+ const Vector3& collisionLocation) {
+
+ Vector3 sphereLocation = sphere.center + velocity * collisionTime;
+ Vector3 normal = (sphereLocation - collisionLocation).direction();
+ Vector3 direction = velocity.direction();
+
+ // subtract off the part in the direction away from the normal.
+ return direction - normal * normal.dot(direction);
+}
+
+
+Vector3 CollisionDetection::closestPointOnLineSegment(
+ const Vector3& v0,
+ const Vector3& v1,
+ const Vector3& point) {
+
+ const Vector3& edge = (v1 - v0);
+ float edgeLength = edge.magnitude();
+
+ if (edgeLength == 0) {
+ // The line segment is a point
+ return v0;
+ }
+
+ return closestPointOnLineSegment(v0, v1, edge / edgeLength, edgeLength, point);
+}
+
+
+Vector3 CollisionDetection::closestPointOnLineSegment(
+ const Vector3& v0,
+ const Vector3& v1,
+ const Vector3& edgeDirection,
+ const float edgeLength,
+ const Vector3& point) {
+
+ debugAssert((v1 - v0).direction().fuzzyEq(edgeDirection));
+ debugAssert(fuzzyEq((v1 - v0).magnitude(), edgeLength));
+
+ // Vector towards the point
+ const Vector3& c = point - v0;
+
+ // Projected onto the edge itself
+ float t = edgeDirection.dot(c);
+
+ if (t <= 0) {
+ // Before the start
+ return v0;
+ } else if (t >= edgeLength) {
+ // After the end
+ return v1;
+ } else {
+ // At distance t along the edge
+ return v0 + edgeDirection * t;
+ }
+}
+
+
+Vector3 CollisionDetection::closestPointOnTrianglePerimeter(
+ const Vector3& v0,
+ const Vector3& v1,
+ const Vector3& v2,
+ const Vector3& point) {
+
+ Vector3 v[3] = {v0, v1, v2};
+ Vector3 edgeDirection[3] = {(v1 - v0), (v2 - v1), (v0 - v2)};
+ float edgeLength[3];
+
+ for (int i = 0; i < 3; ++i) {
+ edgeLength[i] = edgeDirection[i].magnitude();
+ edgeDirection[i] /= edgeLength[i];
+ }
+
+ int edgeIndex;
+ return closestPointOnTrianglePerimeter(v, edgeDirection, edgeLength, point, edgeIndex);
+}
+
+
+Vector3 CollisionDetection::closestPointOnTrianglePerimeter(
+ const Vector3 v[3],
+ const Vector3 edgeDirection[3],
+ const float edgeLength[3],
+ const Vector3& point,
+ int& edgeIndex) {
+
+ // Closest point on segment from v[i] to v[i + 1]
+ Vector3 r[3];
+
+ // Distance squared from r[i] to point
+ float d[3];
+
+ // Index of the next point
+ static const int next[] = {1, 2, 0};
+
+ for (int i = 0; i < 3; ++i) {
+ r[i] = closestPointOnLineSegment(v[i], v[next[i]], edgeDirection[i], edgeLength[i], point);
+ d[i] = (r[i] - point).squaredMagnitude();
+ }
+
+ if (d[0] < d[1]) {
+ if (d[0] < d[2]) {
+ // Between v0 and v1
+ edgeIndex = 0;
+ } else {
+ // Between v2 and v0
+ edgeIndex = 2;
+ }
+ } else {
+ if (d[1] < d[2]) {
+ // Between v1 and v2
+ edgeIndex = 1;
+ } else {
+ // Between v2 and v0
+ edgeIndex = 2;
+ }
+ }
+
+# ifdef G3D_DEBUG
+ {
+ Vector3 diff = r[edgeIndex] - v[edgeIndex];
+ debugAssertM(fuzzyEq(diff.direction().dot(edgeDirection[edgeIndex]), 1.0f) ||
+ diff.fuzzyEq(Vector3::zero()), "Point not on correct triangle edge");
+ float frac = diff.dot(edgeDirection[edgeIndex])/edgeLength[edgeIndex];
+ debugAssertM(frac >= -0.000001, "Point off low side of edge.");
+ debugAssertM(frac <= 1.000001, "Point off high side of edge.");
+ }
+# endif
+
+ return r[edgeIndex];
+}
+
+
+bool CollisionDetection::isPointInsideTriangle(
+ const Vector3& v0,
+ const Vector3& v1,
+ const Vector3& v2,
+ const Vector3& normal,
+ const Vector3& point,
+ float b[3],
+ Vector3::Axis primaryAxis) {
+
+ if (primaryAxis == Vector3::DETECT_AXIS) {
+ primaryAxis = normal.primaryAxis();
+ }
+
+ // Check that the point is within the triangle using a Barycentric
+ // coordinate test on a two dimensional plane.
+ int i, j;
+
+ switch (primaryAxis) {
+ case Vector3::X_AXIS:
+ i = Vector3::Y_AXIS;
+ j = Vector3::Z_AXIS;
+ break;
+
+ case Vector3::Y_AXIS:
+ i = Vector3::Z_AXIS;
+ j = Vector3::X_AXIS;
+ break;
+
+ case Vector3::Z_AXIS:
+ i = Vector3::X_AXIS;
+ j = Vector3::Y_AXIS;
+ break;
+
+ default:
+ // This case is here to supress a warning on Linux
+ i = j = 0;
+ debugAssertM(false, "Should not get here.");
+ break;
+ }
+
+ // See if all barycentric coordinates are non-negative
+
+ // 2D area via cross product
+# define AREA2(d, e, f) (((e)[i] - (d)[i]) * ((f)[j] - (d)[j]) - ((f)[i] - (d)[i]) * ((e)[j] - (d)[j]))
+
+ // Area of the polygon
+ float area = AREA2(v0, v1, v2);
+ if (area == 0) {
+ // This triangle has zero area, so the point must not
+ // be in it unless the triangle point is the test point.
+ return (v0 == point);
+ }
+
+ debugAssert(area != 0);
+
+ float invArea = 1.0f / area;
+
+ // (avoid normalization until absolutely necessary)
+ b[0] = AREA2(point, v1, v2) * invArea;
+
+ if ((b[0] < 0.0f) || (b[0] > 1.0f)) {
+ return false;
+ }
+
+ b[1] = AREA2(v0, point, v2) * invArea;
+ if ((b[1] < 0.0f) || (b[1] > 1.0f)) {
+ return false;
+ }
+
+ b[2] = 1.0f - b[0] - b[1];
+
+# undef AREA2
+
+ return (b[2] >= 0.0f) && (b[2] <= 1.0f);
+}
+
+
+bool CollisionDetection::isPointInsideRectangle(
+ const Vector3& v0,
+ const Vector3& v1,
+ const Vector3& v2,
+ const Vector3& v3,
+ const Vector3& normal,
+ const Vector3& point) {
+
+ return isPointInsideTriangle(v0, v1, v2, normal, point) ||
+ isPointInsideTriangle(v2, v3, v0, normal, point);
+}
+
+
+Vector3 CollisionDetection::closestPointToRectanglePerimeter(
+ const Vector3& v0,
+ const Vector3& v1,
+ const Vector3& v2,
+ const Vector3& v3,
+ const Vector3& point) {
+
+ Vector3 r0 = closestPointOnLineSegment(v0, v1, point);
+ Vector3 r1 = closestPointOnLineSegment(v1, v2, point);
+ Vector3 r2 = closestPointOnLineSegment(v2, v3, point);
+ Vector3 r3 = closestPointOnLineSegment(v3, v0, point);
+
+ double d0 = (r0 - point).squaredMagnitude();
+ double d1 = (r1 - point).squaredMagnitude();
+ double d2 = (r2 - point).squaredMagnitude();
+ double d3 = (r3 - point).squaredMagnitude();
+
+ if (d0 < d1) {
+ if (d0 < d2) {
+ if (d0 < d3) {
+ return r0;
+ } else {
+ return r3;
+ }
+ } else {
+ if (d2 < d3) {
+ return r2;
+ } else {
+ return r3;
+ }
+ }
+ } else {
+ if (d1 < d2) {
+ if (d1 < d3) {
+ return r1;
+ } else {
+ return r3;
+ }
+ } else {
+ if (d2 < d3) {
+ return r2;
+ } else {
+ return r3;
+ }
+ }
+ }
+}
+
+
+Vector3 CollisionDetection::closestPointToRectangle(
+ const Vector3& v0,
+ const Vector3& v1,
+ const Vector3& v2,
+ const Vector3& v3,
+ const Vector3& point) {
+
+ Plane plane(v0, v1, v2);
+
+ // Project the point into the plane
+ double a, b, c, d;
+ plane.getEquation(a, b, c, d);
+
+ double distance = a*point.x + b*point.y + c*point.z + d;
+ Vector3 planePoint = point - distance * plane.normal();
+
+ if (isPointInsideRectangle(v0, v1, v2, v3, plane.normal(), planePoint)) {
+ return planePoint;
+ } else {
+ return closestPointToRectanglePerimeter(v0, v1, v2, v3, planePoint);
+ }
+}
+
+
+bool CollisionDetection::fixedSolidSphereIntersectsFixedSolidSphere(
+ const Sphere& sphere1,
+ const Sphere& sphere2) {
+
+ return (sphere1.center - sphere2.center).squaredMagnitude() < square(sphere1.radius + sphere2.radius);
+}
+
+
+bool CollisionDetection::fixedSolidSphereIntersectsFixedSolidBox(
+ const Sphere& sphere,
+ const Box& box) {
+
+ // If the center of the sphere is within the box, the whole
+ // sphere is within the box.
+ if (box.contains(sphere.center)) {
+ return true;
+ }
+
+ float r2 = square(sphere.radius);
+
+ // Find the closest point on the surface of the box to the sphere. If
+ // this point is within the sphere's radius, they intersect.
+ int f;
+ for (f = 0; f < 6; ++f) {
+ Vector3 v0, v1, v2, v3;
+ box.getFaceCorners(f, v0, v1, v2, v3);
+ if ((closestPointToRectangle(v0, v1, v2, v3, sphere.center) - sphere.center).squaredMagnitude() <= r2) {
+ return true;
+ }
+ }
+
+ return false;
+}
+
+
+bool CollisionDetection::movingSpherePassesThroughFixedBox(
+ const Sphere& sphere,
+ const Vector3& velocity,
+ const Box& box,
+ double timeLimit) {
+
+ // If they intersect originally, they definitely pass through each other.
+ if (fixedSolidSphereIntersectsFixedSolidBox(sphere, box)) {
+ return true;
+ }
+
+ // See if the sphere hits the box during the time period.
+ Vector3 dummy1, dummy2;
+
+ return (collisionTimeForMovingSphereFixedBox(sphere, velocity, box, dummy1, dummy2) < timeLimit);
+}
+
+
+bool CollisionDetection::movingSpherePassesThroughFixedSphere(
+ const Sphere& sphere,
+ const Vector3& velocity,
+ const Sphere& fixedSphere,
+ double timeLimit) {
+
+ if (fixedSolidSphereIntersectsFixedSolidSphere(sphere, fixedSphere)) {
+ return true;
+ }
+
+ // Extend the fixed sphere by the radius of the moving sphere
+ Sphere bigFixed(fixedSphere.center, fixedSphere.radius + sphere.radius);
+ Vector3 dummy1, dummy2;
+
+ // If the sphere collides with the other sphere during the time limit, it passes through
+ return (collisionTimeForMovingPointFixedSphere(sphere.center, velocity, bigFixed, dummy1, dummy2) < timeLimit);
+}
+
+
+
+bool CollisionDetection::fixedSolidSphereIntersectsFixedTriangle(
+ const Sphere& sphere,
+ const Triangle& triangle) {
+
+ // How far is the sphere from the plane of the triangle
+ const Plane& plane = triangle.plane();
+
+ // Does the closest point to the sphere center lie within the triangle?
+ Vector3 v = plane.closestPoint(sphere.center);
+
+ // Is the closest point to the plane within the sphere?
+ if ((v - sphere.center).squaredLength() <= square(sphere.radius)) {
+ // Is it also within the triangle?
+ float b[3];
+ if (isPointInsideTriangle(triangle.vertex(0), triangle.vertex(1), triangle.vertex(2), triangle.normal(),
+ v, b, triangle.primaryAxis())){
+ // The closest point is inside the triangle
+ return true;
+ }
+ }
+
+ // ignored
+ int edgeIndex;
+
+ v = closestPointOnTrianglePerimeter(triangle._vertex, triangle.edgeDirection, triangle.edgeMagnitude, sphere.center, edgeIndex);
+
+ // Is the closest point within the sphere?
+ return ((v - sphere.center).squaredLength() <= square(sphere.radius));
+}
+
+
+////////////////////////////////////////////////////////////////////////////////
+// AABB-triangle overlap test code based on Tomas Akenine-Möller's
+// http://www.cs.lth.se/home/Tomas_Akenine_Moller/code/tribox3.txt
+// Ported 2008-12-28
+
+#define X 0
+#define Y 1
+#define Z 2
+
+#define FINDMINMAX(x0, x1, x2, min, max) \
+ min = max = x0; \
+ if(x1<min) min=x1;\
+ if(x1>max) max=x1;\
+ if(x2<min) min=x2;\
+ if(x2>max) max=x2;
+
+static bool planeBoxOverlap(const Vector3& normal, const Vector3& vert, const Vector3& maxbox) {
+ Vector3 vmin, vmax;
+ float v;
+
+ // for each axis
+ for(int a = 0; a < 3; ++a) {
+ v = vert[a];
+
+ if (normal[a] > 0.0f) {
+ vmin[a] = -maxbox[a] - v;
+ vmax[a] = maxbox[a] - v;
+ } else {
+ vmin[a] = maxbox[a] - v;
+ vmax[a] = -maxbox[a] - v;
+ }
+ }
+
+ if (normal.dot(vmin) > 0.0f) {
+ return false;
+ } else if (normal.dot(vmax) >= 0.0f) {
+ return true;
+ } else {
+ return false;
+ }
+}
+
+/*======================== X-tests ========================*/
+
+#define AXISTEST_X01(a, b, fa, fb) \
+ p0 = a*v0[Y] - b*v0[Z]; \
+ p2 = a*v2[Y] - b*v2[Z]; \
+ if(p0<p2) {min=p0; max=p2;} else {min=p2; max=p0;} \
+ rad = fa * boxhalfsize[Y] + fb * boxhalfsize[Z]; \
+ if(min>rad || max<-rad) return false;
+
+
+#define AXISTEST_X2(a, b, fa, fb) \
+ p0 = a*v0[Y] - b*v0[Z]; \
+ p1 = a*v1[Y] - b*v1[Z]; \
+ if(p0<p1) {min=p0; max=p1;} else {min=p1; max=p0;} \
+ rad = fa * boxhalfsize[Y] + fb * boxhalfsize[Z]; \
+ if(min>rad || max<-rad) return false;
+
+/*======================== Y-tests ========================*/
+
+#define AXISTEST_Y02(a, b, fa, fb) \
+ p0 = -a*v0[X] + b*v0[Z]; \
+ p2 = -a*v2[X] + b*v2[Z]; \
+ if(p0<p2) {min=p0; max=p2;} else {min=p2; max=p0;} \
+ rad = fa * boxhalfsize[X] + fb * boxhalfsize[Z]; \
+ if(min>rad || max<-rad) return false;
+
+#define AXISTEST_Y1(a, b, fa, fb) \
+ p0 = -a*v0[X] + b*v0[Z]; \
+ p1 = -a*v1[X] + b*v1[Z]; \
+ if(p0<p1) {min=p0; max=p1;} else {min=p1; max=p0;} \
+ rad = fa * boxhalfsize[X] + fb * boxhalfsize[Z]; \
+ if(min>rad || max<-rad) return false;
+
+/*======================== Z-tests ========================*/
+
+#define AXISTEST_Z12(a, b, fa, fb) \
+ p1 = a*v1[X] - b*v1[Y]; \
+ p2 = a*v2[X] - b*v2[Y]; \
+ if(p2<p1) {min=p2; max=p1;} else {min=p1; max=p2;} \
+ rad = fa * boxhalfsize[X] + fb * boxhalfsize[Y]; \
+ if(min>rad || max<-rad) return false;
+
+#define AXISTEST_Z0(a, b, fa, fb) \
+ p0 = a*v0[X] - b*v0[Y]; \
+ p1 = a*v1[X] - b*v1[Y]; \
+ if(p0<p1) {min=p0; max=p1;} else {min=p1; max=p0;} \
+ rad = fa * boxhalfsize[X] + fb * boxhalfsize[Y]; \
+ if(min>rad || max<-rad) return false;
+
+bool CollisionDetection::fixedSolidBoxIntersectsFixedTriangle(
+ const AABox& box, const Triangle& tri) {
+
+ // use separating axis theorem to test overlap between triangle and box
+ // need to test for overlap in these directions:
+ // 1) the {x,y,z}-directions (actually, since we use the AABB of the triangle
+ // we do not even need to test these)
+ // 2) normal of the triangle
+ // 3) crossproduct(edge from tri, {x,y,z}-direction)
+ // this gives 3x3=9 more tests
+
+ // This is the fastest branch (on Sun).
+ // Move the triangle to the object space of the box
+ // Triangle vertices in box object space
+
+ const Vector3& boxcenter = box.center();
+ const Vector3& boxhalfsize = box.extent() * 0.5f;
+
+ const Vector3& v0 = tri.vertex(0) - boxcenter;
+ const Vector3& v1 = tri.vertex(1) - boxcenter;
+ const Vector3& v2 = tri.vertex(2) - boxcenter;
+
+ // Compute triangle edges in object space
+ const Vector3& e0 = v1 - v0;
+ const Vector3& e1 = v2 - v1;
+ const Vector3& e2 = v0 - v2;
+
+ // Bullet 3:
+ // test the 9 tests first (this was faster)
+ float min,max,p0,p1,p2,rad;
+ Vector3 fe;
+
+ fe = abs(e0);
+ AXISTEST_X01(e0[Z], e0[Y], fe[Z], fe[Y]);
+ AXISTEST_Y02(e0[Z], e0[X], fe[Z], fe[X]);
+ AXISTEST_Z12(e0[Y], e0[X], fe[Y], fe[X]);
+
+ fe = abs(e1);
+ AXISTEST_X01(e1[Z], e1[Y], fe[Z], fe[Y]);
+ AXISTEST_Y02(e1[Z], e1[X], fe[Z], fe[X]);
+ AXISTEST_Z0 (e1[Y], e1[X], fe[Y], fe[X]);
+
+ fe = abs(e2);
+ AXISTEST_X2 (e2[Z], e2[Y], fe[Z], fe[Y]);
+ AXISTEST_Y1 (e2[Z], e2[X], fe[Z], fe[X]);
+ AXISTEST_Z12(e2[Y], e2[X], fe[Y], fe[X]);
+
+ // Bullet 1:
+ // first test overlap in the {x,y,z}-directions
+ // find min, max of the triangle each direction, and test for overlap in
+ // that direction -- this is equivalent to testing a minimal AABB around
+ // the triangle against the AABB
+
+ // test in X-direction
+ FINDMINMAX(v0[X],v1[X],v2[X],min,max);
+ if (min > boxhalfsize[X] || max < -boxhalfsize[X]) {
+ return false;
+ }
+
+ // test in Y-direction
+ FINDMINMAX(v0[Y],v1[Y],v2[Y],min,max);
+ if (min > boxhalfsize[Y] || max < -boxhalfsize[Y]) {
+ return false;
+ }
+
+ // test in Z-direction
+ FINDMINMAX(v0[Z],v1[Z],v2[Z],min,max);
+ if (min > boxhalfsize[Z] || max < -boxhalfsize[Z]) {
+ return false;
+ }
+
+ // Bullet 2:
+ // test if the box intersects the plane of the triangle
+ // compute plane equation of triangle: normal*x+d=0
+
+ if (! planeBoxOverlap(tri.normal(), v0, boxhalfsize)) {
+ return false;
+ }
+
+ // box and triangle overlap
+ return true;
+}
+#undef X
+#undef Y
+#undef Z
+
+////////////////////////////////////////////////////////////////////////////////
+
+
+} // namespace
+
+#ifdef _MSC_VER
+// Turn off fast floating-point optimizations
+#pragma float_control( pop )
+#pragma warning (pop)
+#endif
diff --git a/dep/src/g3dlite/Color1.cpp b/dep/src/g3dlite/Color1.cpp
new file mode 100644
index 00000000000..04f3f9412b1
--- /dev/null
+++ b/dep/src/g3dlite/Color1.cpp
@@ -0,0 +1,58 @@
+/**
+ @file Color1.cpp
+
+ Color class.
+
+ @author Morgan McGuire, http://graphics.cs.williams.edu
+
+ @created 2007-01-30
+ @edited 2009-03-27
+ */
+
+#include "G3D/platform.h"
+#include "G3D/Color1.h"
+#include "G3D/Color1uint8.h"
+#include "G3D/BinaryInput.h"
+#include "G3D/BinaryOutput.h"
+#include "G3D/Color3.h"
+
+namespace G3D {
+
+const Color1& Color1::one() {
+ static const Color1 x(1.0f);
+ return x;
+}
+
+
+const Color1& Color1::zero() {
+ const static Color1 x(0.0f);
+ return x;
+}
+
+
+Color1::Color1(BinaryInput& bi) {
+ deserialize(bi);
+}
+
+
+Color3 Color1::rgb() const {
+ return Color3(value, value, value);
+}
+
+
+void Color1::deserialize(BinaryInput& bi) {
+ value = bi.readFloat32();
+}
+
+
+void Color1::serialize(BinaryOutput& bo) const {
+ bo.writeFloat32(value);
+}
+
+
+Color1::Color1(const class Color1uint8& other) {
+ value = other.value / 255.0f;
+}
+
+} // namespace G3D
+
diff --git a/dep/src/g3dlite/Color1uint8.cpp b/dep/src/g3dlite/Color1uint8.cpp
new file mode 100644
index 00000000000..21cd564ba92
--- /dev/null
+++ b/dep/src/g3dlite/Color1uint8.cpp
@@ -0,0 +1,38 @@
+/**
+ @file Color1uint8.cpp
+
+ @author Morgan McGuire, http://graphics.cs.williams.edu
+
+ @created 2007-01-30
+ @edited 2007-01-30
+ */
+
+#include "G3D/platform.h"
+#include "G3D/g3dmath.h"
+#include "G3D/Color1uint8.h"
+#include "G3D/Color1.h"
+#include "G3D/BinaryInput.h"
+#include "G3D/BinaryOutput.h"
+
+namespace G3D {
+
+Color1uint8::Color1uint8(const class Color1& c) : value(iClamp(iFloor(c.value * 256), 0, 255)) {
+}
+
+
+Color1uint8::Color1uint8(class BinaryInput& bi) {
+ deserialize(bi);
+}
+
+
+void Color1uint8::serialize(class BinaryOutput& bo) const {
+ bo.writeUInt8(value);
+}
+
+
+void Color1uint8::deserialize(class BinaryInput& bi) {
+ value = bi.readUInt8();
+}
+
+
+}
diff --git a/dep/src/g3dlite/Color3.cpp b/dep/src/g3dlite/Color3.cpp
new file mode 100644
index 00000000000..deb0bd87ee7
--- /dev/null
+++ b/dep/src/g3dlite/Color3.cpp
@@ -0,0 +1,384 @@
+/**
+ @file Color3.cpp
+
+ Color class.
+
+ @author Morgan McGuire, http://graphics.cs.williams.edu
+
+ @created 2001-06-02
+ @edited 2010-01-28
+ */
+
+#include "G3D/platform.h"
+#include <stdlib.h>
+#include "G3D/Color3.h"
+#include "G3D/Vector3.h"
+#include "G3D/format.h"
+#include "G3D/BinaryInput.h"
+#include "G3D/BinaryOutput.h"
+#include "G3D/Color3uint8.h"
+#include "G3D/Any.h"
+#include "G3D/stringutils.h"
+
+namespace G3D {
+
+Color3::Color3(const Any& any) {
+ *this = Color3::zero();
+ any.verifyName("Color3");
+ std::string name = toLower(any.name());
+
+ switch (any.type()) {
+ case Any::TABLE:
+
+ for (Any::AnyTable::Iterator it = any.table().begin(); it.hasMore(); ++it) {
+ const std::string& key = toLower(it->key);
+ if (key == "r") {
+ r = it->value;
+ } else if (key == "g") {
+ g = it->value;
+ } else if (key == "b") {
+ b = it->value;
+ } else {
+ any.verify(false, "Illegal key: " + it->key);
+ }
+ }
+ break;
+
+ case Any::ARRAY:
+ if (name == "color3") {
+ any.verifySize(3);
+ r = any[0];
+ g = any[1];
+ b = any[2];
+ } else if (name == "color3::one") {
+ any.verifySize(0);
+ *this = one();
+ } else if (name == "color3::zero") {
+ any.verifySize(0);
+ *this = zero();
+ } else if (name == "color3::fromargb") {
+ *this = Color3::fromARGB((int)any[0].number());
+ } else {
+ any.verify(false, "Expected Color3 constructor");
+ }
+ break;
+
+ default:
+ any.verify(false, "Bad Color3 constructor");
+ }
+}
+
+
+Color3::operator Any() const {
+ Any a(Any::ARRAY, "Color3");
+ a.append(r, g, b);
+ return a;
+}
+
+
+Color3 Color3::ansiMap(uint32 i) {
+ static const Color3 map[] =
+ {Color3::black(), Color3::red() * 0.75f, Color3::green() * 0.75f, Color3::yellow() * 0.75f,
+ Color3::blue() * 0.75f, Color3::purple() * 0.75f, Color3::cyan() * 0.75f, Color3::white() * 0.75f,
+ Color3::white() * 0.90f, Color3::red(), Color3::green(), Color3::yellow(), Color3::blue(),
+ Color3::purple(), Color3::cyan(), Color3::white()};
+
+ return map[i & 15];
+}
+
+
+Color3 Color3::pastelMap(uint32 i) {
+ uint32 x = Crypto::crc32(&i, sizeof(uint32));
+ // Create fairly bright, saturated colors
+ Vector3 v(((x >> 22) & 1023) / 1023.0f,
+ (((x >> 11) & 2047) / 2047.0f) * 0.5f + 0.25f,
+ ((x & 2047) / 2047.0f) * 0.75f + 0.25f);
+ return Color3::fromHSV(v);
+}
+
+
+const Color3& Color3::red() {
+ static Color3 c(1.0f, 0.0f, 0.0f);
+ return c;
+}
+
+
+const Color3& Color3::green() {
+ static Color3 c(0.0f, 1.0f, 0.0f);
+ return c;
+}
+
+
+const Color3& Color3::blue() {
+ static Color3 c(0.0f, 0.0f, 1.0f);
+ return c;
+}
+
+
+const Color3& Color3::purple() {
+ static Color3 c(0.7f, 0.0f, 1.0f);
+ return c;
+}
+
+
+const Color3& Color3::cyan() {
+ static Color3 c(0.0f, 0.7f, 1.0f);
+ return c;
+}
+
+
+const Color3& Color3::yellow() {
+ static Color3 c(1.0f, 1.0f, 0.0f);
+ return c;
+}
+
+
+const Color3& Color3::brown() {
+ static Color3 c(0.5f, 0.5f, 0.0f);
+ return c;
+}
+
+
+const Color3& Color3::orange() {
+ static Color3 c(1.0f, 0.5f, 0.0f);
+ return c;
+}
+
+
+const Color3& Color3::black() {
+ static Color3 c(0.0f, 0.0f, 0.0f);
+ return c;
+}
+
+const Color3& Color3::zero() {
+ static Color3 c(0.0f, 0.0f, 0.0f);
+ return c;
+}
+
+
+const Color3& Color3::one() {
+ static Color3 c(1.0f, 1.0f, 1.0f);
+ return c;
+}
+
+
+const Color3& Color3::gray() {
+ static Color3 c(0.7f, 0.7f, 0.7f);
+ return c;
+}
+
+
+const Color3& Color3::white() {
+ static Color3 c(1, 1, 1);
+ return c;
+}
+
+
+bool Color3::isFinite() const {
+ return G3D::isFinite(r) && G3D::isFinite(g) && G3D::isFinite(b);
+}
+
+
+Color3::Color3(BinaryInput& bi) {
+ deserialize(bi);
+}
+
+
+void Color3::deserialize(BinaryInput& bi) {
+ r = bi.readFloat32();
+ g = bi.readFloat32();
+ b = bi.readFloat32();
+}
+
+
+void Color3::serialize(BinaryOutput& bo) const {
+ bo.writeFloat32(r);
+ bo.writeFloat32(g);
+ bo.writeFloat32(b);
+}
+
+
+const Color3& Color3::wheelRandom() {
+ static const Color3 colorArray[8] =
+ {Color3::blue(), Color3::red(), Color3::green(),
+ Color3::orange(), Color3::yellow(),
+ Color3::cyan(), Color3::purple(), Color3::brown()};
+
+ return colorArray[iRandom(0, 7)];
+}
+
+
+size_t Color3::hashCode() const {
+ unsigned int rhash = (*(int*)(void*)(&r));
+ unsigned int ghash = (*(int*)(void*)(&g));
+ unsigned int bhash = (*(int*)(void*)(&b));
+
+ return rhash + (ghash * 37) + (bhash * 101);
+}
+
+
+Color3::Color3(const Vector3& v) {
+ r = v.x;
+ g = v.y;
+ b = v.z;
+}
+
+
+Color3::Color3(const class Color3uint8& other) {
+ r = other.r / 255.0f;
+ g = other.g / 255.0f;
+ b = other.b / 255.0f;
+}
+
+
+Color3 Color3::fromARGB(uint32 x) {
+ return Color3((float)((x >> 16) & 0xFF), (float)((x >> 8) & 0xFF), (float)(x & 0xFF)) / 255.0f;
+}
+
+//----------------------------------------------------------------------------
+
+
+Color3 Color3::random() {
+ return Color3(uniformRandom(),
+ uniformRandom(),
+ uniformRandom()).direction();
+}
+
+//----------------------------------------------------------------------------
+Color3& Color3::operator/= (float fScalar) {
+ if (fScalar != 0.0f) {
+ float fInvScalar = 1.0f / fScalar;
+ r *= fInvScalar;
+ g *= fInvScalar;
+ b *= fInvScalar;
+ } else {
+ r = (float)G3D::finf();
+ g = (float)G3D::finf();
+ b = (float)G3D::finf();
+ }
+
+ return *this;
+}
+
+//----------------------------------------------------------------------------
+float Color3::unitize (float fTolerance) {
+ float fLength = length();
+
+ if ( fLength > fTolerance ) {
+ float fInvLength = 1.0f / fLength;
+ r *= fInvLength;
+ g *= fInvLength;
+ b *= fInvLength;
+ } else {
+ fLength = 0.0f;
+ }
+
+ return fLength;
+}
+
+//----------------------------------------------------------------------------
+Color3 Color3::fromHSV(const Vector3& _hsv) {
+ debugAssertM((_hsv.x <= 1.0f && _hsv.x >= 0.0f)
+ && (_hsv.y <= 1.0f && _hsv.y >= 0.0f)
+ && ( _hsv.z <= 1.0f && _hsv.z >= 0.0f), "H,S,V must be between [0,1]");
+ const int i = iMin(5, G3D::iFloor(6.0 * _hsv.x));
+ const float f = 6.0f * _hsv.x - i;
+ const float m = _hsv.z * (1.0f - (_hsv.y));
+ const float n = _hsv.z * (1.0f - (_hsv.y * f));
+ const float k = _hsv.z * (1.0f - (_hsv.y * (1 - f)));
+ switch(i) {
+ case 0:
+ return Color3(_hsv.z, k, m);
+
+ case 1:
+ return Color3(n, _hsv.z, m);
+
+ case 2:
+ return Color3(m, _hsv.z, k);
+
+ case 3:
+ return Color3(m, n, _hsv.z);
+
+ case 4:
+ return Color3(k, m, _hsv.z);
+
+ case 5:
+ return Color3(_hsv.z, m, n);
+
+ default:
+ debugAssertM(false, "fell through switch..");
+ }
+ return Color3::black();
+}
+
+
+Vector3 Color3::toHSV(const Color3& _rgb) {
+ debugAssertM((_rgb.r <= 1.0f && _rgb.r >= 0.0f)
+ && (_rgb.g <= 1.0f && _rgb.g >= 0.0f)
+ && (_rgb.b <= 1.0f && _rgb.b >= 0.0f), "R,G,B must be between [0,1]");
+ Vector3 hsv = Vector3::zero();
+ hsv.z = G3D::max(G3D::max(_rgb.r, _rgb.g), _rgb.b);
+ if (G3D::fuzzyEq(hsv.z, 0.0f)) {
+ return hsv;
+ }
+
+ const float x = G3D::min(G3D::min(_rgb.r, _rgb.g), _rgb.b);
+ hsv.y = (hsv.z - x) / hsv.z;
+
+ if (G3D::fuzzyEq(hsv.y, 0.0f)) {
+ return hsv;
+ }
+
+ Vector3 rgbN;
+ rgbN.x = (hsv.z - _rgb.r) / (hsv.z - x);
+ rgbN.y = (hsv.z - _rgb.g) / (hsv.z - x);
+ rgbN.z = (hsv.z - _rgb.b) / (hsv.z - x);
+
+ if (_rgb.r == hsv.z) { // note from the max we know that it exactly equals one of the three.
+ hsv.x = (_rgb.g == x)? 5.0f + rgbN.z : 1.0f - rgbN.y;
+ } else if (_rgb.g == hsv.z) {
+ hsv.x = (_rgb.b == x)? 1.0f + rgbN.x : 3.0f - rgbN.z;
+ } else {
+ hsv.x = (_rgb.r == x)? 3.0f + rgbN.y : 5.0f - rgbN.x;
+ }
+
+ hsv.x /= 6.0f;
+
+ return hsv;
+}
+
+Color3 Color3::jetColorMap(const float& val) {
+ debugAssertM(val <= 1.0f && val >= 0.0f , "value should be in [0,1]");
+
+ //truncated triangles where sides have slope 4
+ Color3 jet;
+
+ jet.r = G3D::min(4.0f * val - 1.5f,-4.0f * val + 4.5f) ;
+ jet.g = G3D::min(4.0f * val - 0.5f,-4.0f * val + 3.5f) ;
+ jet.b = G3D::min(4.0f * val + 0.5f,-4.0f * val + 2.5f) ;
+
+
+ jet.r = G3D::clamp(jet.r, 0.0f, 1.0f);
+ jet.g = G3D::clamp(jet.g, 0.0f, 1.0f);
+ jet.b = G3D::clamp(jet.b, 0.0f, 1.0f);
+
+ return jet;
+}
+
+
+
+
+
+std::string Color3::toString() const {
+ return G3D::format("(%g, %g, %g)", r, g, b);
+}
+
+//----------------------------------------------------------------------------
+
+Color3 Color3::rainbowColorMap(float hue) {
+ return fromHSV(Vector3(hue, 1.0f, 1.0f));
+}
+
+
+}; // namespace
+
diff --git a/dep/src/g3dlite/Color3uint8.cpp b/dep/src/g3dlite/Color3uint8.cpp
new file mode 100644
index 00000000000..a744710c752
--- /dev/null
+++ b/dep/src/g3dlite/Color3uint8.cpp
@@ -0,0 +1,45 @@
+/**
+ @file Color3uint8.cpp
+
+ @author Morgan McGuire, http://graphics.cs.williams.edu
+
+ @created 2003-04-07
+ @edited 2006-01-07
+ */
+
+#include "G3D/platform.h"
+#include "G3D/g3dmath.h"
+#include "G3D/Color3uint8.h"
+#include "G3D/Color3.h"
+#include "G3D/BinaryInput.h"
+#include "G3D/BinaryOutput.h"
+
+namespace G3D {
+
+Color3uint8::Color3uint8(const class Color3& c) {
+ r = iMin(255, iFloor(c.r * 256));
+ g = iMin(255, iFloor(c.g * 256));
+ b = iMin(255, iFloor(c.b * 256));
+}
+
+
+Color3uint8::Color3uint8(class BinaryInput& bi) {
+ deserialize(bi);
+}
+
+
+void Color3uint8::serialize(class BinaryOutput& bo) const {
+ bo.writeUInt8(r);
+ bo.writeUInt8(g);
+ bo.writeUInt8(b);
+}
+
+
+void Color3uint8::deserialize(class BinaryInput& bi) {
+ r = bi.readUInt8();
+ g = bi.readUInt8();
+ b = bi.readUInt8();
+}
+
+
+}
diff --git a/dep/src/g3dlite/Color4.cpp b/dep/src/g3dlite/Color4.cpp
new file mode 100644
index 00000000000..eab09eb9c7e
--- /dev/null
+++ b/dep/src/g3dlite/Color4.cpp
@@ -0,0 +1,192 @@
+/**
+ @file Color4.cpp
+
+ Color class.
+
+ @author Morgan McGuire, http://graphics.cs.williams.edu
+ @cite Portions by Laura Wollstadt, graphics3d.com
+ @cite Portions based on Dave Eberly's Magic Software Library at http://www.magic-software.com
+
+
+ @created 2002-06-25
+ @edited 2009-11-10
+ */
+
+#include <stdlib.h>
+#include "G3D/Color4.h"
+#include "G3D/Color4uint8.h"
+#include "G3D/Vector4.h"
+#include "G3D/format.h"
+#include "G3D/BinaryInput.h"
+#include "G3D/BinaryOutput.h"
+#include "G3D/Any.h"
+#include "G3D/stringutils.h"
+
+namespace G3D {
+
+Color4::Color4(const Any& any) {
+ *this = Color4::zero();
+ any.verifyName("Color4");
+
+ if (any.type() == Any::TABLE) {
+ for (Any::AnyTable::Iterator it = any.table().begin(); it.hasMore(); ++it) {
+ const std::string& key = toLower(it->key);
+ if (key == "r") {
+ r = it->value;
+ } else if (key == "g") {
+ g = it->value;
+ } else if (key == "b") {
+ b = it->value;
+ } else if (key == "a") {
+ a = it->value;
+ } else {
+ any.verify(false, "Illegal key: " + it->key);
+ }
+ }
+ } else if (toLower(any.name()) == "color4") {
+ r = any[0];
+ g = any[1];
+ b = any[2];
+ a = any[3];
+ } else {
+ any.verifyName("Color4::fromARGB");
+ *this = Color4::fromARGB((int)any[0].number());
+ }
+}
+
+
+Color4::operator Any() const {
+ Any any(Any::ARRAY, "Color4");
+ any.append(r, g, b, a);
+ return any;
+}
+
+
+const Color4& Color4::one() {
+ const static Color4 x(1.0f, 1.0f, 1.0f, 1.0f);
+ return x;
+}
+
+
+const Color4& Color4::zero() {
+ static Color4 c(0.0f, 0.0f, 0.0f, 0.0f);
+ return c;
+}
+
+
+const Color4& Color4::inf() {
+ static Color4 c((float)G3D::finf(), (float)G3D::finf(), (float)G3D::finf(), (float)G3D::finf());
+ return c;
+}
+
+
+const Color4& Color4::nan() {
+ static Color4 c((float)G3D::fnan(), (float)G3D::fnan(), (float)G3D::fnan(), (float)G3D::fnan());
+ return c;
+}
+
+
+const Color4& Color4::clear() {
+ return Color4::zero();
+}
+
+
+Color4::Color4(const Vector4& v) {
+ r = v.x;
+ g = v.y;
+ b = v.z;
+ a = v.w;
+}
+
+
+Color4::Color4(const Color4uint8& c) : r(c.r), g(c.g), b(c.b), a(c.a) {
+ *this /= 255.0f;
+}
+
+size_t Color4::hashCode() const {
+ unsigned int rhash = (*(int*)(void*)(&r));
+ unsigned int ghash = (*(int*)(void*)(&g));
+ unsigned int bhash = (*(int*)(void*)(&b));
+ unsigned int ahash = (*(int*)(void*)(&a));
+
+ return rhash + (ghash * 37) + (bhash * 101) + (ahash * 241);
+}
+
+Color4 Color4::fromARGB(uint32 x) {
+ return Color4(
+ (float)((x >> 16) & 0xFF),
+ (float)((x >> 8) & 0xFF),
+ (float)(x & 0xFF),
+ (float)((x >> 24) & 0xFF)) / 255.0;
+}
+
+
+Color4::Color4(BinaryInput& bi) {
+ deserialize(bi);
+}
+
+
+void Color4::deserialize(BinaryInput& bi) {
+ r = bi.readFloat32();
+ g = bi.readFloat32();
+ b = bi.readFloat32();
+ a = bi.readFloat32();
+}
+
+
+void Color4::serialize(BinaryOutput& bo) const {
+ bo.writeFloat32(r);
+ bo.writeFloat32(g);
+ bo.writeFloat32(b);
+ bo.writeFloat32(a);
+}
+
+
+//----------------------------------------------------------------------------
+
+Color4 Color4::operator/ (float fScalar) const {
+ Color4 kQuot;
+
+ if (fScalar != 0.0f) {
+ float fInvScalar = 1.0f / fScalar;
+ kQuot.r = fInvScalar * r;
+ kQuot.g = fInvScalar * g;
+ kQuot.b = fInvScalar * b;
+ kQuot.a = fInvScalar * a;
+ return kQuot;
+
+ } else {
+
+ return Color4::inf();
+ }
+}
+
+//----------------------------------------------------------------------------
+
+Color4& Color4::operator/= (float fScalar) {
+ if (fScalar != 0.0f) {
+ float fInvScalar = 1.0f / fScalar;
+ r *= fInvScalar;
+ g *= fInvScalar;
+ b *= fInvScalar;
+ a *= fInvScalar;
+ } else {
+ r = (float)G3D::finf();
+ g = (float)G3D::finf();
+ b = (float)G3D::finf();
+ a = (float)G3D::finf();
+ }
+
+ return *this;
+}
+
+//----------------------------------------------------------------------------
+
+std::string Color4::toString() const {
+ return G3D::format("(%g, %g, %g, %g)", r, g, b, a);
+}
+
+//----------------------------------------------------------------------------
+
+}; // namespace
+
diff --git a/dep/src/g3dlite/Color4uint8.cpp b/dep/src/g3dlite/Color4uint8.cpp
new file mode 100644
index 00000000000..5cc3a578aca
--- /dev/null
+++ b/dep/src/g3dlite/Color4uint8.cpp
@@ -0,0 +1,47 @@
+/**
+ @file Color4uint8.cpp
+
+ @author Morgan McGuire, http://graphics.cs.williams.edu
+
+ @created 2003-04-07
+ @edited 2006-01-07
+ */
+#include "G3D/platform.h"
+#include "G3D/g3dmath.h"
+#include "G3D/Color4uint8.h"
+#include "G3D/Color4.h"
+#include "G3D/BinaryInput.h"
+#include "G3D/BinaryOutput.h"
+
+namespace G3D {
+
+Color4uint8::Color4uint8(const class Color4& c) {
+ r = iMin(255, iFloor(c.r * 256));
+ g = iMin(255, iFloor(c.g * 256));
+ b = iMin(255, iFloor(c.b * 256));
+ a = iMin(255, iFloor(c.a * 256));
+}
+
+
+Color4uint8::Color4uint8(class BinaryInput& bi) {
+ deserialize(bi);
+}
+
+
+void Color4uint8::serialize(class BinaryOutput& bo) const {
+ bo.writeUInt8(r);
+ bo.writeUInt8(g);
+ bo.writeUInt8(b);
+ bo.writeUInt8(a);
+}
+
+
+void Color4uint8::deserialize(class BinaryInput& bi) {
+ r = bi.readUInt8();
+ g = bi.readUInt8();
+ b = bi.readUInt8();
+ a = bi.readUInt8();
+}
+
+
+}
diff --git a/dep/src/g3dlite/Cone.cpp b/dep/src/g3dlite/Cone.cpp
new file mode 100644
index 00000000000..3104b8424a7
--- /dev/null
+++ b/dep/src/g3dlite/Cone.cpp
@@ -0,0 +1,79 @@
+/**
+ @file Cone.cpp
+
+ Cone class
+
+ @maintainer Morgan McGuire, http://graphics.cs.williams.edu
+
+ @created 2001-07-09
+ @edited 2006-01-29
+*/
+
+#include "G3D/platform.h"
+#include "G3D/Cone.h"
+#include "G3D/Line.h"
+#include "G3D/Sphere.h"
+#include "G3D/Box.h"
+
+namespace G3D {
+
+Cone::Cone(const Vector3 &tip, const Vector3 &direction, float angle) {
+ this->tip = tip;
+ this->direction = direction.direction();
+ this->angle = angle;
+
+ debugAssert(angle >= 0);
+ debugAssert(angle <= pi());
+}
+
+/**
+ Forms the smallest cone that contains the box. Undefined if
+ the tip is inside or on the box.
+ */
+Cone::Cone(const Vector3& tip, const Box& box) {
+ this->tip = tip;
+ this->direction = (box.center() - tip).direction();
+
+ // Find the biggest angle
+ float smallestDotProduct = direction.dot((box.corner(0) - tip).direction());
+
+ for (int i = 1; i < 8; ++i) {
+ float dp = direction.dot((box.corner(i) - tip).direction());
+
+ debugAssert(dp > 0);
+
+ if (dp < smallestDotProduct) {
+ smallestDotProduct = dp;
+ }
+ }
+
+ angle = acosf(smallestDotProduct);
+}
+
+
+bool Cone::intersects(const Sphere& b) const {
+ // If the bounding sphere contains the tip, then
+ // they definitely touch.
+ if (b.contains(this->tip)) {
+ return true;
+ }
+
+ // Move the tip backwards, effectively making the cone bigger
+ // to account for the radius of the sphere.
+
+ Vector3 tip = this->tip - direction * b.radius / sinf(angle);
+
+ return Cone(tip, direction, angle).contains(b.center);
+}
+
+
+bool Cone::contains(const Vector3& v) const {
+
+ Vector3 d = (v - tip).direction();
+
+ float x = d.dot(direction);
+
+ return (x > 0) && (x >= cosf(angle));
+}
+
+}; // namespace
diff --git a/dep/src/g3dlite/ConvexPolyhedron.cpp b/dep/src/g3dlite/ConvexPolyhedron.cpp
new file mode 100644
index 00000000000..5fa76e3ed41
--- /dev/null
+++ b/dep/src/g3dlite/ConvexPolyhedron.cpp
@@ -0,0 +1,457 @@
+/**
+ @file ConvexPolyhedron.cpp
+
+ @author Morgan McGuire, http://graphics.cs.williams.edu
+
+ @created 2001-11-11
+ @edited 2009-08-10
+
+ Copyright 2000-2009, Morgan McGuire.
+ All rights reserved.
+ */
+
+#include "G3D/platform.h"
+#include "G3D/ConvexPolyhedron.h"
+#include "G3D/debug.h"
+
+namespace G3D {
+
+ConvexPolygon::ConvexPolygon(const Array<Vector3>& __vertex) : _vertex(__vertex) {
+ // Intentionally empty
+}
+
+
+ConvexPolygon::ConvexPolygon(const Vector3& v0, const Vector3& v1, const Vector3& v2) {
+ _vertex.append(v0, v1, v2);
+}
+
+
+bool ConvexPolygon::isEmpty() const {
+ return (_vertex.length() == 0) || (getArea() <= fuzzyEpsilon);
+}
+
+
+float ConvexPolygon::getArea() const {
+
+ if (_vertex.length() < 3) {
+ return 0;
+ }
+
+ float sum = 0;
+
+ int length = _vertex.length();
+ // Split into triangle fan, compute individual area
+ for (int v = 2; v < length; v++) {
+ int i0 = 0;
+ int i1 = v - 1;
+ int i2 = v;
+
+ sum += (_vertex[i1] - _vertex[i0]).cross(_vertex[i2] - _vertex[i0]).magnitude() / 2;
+ }
+
+ return sum;
+}
+
+void ConvexPolygon::cut(const Plane& plane, ConvexPolygon &above, ConvexPolygon &below) {
+ DirectedEdge edge;
+ cut(plane, above, below, edge);
+}
+
+void ConvexPolygon::cut(const Plane& plane, ConvexPolygon &above, ConvexPolygon &below, DirectedEdge &newEdge) {
+ above._vertex.resize(0);
+ below._vertex.resize(0);
+
+ if (isEmpty()) {
+ //debugPrintf("Empty\n");
+ return;
+ }
+
+ int v = 0;
+ int length = _vertex.length();
+
+
+ Vector3 polyNormal = normal();
+ Vector3 planeNormal= plane.normal();
+
+ // See if the polygon is *in* the plane.
+ if (planeNormal.fuzzyEq(polyNormal) || planeNormal.fuzzyEq(-polyNormal)) {
+ // Polygon is parallel to the plane. It must be either above,
+ // below, or in the plane.
+
+ double a, b, c, d;
+ Vector3 pt = _vertex[0];
+
+ plane.getEquation(a,b,c,d);
+ float r = (float)(a * pt.x + b * pt.y + c * pt.z + d);
+
+ if (fuzzyGe(r, 0)) {
+ // The polygon is entirely in the plane.
+ //debugPrintf("Entirely above\n");
+ above = *this;
+ return;
+ } else {
+ //debugPrintf("Entirely below (1)\n");
+ below = *this;
+ return;
+ }
+ }
+
+
+ // Number of edges crossing the plane. Used for
+ // debug assertions.
+ int count = 0;
+
+ // True when the last _vertex we looked at was above the plane
+ bool lastAbove = plane.halfSpaceContains(_vertex[v]);
+
+ if (lastAbove) {
+ above._vertex.append(_vertex[v]);
+ } else {
+ below._vertex.append(_vertex[v]);
+ }
+
+ for (v = 1; v < length; v++) {
+ bool isAbove = plane.halfSpaceContains(_vertex[v]);
+
+ if (lastAbove ^ isAbove) {
+ // Switched sides.
+ // Create an interpolated point that lies
+ // in the plane, between the two points.
+ Line line = Line::fromTwoPoints(_vertex[v - 1], _vertex[v]);
+ Vector3 interp = line.intersection(plane);
+
+ if (! interp.isFinite()) {
+
+ // Since the polygon is not in the plane (we checked above),
+ // it must be the case that this edge (and only this edge)
+ // is in the plane. This only happens when the polygon is
+ // entirely below the plane except for one edge. This edge
+ // forms a degenerate polygon, so just treat the whole polygon
+ // as below the plane.
+ below = *this;
+ above._vertex.resize(0);
+ //debugPrintf("Entirely below\n");
+ return;
+ }
+
+ above._vertex.append(interp);
+ below._vertex.append(interp);
+ if (lastAbove) {
+ newEdge.stop = interp;
+ } else {
+ newEdge.start = interp;
+ }
+ count++;
+ }
+
+ lastAbove = isAbove;
+ if (lastAbove) {
+ above._vertex.append(_vertex[v]);
+ } else {
+ below._vertex.append(_vertex[v]);
+ }
+ }
+
+ // Loop back to the first point, seeing if an interpolated point is
+ // needed.
+ bool isAbove = plane.halfSpaceContains(_vertex[0]);
+ if (lastAbove ^ isAbove) {
+ Line line = Line::fromTwoPoints(_vertex[length - 1], _vertex[0]);
+ Vector3 interp = line.intersection(plane);
+ if (! interp.isFinite()) {
+ // Since the polygon is not in the plane (we checked above),
+ // it must be the case that this edge (and only this edge)
+ // is in the plane. This only happens when the polygon is
+ // entirely below the plane except for one edge. This edge
+ // forms a degenerate polygon, so just treat the whole polygon
+ // as below the plane.
+ below = *this;
+ above._vertex.resize(0);
+ //debugPrintf("Entirely below\n");
+ return;
+ }
+
+ above._vertex.append(interp);
+ below._vertex.append(interp);
+ debugAssertM(count < 2, "Convex polygons may only intersect planes at two edges.");
+ if (lastAbove) {
+ newEdge.stop = interp;
+ } else {
+ newEdge.start = interp;
+ }
+ ++count;
+ }
+
+ debugAssertM((count == 2) || (count == 0), "Convex polygons may only intersect planes at two edges.");
+}
+
+
+ConvexPolygon ConvexPolygon::inverse() const {
+ ConvexPolygon result;
+ int length = _vertex.length();
+ result._vertex.resize(length);
+
+ for (int v = 0; v < length; v++) {
+ result._vertex[v] = _vertex[length - v - 1];
+ }
+
+ return result;
+}
+
+
+void ConvexPolygon::removeDuplicateVertices(){
+ // Any valid polygon should have 3 or more vertices, but why take chances?
+ if (_vertex.size() >= 2){
+
+ // Remove duplicate vertices.
+ for (int i=0;i<_vertex.size()-1;++i){
+ if (_vertex[i].fuzzyEq(_vertex[i+1])){
+ _vertex.remove(i+1);
+ --i; // Don't move forward.
+ }
+ }
+
+ // Check the last vertex against the first.
+ if (_vertex[_vertex.size()-1].fuzzyEq(_vertex[0])){
+ _vertex.pop();
+ }
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////////
+
+ConvexPolyhedron::ConvexPolyhedron(const Array<ConvexPolygon>& _face) : face(_face) {
+ // Intentionally empty
+}
+
+
+float ConvexPolyhedron::getVolume() const {
+
+ if (face.length() < 4) {
+ return 0;
+ }
+
+ // The volume of any pyramid is 1/3 * h * base area.
+ // Discussion at: http://nrich.maths.org/mathsf/journalf/oct01/art1/
+
+ float sum = 0;
+
+ // Choose the first _vertex of the first face as the origin.
+ // This lets us skip one face, too, and avoids negative heights.
+ Vector3 v0 = face[0]._vertex[0];
+ for (int f = 1; f < face.length(); f++) {
+ const ConvexPolygon& poly = face[f];
+
+ float height = (poly._vertex[0] - v0).dot(poly.normal());
+ float base = poly.getArea();
+
+ sum += height * base;
+ }
+
+ return sum / 3;
+}
+
+bool ConvexPolyhedron::isEmpty() const {
+ return (face.length() == 0) || (getVolume() <= fuzzyEpsilon);
+}
+
+void ConvexPolyhedron::cut(const Plane& plane, ConvexPolyhedron &above, ConvexPolyhedron &below) {
+ above.face.resize(0);
+ below.face.resize(0);
+
+ Array<DirectedEdge> edge;
+
+ int f;
+
+ // See if the plane cuts this polyhedron at all. Detect when
+ // the polyhedron is entirely to one side or the other.
+ //{
+ int numAbove = 0, numIn = 0, numBelow = 0;
+ bool ruledOut = false;
+ double d;
+ Vector3 abc;
+ plane.getEquation(abc, d);
+
+ // This number has to be fairly large to prevent precision problems down
+ // the road.
+ const float eps = 0.005f;
+ for (f = face.length() - 1; (f >= 0) && (!ruledOut); f--) {
+ const ConvexPolygon& poly = face[f];
+ for (int v = poly._vertex.length() - 1; (v >= 0) && (!ruledOut); v--) {
+ double r = abc.dot(poly._vertex[v]) + d;
+ if (r > eps) {
+ numAbove++;
+ } else if (r < -eps) {
+ numBelow++;
+ } else {
+ numIn++;
+ }
+
+ ruledOut = (numAbove != 0) && (numBelow !=0);
+ }
+ }
+
+ if (numBelow == 0) {
+ above = *this;
+ return;
+ } else if (numAbove == 0) {
+ below = *this;
+ return;
+ }
+ //}
+
+ // Clip each polygon, collecting split edges.
+ for (f = face.length() - 1; f >= 0; f--) {
+ ConvexPolygon a, b;
+ DirectedEdge e;
+ face[f].cut(plane, a, b, e);
+
+ bool aEmpty = a.isEmpty();
+ bool bEmpty = b.isEmpty();
+
+ //debugPrintf("\n");
+ if (! aEmpty) {
+ //debugPrintf(" Above %f\n", a.getArea());
+ above.face.append(a);
+ }
+
+ if (! bEmpty) {
+ //debugPrintf(" Below %f\n", b.getArea());
+ below.face.append(b);
+ }
+
+ if (! aEmpty && ! bEmpty) {
+ //debugPrintf(" == Split\n");
+ edge.append(e);
+ } else {
+ // Might be the case that the polygon is entirely on
+ // one side of the plane yet there is an edge we need
+ // because it touches the plane.
+ //
+ // Extract the non-empty _vertex list and examine it.
+ // If we find exactly one edge in the plane, add that edge.
+ const Array<Vector3>& _vertex = (aEmpty ? b._vertex : a._vertex);
+ int L = _vertex.length();
+ int count = 0;
+ for (int v = 0; v < L; v++) {
+ if (plane.fuzzyContains(_vertex[v]) && plane.fuzzyContains(_vertex[(v + 1) % L])) {
+ e.start = _vertex[v];
+ e.stop = _vertex[(v + 1) % L];
+ count++;
+ }
+ }
+
+ if (count == 1) {
+ edge.append(e);
+ }
+ }
+ }
+
+ if (above.face.length() == 1) {
+ // Only one face above means that this entire
+ // polyhedron is below the plane. Move that face over.
+ below.face.append(above.face[0]);
+ above.face.resize(0);
+ } else if (below.face.length() == 1) {
+ // This shouldn't happen, but it arises in practice
+ // from numerical imprecision.
+ above.face.append(below.face[0]);
+ below.face.resize(0);
+ }
+
+ if ((above.face.length() > 0) && (below.face.length() > 0)) {
+ // The polyhedron was actually cut; create a cap polygon
+ ConvexPolygon cap;
+
+ // Collect the final polgyon by sorting the edges
+ int numVertices = edge.length();
+/*debugPrintf("\n");
+for (int xx=0; xx < numVertices; xx++) {
+ std::string s1 = edge[xx].start.toString();
+ std::string s2 = edge[xx].stop.toString();
+ debugPrintf("%s -> %s\n", s1.c_str(), s2.c_str());
+}
+*/
+
+ // Need at least three points to make a polygon
+ debugAssert(numVertices >= 3);
+
+ Vector3 last_vertex = edge.last().stop;
+ cap._vertex.append(last_vertex);
+
+ // Search for the next _vertex. Because of accumulating
+ // numerical error, we have to find the closest match, not
+ // just the one we expect.
+ for (int v = numVertices - 1; v >= 0; v--) {
+ // matching edge index
+ int index = 0;
+ int num = edge.length();
+ double distance = (edge[index].start - last_vertex).squaredMagnitude();
+ for (int e = 1; e < num; e++) {
+ double d = (edge[e].start - last_vertex).squaredMagnitude();
+
+ if (d < distance) {
+ // This is the new closest one
+ index = e;
+ distance = d;
+ }
+ }
+
+ // Don't tolerate ridiculous error.
+ debugAssertM(distance < 0.02, "Edge missing while closing polygon.");
+
+ last_vertex = edge[index].stop;
+ cap._vertex.append(last_vertex);
+ }
+
+ //debugPrintf("\n");
+ //debugPrintf("Cap (both) %f\n", cap.getArea());
+ above.face.append(cap);
+ below.face.append(cap.inverse());
+ }
+
+ // Make sure we put enough faces on each polyhedra
+ debugAssert((above.face.length() == 0) || (above.face.length() >= 4));
+ debugAssert((below.face.length() == 0) || (below.face.length() >= 4));
+}
+
+///////////////////////////////////////////////
+
+ConvexPolygon2D::ConvexPolygon2D(const Array<Vector2>& pts, bool reverse) : m_vertex(pts) {
+ if (reverse) {
+ m_vertex.reverse();
+ }
+}
+
+
+bool ConvexPolygon2D::contains(const Vector2& p, bool reverse) const {
+ // Compute the signed area of each polygon from p to an edge.
+ // If the area is non-negative for all polygons then p is inside
+ // the polygon. (To adapt this algorithm for a concave polygon,
+ // the *sum* of the areas must be non-negative).
+
+ float r = reverse ? -1 : 1;
+
+ for (int i0 = 0; i0 < m_vertex.size(); ++i0) {
+ int i1 = (i0 + 1) % m_vertex.size();
+ const Vector2& v0 = m_vertex[i0];
+ const Vector2& v1 = m_vertex[i1];
+
+ Vector2 e0 = v0 - p;
+ Vector2 e1 = v1 - p;
+
+ // Area = (1/2) cross product, negated to be ccw in
+ // a 2D space; we neglect the 1/2
+ float area = -(e0.x * e1.y - e0.y * e1.x);
+
+ if (area * r < 0) {
+ return false;
+ }
+ }
+
+ return true;
+}
+
+
+}
+
diff --git a/dep/src/g3dlite/CoordinateFrame.cpp b/dep/src/g3dlite/CoordinateFrame.cpp
new file mode 100644
index 00000000000..9b639b62082
--- /dev/null
+++ b/dep/src/g3dlite/CoordinateFrame.cpp
@@ -0,0 +1,436 @@
+/**
+ @file CoordinateFrame.cpp
+
+ Coordinate frame class
+
+ @maintainer Morgan McGuire, http://graphics.cs.williams.edu
+
+ @created 2001-06-02
+ @edited 2009-11-13
+
+ Copyright 2000-2010, Morgan McGuire.
+ All rights reserved.
+*/
+
+#include "G3D/platform.h"
+#include "G3D/CoordinateFrame.h"
+#include "G3D/Quat.h"
+#include "G3D/Matrix4.h"
+#include "G3D/Box.h"
+#include "G3D/AABox.h"
+#include "G3D/Sphere.h"
+#include "G3D/Triangle.h"
+#include "G3D/Ray.h"
+#include "G3D/Capsule.h"
+#include "G3D/Cylinder.h"
+#include "G3D/UprightFrame.h"
+#include "G3D/Any.h"
+#include "G3D/stringutils.h"
+
+namespace G3D {
+
+CoordinateFrame::CoordinateFrame(const Any& any) {
+ any.verifyName("CFrame");
+ if (toUpper(any.name()) == "CFRAME") {
+ any.verifyType(Any::TABLE, Any::ARRAY);
+ if (any.type() == Any::TABLE) {
+ rotation = any["rotation"];
+ translation = any["translation"];
+ } else {
+ any.verifySize(2);
+ rotation = any[0];
+ translation = any[1];
+ }
+ } else {
+ any.verifyName("CFrame::fromXYZYPRDegrees");
+ any.verifyType(Any::ARRAY);
+ any.verifySize(3, 6);
+
+ int s = any.size();
+
+ *this = fromXYZYPRDegrees(any[0], any[1], any[2],
+ (s > 3) ? any[3].number() : 0.0f,
+ (s > 4) ? any[4].number() : 0.0f,
+ (s > 5) ? any[5].number() : 0.0f);
+ }
+}
+
+
+CoordinateFrame::operator Any() const {
+ float x, y, z, yaw, pitch, roll;
+ getXYZYPRDegrees(x, y, z, yaw, pitch, roll);
+ Any a(Any::ARRAY, "CFrame::fromXYZYPRDegrees");
+ a.append(x, y, z, yaw);
+ if ( ! G3D::fuzzyEq(yaw, 0.0f) || ! G3D::fuzzyEq(pitch, 0.0f) || ! G3D::fuzzyEq(roll, 0.0f)) {
+ a.append(yaw);
+ if (! G3D::fuzzyEq(pitch, 0.0f) || ! G3D::fuzzyEq(roll, 0.0f)) {
+ a.append(pitch);
+ if (! G3D::fuzzyEq(roll, 0.0f)) {
+ a.append(roll);
+ }
+ }
+ }
+ return a;
+}
+
+
+CoordinateFrame::CoordinateFrame(const class UprightFrame& f) {
+ *this = f.toCoordinateFrame();
+}
+
+
+CoordinateFrame::CoordinateFrame() :
+ rotation(Matrix3::identity()), translation(Vector3::zero()) {
+}
+
+CoordinateFrame CoordinateFrame::fromXYZYPRRadians(float x, float y, float z, float yaw,
+ float pitch, float roll) {
+ Matrix3 rotation = Matrix3::fromAxisAngle(Vector3::unitY(), yaw);
+
+ rotation = Matrix3::fromAxisAngle(rotation.column(0), pitch) * rotation;
+ rotation = Matrix3::fromAxisAngle(rotation.column(2), roll) * rotation;
+
+ const Vector3 translation(x, y, z);
+
+ return CoordinateFrame(rotation, translation);
+}
+
+
+void CoordinateFrame::getXYZYPRRadians(float& x, float& y, float& z,
+ float& yaw, float& pitch, float& roll) const {
+ x = translation.x;
+ y = translation.y;
+ z = translation.z;
+
+ const Vector3& look = lookVector();
+
+ if (abs(look.y) > 0.99f) {
+ // Looking nearly straight up or down
+
+ yaw = G3D::pi() + atan2(look.x, look.z);
+ pitch = asin(look.y);
+ roll = 0.0f;
+
+ } else {
+
+ // Yaw cannot be affected by others, so pull it first
+ yaw = G3D::pi() + atan2(look.x, look.z);
+
+ // Pitch is the elevation of the yaw vector
+ pitch = asin(look.y);
+
+ Vector3 actualRight = rightVector();
+ Vector3 expectedRight = look.cross(Vector3::unitY());
+
+ roll = 0;//acos(actualRight.dot(expectedRight)); TODO
+ }
+}
+
+
+void CoordinateFrame::getXYZYPRDegrees(float& x, float& y, float& z,
+ float& yaw, float& pitch, float& roll) const {
+ getXYZYPRRadians(x, y, z, yaw, pitch, roll);
+ yaw = toDegrees(yaw);
+ pitch = toDegrees(pitch);
+ roll = toDegrees(roll);
+}
+
+
+CoordinateFrame CoordinateFrame::fromXYZYPRDegrees(float x, float y, float z,
+ float yaw, float pitch, float roll) {
+ return fromXYZYPRRadians(x, y, z, toRadians(yaw), toRadians(pitch), toRadians(roll));
+}
+
+
+Ray CoordinateFrame::lookRay() const {
+ return Ray::fromOriginAndDirection(translation, lookVector());
+}
+
+
+bool CoordinateFrame::fuzzyEq(const CoordinateFrame& other) const {
+
+ for (int c = 0; c < 3; ++c) {
+ for (int r = 0; r < 3; ++r) {
+ if (! G3D::fuzzyEq(other.rotation[r][c], rotation[r][c])) {
+ return false;
+ }
+ }
+ if (! G3D::fuzzyEq(translation[c], other.translation[c])) {
+ return false;
+ }
+ }
+
+ return true;
+}
+
+
+bool CoordinateFrame::fuzzyIsIdentity() const {
+ const Matrix3& I = Matrix3::identity();
+
+ for (int c = 0; c < 3; ++c) {
+ for (int r = 0; r < 3; ++r) {
+ if (fuzzyNe(I[r][c], rotation[r][c])) {
+ return false;
+ }
+ }
+ if (fuzzyNe(translation[c], 0)) {
+ return false;
+ }
+ }
+
+ return true;
+}
+
+
+bool CoordinateFrame::isIdentity() const {
+ return
+ (translation == Vector3::zero()) &&
+ (rotation == Matrix3::identity());
+}
+
+
+Matrix4 CoordinateFrame::toMatrix4() const {
+ return Matrix4(*this);
+}
+
+
+std::string CoordinateFrame::toXML() const {
+ return G3D::format(
+ "<COORDINATEFRAME>\n %lf,%lf,%lf,%lf,\n %lf,%lf,%lf,%lf,\n %lf,%lf,%lf,%lf,\n %lf,%lf,%lf,%lf\n</COORDINATEFRAME>\n",
+ rotation[0][0], rotation[0][1], rotation[0][2], translation.x,
+ rotation[1][0], rotation[1][1], rotation[1][2], translation.y,
+ rotation[2][0], rotation[2][1], rotation[2][2], translation.z,
+ 0.0, 0.0, 0.0, 1.0);
+}
+
+
+Plane CoordinateFrame::toObjectSpace(const Plane& p) const {
+ Vector3 N, P;
+ double d;
+ p.getEquation(N, d);
+ P = N * (float)d;
+ P = pointToObjectSpace(P);
+ N = normalToObjectSpace(N);
+ return Plane(N, P);
+}
+
+
+Plane CoordinateFrame::toWorldSpace(const Plane& p) const {
+ Vector3 N, P;
+ double d;
+ p.getEquation(N, d);
+ P = N * (float)d;
+ P = pointToWorldSpace(P);
+ N = normalToWorldSpace(N);
+ return Plane(N, P);
+}
+
+
+Triangle CoordinateFrame::toObjectSpace(const Triangle& t) const {
+ return Triangle(pointToObjectSpace(t.vertex(0)),
+ pointToObjectSpace(t.vertex(1)),
+ pointToObjectSpace(t.vertex(2)));
+}
+
+
+Triangle CoordinateFrame::toWorldSpace(const Triangle& t) const {
+ return Triangle(pointToWorldSpace(t.vertex(0)),
+ pointToWorldSpace(t.vertex(1)),
+ pointToWorldSpace(t.vertex(2)));
+}
+
+
+Cylinder CoordinateFrame::toWorldSpace(const Cylinder& c) const {
+ return Cylinder(
+ pointToWorldSpace(c.point(0)),
+ pointToWorldSpace(c.point(1)),
+ c.radius());
+}
+
+
+Capsule CoordinateFrame::toWorldSpace(const Capsule& c) const {
+ return Capsule(
+ pointToWorldSpace(c.point(0)),
+ pointToWorldSpace(c.point(1)),
+ c.radius());
+}
+
+
+Box CoordinateFrame::toWorldSpace(const AABox& b) const {
+ Box b2(b);
+ return toWorldSpace(b2);
+}
+
+
+Box CoordinateFrame::toWorldSpace(const Box& b) const {
+ Box out(b);
+
+ for (int i = 0; i < 8; ++i) {
+ out._corner[i] = pointToWorldSpace(b._corner[i]);
+ debugAssert(! isNaN(out._corner[i].x));
+ }
+
+ for (int i = 0; i < 3; ++i) {
+ out._axis[i] = vectorToWorldSpace(b._axis[i]);
+ }
+
+ out._center = pointToWorldSpace(b._center);
+
+ return out;
+}
+
+
+Box CoordinateFrame::toObjectSpace(const Box &b) const {
+ return inverse().toWorldSpace(b);
+}
+
+
+Box CoordinateFrame::toObjectSpace(const AABox& b) const {
+ return toObjectSpace(Box(b));
+}
+
+
+CoordinateFrame::CoordinateFrame(class BinaryInput& b) : rotation(Matrix3::zero()) {
+ deserialize(b);
+}
+
+
+void CoordinateFrame::deserialize(class BinaryInput& b) {
+ rotation.deserialize(b);
+ translation.deserialize(b);
+}
+
+
+void CoordinateFrame::serialize(class BinaryOutput& b) const {
+ rotation.serialize(b);
+ translation.serialize(b);
+}
+
+
+Sphere CoordinateFrame::toWorldSpace(const Sphere &b) const {
+ return Sphere(pointToWorldSpace(b.center), b.radius);
+}
+
+
+Sphere CoordinateFrame::toObjectSpace(const Sphere &b) const {
+ return Sphere(pointToObjectSpace(b.center), b.radius);
+}
+
+
+Ray CoordinateFrame::toWorldSpace(const Ray& r) const {
+ return Ray::fromOriginAndDirection(pointToWorldSpace(r.origin()), vectorToWorldSpace(r.direction()));
+}
+
+
+Ray CoordinateFrame::toObjectSpace(const Ray& r) const {
+ return Ray::fromOriginAndDirection(pointToObjectSpace(r.origin()), vectorToObjectSpace(r.direction()));
+}
+
+
+void CoordinateFrame::lookAt(const Vector3 &target) {
+ lookAt(target, Vector3::unitY());
+}
+
+
+void CoordinateFrame::lookAt(
+ const Vector3& target,
+ Vector3 up) {
+
+ up = up.direction();
+
+ Vector3 look = (target - translation).direction();
+ if (fabs(look.dot(up)) > .99f) {
+ up = Vector3::unitX();
+ if (fabs(look.dot(up)) > .99f) {
+ up = Vector3::unitY();
+ }
+ }
+
+ up -= look * look.dot(up);
+ up.unitize();
+
+ Vector3 z = -look;
+ Vector3 x = -z.cross(up);
+ x.unitize();
+
+ Vector3 y = z.cross(x);
+
+ rotation.setColumn(0, x);
+ rotation.setColumn(1, y);
+ rotation.setColumn(2, z);
+}
+
+
+CoordinateFrame CoordinateFrame::lerp(
+ const CoordinateFrame& other,
+ float alpha) const {
+
+ if (alpha == 1.0f) {
+ return other;
+ } else if (alpha == 0.0f) {
+ return *this;
+ } else {
+ Quat q1 = Quat(this->rotation);
+ Quat q2 = Quat(other.rotation);
+
+ return CoordinateFrame(
+ q1.slerp(q2, alpha).toRotationMatrix(),
+ this->translation * (1 - alpha) + other.translation * alpha);
+ }
+}
+
+
+void CoordinateFrame::pointToWorldSpace(const Array<Vector3>& v, Array<Vector3>& vout) const {
+ vout.resize(v.size());
+
+ for (int i = v.size() - 1; i >= 0; --i) {
+ vout[i] = pointToWorldSpace(v[i]);
+ }
+}
+
+
+void CoordinateFrame::normalToWorldSpace(const Array<Vector3>& v, Array<Vector3>& vout) const {
+ vout.resize(v.size());
+
+ for (int i = v.size() - 1; i >= 0; --i) {
+ vout[i] = normalToWorldSpace(v[i]);
+ }
+}
+
+
+void CoordinateFrame::vectorToWorldSpace(const Array<Vector3>& v, Array<Vector3>& vout) const {
+ vout.resize(v.size());
+
+ for (int i = v.size() - 1; i >= 0; --i) {
+ vout[i] = vectorToWorldSpace(v[i]);
+ }
+}
+
+
+void CoordinateFrame::pointToObjectSpace(const Array<Vector3>& v, Array<Vector3>& vout) const {
+ vout.resize(v.size());
+
+ for (int i = v.size() - 1; i >= 0; --i) {
+ vout[i] = pointToObjectSpace(v[i]);
+ }
+}
+
+
+void CoordinateFrame::normalToObjectSpace(const Array<Vector3>& v, Array<Vector3>& vout) const {
+ vout.resize(v.size());
+
+ for (int i = v.size() - 1; i >= 0; --i) {
+ vout[i] = normalToObjectSpace(v[i]);
+ }
+}
+
+
+void CoordinateFrame::vectorToObjectSpace(const Array<Vector3>& v, Array<Vector3>& vout) const {
+ vout.resize(v.size());
+
+ for (int i = v.size() - 1; i >= 0; --i) {
+ vout[i] = vectorToObjectSpace(v[i]);
+ }
+}
+
+} // namespace
diff --git a/dep/src/g3dlite/Crypto.cpp b/dep/src/g3dlite/Crypto.cpp
index 03851193e57..c69b23375ce 100644
--- a/dep/src/g3dlite/Crypto.cpp
+++ b/dep/src/g3dlite/Crypto.cpp
@@ -1,7 +1,8 @@
/**
@file Crypto.cpp
- @author Morgan McGuire, matrix@graphics3d.com
+ @author Morgan McGuire, http://graphics.cs.williams.edu
+
@created 2006-03-28
@edited 2006-04-06
@@ -10,8 +11,10 @@
#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);
@@ -20,125 +23,48 @@ int Crypto::smallPrime(int n) {
// 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,
+ 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,
+ 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) {
- static const uint32 crc32Table[256] = {
- 0x00000000, 0x77073096, 0xEE0E612C, 0x990951BA,
- 0x076DC419, 0x706AF48F, 0xE963A535, 0x9E6495A3,
- 0x0EDB8832, 0x79DCB8A4, 0xE0D5E91E, 0x97D2D988,
- 0x09B64C2B, 0x7EB17CBD, 0xE7B82D07, 0x90BF1D91,
- 0x1DB71064, 0x6AB020F2, 0xF3B97148, 0x84BE41DE,
- 0x1ADAD47D, 0x6DDDE4EB, 0xF4D4B551, 0x83D385C7,
- 0x136C9856, 0x646BA8C0, 0xFD62F97A, 0x8A65C9EC,
- 0x14015C4F, 0x63066CD9, 0xFA0F3D63, 0x8D080DF5,
- 0x3B6E20C8, 0x4C69105E, 0xD56041E4, 0xA2677172,
- 0x3C03E4D1, 0x4B04D447, 0xD20D85FD, 0xA50AB56B,
- 0x35B5A8FA, 0x42B2986C, 0xDBBBC9D6, 0xACBCF940,
- 0x32D86CE3, 0x45DF5C75, 0xDCD60DCF, 0xABD13D59,
- 0x26D930AC, 0x51DE003A, 0xC8D75180, 0xBFD06116,
- 0x21B4F4B5, 0x56B3C423, 0xCFBA9599, 0xB8BDA50F,
- 0x2802B89E, 0x5F058808, 0xC60CD9B2, 0xB10BE924,
- 0x2F6F7C87, 0x58684C11, 0xC1611DAB, 0xB6662D3D,
-
- 0x76DC4190, 0x01DB7106, 0x98D220BC, 0xEFD5102A,
- 0x71B18589, 0x06B6B51F, 0x9FBFE4A5, 0xE8B8D433,
- 0x7807C9A2, 0x0F00F934, 0x9609A88E, 0xE10E9818,
- 0x7F6A0DBB, 0x086D3D2D, 0x91646C97, 0xE6635C01,
- 0x6B6B51F4, 0x1C6C6162, 0x856530D8, 0xF262004E,
- 0x6C0695ED, 0x1B01A57B, 0x8208F4C1, 0xF50FC457,
- 0x65B0D9C6, 0x12B7E950, 0x8BBEB8EA, 0xFCB9887C,
- 0x62DD1DDF, 0x15DA2D49, 0x8CD37CF3, 0xFBD44C65,
- 0x4DB26158, 0x3AB551CE, 0xA3BC0074, 0xD4BB30E2,
- 0x4ADFA541, 0x3DD895D7, 0xA4D1C46D, 0xD3D6F4FB,
- 0x4369E96A, 0x346ED9FC, 0xAD678846, 0xDA60B8D0,
- 0x44042D73, 0x33031DE5, 0xAA0A4C5F, 0xDD0D7CC9,
- 0x5005713C, 0x270241AA, 0xBE0B1010, 0xC90C2086,
- 0x5768B525, 0x206F85B3, 0xB966D409, 0xCE61E49F,
- 0x5EDEF90E, 0x29D9C998, 0xB0D09822, 0xC7D7A8B4,
- 0x59B33D17, 0x2EB40D81, 0xB7BD5C3B, 0xC0BA6CAD,
-
- 0xEDB88320, 0x9ABFB3B6, 0x03B6E20C, 0x74B1D29A,
- 0xEAD54739, 0x9DD277AF, 0x04DB2615, 0x73DC1683,
- 0xE3630B12, 0x94643B84, 0x0D6D6A3E, 0x7A6A5AA8,
- 0xE40ECF0B, 0x9309FF9D, 0x0A00AE27, 0x7D079EB1,
- 0xF00F9344, 0x8708A3D2, 0x1E01F268, 0x6906C2FE,
- 0xF762575D, 0x806567CB, 0x196C3671, 0x6E6B06E7,
- 0xFED41B76, 0x89D32BE0, 0x10DA7A5A, 0x67DD4ACC,
- 0xF9B9DF6F, 0x8EBEEFF9, 0x17B7BE43, 0x60B08ED5,
- 0xD6D6A3E8, 0xA1D1937E, 0x38D8C2C4, 0x4FDFF252,
- 0xD1BB67F1, 0xA6BC5767, 0x3FB506DD, 0x48B2364B,
- 0xD80D2BDA, 0xAF0A1B4C, 0x36034AF6, 0x41047A60,
- 0xDF60EFC3, 0xA867DF55, 0x316E8EEF, 0x4669BE79,
- 0xCB61B38C, 0xBC66831A, 0x256FD2A0, 0x5268E236,
- 0xCC0C7795, 0xBB0B4703, 0x220216B9, 0x5505262F,
- 0xC5BA3BBE, 0xB2BD0B28, 0x2BB45A92, 0x5CB36A04,
- 0xC2D7FFA7, 0xB5D0CF31, 0x2CD99E8B, 0x5BDEAE1D,
-
- 0x9B64C2B0, 0xEC63F226, 0x756AA39C, 0x026D930A,
- 0x9C0906A9, 0xEB0E363F, 0x72076785, 0x05005713,
- 0x95BF4A82, 0xE2B87A14, 0x7BB12BAE, 0x0CB61B38,
- 0x92D28E9B, 0xE5D5BE0D, 0x7CDCEFB7, 0x0BDBDF21,
- 0x86D3D2D4, 0xF1D4E242, 0x68DDB3F8, 0x1FDA836E,
- 0x81BE16CD, 0xF6B9265B, 0x6FB077E1, 0x18B74777,
- 0x88085AE6, 0xFF0F6A70, 0x66063BCA, 0x11010B5C,
- 0x8F659EFF, 0xF862AE69, 0x616BFFD3, 0x166CCF45,
- 0xA00AE278, 0xD70DD2EE, 0x4E048354, 0x3903B3C2,
- 0xA7672661, 0xD06016F7, 0x4969474D, 0x3E6E77DB,
- 0xAED16A4A, 0xD9D65ADC, 0x40DF0B66, 0x37D83BF0,
- 0xA9BCAE53, 0xDEBB9EC5, 0x47B2CF7F, 0x30B5FFE9,
- 0xBDBDF21C, 0xCABAC28A, 0x53B39330, 0x24B4A3A6,
- 0xBAD03605, 0xCDD70693, 0x54DE5729, 0x23D967BF,
- 0xB3667A2E, 0xC4614AB8, 0x5D681B02, 0x2A6F2B94,
- 0xB40BBE37, 0xC30C8EA1, 0x5A05DF1B, 0x2D02EF8D,
- };
-
- // By definition, initialize to all binary 1's
- uint32 value = 0xFFFFFFFF;
-
- for (size_t i = 0; i < numBytes; ++i) {
- value = (value >> 8 ) ^ crc32Table[static_cast<const uint8*>(byte)[i] ^ (value & 0xFF)];
- }
-
- return value;
+ return ::crc32(::crc32(0, Z_NULL, 0), static_cast<const Bytef *>(byte), numBytes);
}
} // G3D
-
diff --git a/dep/src/g3dlite/Crypto_md5.cpp b/dep/src/g3dlite/Crypto_md5.cpp
new file mode 100644
index 00000000000..c7ee535d61e
--- /dev/null
+++ b/dep/src/g3dlite/Crypto_md5.cpp
@@ -0,0 +1,471 @@
+/**
+ @file Crypto_md5.cpp
+
+ @author Morgan McGuire, http://graphics.cs.williams.edu
+ Copyright 2006-2007, Morgan McGuire. All rights reserved.
+
+ @created 2006-03-28
+ @edited 2006-04-06
+ */
+
+#include "G3D/platform.h"
+#include "G3D/Crypto.h"
+#include "G3D/BinaryInput.h"
+#include "G3D/BinaryOutput.h"
+
+#include <cstring>
+
+namespace G3D {
+
+
+MD5Hash::MD5Hash(class BinaryInput& b) {
+ deserialize(b);
+}
+
+
+void MD5Hash::deserialize(class BinaryInput& b) {
+ b.readBytes(value, 16);
+}
+
+
+void MD5Hash::serialize(class BinaryOutput& b) const {
+ b.writeBytes(value, 16);
+}
+
+
+typedef unsigned char md5_byte_t; /* 8-bit byte */
+typedef unsigned int md5_word_t; /* 32-bit word */
+
+/* Define the state of the MD5 Algorithm. */
+typedef struct md5_state_s {
+ md5_word_t count[2]; /* message length in bits, lsw first */
+ md5_word_t abcd[4]; /* digest buffer */
+ md5_byte_t buf[64]; /* accumulate block */
+} md5_state_t;
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+/* Initialize the algorithm. */
+static void md5_init(md5_state_t *pms);
+
+/* Append a string to the message. */
+static void md5_append(md5_state_t *pms, const md5_byte_t *data, int nbytes);
+
+/* Finish the message and return the digest. */
+void md5_finish(md5_state_t *pms, md5_byte_t digest[16]);
+#ifdef __cplusplus
+}
+#endif
+
+
+
+MD5Hash Crypto::md5(const void* data, size_t n) {
+ md5_state_t state;
+ md5_init(&state);
+ md5_append(&state, (const uint8*)data, (int)n);
+
+ MD5Hash h;
+ md5_finish(&state, &(h[0]));
+ return h;
+}
+
+/*
+ Copyright (C) 1999, 2000, 2002 Aladdin Enterprises. All rights reserved.
+
+ This software is provided 'as-is', without any express or implied
+ warranty. In no event will the authors be held liable for any damages
+ arising from the use of this software.
+
+ Permission is granted to anyone to use this software for any purpose,
+ including commercial applications, and to alter it and redistribute it
+ freely, subject to the following restrictions:
+
+ 1. The origin of this software must not be misrepresented; you must not
+ claim that you wrote the original software. If you use this software
+ in a product, an acknowledgment in the product documentation would be
+ appreciated but is not required.
+ 2. Altered source versions must be plainly marked as such, and must not be
+ misrepresented as being the original software.
+ 3. This notice may not be removed or altered from any source distribution.
+
+ L. Peter Deutsch
+ ghost@aladdin.com
+
+ */
+/*
+ Independent implementation of MD5 (RFC 1321).
+
+ This code implements the MD5 Algorithm defined in RFC 1321, whose
+ text is available at
+ http://www.ietf.org/rfc/rfc1321.txt
+ The code is derived from the text of the RFC, including the test suite
+ (section A.5) but excluding the rest of Appendix A. It does not include
+ any code or documentation that is identified in the RFC as being
+ copyrighted.
+
+ The original and principal author of md5.c is L. Peter Deutsch
+ <ghost@aladdin.com>. Other authors are noted in the change history
+ that follows (in reverse chronological order):
+
+ 2002-04-13 lpd Clarified derivation from RFC 1321; now handles byte order
+ either statically or dynamically; added missing #include <string.h>
+ in library.
+ 2002-03-11 lpd Corrected argument list for main(), and added int return
+ type, in test program and T value program.
+ 2002-02-21 lpd Added missing #include <stdio.h> in test program.
+ 2000-07-03 lpd Patched to eliminate warnings about "constant is
+ unsigned in ANSI C, signed in traditional"; made test program
+ self-checking.
+ 1999-11-04 lpd Edited comments slightly for automatic TOC extraction.
+ 1999-10-18 lpd Fixed typo in header comment (ansi2knr rather than md5).
+ 1999-05-03 lpd Original version.
+ */
+
+/*
+ * This package supports both compile-time and run-time determination of CPU
+ * byte order. If ARCH_IS_BIG_ENDIAN is defined as 0, the code will be
+ * compiled to run only on little-endian CPUs; if ARCH_IS_BIG_ENDIAN is
+ * defined as non-zero, the code will be compiled to run only on big-endian
+ * CPUs; if ARCH_IS_BIG_ENDIAN is not defined, the code will be compiled to
+ * run on either big- or little-endian CPUs, but will run slightly less
+ * efficiently on either one than if ARCH_IS_BIG_ENDIAN is defined.
+ */
+
+
+#if defined(G3D_LINUX) || defined(G3D_OSX)
+# if defined(G3D_OSX_PPC)
+# include <ppc/endian.h>
+# elif defined(G3D_OSX_INTEL)
+# include <i386/endian.h>
+# elif defined(__linux__)
+# include <endian.h>
+# elif defined(__FreeBSD__)
+# include <sys/endian.h>
+# endif
+#else
+# define BYTE_ORDER 0
+#endif
+
+#define T_MASK ((md5_word_t)~0)
+#define T1 /* 0xd76aa478 */ (T_MASK ^ 0x28955b87)
+#define T2 /* 0xe8c7b756 */ (T_MASK ^ 0x173848a9)
+#define T3 0x242070db
+#define T4 /* 0xc1bdceee */ (T_MASK ^ 0x3e423111)
+#define T5 /* 0xf57c0faf */ (T_MASK ^ 0x0a83f050)
+#define T6 0x4787c62a
+#define T7 /* 0xa8304613 */ (T_MASK ^ 0x57cfb9ec)
+#define T8 /* 0xfd469501 */ (T_MASK ^ 0x02b96afe)
+#define T9 0x698098d8
+#define T10 /* 0x8b44f7af */ (T_MASK ^ 0x74bb0850)
+#define T11 /* 0xffff5bb1 */ (T_MASK ^ 0x0000a44e)
+#define T12 /* 0x895cd7be */ (T_MASK ^ 0x76a32841)
+#define T13 0x6b901122
+#define T14 /* 0xfd987193 */ (T_MASK ^ 0x02678e6c)
+#define T15 /* 0xa679438e */ (T_MASK ^ 0x5986bc71)
+#define T16 0x49b40821
+#define T17 /* 0xf61e2562 */ (T_MASK ^ 0x09e1da9d)
+#define T18 /* 0xc040b340 */ (T_MASK ^ 0x3fbf4cbf)
+#define T19 0x265e5a51
+#define T20 /* 0xe9b6c7aa */ (T_MASK ^ 0x16493855)
+#define T21 /* 0xd62f105d */ (T_MASK ^ 0x29d0efa2)
+#define T22 0x02441453
+#define T23 /* 0xd8a1e681 */ (T_MASK ^ 0x275e197e)
+#define T24 /* 0xe7d3fbc8 */ (T_MASK ^ 0x182c0437)
+#define T25 0x21e1cde6
+#define T26 /* 0xc33707d6 */ (T_MASK ^ 0x3cc8f829)
+#define T27 /* 0xf4d50d87 */ (T_MASK ^ 0x0b2af278)
+#define T28 0x455a14ed
+#define T29 /* 0xa9e3e905 */ (T_MASK ^ 0x561c16fa)
+#define T30 /* 0xfcefa3f8 */ (T_MASK ^ 0x03105c07)
+#define T31 0x676f02d9
+#define T32 /* 0x8d2a4c8a */ (T_MASK ^ 0x72d5b375)
+#define T33 /* 0xfffa3942 */ (T_MASK ^ 0x0005c6bd)
+#define T34 /* 0x8771f681 */ (T_MASK ^ 0x788e097e)
+#define T35 0x6d9d6122
+#define T36 /* 0xfde5380c */ (T_MASK ^ 0x021ac7f3)
+#define T37 /* 0xa4beea44 */ (T_MASK ^ 0x5b4115bb)
+#define T38 0x4bdecfa9
+#define T39 /* 0xf6bb4b60 */ (T_MASK ^ 0x0944b49f)
+#define T40 /* 0xbebfbc70 */ (T_MASK ^ 0x4140438f)
+#define T41 0x289b7ec6
+#define T42 /* 0xeaa127fa */ (T_MASK ^ 0x155ed805)
+#define T43 /* 0xd4ef3085 */ (T_MASK ^ 0x2b10cf7a)
+#define T44 0x04881d05
+#define T45 /* 0xd9d4d039 */ (T_MASK ^ 0x262b2fc6)
+#define T46 /* 0xe6db99e5 */ (T_MASK ^ 0x1924661a)
+#define T47 0x1fa27cf8
+#define T48 /* 0xc4ac5665 */ (T_MASK ^ 0x3b53a99a)
+#define T49 /* 0xf4292244 */ (T_MASK ^ 0x0bd6ddbb)
+#define T50 0x432aff97
+#define T51 /* 0xab9423a7 */ (T_MASK ^ 0x546bdc58)
+#define T52 /* 0xfc93a039 */ (T_MASK ^ 0x036c5fc6)
+#define T53 0x655b59c3
+#define T54 /* 0x8f0ccc92 */ (T_MASK ^ 0x70f3336d)
+#define T55 /* 0xffeff47d */ (T_MASK ^ 0x00100b82)
+#define T56 /* 0x85845dd1 */ (T_MASK ^ 0x7a7ba22e)
+#define T57 0x6fa87e4f
+#define T58 /* 0xfe2ce6e0 */ (T_MASK ^ 0x01d3191f)
+#define T59 /* 0xa3014314 */ (T_MASK ^ 0x5cfebceb)
+#define T60 0x4e0811a1
+#define T61 /* 0xf7537e82 */ (T_MASK ^ 0x08ac817d)
+#define T62 /* 0xbd3af235 */ (T_MASK ^ 0x42c50dca)
+#define T63 0x2ad7d2bb
+#define T64 /* 0xeb86d391 */ (T_MASK ^ 0x14792c6e)
+
+
+static void
+md5_process(md5_state_t *pms, const md5_byte_t *data /*[64]*/)
+{
+ md5_word_t
+ a = pms->abcd[0], b = pms->abcd[1],
+ c = pms->abcd[2], d = pms->abcd[3];
+ md5_word_t t;
+#if BYTE_ORDER > 0
+ /* Define storage only for big-endian CPUs. */
+ md5_word_t X[16];
+#else
+ /* Define storage for little-endian or both types of CPUs. */
+ md5_word_t xbuf[16];
+ const md5_word_t *X;
+#endif
+
+ {
+#if BYTE_ORDER == 0
+ /*
+ * Determine dynamically whether this is a big-endian or
+ * little-endian machine, since we can use a more efficient
+ * algorithm on the latter.
+ */
+ static const int w = 1;
+
+ if (*((const md5_byte_t *)&w)) /* dynamic little-endian */
+#endif
+#if BYTE_ORDER <= 0 /* little-endian */
+ {
+ /*
+ * On little-endian machines, we can process properly aligned
+ * data without copying it.
+ */
+ if (!((data - (const md5_byte_t *)0) & 3)) {
+ /* data are properly aligned */
+ X = (const md5_word_t *)data;
+ } else {
+ /* not aligned */
+ memcpy(xbuf, data, 64);
+ X = xbuf;
+ }
+ }
+#endif
+#if BYTE_ORDER == 0
+ else /* dynamic big-endian */
+#endif
+#if BYTE_ORDER >= 0 /* big-endian */
+ {
+ /*
+ * On big-endian machines, we must arrange the bytes in the
+ * right order.
+ */
+ const md5_byte_t *xp = data;
+ int i;
+
+# if BYTE_ORDER == 0
+ X = xbuf; /* (dynamic only) */
+# else
+# define xbuf X /* (static only) */
+# endif
+ for (i = 0; i < 16; ++i, xp += 4)
+ xbuf[i] = xp[0] + (xp[1] << 8) + (xp[2] << 16) + (xp[3] << 24);
+ }
+#endif
+ }
+
+#define ROTATE_LEFT(x, n) (((x) << (n)) | ((x) >> (32 - (n))))
+
+ /* Round 1. */
+ /* Let [abcd k s i] denote the operation
+ a = b + ((a + F(b,c,d) + X[k] + T[i]) <<< s). */
+#define F(x, y, z) (((x) & (y)) | (~(x) & (z)))
+#define SET(a, b, c, d, k, s, Ti)\
+ t = a + F(b,c,d) + X[k] + Ti;\
+ a = ROTATE_LEFT(t, s) + b
+ /* Do the following 16 operations. */
+ SET(a, b, c, d, 0, 7, T1);
+ SET(d, a, b, c, 1, 12, T2);
+ SET(c, d, a, b, 2, 17, T3);
+ SET(b, c, d, a, 3, 22, T4);
+ SET(a, b, c, d, 4, 7, T5);
+ SET(d, a, b, c, 5, 12, T6);
+ SET(c, d, a, b, 6, 17, T7);
+ SET(b, c, d, a, 7, 22, T8);
+ SET(a, b, c, d, 8, 7, T9);
+ SET(d, a, b, c, 9, 12, T10);
+ SET(c, d, a, b, 10, 17, T11);
+ SET(b, c, d, a, 11, 22, T12);
+ SET(a, b, c, d, 12, 7, T13);
+ SET(d, a, b, c, 13, 12, T14);
+ SET(c, d, a, b, 14, 17, T15);
+ SET(b, c, d, a, 15, 22, T16);
+#undef SET
+
+ /* Round 2. */
+ /* Let [abcd k s i] denote the operation
+ a = b + ((a + G(b,c,d) + X[k] + T[i]) <<< s). */
+#define G(x, y, z) (((x) & (z)) | ((y) & ~(z)))
+#define SET(a, b, c, d, k, s, Ti)\
+ t = a + G(b,c,d) + X[k] + Ti;\
+ a = ROTATE_LEFT(t, s) + b
+ /* Do the following 16 operations. */
+ SET(a, b, c, d, 1, 5, T17);
+ SET(d, a, b, c, 6, 9, T18);
+ SET(c, d, a, b, 11, 14, T19);
+ SET(b, c, d, a, 0, 20, T20);
+ SET(a, b, c, d, 5, 5, T21);
+ SET(d, a, b, c, 10, 9, T22);
+ SET(c, d, a, b, 15, 14, T23);
+ SET(b, c, d, a, 4, 20, T24);
+ SET(a, b, c, d, 9, 5, T25);
+ SET(d, a, b, c, 14, 9, T26);
+ SET(c, d, a, b, 3, 14, T27);
+ SET(b, c, d, a, 8, 20, T28);
+ SET(a, b, c, d, 13, 5, T29);
+ SET(d, a, b, c, 2, 9, T30);
+ SET(c, d, a, b, 7, 14, T31);
+ SET(b, c, d, a, 12, 20, T32);
+#undef SET
+
+ /* Round 3. */
+ /* Let [abcd k s t] denote the operation
+ a = b + ((a + H(b,c,d) + X[k] + T[i]) <<< s). */
+#define H(x, y, z) ((x) ^ (y) ^ (z))
+#define SET(a, b, c, d, k, s, Ti)\
+ t = a + H(b,c,d) + X[k] + Ti;\
+ a = ROTATE_LEFT(t, s) + b
+ /* Do the following 16 operations. */
+ SET(a, b, c, d, 5, 4, T33);
+ SET(d, a, b, c, 8, 11, T34);
+ SET(c, d, a, b, 11, 16, T35);
+ SET(b, c, d, a, 14, 23, T36);
+ SET(a, b, c, d, 1, 4, T37);
+ SET(d, a, b, c, 4, 11, T38);
+ SET(c, d, a, b, 7, 16, T39);
+ SET(b, c, d, a, 10, 23, T40);
+ SET(a, b, c, d, 13, 4, T41);
+ SET(d, a, b, c, 0, 11, T42);
+ SET(c, d, a, b, 3, 16, T43);
+ SET(b, c, d, a, 6, 23, T44);
+ SET(a, b, c, d, 9, 4, T45);
+ SET(d, a, b, c, 12, 11, T46);
+ SET(c, d, a, b, 15, 16, T47);
+ SET(b, c, d, a, 2, 23, T48);
+#undef SET
+
+ /* Round 4. */
+ /* Let [abcd k s t] denote the operation
+ a = b + ((a + I(b,c,d) + X[k] + T[i]) <<< s). */
+#define I(x, y, z) ((y) ^ ((x) | ~(z)))
+#define SET(a, b, c, d, k, s, Ti)\
+ t = a + I(b,c,d) + X[k] + Ti;\
+ a = ROTATE_LEFT(t, s) + b
+ /* Do the following 16 operations. */
+ SET(a, b, c, d, 0, 6, T49);
+ SET(d, a, b, c, 7, 10, T50);
+ SET(c, d, a, b, 14, 15, T51);
+ SET(b, c, d, a, 5, 21, T52);
+ SET(a, b, c, d, 12, 6, T53);
+ SET(d, a, b, c, 3, 10, T54);
+ SET(c, d, a, b, 10, 15, T55);
+ SET(b, c, d, a, 1, 21, T56);
+ SET(a, b, c, d, 8, 6, T57);
+ SET(d, a, b, c, 15, 10, T58);
+ SET(c, d, a, b, 6, 15, T59);
+ SET(b, c, d, a, 13, 21, T60);
+ SET(a, b, c, d, 4, 6, T61);
+ SET(d, a, b, c, 11, 10, T62);
+ SET(c, d, a, b, 2, 15, T63);
+ SET(b, c, d, a, 9, 21, T64);
+#undef SET
+
+ /* Then perform the following additions. (That is increment each
+ of the four registers by the value it had before this block
+ was started.) */
+ pms->abcd[0] += a;
+ pms->abcd[1] += b;
+ pms->abcd[2] += c;
+ pms->abcd[3] += d;
+}
+
+void
+md5_init(md5_state_t *pms)
+{
+ pms->count[0] = pms->count[1] = 0;
+ pms->abcd[0] = 0x67452301;
+ pms->abcd[1] = /*0xefcdab89*/ T_MASK ^ 0x10325476;
+ pms->abcd[2] = /*0x98badcfe*/ T_MASK ^ 0x67452301;
+ pms->abcd[3] = 0x10325476;
+}
+
+void
+md5_append(md5_state_t *pms, const md5_byte_t *data, int nbytes)
+{
+ const md5_byte_t *p = data;
+ int left = nbytes;
+ int offset = (pms->count[0] >> 3) & 63;
+ md5_word_t nbits = (md5_word_t)(nbytes << 3);
+
+ if (nbytes <= 0)
+ return;
+
+ /* Update the message length. */
+ pms->count[1] += nbytes >> 29;
+ pms->count[0] += nbits;
+ if (pms->count[0] < nbits)
+ pms->count[1]++;
+
+ /* Process an initial partial block. */
+ if (offset) {
+ int copy = (offset + nbytes > 64 ? 64 - offset : nbytes);
+
+ memcpy(pms->buf + offset, p, copy);
+ if (offset + copy < 64)
+ return;
+ p += copy;
+ left -= copy;
+ md5_process(pms, pms->buf);
+ }
+
+ /* Process full blocks. */
+ for (; left >= 64; p += 64, left -= 64)
+ md5_process(pms, p);
+
+ /* Process a final partial block. */
+ if (left)
+ memcpy(pms->buf, p, left);
+}
+
+void
+md5_finish(md5_state_t *pms, md5_byte_t digest[16])
+{
+ static const md5_byte_t pad[64] = {
+ 0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
+ };
+ md5_byte_t data[8];
+ int i;
+
+ /* Save the length before padding. */
+ for (i = 0; i < 8; ++i)
+ data[i] = (md5_byte_t)(pms->count[i >> 2] >> ((i & 3) << 3));
+ /* Pad to 56 bytes mod 64. */
+ md5_append(pms, pad, ((55 - (pms->count[0] >> 3)) & 63) + 1);
+ /* Append the length. */
+ md5_append(pms, data, 8);
+ for (i = 0; i < 16; ++i)
+ digest[i] = (md5_byte_t)(pms->abcd[i >> 2] >> ((i & 3) << 3));
+}
+
+}
diff --git a/dep/src/g3dlite/Cylinder.cpp b/dep/src/g3dlite/Cylinder.cpp
new file mode 100644
index 00000000000..7a7b9f9440d
--- /dev/null
+++ b/dep/src/g3dlite/Cylinder.cpp
@@ -0,0 +1,176 @@
+/**
+ @file Cylinder.cpp
+
+ @maintainer Morgan McGuire, http://graphics.cs.williams.edu
+
+ @created 2003-02-07
+ @edited 2006-02-18
+
+ Copyright 2000-2006, Morgan McGuire.
+ All rights reserved.
+ */
+
+#include "G3D/platform.h"
+#include "G3D/Cylinder.h"
+#include "G3D/BinaryInput.h"
+#include "G3D/BinaryOutput.h"
+#include "G3D/LineSegment.h"
+#include "G3D/CoordinateFrame.h"
+#include "G3D/Line.h"
+#include "G3D/AABox.h"
+
+namespace G3D {
+
+Cylinder::Cylinder(class BinaryInput& b) {
+ deserialize(b);
+}
+
+
+Cylinder::Cylinder() {
+}
+
+
+Cylinder::Cylinder(const Vector3& _p1, const Vector3& _p2, float _r)
+ : p1(_p1), p2(_p2), mRadius(_r) {
+}
+
+
+void Cylinder::serialize(class BinaryOutput& b) const {
+ p1.serialize(b);
+ p2.serialize(b);
+ b.writeFloat64(mRadius);
+}
+
+
+void Cylinder::deserialize(class BinaryInput& b) {
+ p1.deserialize(b);
+ p2.deserialize(b);
+ mRadius = b.readFloat64();
+}
+
+
+Line Cylinder::axis() const {
+ return Line::fromTwoPoints(p1, p2);
+}
+
+
+
+float Cylinder::radius() const {
+ return mRadius;
+}
+
+
+float Cylinder::volume() const {
+ return
+ (float)pi() * square(mRadius) * (p1 - p2).magnitude();
+}
+
+
+float Cylinder::area() const {
+ return
+ // Sides
+ (twoPi() * mRadius) * height() +
+
+ // Caps
+ twoPi() * square(mRadius);
+}
+
+void Cylinder::getBounds(AABox& out) const {
+ Vector3 min = p1.min(p2) - (Vector3(1, 1, 1) * mRadius);
+ Vector3 max = p1.max(p2) + (Vector3(1, 1, 1) * mRadius);
+ out = AABox(min, max);
+}
+
+bool Cylinder::contains(const Vector3& p) const {
+ return LineSegment::fromTwoPoints(p1, p2).distanceSquared(p) <= square(mRadius);
+}
+
+
+void Cylinder::getReferenceFrame(CoordinateFrame& cframe) const {
+ cframe.translation = center();
+
+ Vector3 Y = (p1 - p2).direction();
+ Vector3 X = (abs(Y.dot(Vector3::unitX())) > 0.9) ? Vector3::unitY() : Vector3::unitX();
+ Vector3 Z = X.cross(Y).direction();
+ X = Y.cross(Z);
+ cframe.rotation.setColumn(0, X);
+ cframe.rotation.setColumn(1, Y);
+ cframe.rotation.setColumn(2, Z);
+}
+
+
+void Cylinder::getRandomSurfacePoint(Vector3& p, Vector3& N) const {
+ float h = height();
+ float r = radius();
+
+ // Create a random point on a standard cylinder and then rotate to the global frame.
+
+ // Relative areas (factor of 2PI already taken out)
+ float capRelArea = square(r) / 2.0f;
+ float sideRelArea = r * h;
+
+ float r1 = uniformRandom(0, capRelArea * 2 + sideRelArea);
+
+ if (r1 < capRelArea * 2) {
+
+ // Select a point uniformly at random on a disk
+ // @cite http://mathworld.wolfram.com/DiskPointPicking.html
+ float a = uniformRandom(0, (float)twoPi());
+ float r2 = sqrt(uniformRandom(0, 1)) * r;
+ p.x = cos(a) * r2;
+ p.z = sin(a) * r2;
+
+ N.x = 0;
+ N.z = 0;
+ if (r1 < capRelArea) {
+ // Top
+ p.y = h / 2.0f;
+ N.y = 1;
+ } else {
+ // Bottom
+ p.y = -h / 2.0f;
+ N.y = -1;
+ }
+ } else {
+ // Side
+ float a = uniformRandom(0, (float)twoPi());
+ N.x = cos(a);
+ N.y = 0;
+ N.z = sin(a);
+ p.x = N.x * r;
+ p.z = N.y * r;
+ p.y = uniformRandom(-h / 2.0f, h / 2.0f);
+ }
+
+ // Transform to world space
+ CoordinateFrame cframe;
+ getReferenceFrame(cframe);
+
+ p = cframe.pointToWorldSpace(p);
+ N = cframe.normalToWorldSpace(N);
+}
+
+
+Vector3 Cylinder::randomInteriorPoint() const {
+ float h = height();
+ float r = radius();
+
+ // Create a random point in a standard cylinder and then rotate to the global frame.
+
+ // Select a point uniformly at random on a disk
+ // @cite http://mathworld.wolfram.com/DiskPointPicking.html
+ float a = uniformRandom(0, (float)twoPi());
+ float r2 = sqrt(uniformRandom(0, 1)) * r;
+
+ Vector3 p( cos(a) * r2,
+ uniformRandom(-h / 2.0f, h / 2.0f),
+ sin(a) * r2);
+
+ // Transform to world space
+ CoordinateFrame cframe;
+ getReferenceFrame(cframe);
+
+ return cframe.pointToWorldSpace(p);
+}
+
+} // namespace
diff --git a/dep/src/g3dlite/GCamera.cpp b/dep/src/g3dlite/GCamera.cpp
new file mode 100644
index 00000000000..64b0c94543e
--- /dev/null
+++ b/dep/src/g3dlite/GCamera.cpp
@@ -0,0 +1,502 @@
+/**
+ @file GCamera.cpp
+
+ @author Morgan McGuire, http://graphics.cs.williams.edu
+ @author Jeff Marsceill, 08jcm@williams.edu
+
+ @created 2005-07-20
+ @edited 2009-11-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"
+#include "G3D/Any.h"
+#include "G3D/stringutils.h"
+
+namespace G3D {
+
+GCamera::GCamera(const Any& any) {
+ any.verifyName("GCamera");
+ any.verifyType(Any::TABLE);
+ *this = GCamera();
+
+ const Any::AnyTable& table = any.table();
+ Any::AnyTable::Iterator it = table.begin();
+ while (it.hasMore()) {
+ const std::string& k = toUpper(it->key);
+ if (k == "FOVDIRECTION") {
+ const std::string& v = toUpper(it->value);
+ if (v == "HORIZONTAL") {
+ m_direction = HORIZONTAL;
+ } else if (v == "VERTICAL") {
+ m_direction = VERTICAL;
+ } else {
+ any.verify(false, "fovDirection must be \"HORIZONTAL\" or \"VERTICAL\"");
+ }
+ } else if (k == "COORDINATEFRAME") {
+ m_cframe = it->value;
+ } else if (k == "FOVDEGREES") {
+ m_fieldOfView = toRadians(it->value.number());
+ } else if (k == "NEARPLANEZ") {
+ m_nearPlaneZ = it->value;
+ } else if (k == "FARPLANEZ") {
+ m_farPlaneZ = it->value;
+ } else {
+ any.verify(false, std::string("Illegal key in table: ") + it->key);
+ }
+ ++it;
+ }
+}
+
+
+GCamera::operator Any() const {
+ Any any(Any::TABLE, "GCamera");
+
+ any.set("fovDirection", std::string((m_direction == HORIZONTAL) ? "HORIZONTAL" : "VERTICAL"));
+ any.set("fovDegrees", toDegrees(m_fieldOfView));
+ any.set("nearPlaneZ", nearPlaneZ());
+ any.set("farPlaneZ", farPlaneZ());
+ any.set("coordinateFrame", coordinateFrame());
+
+ return any;
+}
+
+
+GCamera::GCamera() {
+ setNearPlaneZ(-0.2f);
+ setFarPlaneZ(-100.0f);
+ setFieldOfView((float)toRadians(90.0f), HORIZONTAL);
+}
+
+
+GCamera::GCamera(const Matrix4& proj, const CFrame& frame) {
+ float left, right, bottom, top, nearval, farval;
+ proj.getPerspectiveProjectionParameters(left, right, bottom, top, nearval, farval);
+ setNearPlaneZ(-nearval);
+ setFarPlaneZ(-farval);
+ float x = right;
+
+ // Assume horizontal field of view
+ setFieldOfView(atan2(x, -m_nearPlaneZ) * 2.0f, HORIZONTAL);
+ setCoordinateFrame(frame);
+}
+
+
+GCamera::~GCamera() {
+}
+
+
+void GCamera::getCoordinateFrame(CoordinateFrame& c) const {
+ c = m_cframe;
+}
+
+
+void GCamera::setCoordinateFrame(const CoordinateFrame& c) {
+ m_cframe = c;
+}
+
+
+void GCamera::setFieldOfView(float angle, FOVDirection dir) {
+ debugAssert((angle < pi()) && (angle > 0));
+
+ m_fieldOfView = angle;
+ m_direction = dir;
+}
+
+
+float GCamera::imagePlaneDepth() const{
+ return -m_nearPlaneZ;
+}
+
+float GCamera::viewportWidth(const Rect2D& viewport) const {
+ // Compute the side of a square at the near plane based on our field of view
+ float s = 2.0f * -m_nearPlaneZ * tan(m_fieldOfView * 0.5f);
+
+ if (m_direction == VERTICAL) {
+ s *= viewport.width() / viewport.height();
+ }
+
+ return s;
+}
+
+
+float GCamera::viewportHeight(const Rect2D& viewport) const {
+ // Compute the side of a square at the near plane based on our field of view
+ float s = 2.0f * -m_nearPlaneZ * tan(m_fieldOfView * 0.5f);
+
+ debugAssert(m_fieldOfView < toRadians(180));
+ if (m_direction == HORIZONTAL) {
+ s *= viewport.height() / viewport.width();
+ }
+
+ return s;
+}
+
+
+Ray GCamera::worldRay(float x, float y, const Rect2D& viewport) const {
+
+ int screenWidth = iFloor(viewport.width());
+ int screenHeight = iFloor(viewport.height());
+
+ Vector3 origin = m_cframe.translation;
+
+ float cx = screenWidth / 2.0f;
+ float cy = screenHeight / 2.0f;
+
+ float vw = viewportWidth(viewport);
+ float vh = viewportHeight(viewport);
+
+ Vector3 direction = Vector3( (x - cx) * vw / screenWidth,
+ -(y - cy) * vh / screenHeight,
+ m_nearPlaneZ);
+
+ direction = m_cframe.vectorToWorldSpace(direction);
+
+ // Normalize the direction (we didn't do it before)
+ direction = direction.direction();
+
+ return Ray::fromOriginAndDirection(origin, direction);
+}
+
+
+void GCamera::getProjectPixelMatrix(const Rect2D& viewport, Matrix4& P) const {
+ getProjectUnitMatrix(viewport, P);
+ float screenWidth = viewport.width();
+ float screenHeight = viewport.height();
+
+ float sx = screenWidth / 2.0;
+ float sy = screenHeight / 2.0;
+
+ P = Matrix4(sx, 0, 0, sx + viewport.x0(),
+ 0, -sy, 0, sy + viewport.y0(),
+ 0, 0, 1, 0,
+ 0, 0, 0, 1) * P;
+}
+
+
+void GCamera::getProjectUnitMatrix(const Rect2D& viewport, Matrix4& P) const {
+
+ float screenWidth = viewport.width();
+ float screenHeight = viewport.height();
+
+ float r, l, t, b, n, f, x, y;
+
+ 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 >= finf()) {
+ // Infinite far plane
+ z = 1.0f / (((-1.0f / n) * v.z) + 1.0f / n);
+ } else {
+ z = 1.0f / ((((1.0f / f) - (1.0f / n)) * v.z) + 1.0f / n);
+ }
+
+ const Ray& ray = worldRay(v.x, 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 {
+ (void)viewport;
+ if (z >= 0) {
+ return finf();
+ }
+ return area * (float)square(imagePlaneDepth() / z);
+}
+
+
+void GCamera::getClipPlanes(
+ const Rect2D& viewport,
+ Array<Plane>& clip) const {
+
+ Frustum fr;
+ frustum(viewport, fr);
+ clip.resize(fr.faceArray.size(), DONT_SHRINK_UNDERLYING_ARRAY);
+ for (int f = 0; f < clip.size(); ++f) {
+ clip[f] = fr.faceArray[f].plane;
+ }
+}
+
+
+GCamera::Frustum GCamera::frustum(const Rect2D& viewport) const {
+ Frustum f;
+ frustum(viewport, f);
+ return f;
+}
+
+
+void GCamera::frustum(const Rect2D& viewport, Frustum& fr) const {
+
+ // The volume is the convex hull of the vertices definining the view
+ // frustum and the light source point at infinity.
+
+ const float x = viewportWidth(viewport) / 2;
+ const float y = viewportHeight(viewport) / 2;
+ const float zn = m_nearPlaneZ;
+ const float zf = m_farPlaneZ;
+ float xx, zz, yy;
+
+ float halfFOV = m_fieldOfView * 0.5f;
+
+ // This computes the normal, which is based on the complement of the
+ // halfFOV angle, so the equations are "backwards"
+ if (m_direction == VERTICAL) {
+ yy = -cosf(halfFOV);
+ xx = yy * viewport.height() / viewport.width();
+ zz = -sinf(halfFOV);
+ } else {
+ xx = -cosf(halfFOV);
+ yy = xx * viewport.width() / viewport.height();
+ zz = -sinf(halfFOV);
+ }
+
+ // Near face (ccw from UR)
+ fr.vertexPos.append(
+ Vector4( x, y, zn, 1),
+ Vector4(-x, y, zn, 1),
+ Vector4(-x, -y, zn, 1),
+ Vector4( x, -y, zn, 1));
+
+ // Far face (ccw from UR, from origin)
+ if (m_farPlaneZ == -finf()) {
+ fr.vertexPos.append(Vector4( x, y, zn, 0),
+ Vector4(-x, y, zn, 0),
+ Vector4(-x, -y, zn, 0),
+ Vector4( x, -y, zn, 0));
+ } else {
+ // Finite
+ const float s = zf / zn;
+ fr.vertexPos.append(Vector4( x * s, y * s, zf, 1),
+ Vector4(-x * s, y * s, zf, 1),
+ Vector4(-x * s, -y * s, zf, 1),
+ Vector4( x * s, -y * s, zf, 1));
+ }
+
+ Frustum::Face face;
+
+ // Near plane (wind backwards so normal faces into frustum)
+ // Recall that nearPlane, farPlane are positive numbers, so
+ // we need to negate them to produce actual z values.
+ face.plane = Plane(Vector3(0,0,-1), Vector3(0,0,m_nearPlaneZ));
+ face.vertexIndex[0] = 3;
+ face.vertexIndex[1] = 2;
+ face.vertexIndex[2] = 1;
+ face.vertexIndex[3] = 0;
+ fr.faceArray.append(face);
+
+ // Right plane
+ face.plane = Plane(Vector3(xx, 0, zz), Vector3::zero());
+ face.vertexIndex[0] = 0;
+ face.vertexIndex[1] = 4;
+ face.vertexIndex[2] = 7;
+ face.vertexIndex[3] = 3;
+ fr.faceArray.append(face);
+
+ // Left plane
+ face.plane = Plane(Vector3(-fr.faceArray.last().plane.normal().x, 0, fr.faceArray.last().plane.normal().z), Vector3::zero());
+ face.vertexIndex[0] = 5;
+ face.vertexIndex[1] = 1;
+ face.vertexIndex[2] = 2;
+ face.vertexIndex[3] = 6;
+ fr.faceArray.append(face);
+
+ // Top plane
+ face.plane = Plane(Vector3(0, yy, zz), Vector3::zero());
+ face.vertexIndex[0] = 1;
+ face.vertexIndex[1] = 5;
+ face.vertexIndex[2] = 4;
+ face.vertexIndex[3] = 0;
+ fr.faceArray.append(face);
+
+ // Bottom plane
+ face.plane = Plane(Vector3(0, -fr.faceArray.last().plane.normal().y, fr.faceArray.last().plane.normal().z), Vector3::zero());
+ face.vertexIndex[0] = 2;
+ face.vertexIndex[1] = 3;
+ face.vertexIndex[2] = 7;
+ face.vertexIndex[3] = 6;
+ fr.faceArray.append(face);
+
+ // Far plane
+ if (-m_farPlaneZ < finf()) {
+ face.plane = Plane(Vector3(0, 0, 1), Vector3(0, 0, m_farPlaneZ));
+ face.vertexIndex[0] = 4;
+ face.vertexIndex[1] = 5;
+ face.vertexIndex[2] = 6;
+ face.vertexIndex[3] = 7;
+ fr.faceArray.append(face);
+ }
+
+ // Transform vertices to world space
+ for (int v = 0; v < fr.vertexPos.size(); ++v) {
+ fr.vertexPos[v] = m_cframe.toWorldSpace(fr.vertexPos[v]);
+ }
+
+ // Transform planes to world space
+ for (int p = 0; p < fr.faceArray.size(); ++p) {
+ // Since there is no scale factor, we don't have to
+ // worry about the inverse transpose of the normal.
+ Vector3 normal;
+ float d;
+
+ fr.faceArray[p].plane.getEquation(normal, d);
+
+ Vector3 newNormal = m_cframe.rotation * normal;
+
+ if (isFinite(d)) {
+ d = (newNormal * -d + m_cframe.translation).dot(newNormal);
+ fr.faceArray[p].plane = Plane(newNormal, newNormal * d);
+ } else {
+ // When d is infinite, we can't multiply 0's by it without
+ // generating NaNs.
+ fr.faceArray[p].plane = Plane::fromEquation(newNormal.x, newNormal.y, newNormal.z, d);
+ }
+ }
+}
+
+void GCamera::getNearViewportCorners
+(const Rect2D& viewport,
+ Vector3& outUR,
+ Vector3& outUL,
+ Vector3& outLL,
+ Vector3& outLR) const {
+
+ // Must be kept in sync with getFrustum()
+ const float w = viewportWidth(viewport) / 2.0f;
+ const float h = viewportHeight(viewport) / 2.0f;
+ const float z = nearPlaneZ();
+
+ // Compute the points
+ outUR = Vector3( w, h, z);
+ outUL = Vector3(-w, h, z);
+ outLL = Vector3(-w, -h, z);
+ outLR = Vector3( w, -h, z);
+
+ // Take to world space
+ outUR = m_cframe.pointToWorldSpace(outUR);
+ outUL = m_cframe.pointToWorldSpace(outUL);
+ outLR = m_cframe.pointToWorldSpace(outLR);
+ outLL = m_cframe.pointToWorldSpace(outLL);
+}
+
+void GCamera::getFarViewportCorners(
+ const Rect2D& viewport,
+ Vector3& outUR,
+ Vector3& outUL,
+ Vector3& outLL,
+ Vector3& outLR) const {
+
+ // Must be kept in sync with getFrustum()
+ const float w = viewportWidth(viewport) * m_farPlaneZ / m_nearPlaneZ;
+ const float h = viewportHeight(viewport) * m_farPlaneZ / m_nearPlaneZ;
+ const float z = m_farPlaneZ;
+
+ // Compute the points
+ outUR = Vector3( w/2, h/2, z);
+ outUL = Vector3(-w/2, h/2, z);
+ outLL = Vector3(-w/2, -h/2, z);
+ outLR = Vector3( w/2, -h/2, z);
+
+ // Take to world space
+ outUR = m_cframe.pointToWorldSpace(outUR);
+ outUL = m_cframe.pointToWorldSpace(outUL);
+ outLR = m_cframe.pointToWorldSpace(outLR);
+ outLL = m_cframe.pointToWorldSpace(outLL);
+}
+
+
+
+void GCamera::setPosition(const Vector3& t) {
+ m_cframe.translation = t;
+}
+
+
+void GCamera::lookAt(const Vector3& position, const Vector3& up) {
+ m_cframe.lookAt(position, up);
+}
+
+
+void GCamera::serialize(BinaryOutput& bo) const {
+ bo.writeFloat32(m_fieldOfView);
+ bo.writeFloat32(imagePlaneDepth());
+ debugAssert(nearPlaneZ() < 0.0f);
+ bo.writeFloat32(nearPlaneZ());
+ debugAssert(farPlaneZ() < 0.0f);
+ bo.writeFloat32(farPlaneZ());
+ m_cframe.serialize(bo);
+ bo.writeInt8(m_direction);
+}
+
+
+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/dep/src/g3dlite/GImage.cpp b/dep/src/g3dlite/GImage.cpp
new file mode 100644
index 00000000000..9527e96cf67
--- /dev/null
+++ b/dep/src/g3dlite/GImage.cpp
@@ -0,0 +1,1166 @@
+/**
+ \file GImage.cpp
+ \author Morgan McGuire, http://graphics.cs.williams.edu
+ Copyright 2002-2010, Morgan McGuire
+
+ \created 2002-05-27
+ \edited 2010-01-04
+ */
+#include "G3D/platform.h"
+#include "G3D/GImage.h"
+#include "G3D/debug.h"
+#include "G3D/stringutils.h"
+#include "G3D/TextInput.h"
+#include "G3D/TextOutput.h"
+#include "G3D/BinaryInput.h"
+#include "G3D/BinaryOutput.h"
+#include "G3D/Log.h"
+#include "G3D/fileutils.h"
+
+#ifdef G3D_LINUX
+# include <png.h>
+#else
+# include "png.h"
+#endif
+
+#include <sys/stat.h>
+#include <assert.h>
+#include <sys/types.h>
+
+//////////////////////////////////////////////////////////////////////////////////////////////
+
+namespace G3D {
+
+void GImage::LtoRGBA(
+ const uint8* in,
+ uint8* out,
+ int numPixels) {
+
+ for (int i = 0; i < numPixels; ++i) {
+ int v = in[i];
+ int i4 = i * 4;
+
+ out[i4 + 0] = v;
+ out[i4 + 1] = v;
+ out[i4 + 2] = v;
+ out[i4 + 3] = 255;
+ }
+}
+
+
+void GImage::LtoRGB(
+ const uint8* in,
+ uint8* out,
+ int numPixels) {
+
+ for (int i = 0; i < numPixels; ++i) {
+ int v = in[i];
+ int i3 = i * 3;
+
+ out[i3 + 0] = v;
+ out[i3 + 1] = v;
+ out[i3 + 2] = v;
+ }
+}
+
+
+void GImage::RGBtoRGBA(
+ const uint8* in,
+ uint8* out,
+ int numPixels) {
+
+ for (int i = 0; i < numPixels; ++i) {
+ int i3 = i * 3;
+ int i4 = i3 + i;
+
+ out[i4 + 0] = in[i3 + 0];
+ out[i4 + 1] = in[i3 + 1];
+ out[i4 + 2] = in[i3 + 2];
+ out[i4 + 3] = 255;
+ }
+}
+
+
+void GImage::RGBAtoRGB(
+ const uint8* in,
+ uint8* out,
+ int numPixels) {
+
+ for (int i = 0; i < numPixels; ++i) {
+ int i3 = i * 3;
+ int i4 = i3 + i;
+
+ out[i3 + 0] = in[i4 + 0];
+ out[i3 + 1] = in[i4 + 1];
+ out[i3 + 2] = in[i4 + 2];
+ }
+}
+
+
+void GImage::RGBtoBGRA(
+ const uint8* in,
+ uint8* out,
+ int numPixels) {
+
+ for (int i = 0; i < numPixels; ++i) {
+ int i3 = i * 3;
+ int i4 = i3 + i;
+
+ out[i4 + 2] = in[i3 + 0];
+ out[i4 + 1] = in[i3 + 1];
+ out[i4 + 0] = in[i3 + 2];
+ out[i4 + 3] = 255;
+ }
+}
+
+
+
+void GImage::RGBtoBGR(
+ const uint8* in,
+ uint8* out,
+ int numPixels) {
+
+ for (int i = 0; i < numPixels; ++i) {
+ int i3 = i * 3;
+
+ int r = in[i3 + 0];
+ int g = in[i3 + 1];
+ int b = in[i3 + 2];
+
+ out[i3 + 2] = r;
+ out[i3 + 1] = g;
+ out[i3 + 0] = b;
+ }
+}
+
+
+void GImage::RGBxRGBtoRGBA(
+ const uint8* colorRGB,
+ const uint8* alphaRGB,
+ uint8* out,
+ int numPixels) {
+
+ for (int i = numPixels - 1; i >= 0; --i) {
+ int i3 = i * 3;
+ int i4 = i3 + i;
+
+ out[i4 + 0] = colorRGB[i3 + 0];
+ out[i4 + 1] = colorRGB[i3 + 1];
+ out[i4 + 2] = colorRGB[i3 + 2];
+ out[i4 + 3] = alphaRGB[i3 + 0];
+ }
+}
+
+
+void GImage::RGBtoARGB(
+ const uint8* in,
+ uint8* out,
+ int numPixels) {
+
+ for (int i = 0; i < numPixels; ++i) {
+ int i3 = i * 3;
+ int i4 = i3 + i;
+
+ out[i4 + 0] = 255;
+ out[i4 + 1] = in[i3 + 0];
+ out[i4 + 2] = in[i3 + 1];
+ out[i4 + 3] = in[i3 + 2];
+ }
+}
+
+
+void GImage::flipRGBVertical(
+ const uint8* in,
+ uint8* out,
+ int width,
+ int height) {
+
+
+ // Allocate a temp row so the operation
+ // is still safe if in == out
+ uint8* temp = (uint8*)System::malloc(width * 3);
+ alwaysAssertM(temp != NULL, "Out of memory");
+
+ int oneRow = width * 3;
+ int N = height / 2;
+
+ // if height is an odd value, don't swap odd middle row
+ for (int i = 0; i < N; ++i) {
+ int topOff = i * oneRow;
+ int botOff = (height - i - 1) * oneRow;
+ System::memcpy(temp, in + topOff, oneRow);
+ System::memcpy(out + topOff, in + botOff, oneRow);
+ System::memcpy(out + botOff, temp, oneRow);
+ }
+
+ System::free(temp);
+}
+
+
+void GImage::flipRGBAVertical(
+ const uint8* in,
+ uint8* out,
+ int width,
+ int height) {
+
+
+ // Allocate a temp row so the operation
+ // is still safe if in == out
+ uint8* temp = (uint8*)System::malloc(width * 4);
+ alwaysAssertM(temp != NULL, "Out of memory");
+
+ int oneRow = width * 4;
+
+ // if height is an odd value, don't swap odd middle row
+ for (int i = 0; i < height / 2; ++i) {
+ int topOff = i * oneRow;
+ int botOff = (height - i - 1) * oneRow;
+ System::memcpy(temp, in + topOff, oneRow);
+ System::memcpy(out + topOff, in + botOff, oneRow);
+ System::memcpy(out + botOff, temp, oneRow);
+ }
+
+ System::free(temp);
+}
+
+////////////////////////////////////////////////////////////////////////////////////////
+
+void GImage::decode(
+ BinaryInput& input,
+ Format format) {
+
+ switch (format) {
+ case PPM_ASCII:
+ decodePPMASCII(input);
+ break;
+
+ case PPM_BINARY:
+ decodePPM(input);
+ break;
+
+ case PNG:
+ decodePNG(input);
+ break;
+
+ case JPEG:
+ decodeJPEG(input);
+ break;
+
+ case TGA:
+ decodeTGA(input);
+ break;
+
+ case BMP:
+ decodeBMP(input);
+ break;
+
+ case ICO:
+ decodeICO(input);
+ break;
+
+ case PCX:
+ decodePCX(input);
+ break;
+
+ default:
+ debugAssert(false);
+ }
+
+ debugAssert(m_width >= 0);
+ debugAssert(m_height >= 0);
+ debugAssert(m_channels == 1 || m_channels == 3 || m_channels == 4);
+ debugAssert(m_byte != NULL);
+}
+
+
+void GImage::decodePCX(
+ BinaryInput& input) {
+
+ uint8 manufacturer = input.readUInt8();
+ uint8 version = input.readUInt8();
+ uint8 encoding = input.readUInt8();
+ uint8 bitsPerPixel = input.readUInt8();
+
+ uint16 xmin = input.readUInt16();
+ uint16 ymin = input.readUInt16();
+ uint16 xmax = input.readUInt16();
+ uint16 ymax = input.readUInt16();
+
+ uint16 horizDPI = input.readUInt16();
+ uint16 vertDPI = input.readUInt16();
+
+ Color3uint8 colorMap[16];
+ input.readBytes(colorMap, 48);
+
+ input.skip(1);
+
+ uint8 planes = input.readUInt8();
+ uint16 bytesPerLine = input.readUInt16();
+ uint16 paletteType = input.readUInt16();
+ input.skip(4 + 54);
+
+ (void)bytesPerLine;
+
+ m_width = xmax - xmin + 1;
+ m_height = ymax - ymin + 1;
+ m_channels = 3;
+
+ if ((manufacturer != 0x0A) || (encoding != 0x01)) {
+ throw GImage::Error("PCX file is corrupted", input.getFilename());
+ }
+
+ (void)version;
+ (void)vertDPI;
+ (void)horizDPI;
+
+ if ((bitsPerPixel != 8) || ((planes != 1) && (planes != 3))) {
+ throw GImage::Error("Only 8-bit paletted and 24-bit PCX files supported.", input.getFilename());
+ }
+
+ // Prepare the pointer object for the pixel data
+ m_byte = (uint8*)m_memMan->alloc(m_width * m_height * 3);
+
+ if ((paletteType == 1) && (planes == 3)) {
+
+ Color3uint8* pixel = pixel3();
+
+ // Iterate over each scan line
+ for (int row = 0; row < m_height; ++row) {
+ // Read each scan line once per plane
+ for (int plane = 0; plane < planes; ++plane) {
+ int p = row * m_width;
+ int p1 = p + m_width;
+ while (p < p1) {
+ uint8 value = input.readUInt8();
+ int length = 1;
+
+ if (value >= 192) {
+ // This is the length, not the value. Mask off
+ // the two high bits and read the true index.
+ length = value & 0x3F;
+ value = input.readUInt8();
+ }
+
+ // Set the whole run
+ for (int i = length - 1; i >= 0; --i, ++p) {
+ debugAssert(p < m_width * m_height);
+ pixel[p][plane] = value;
+ }
+ }
+ }
+ }
+
+ } else if (planes == 1) {
+
+ Color3uint8 palette[256];
+
+ int imageBeginning = input.getPosition();
+ int paletteBeginning = input.getLength() - 769;
+
+ input.setPosition(paletteBeginning);
+
+ uint8 dummy = input.readUInt8();
+
+ if (dummy != 12) {
+ Log::common()->println("\n*********************");
+ Log::common()->printf("Warning: Corrupted PCX file (palette marker byte was missing) \"%s\"\nLoading anyway\n\n", input.getFilename().c_str());
+ }
+
+ input.readBytes(palette, sizeof(palette));
+ input.setPosition(imageBeginning);
+
+ Color3uint8* pixel = pixel3();
+
+ // The palette indices are run length encoded.
+ int p = 0;
+ while (p < m_width * m_height) {
+ uint8 index = input.readUInt8();
+ uint8 length = 1;
+
+ if (index >= 192) {
+ // This is the length, not the index. Mask off
+ // the two high bits and read the true index.
+ length = index & 0x3F;
+ index = input.readUInt8();
+ }
+
+ Color3uint8 color = palette[index];
+
+ // Set the whole run
+ for (int i = length - 1; i >= 0; --i, ++p) {
+ if (p > m_width * m_height) {
+ break;
+ }
+ pixel[p] = color;
+ }
+
+ }
+
+ } else {
+ throw GImage::Error("Unsupported PCX file type.", input.getFilename());
+ }
+}
+
+
+GImage::Format GImage::resolveFormat(const std::string& filename) {
+ BinaryInput b(filename, G3D_LITTLE_ENDIAN);
+ if (b.size() <= 0) {
+ throw Error("File not found.", filename);
+ }
+
+ return resolveFormat(filename, b.getCArray(), b.size(), AUTODETECT);
+}
+
+
+GImage::Format GImage::resolveFormat(
+ const std::string& filename,
+ const uint8* data,
+ int dataLen,
+ Format maybeFormat) {
+
+ // Return the provided format if it is specified.
+ if (maybeFormat != AUTODETECT) {
+ return maybeFormat;
+ }
+
+ std::string extension = toUpper(filenameExt(filename));
+
+ if ((extension == "PPM") || (extension == "PGM") || (extension == "PBM")) {
+ // There are two PPM formats (binary and ASCII); we handle them differently
+ if (dataLen > 3) {
+ if (!memcmp(data, "P6", 2) || !memcmp(data, "P5", 2)) {
+ return PPM_BINARY;
+ } else {
+ return PPM_ASCII;
+ }
+ }
+ }
+
+ Format tmp = stringToFormat(extension);
+ if ((tmp != AUTODETECT) && (tmp != UNKNOWN)) {
+ return tmp;
+ }
+
+ // Try and autodetect from the file itself by looking at the first
+ // character.
+
+ // We can't look at the character if it is null.
+ debugAssert(data != NULL);
+
+ if ((dataLen > 3) && (! memcmp(data, "P3", 2) || (! memcmp(data, "P2", 2)) || (! memcmp(data, "P1", 2)))) {
+ return PPM_ASCII;
+ }
+
+ if ((dataLen > 3) && (!memcmp(data, "P6", 2) ||!memcmp(data, "P5", 2))) {
+ return PPM_BINARY;
+ }
+
+ if (dataLen > 8) {
+ if (!png_sig_cmp((png_bytep)data, 0, 8)) {
+ return PNG;
+ }
+ }
+
+ if ((dataLen > 0) && (data[0] == 'B')) {
+ return BMP;
+ }
+
+ if (dataLen > 10) {
+ if ((dataLen > 11) && (data[0] == 0xFF) &&
+ (memcmp(&data[6], "JFIF", 4) == 0)) {
+ return JPEG;
+ }
+ }
+
+ if (dataLen > 40) {
+ if (memcmp(&data[dataLen - 18], "TRUEVISION-XFILE", 16) == 0) {
+ return TGA;
+ }
+ }
+
+ if ((dataLen > 4) && (data[0] == 0) && (data[1] == 0) && (data[2] == 0) && (data[3] == 1)) {
+ return ICO;
+ }
+
+ if ((dataLen > 0) && (data[0] == 10)) {
+ return PCX;
+ }
+
+ return UNKNOWN;
+}
+
+
+GImage::GImage(
+ const std::string& filename,
+ Format format,
+ const MemoryManager::Ref& m) :
+ m_memMan(m),
+ m_byte(NULL),
+ m_channels(0),
+ m_width(0),
+ m_height(0) {
+
+ load(filename, format);
+}
+
+
+void GImage::load(
+ const std::string& filename,
+ Format format) {
+
+ clear();
+
+ try {
+ BinaryInput b(filename, G3D_LITTLE_ENDIAN);
+ if (b.size() <= 0) {
+ throw Error("File not found.", filename);
+ }
+
+ alwaysAssertM(this != NULL, "Corrupt GImage");
+ decode(b, resolveFormat(filename, b.getCArray(), b.size(), format));
+ } catch (const std::string& error) {
+ throw Error(error, filename);
+ }
+}
+
+
+GImage::GImage(
+ const uint8* data,
+ int length,
+ Format format,
+ const MemoryManager::Ref& m) :
+ m_memMan(m),
+ m_byte(NULL),
+ m_channels(0),
+ m_width(0),
+ m_height(0) {
+
+ BinaryInput b(data, length, G3D_LITTLE_ENDIAN);
+ // It is safe to cast away the const because we
+ // know we don't corrupt the data.
+
+ decode(b, resolveFormat("", data, length, format));
+}
+
+
+GImage::GImage(
+ int width,
+ int height,
+ int channels,
+ const MemoryManager::Ref& mem) :
+ m_memMan(mem),
+ m_byte(0),
+ m_channels(0),
+ m_width(0),
+ m_height(0) {
+
+ resize(width, height, channels);
+}
+
+
+void GImage::resize(
+ int width,
+ int height,
+ int channels,
+ bool zero) {
+
+ debugAssert(width >= 0);
+ debugAssert(height >= 0);
+ debugAssert(channels >= 1);
+
+ clear();
+
+ m_width = width;
+ m_height = height;
+ m_channels = channels;
+ size_t sz = width * height * channels;
+
+ if (sz > 0) {
+ m_byte = (uint8*)m_memMan->alloc(sz);
+ if (zero) {
+ System::memset(m_byte, 0, sz);
+ }
+ debugAssert(isValidHeapPointer(m_byte));
+ }
+}
+
+
+void GImage::_copy(
+ const GImage& other) {
+
+ clear();
+
+ m_width = other.m_width;
+ m_height = other.m_height;
+ m_channels = other.m_channels;
+ int s = m_width * m_height * m_channels * sizeof(uint8);
+ m_byte = (uint8*)m_memMan->alloc(s);
+ debugAssert(isValidHeapPointer(m_byte));
+ memcpy(m_byte, other.m_byte, s);
+}
+
+
+void GImage::flipHorizontal() {
+ uint8 temp[4];
+ int rowBytes = m_width * m_channels;
+ for (int y = 0; y < m_height; ++y) {
+ uint8* row = m_byte + y * rowBytes;
+ for (int x = 0; x < m_width / 2; ++x) {
+ System::memcpy(temp, row + x * m_channels, m_channels);
+ System::memcpy(row + x * m_channels, row + (m_width - x - 1) * m_channels, m_channels);
+ System::memcpy(row + (m_width - x - 1) * m_channels, temp, m_channels);
+ }
+ }
+}
+
+
+void GImage::flipVertical() {
+ uint8* old = m_byte;
+ m_byte = (uint8*)m_memMan->alloc(m_width * m_height * m_channels);
+
+ // We could do this with only a single-row temp buffer, but then
+ // we'd have to copy twice as much data.
+ int rowBytes = m_width * m_channels;
+ for (int y = 0; y < m_height; ++y) {
+ System::memcpy(m_byte + y * rowBytes, old + (m_height - y - 1) * rowBytes, rowBytes);
+ }
+
+ m_memMan->free(old);
+}
+
+
+void GImage::rotate90CW(int numTimes) {
+
+ uint8* old = NULL;
+ numTimes = iWrap(numTimes, 4);
+ if (numTimes > 0) {
+ (uint8*)m_memMan->alloc(m_width * m_height * m_channels);
+ }
+ for (int j = 0; j < numTimes; ++j) {
+ {
+ uint8* temp = old;
+ uint8* old = m_byte;
+ m_byte = temp;
+ }
+
+ {
+ int temp = m_width;
+ m_width = m_height;
+ m_height = temp;
+ }
+
+ int rowBytes = m_width * m_channels;
+ for (int y = 0; y < m_height; ++y) {
+ for (int x = 0; x < m_width; ++x) {
+ uint8* dst = m_byte + x + y * rowBytes;
+ uint8* src = old + y + (m_height - x - 1) * rowBytes;
+ System::memcpy(dst, src, m_channels);
+ }
+ }
+ }
+ m_memMan->free(old);
+}
+
+
+
+GImage::GImage(
+ const GImage& other,
+ const MemoryManager::Ref& m) : m_memMan(m), m_byte(NULL) {
+
+ _copy(other);
+}
+
+
+GImage::~GImage() {
+ clear();
+}
+
+
+void GImage::clear() {
+ m_width = 0;
+ m_height = 0;
+ m_memMan->free(m_byte);
+ m_byte = NULL;
+}
+
+
+GImage& GImage::operator=(const GImage& other) {
+ _copy(other);
+ return *this;
+}
+
+
+bool GImage::copySubImage(
+ GImage & dest, const GImage & src,
+ int srcX, int srcY, int srcWidth, int srcHeight) {
+ if ((src.m_width < srcX + srcWidth) ||
+ (src.m_height < srcY + srcHeight) ||
+ (srcY < 0) ||
+ (srcX < 0)) {
+
+ return false;
+ }
+
+ dest.resize(srcWidth, srcHeight, src.m_channels);
+
+ bool ret;
+ ret = pasteSubImage(dest, src, 0, 0, srcX, srcY, srcWidth, srcHeight);
+ debugAssert(ret);
+
+ return true;
+}
+
+
+bool GImage::pasteSubImage(
+ GImage & dest, const GImage & src,
+ int destX, int destY,
+ int srcX, int srcY, int srcWidth, int srcHeight) {
+
+ if ((src.m_width < srcX + srcWidth) ||
+ (src.m_height < srcY + srcHeight) ||
+ (dest.m_width < destX + srcWidth) ||
+ (dest.m_height < destY + srcHeight) ||
+ (srcY < 0) ||
+ (srcX < 0) ||
+ (destY < 0) ||
+ (destX < 0) ||
+ (src.channels() != dest.channels())) {
+
+ return false;
+ }
+
+ for (int i = 0; i < srcHeight; i++) {
+ const uint8* srcRow = src.byte() +
+ ((i + srcY) * src.m_width + srcX) * src.channels();
+ uint8* destRow = dest.byte() +
+ ((i + destY) * dest.width() + destX) * dest.channels();
+ memcpy(destRow, srcRow, srcWidth * src.m_channels);
+ }
+
+ return true;
+}
+
+
+bool GImage::supportedFormat(
+ const std::string& format) {
+
+ return (stringToFormat(format) != UNKNOWN);
+}
+
+
+GImage::Format GImage::stringToFormat(
+ const std::string& format) {
+
+ std::string extension = toUpper(format);
+
+ if ((extension == "JPG") || (extension == "JPEG")) {
+ return JPEG;
+ } else if (extension == "TGA") {
+ return TGA;
+ } else if (extension == "BMP") {
+ return BMP;
+ } else if (extension == "PCX") {
+ return PCX;
+ } else if (extension == "ICO") {
+ return ICO;
+ } else if (extension == "PNG") {
+ return PNG;
+ } else {
+ return UNKNOWN;
+ }
+ // Don't put PPM here, since it has two versions
+}
+
+
+void GImage::save(
+ const std::string& filename,
+ Format format) const {
+
+ BinaryOutput b(filename, G3D_LITTLE_ENDIAN);
+ encode(resolveFormat(filename, NULL, 0, format), b);
+ b.commit(false);
+}
+
+
+void GImage::encode(
+ Format format,
+ uint8*& outData,
+ int& outLength) const {
+
+ BinaryOutput out;
+
+ encode(format, out);
+
+ outData = (uint8*)System::malloc(out.size());
+ debugAssert(outData);
+ outLength = out.size();
+
+ out.commit(outData);
+}
+
+
+void GImage::encode(
+ Format format,
+ BinaryOutput& out) const {
+
+ switch (format) {
+ case PPM_ASCII:
+ encodePPMASCII(out);
+ break;
+
+ case PPM_BINARY:
+ encodePPM(out);
+ break;
+
+ case PNG:
+ encodePNG(out);
+ break;
+
+ case JPEG:
+ encodeJPEG(out);
+ break;
+
+ case BMP:
+ encodeBMP(out);
+ break;
+
+ case TGA:
+ encodeTGA(out);
+ break;
+
+ default:
+ debugAssert(false);
+ }
+}
+
+
+void GImage::insertRedAsAlpha(const GImage& alpha, GImage& output) const {
+ debugAssert(alpha.width() == width());
+ debugAssert(alpha.height() == height());
+
+ // make sure output GImage is valid
+ if (output.width() != width() || output.height() != height() || output.channels() != 4) {
+ output.resize(width(), height(), 4);
+ }
+
+ int N = m_width * m_height;
+ for (int i = 0; i < N; ++i) {
+ output.byte()[i * 4 + 0] = byte()[i * m_channels + 0];
+ output.byte()[i * 4 + 1] = byte()[i * m_channels + 1];
+ output.byte()[i * 4 + 2] = byte()[i * m_channels + 2];
+ output.byte()[i * 4 + 3] = alpha.byte()[i * alpha.m_channels];
+ }
+}
+
+
+void GImage::stripAlpha(GImage& output) const {
+
+ if (output.m_width != m_width || output.m_height != m_height || output.m_channels != 3) {
+ output.resize(m_width, m_height, 3);
+ }
+
+ int N = m_width * m_height;
+ for (int i = 0; i < N; ++i) {
+ output.byte()[i * 3 + 0] = byte()[i * m_channels + 0];
+ output.byte()[i * 3 + 1] = byte()[i * m_channels + 1];
+ output.byte()[i * 3 + 2] = byte()[i * m_channels + 2];
+ }
+}
+
+
+int GImage::sizeInMemory() const {
+ return sizeof(GImage) + m_width * m_height * m_channels;
+}
+
+
+void GImage::computeNormalMap(
+ const GImage& bump,
+ GImage& normal,
+ const BumpMapPreprocess& preprocess) {
+ computeNormalMap(bump.m_width, bump.m_height, bump.m_channels,
+ bump.byte(), normal, preprocess);
+}
+
+void GImage::computeNormalMap(
+ int width,
+ int height,
+ int channels,
+ const uint8* src,
+ GImage& normal,
+ const BumpMapPreprocess& preprocess) {
+
+ float whiteHeightInPixels = preprocess.zExtentPixels;
+ bool lowPassBump = preprocess.lowPassFilter;
+ bool scaleHeightByNz = preprocess.scaleZByNz;
+
+ if (whiteHeightInPixels < 0.0f) {
+ // Default setting scales so that a gradient ramp
+ // over the whole image becomes a 45-degree angle
+
+ // Account for potentially non-square aspect ratios
+ whiteHeightInPixels = max(width, height) * -whiteHeightInPixels;
+ }
+
+ debugAssert(whiteHeightInPixels >= 0);
+
+ const int w = width;
+ const int h = height;
+ const int stride = channels;
+
+ normal.resize(w, h, 4);
+
+ const uint8* const B = src;
+ Color4uint8* const N = normal.pixel4();
+
+ // 1/s for the scale factor that each ELEVATION should be multiplied by.
+ // We avoid actually multiplying by this and instead just divide it out of z.
+ float elevationInvScale = 255.0f / whiteHeightInPixels;
+
+ for (int y = 0; y < h; ++y) {
+ for (int x = 0; x < w; ++x) {
+ // Index into normal map pixel
+ int i = x + y * w;
+
+ // Index into bump map *byte*
+ int j = stride * i;
+
+ Vector3 delta;
+
+ // Get a value from B (with wrapping lookup) relative to (x, y)
+ // and divide by 255
+ #define ELEVATION(DX, DY) ((int)B[(((DX + x + w) % w) + \
+ ((DY + y + h) % h) * w) * stride])
+
+
+ // Sobel filter to compute the normal.
+ //
+ // Y Filter (X filter is the transpose)
+ // [ -1 -2 -1 ]
+ // [ 0 0 0 ]
+ // [ 1 2 1 ]
+
+ // Write the Y value directly into the x-component so we don't have
+ // to explicitly compute a cross product at the end. Does not
+ // go out of bounds because the above is computed mod (width, height)
+ delta.y = -( ELEVATION(-1, -1) * 1 + ELEVATION( 0, -1) * 2 + ELEVATION( 1, -1) * 1 +
+ -ELEVATION(-1, 1) * 1 + -ELEVATION( 0, 1) * 2 + -ELEVATION( 1, 1) * 1);
+
+ delta.x = -(-ELEVATION(-1, -1) * 1 + ELEVATION( 1, -1) * 1 +
+ -ELEVATION(-1, 0) * 2 + ELEVATION( 1, 0) * 2 +
+ -ELEVATION(-1, 1) * 1 + ELEVATION( 1, 1) * 1);
+
+ // The scale of each filter row is 4, the filter width is two pixels,
+ // and the "normal" range is 0-255.
+ delta.z = 4 * 2 * elevationInvScale;
+
+ // Delta is now scaled in pixels; normalize
+ delta = delta.direction();
+
+ // Copy over the bump value into the alpha channel.
+ float H = B[j] / 255.0f;
+
+ if (lowPassBump) {
+ H = (ELEVATION(-1, -1) + ELEVATION( 0, -1) + ELEVATION(1, -1) +
+ ELEVATION(-1, 0) + ELEVATION( 0, 0) + ELEVATION(1, 0) +
+ ELEVATION(-1, 1) + ELEVATION( 0, 1) + ELEVATION(1, 1)) / (255.0f * 9.0f);
+ }
+# undef ELEVATION
+
+ if (scaleHeightByNz) {
+ // delta.z can't possibly be negative, so we avoid actually
+ // computing the absolute value.
+ H *= delta.z;
+ }
+
+ N[i].a = iRound(H * 255.0f);
+
+ // Pack into byte range
+ delta = delta * 127.5f + Vector3(127.5f, 127.5f, 127.5f);
+ N[i].r = iClamp(iRound(delta.x), 0, 255);
+ N[i].g = iClamp(iRound(delta.y), 0, 255);
+ N[i].b = iClamp(iRound(delta.z), 0, 255);
+ }
+ }
+}
+
+
+/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+void GImage::convertToL8() {
+ switch (m_channels) {
+ case 1:
+ return;
+
+ case 3:
+ {
+ // Average
+ Color3uint8* src = (Color3uint8*)m_byte;
+ m_byte = NULL;
+ resize(m_width, m_height, 1);
+ for (int i = m_width * m_height - 1; i >= 0; --i) {
+ const Color3uint8 s = src[i];
+ uint8& d = m_byte[i];
+ d = ((int)s.r + (int)s.g + (int)s.b) / 3;
+ }
+ m_memMan->free(src);
+ }
+ break;
+
+ case 4:
+ {
+ // Average
+ Color4uint8* src = (Color4uint8*)m_byte;
+ m_byte = NULL;
+ resize(m_width, m_height, 1);
+ for (int i = m_width * m_height - 1; i >= 0; --i) {
+ const Color4uint8 s = src[i];
+ uint8& d = m_byte[i];
+ d = ((int)s.r + (int)s.g + (int)s.b) / 3;
+ }
+ m_memMan->free(src);
+ }
+ return;
+
+ default:
+ alwaysAssertM(false, "Bad number of channels in input image");
+ }
+}
+
+
+void GImage::convertToRGBA() {
+ switch (m_channels) {
+ case 1:
+ {
+ // Spread
+ uint8* old = m_byte;
+ m_byte = NULL;
+ resize(m_width, m_height, 4);
+ for (int i = m_width * m_height - 1; i >= 0; --i) {
+ const uint8 s = old[i];
+ Color4uint8& d = ((Color4uint8*)m_byte)[i];
+ d.r = d.g = d.b = s;
+ d.a = 255;
+ }
+ m_memMan->free(m_byte);
+ }
+ break;
+
+ case 3:
+ {
+ // Add alpha
+ Color3uint8* old = (Color3uint8*)m_byte;
+ m_byte = NULL;
+ resize(m_width, m_height, 4);
+ for (int i = m_width * m_height - 1; i >= 0; --i) {
+ const Color3uint8 s = old[i];
+ Color4uint8& d = ((Color4uint8*)m_byte)[i];
+ d.r = s.r;
+ d.g = s.g;
+ d.b = s.b;
+ d.a = 255;
+ }
+ m_memMan->free(old);
+ }
+ break;
+
+ case 4:
+ // Already RGBA
+ return;
+
+ default:
+ alwaysAssertM(false, "Bad number of channels in input image");
+ }
+}
+
+
+void GImage::convertToRGB() {
+ switch (m_channels) {
+ case 1:
+ {
+ // Spread
+ uint8* old = m_byte;
+ m_byte = NULL;
+ resize(m_width, m_height, 3);
+ for (int i = m_width * m_height - 1; i >= 0; --i) {
+ const uint8 s = old[i];
+ Color3uint8& d = ((Color3uint8*)m_byte)[i];
+ d.r = d.g = d.b = s;
+ }
+ m_memMan->free(old);
+ }
+ break;
+
+ case 3:
+ return;
+
+ case 4:
+ // Strip alpha
+ {
+ Color4uint8* old = (Color4uint8*)m_byte;
+ m_byte = NULL;
+ resize(m_width, m_height, 3);
+ for (int i = m_width * m_height - 1; i >= 0; --i) {
+ const Color4uint8 s = old[i];
+ Color3uint8& d = ((Color3uint8*)m_byte)[i];
+ d.r = s.r;
+ d.g = s.g;
+ d.b = s.b;
+ }
+ m_memMan->free(old);
+ }
+ break;
+
+ default:
+ alwaysAssertM(false, "Bad number of channels in input image");
+ }
+}
+
+
+void GImage::R8G8B8_to_Y8U8V8(int width, int height, const uint8* _in, uint8* _out) {
+ const Color3uint8* in = reinterpret_cast<const Color3uint8*>(_in);
+ Color3uint8* out = reinterpret_cast<Color3uint8*>(_out);
+
+ Color3uint8 p;
+ for (int i = width * height - 1; i >= 0; --i) {
+ p.r = iClamp(iRound(in->r * 0.229 + in->g * 0.587 + in->b * 0.114), 0, 255);
+ p.g = iClamp(iRound(in->r * -0.147 + in->g * -0.289 + in->b * 0.436) + 127, 0, 255);
+ p.b = iClamp(iRound(in->r * 0.615 + in->g * -0.515 + in->b * -0.100) + 127, 0, 255);
+ *out = p;
+ ++in;
+ ++out;
+ }
+}
+
+
+
+void GImage::Y8U8V8_to_R8G8B8(int width, int height, const uint8* _in, uint8* _out) {
+ const Color3uint8* in = reinterpret_cast<const Color3uint8*>(_in);
+ Color3uint8* out = reinterpret_cast<Color3uint8*>(_out);
+
+ Color3uint8 p;
+ for (int i = width * height - 1; i >= 0; --i) {
+ p.r = iClamp(iRound(in->r * 1.0753 + (in->b - 127) * 1.2256), 0, 255);
+ p.g = iClamp(iRound(in->r * 1.0753 + (in->g - 127) * -0.3946 + (in->b - 127) * -0.4947), 0, 255);
+ p.b = iClamp(iRound(in->r * 1.0753 + (in->g - 127) * 2.0320 + (in->b - 127) * 0.0853), 0, 255);
+ *out = p;
+ ++in;
+ ++out;
+ }
+}
+
+
+void GImage::makeCheckerboard(GImage& im, int checkerSize, const Color4uint8& A, const Color4uint8& B) {
+ for (int y = 0; y < im.m_height; ++y) {
+ for (int x = 0; x < im.m_width; ++x) {
+ bool checker = isOdd((x / checkerSize) + (y / checkerSize));
+ const Color4uint8& color = checker ? A : B;
+ for (int c = 0; c < im.m_channels; ++c) {
+ uint8* v = im.byte() + (x + y * im.m_width) * im.m_channels + c;
+ *v = color[c];
+ }
+ }
+ }
+}
+
+}
+
diff --git a/dep/src/g3dlite/GImage_bayer.cpp b/dep/src/g3dlite/GImage_bayer.cpp
new file mode 100644
index 00000000000..3d08e8ade5f
--- /dev/null
+++ b/dep/src/g3dlite/GImage_bayer.cpp
@@ -0,0 +1,298 @@
+/**
+ @file GImage_bayer.cpp
+ @author Morgan McGuire, http://graphics.cs.williams.edu
+ @created 2002-05-27
+ @edited 2006-05-10
+ */
+#include "G3D/platform.h"
+#include "G3D/GImage.h"
+
+namespace G3D {
+
+void GImage::BAYER_G8B8_R8G8_to_Quarter_R8G8B8(int width, int height, const uint8* in, uint8* out) {
+ debugAssert(in != out);
+
+ int halfHeight = height / 2;
+ int halfWidth = width / 2;
+
+ int dst_off = 0;
+ for (int y = 0; y < halfHeight; ++y) {
+ for (int x = 0; x < halfWidth; ++x) {
+ // GBRG
+ int src_off = x*2 + y*2*width;
+ out[dst_off] = in[src_off+width]; // red
+ out[dst_off+1] = ((int)in[src_off] + (int)in[src_off+width+1])/2; // green
+ out[dst_off+2] = in[src_off+1]; // blue
+
+ dst_off = dst_off + 3;
+ }
+ }
+}
+
+
+void GImage::Quarter_R8G8B8_to_BAYER_G8B8_R8G8(int inWidth, int inHeight, const uint8* in, uint8* out) {
+ // Undo quarter-size Bayer as best we can. This code isn't very efficient, but it
+ // also isn't used very frequently.
+
+ debugAssert(out != in);
+
+ int outWidth = 2 * inWidth;
+ int outHeight = 2 * inHeight;
+
+ for (int y = 0; y < outHeight; ++y) {
+ for (int x = 0; x < outWidth; ++x) {
+ const Color3uint8* inp = ((const Color3uint8*)in) + ((x/2) + (y/2)* inWidth);
+ uint8* outp = out + x + y * outWidth;
+
+ if (isEven(y)) {
+ // GB row
+ if (isEven(x)) {
+ // Green
+ *outp = inp->g;
+ } else {
+ // Blue
+ *outp = inp->b;
+ }
+ } else {
+ // RG row
+ if (isEven(x)) {
+ // Red
+ *outp = inp->r;
+ } else {
+ // Green
+ *outp = inp->g;
+ }
+ }
+ }
+ }
+}
+
+
+/** Applies a 5x5 filter to monochrome image I (wrapping at the boundaries) */
+static uint8 applyFilter(
+ const uint8* I,
+ int x,
+ int y,
+ int w,
+ int h,
+ const float filter[5][5]) {
+
+ debugAssert(isEven(w));
+ debugAssert(isEven(h));
+
+ float sum = 0.0f;
+ float denom = 0.0f;
+
+ for (int dy = 0; dy < 5; ++dy) {
+ int offset = ((y + dy + h - 2) % h) * w;
+
+ for (int dx = 0; dx < 5; ++dx) {
+ float f = filter[dy][dx];
+ sum += f * I[((x + dx + w - 2) % w) + offset];
+ denom += f;
+ }
+ }
+
+ return (uint8)iClamp(iRound(sum / denom), 0, 255);
+}
+
+////////////////////////////////////////////////////////////////////////////////////////////////
+//
+// Bayer conversions
+//
+
+// There are two kinds of rows (GR and BG).
+// In each row, there are two kinds of pixels (G/R, B/G).
+// We express the four kinds of INPUT pixels as:
+// GRG, GRG, BGB, BGG
+//
+// There are three kinds of OUTPUT pixels: R, G, B.
+// Thus there are nominally 12 different I/O combinations,
+// but several are impulses because needed output at that
+// location *is* the input (e.g., G_GRG and G_BGG).
+//
+// The following 5x5 row-major filters are named as output_input.
+
+// Green
+static const float G_GRR[5][5] =
+{{ 0.0f, 0.0f, -1.0f, 0.0f, 0.0f},
+{ 0.0f, 0.0f, 2.0f, 0.0f, 0.0f},
+{ -1.0f, 2.0f, 4.0f, 2.0f, -1.0f},
+{ 0.0f, 0.0f, 2.0f, 0.0f, 0.0f},
+{ 0.0f, 0.0f, -1.0f, 0.0f, 0.0f}};
+
+static const float G_BGB[5][5] =
+{{ 0.0f, 0.0f, -1.0f, 0.0f, 0.0f},
+{ 0.0f, 0.0f, 2.0f, 0.0f, 0.0f},
+{ -1.0f, 2.0f, 4.0f, 2.0f, -1.0f},
+{ 0.0f, 0.0f, 2.0f, 0.0f, 0.0f},
+{ 0.0f, 0.0f, -1.0f, 0.0f, 0.0f}};
+
+// Red
+//(the caption in the paper is wrong for this case:
+// "R row B column really means R row G column"
+static const float R_GRG[5][5] =
+{{ 0.0f, 0.0f, 0.5f, 0.0f, 0.0f},
+{ 0.0f, -1.0f, 0.0f, -1.0f, 0.0f},
+{ -1.0f, 4.0f, 5.0f, 4.0f, -1.0f},
+{ 0.0f, -1.0f, 0.0f, -1.0f, 0.0f},
+{ 0.0f, 0.0f, 0.5f, 0.0f, 0.0f}};
+
+static const float R_BGG[5][5] =
+{{ 0.0f, 0.0f, -1.0f, 0.0f, 0.0f},
+{ 0.0f, -1.0f, 4.0f, -1.0f, 0.0f},
+{ 0.5f, 0.0f, 5.0f, 0.0f, 0.5f},
+{ 0.0f, -1.0f, 4.0f, -1.0f, 0.0f},
+{ 0.0f, 0.0f, -1.0f, 0.0f, 0.0f}};
+
+static const float R_BGB[5][5] =
+{{ 0.0f, 0.0f, -3.0f/2.0f, 0.0f, 0.0f},
+{ 0.0f, 2.0f, 0.0f, 2.0f, 0.0f},
+{-3.0f/2.0f, 0.0f, 6.0f, 0.0f, -3.0f/2.0f},
+{ 0.0f, 2.0f, 0.0f, 2.0f, 0.0f},
+{ 0.0f, 0.0f, -3.0f/2.0f, 0.0f, 0.0f}};
+
+
+// Blue
+//(the caption in the paper is wrong for this case:
+// "B row R column really means B row G column")
+#define B_BGG R_GRG
+#define B_GRG R_BGG
+#define B_GRR R_BGB
+
+
+void GImage::BAYER_R8G8_G8B8_to_R8G8B8_MHC(int w, int h, const uint8* in, uint8* _out) {
+ debugAssert(in != _out);
+
+ Color3uint8* out = (Color3uint8*)_out;
+
+ for (int y = 0; y < h; ++y) {
+
+ // Row beginning in the input array.
+ int offset = y * w;
+
+ // RG row
+ for (int x = 0; x < w; ++x, ++out) {
+ // R pixel
+ {
+ out->r = in[x + offset];
+ out->g = applyFilter(in, x, y, w, h, G_GRR);
+ out->b = applyFilter(in, x, y, w, h, B_GRR);
+ }
+ ++x; ++out;
+
+ // G pixel
+ {
+ out->r = applyFilter(in, x, y, w, h, R_GRG);
+ out->g = in[x + offset];
+ out->b = applyFilter(in, x, y, w, h, B_GRG);
+ }
+ }
+
+ ++y;
+ offset += w;
+
+ // GB row
+ for (int x = 0; x < w; ++x, ++out) {
+ // G pixel
+ {
+ out->r = applyFilter(in, x, y, w, h, R_BGG);
+ out->g = in[x + offset];
+ out->b = applyFilter(in, x, y, w, h, B_BGG);
+ }
+ ++x; ++out;
+
+ // B pixel
+ {
+ out->r = applyFilter(in, x, y, w, h, R_BGB);
+ out->g = applyFilter(in, x, y, w, h, G_BGB);
+ out->b = in[x + offset];
+ }
+ }
+ }
+}
+
+static void swapRedAndBlue(int N, Color3uint8* out) {
+ for (int i = N - 1; i >= 0; --i) {
+ uint8 tmp = out[i].r;
+ out[i].r = out[i].b;
+ out[i].b = tmp;
+ }
+}
+
+void GImage::BAYER_G8R8_B8G8_to_R8G8B8_MHC(int w, int h, const uint8* in, uint8* _out) {
+ // Run the equivalent function for red
+ BAYER_G8B8_R8G8_to_R8G8B8_MHC(w, h, in, _out);
+
+ // Now swap red and blue
+ swapRedAndBlue(w * h, (Color3uint8*)_out);
+}
+
+
+void GImage::BAYER_B8G8_G8R8_to_R8G8B8_MHC(int w, int h, const uint8* in, uint8* _out) {
+ // Run the equivalent function for red
+ BAYER_R8G8_G8B8_to_R8G8B8_MHC(w, h, in, _out);
+
+ // Now swap red and blue
+ swapRedAndBlue(w * h, (Color3uint8*)_out);
+}
+
+
+void GImage::BAYER_G8B8_R8G8_to_R8G8B8_MHC(int w, int h, const uint8* in, uint8* _out) {
+
+ debugAssert(in != _out);
+
+ Color3uint8* out = (Color3uint8*)_out;
+
+ for (int y = 0; y < h; ++y) {
+
+ // Row beginning in the input array.
+ int offset = y * w;
+
+ // GB row
+ for (int x = 0; x < w; ++x, ++out) {
+ // G pixel
+ {
+ out->r = applyFilter(in, x, y, w, h, R_BGG);
+ out->g = in[x + offset];
+ out->b = applyFilter(in, x, y, w, h, B_BGG);
+ }
+ ++x; ++out;
+
+ // B pixel
+ {
+ out->r = applyFilter(in, x, y, w, h, R_BGB);
+ out->g = applyFilter(in, x, y, w, h, G_BGB);
+ out->b = in[x + offset];
+ }
+ }
+
+ ++y;
+ offset += w;
+
+ // RG row
+ for (int x = 0; x < w; ++x, ++out) {
+ // R pixel
+ {
+ out->r = in[x + offset];
+ out->g = applyFilter(in, x, y, w, h, G_GRR);
+ out->b = applyFilter(in, x, y, w, h, B_GRR);
+ }
+ ++x; ++out;
+
+ // G pixel
+ {
+ out->r = applyFilter(in, x, y, w, h, R_GRG);
+ out->g = in[x + offset];
+ out->b = applyFilter(in, x, y, w, h, B_GRG);
+ }
+ }
+ }
+
+}
+
+#undef B_BGG
+#undef B_GRG
+#undef B_GRR
+
+}
diff --git a/dep/src/g3dlite/GImage_bmp.cpp b/dep/src/g3dlite/GImage_bmp.cpp
new file mode 100644
index 00000000000..425a7e1a1d2
--- /dev/null
+++ b/dep/src/g3dlite/GImage_bmp.cpp
@@ -0,0 +1,717 @@
+/**
+ @file GImage_bmp.cpp
+ @author Morgan McGuire, http://graphics.cs.williams.edu
+ @created 2002-05-27
+ @edited 2006-05-10
+ */
+#include "G3D/platform.h"
+#include "G3D/GImage.h"
+#include "G3D/BinaryInput.h"
+#include "G3D/BinaryOutput.h"
+#include "G3D/Log.h"
+
+namespace G3D {
+
+#ifndef G3D_WIN32
+/**
+ This is used by the Windows bitmap I/O.
+ */
+static const int BI_RGB = 0;
+#endif
+
+void GImage::encodeBMP(
+ BinaryOutput& out) const {
+
+ debugAssert(m_channels == 1 || m_channels == 3);
+ out.setEndian(G3D_LITTLE_ENDIAN);
+
+ uint8 red;
+ uint8 green;
+ uint8 blue;
+ int pixelBufferSize = m_width * m_height * 3;
+ int fileHeaderSize = 14;
+ int infoHeaderSize = 40;
+ int BMScanWidth;
+ int BMPadding;
+
+ // First write the BITMAPFILEHEADER
+ //
+ // WORD bfType;
+ // DWORD bfSize;
+ // WORD bfReserved1;
+ // WORD bfReserved2;
+ // DWORD bfOffBits;
+
+ // Type
+ out.writeUInt8('B');
+ out.writeUInt8('M');
+
+ // File size
+ out.writeUInt32(fileHeaderSize + infoHeaderSize + pixelBufferSize);
+
+ // Two reserved fields set to zero
+ out.writeUInt16(0);
+ out.writeUInt16(0);
+
+ // The offset, in bytes, from the BITMAPFILEHEADER structure
+ // to the bitmap bits.
+ out.writeUInt32(infoHeaderSize + fileHeaderSize);
+
+ // Now the BITMAPINFOHEADER
+ //
+ // DWORD biSize;
+ // LONG biWidth;
+ // LONG biHeight;
+ // WORD biPlanes;
+ // WORD biBitCount
+ // DWORD biCompression;
+ // DWORD biSizeImage;
+ // LONG biXPelsPerMeter;
+ // LONG biYPelsPerMeter;
+ // DWORD biClrUsed;
+ // DWORD biClrImportant;
+
+ // Size of the info header
+ out.writeUInt32(infoHeaderSize);
+
+ // Width and height of the image
+ out.writeUInt32(m_width);
+ out.writeUInt32(m_height);
+
+ // Planes ("must be set to 1")
+ out.writeUInt16(1);
+
+ // BitCount and CompressionType
+ out.writeUInt16(24);
+ out.writeUInt32(BI_RGB);
+
+ // Image size ("may be zero for BI_RGB bitmaps")
+ out.writeUInt32(0);
+
+ // biXPelsPerMeter
+ out.writeUInt32(0);
+ // biYPelsPerMeter
+ out.writeUInt32(0);
+
+ // biClrUsed
+ out.writeUInt32(0);
+
+ // biClrImportant
+ out.writeUInt32(0);
+
+ BMScanWidth = m_width * 3;
+
+ if (BMScanWidth & 3) {
+ BMPadding = 4 - (BMScanWidth & 3);
+ } else {
+ BMPadding = 0;
+ }
+
+ int hStart = m_height - 1;
+ int hEnd = -1;
+ int hDir = -1;
+ int dest;
+
+ // Write the pixel data
+ for (int h = hStart; h != hEnd; h += hDir) {
+ dest = m_channels * h * m_width;
+ for (int w = 0; w < m_width; ++w) {
+
+ if (m_channels == 3) {
+ red = m_byte[dest];
+ green = m_byte[dest + 1];
+ blue = m_byte[dest + 2];
+ } else {
+ red = m_byte[dest];
+ green = m_byte[dest];
+ blue = m_byte[dest];
+ }
+
+ out.writeUInt8(blue);
+ out.writeUInt8(green);
+ out.writeUInt8(red);
+
+ dest += m_channels;
+ }
+
+ if (BMPadding > 0) {
+ out.skip(BMPadding);
+ }
+ }
+}
+
+
+void GImage::decodeBMP(
+ BinaryInput& input) {
+
+ // The BMP decoding uses these flags.
+ static const uint16 PICTURE_NONE = 0x0000;
+ static const uint16 PICTURE_BITMAP = 0x1000;
+
+ // Compression Flags
+ static const uint16 PICTURE_UNCOMPRESSED = 0x0100;
+ static const uint16 PICTURE_MONOCHROME = 0x0001;
+ static const uint16 PICTURE_4BIT = 0x0002;
+ static const uint16 PICTURE_8BIT = 0x0004;
+ static const uint16 PICTURE_16BIT = 0x0008;
+ static const uint16 PICTURE_24BIT = 0x0010;
+ static const uint16 PICTURE_32BIT = 0x0020;
+
+ (void)PICTURE_16BIT;
+ (void)PICTURE_32BIT;
+
+ // This is a simple BMP loader that can handle uncompressed BMP files.
+ // Verify this is a BMP file by looking for the BM tag.
+ input.reset();
+ std::string tag = input.readString(2);
+ if (tag != "BM") {
+ throw Error("Not a BMP file", input.getFilename());
+ }
+
+ m_channels = 3;
+ // Skip to the BITMAPINFOHEADER's width and height
+ input.skip(16);
+
+ m_width = input.readUInt32();
+ m_height = input.readUInt32();
+
+ // Skip to the bit count and compression type
+ input.skip(2);
+
+ uint16 bitCount = input.readUInt16();
+ uint32 compressionType = input.readUInt32();
+
+ uint8 red;
+ uint8 green;
+ uint8 blue;
+ uint8 blank;
+
+ // Only uncompressed bitmaps are supported by this code
+ if ((int32)compressionType != BI_RGB) {
+ throw Error("BMP images must be uncompressed", input.getFilename());
+ }
+
+ uint8* palette = NULL;
+
+ // Create the palette if needed
+ if (bitCount <= 8) {
+
+ // Skip to the palette color count in the header
+ input.skip(12);
+
+ int numColors = input.readUInt32();
+
+ palette = (uint8*)System::malloc(numColors * 3);
+ debugAssert(palette);
+
+ // Skip past the end of the header to the palette info
+ input.skip(4);
+
+ int c;
+ for(c = 0; c < numColors * 3; c += 3) {
+ // Palette information in bitmaps is stored in BGR_ format.
+ // That means it's blue-green-red-blank, for each entry.
+ blue = input.readUInt8();
+ green = input.readUInt8();
+ red = input.readUInt8();
+ blank = input.readUInt8();
+
+ palette[c] = red;
+ palette[c + 1] = green;
+ palette[c + 2] = blue;
+ }
+ }
+
+ int hStart = 0;
+ int hEnd = 0;
+ int hDir = 0;
+
+ if (m_height < 0) {
+ m_height = -m_height;
+ hStart = 0;
+ hEnd = m_height;
+ hDir = 1;
+ } else {
+ //height = height;
+ hStart = m_height - 1;
+ hEnd = -1;
+ hDir = -1;
+ }
+
+ m_byte = (uint8*)m_memMan->alloc(m_width * m_height * 3);
+ debugAssert(m_byte);
+
+ int BMScanWidth;
+ int BMPadding;
+ uint8 BMGroup;
+ uint8 BMPixel8;
+ int currPixel;
+ int dest;
+ int flags = PICTURE_NONE;
+
+ if (bitCount == 1) {
+ // Note that this file is not necessarily grayscale, since it's possible
+ // the palette is blue-and-white, or whatever. But of course most image
+ // programs only write 1-bit images if they're black-and-white.
+ flags = PICTURE_BITMAP | PICTURE_UNCOMPRESSED | PICTURE_MONOCHROME;
+
+ // For bitmaps, each scanline is dword-aligned.
+ BMScanWidth = (m_width + 7) >> 3;
+ if (BMScanWidth & 3) {
+ BMScanWidth += 4 - (BMScanWidth & 3);
+ }
+
+ // Powers of 2
+ int pow2[8] = {0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80};
+
+ for (int h = hStart; h != hEnd; h += hDir) {
+
+ currPixel = 0;
+ dest = 3 * h * m_width;
+
+ for (int w = 0; w < BMScanWidth; ++w) {
+
+ BMGroup = input.readUInt8();
+
+ // Now we read the pixels. Usually there are eight pixels per byte,
+ // since each pixel is represented by one bit, but if the width
+ // is not a multiple of eight, the last byte will have some bits
+ // set, with the others just being extra. Plus there's the
+ // dword-alignment padding. So we keep checking to see if we've
+ // already read "width" number of pixels.
+ for (int i = 7; i >= 0; --i) {
+ if (currPixel < m_width) {
+ int src = 3 * ((BMGroup & pow2[i]) >> i);
+
+ m_byte[dest] = palette[src];
+ m_byte[dest + 1] = palette[src + 1];
+ m_byte[dest + 2] = palette[src + 2];
+
+ ++currPixel;
+ dest += 3;
+ }
+ }
+ }
+ }
+
+ } else if (bitCount == 4) {
+
+ flags = PICTURE_BITMAP | PICTURE_UNCOMPRESSED | PICTURE_4BIT;
+
+ // For bitmaps, each scanline is dword-aligned.
+ int BMScanWidth = (m_width + 1) >> 1;
+ if (BMScanWidth & 3) {
+ BMScanWidth += 4 - (BMScanWidth & 3);
+ }
+
+ for (int h = hStart; h != hEnd; h += hDir) {
+
+ currPixel = 0;
+ dest = 3 * h * m_width;
+
+ for (int w = 0; w < BMScanWidth; w++) {
+
+ BMGroup = input.readUInt8();
+ int src[2];
+ src[0] = 3 * ((BMGroup & 0xF0) >> 4);
+ src[1] = 3 * (BMGroup & 0x0F);
+
+ // Now we read the pixels. Usually there are two pixels per byte,
+ // since each pixel is represented by four bits, but if the width
+ // is not a multiple of two, the last byte will have only four bits
+ // set, with the others just being extra. Plus there's the
+ // dword-alignment padding. So we keep checking to see if we've
+ // already read "Width" number of pixels.
+
+ for (int i = 0; i < 2; ++i) {
+ if (currPixel < m_width) {
+ int tsrc = src[i];
+
+ m_byte[dest] = palette[tsrc];
+ m_byte[dest + 1] = palette[tsrc + 1];
+ m_byte[dest + 2] = palette[tsrc + 2];
+
+ ++currPixel;
+ dest += 3;
+ }
+ }
+ }
+ }
+
+ } else if (bitCount == 8) {
+
+ flags = PICTURE_BITMAP | PICTURE_UNCOMPRESSED | PICTURE_8BIT;
+
+ // For bitmaps, each scanline is dword-aligned.
+ BMScanWidth = m_width;
+ if (BMScanWidth & 3) {
+ BMScanWidth += 4 - (BMScanWidth & 3);
+ }
+
+ for (int h = hStart; h != hEnd; h += hDir) {
+
+ currPixel = 0;
+
+ for (int w = 0; w < BMScanWidth; ++w) {
+
+ BMPixel8 = input.readUInt8();
+
+ if (currPixel < m_width) {
+ dest = 3 * ((h * m_width) + currPixel);
+ int src = 3 * BMPixel8;
+
+ m_byte[dest] = palette[src];
+ m_byte[dest + 1] = palette[src + 1];
+ m_byte[dest + 2] = palette[src + 2];
+
+ ++currPixel;
+ }
+ }
+ }
+
+ } else if (bitCount == 16) {
+
+ m_memMan->free(m_byte);
+ m_byte = NULL;
+ System::free(palette);
+ palette = NULL;
+ throw Error("16-bit bitmaps not supported", input.getFilename());
+
+ } else if (bitCount == 24) {
+ input.skip(20);
+
+ flags = PICTURE_BITMAP | PICTURE_UNCOMPRESSED | PICTURE_24BIT;
+
+ // For bitmaps, each scanline is dword-aligned.
+ BMScanWidth = m_width * 3;
+
+ if (BMScanWidth & 3) {
+ BMPadding = 4 - (BMScanWidth & 3);
+ } else {
+ BMPadding = 0;
+ }
+
+ for (int h = hStart; h != hEnd; h += hDir) {
+ dest = 3 * h * m_width;
+ for (int w = 0; w < m_width; ++w) {
+
+ blue = input.readUInt8();
+ green = input.readUInt8();
+ red = input.readUInt8();
+
+ m_byte[dest] = red;
+ m_byte[dest + 1] = green;
+ m_byte[dest + 2] = blue;
+
+ dest += 3;
+ }
+
+ if (BMPadding) {
+ input.skip(2);
+ }
+ }
+
+ } else if (bitCount == 32) {
+
+ m_memMan->free(m_byte);
+ m_byte = NULL;
+ System::free(palette);
+ palette = NULL;
+ throw Error("32 bit bitmaps not supported", input.getFilename());
+
+ } else {
+ // We support all possible bit depths, so if the
+ // code gets here, it's not even a real bitmap.
+ m_memMan->free(m_byte);
+ m_byte = NULL;
+ throw Error("Not a bitmap!", input.getFilename());
+ }
+
+ System::free(palette);
+ palette = NULL;
+}
+
+
+void GImage::decodeICO(
+ BinaryInput& input) {
+
+ // Header
+ uint16 r = input.readUInt16();
+ debugAssert(r == 0);
+ r = input.readUInt16();
+ debugAssert(r == 1);
+
+ // Read the number of icons, although we'll only load the
+ // first one.
+ int count = input.readUInt16();
+
+ m_channels = 4;
+
+ debugAssert(count > 0);
+
+ const uint8* headerBuffer = input.getCArray() + input.getPosition();
+ int maxWidth = 0, maxHeight = 0;
+ int maxHeaderNum = 0;
+ for (int currentHeader = 0; currentHeader < count; ++currentHeader) {
+
+ const uint8* curHeaderBuffer = headerBuffer + (currentHeader * 16);
+ int tmpWidth = curHeaderBuffer[0];
+ int tmpHeight = curHeaderBuffer[1];
+ // Just in case there is a non-square icon, checking area
+ if ((tmpWidth * tmpHeight) > (maxWidth * maxHeight)) {
+ maxWidth = tmpWidth;
+ maxHeight = tmpHeight;
+ maxHeaderNum = currentHeader;
+ }
+ }
+
+ input.skip(maxHeaderNum * 16);
+
+ m_width = input.readUInt8();
+ m_height = input.readUInt8();
+ int numColors = input.readUInt8();
+
+ m_byte = (uint8*)m_memMan->alloc(m_width * m_height * m_channels);
+ debugAssert(m_byte);
+
+ // Bit mask for packed bits
+ int mask = 0;
+
+ int bitsPerPixel = 8;
+
+ switch (numColors) {
+ case 2:
+ mask = 0x01;
+ bitsPerPixel = 1;
+ break;
+
+ case 16:
+ mask = 0x0F;
+ bitsPerPixel = 4;
+ break;
+
+ case 0:
+ numColors = 256;
+ mask = 0xFF;
+ bitsPerPixel = 8;
+ break;
+
+ default:
+ throw Error("Unsupported ICO color count.", input.getFilename());
+ }
+
+ input.skip(5);
+ // Skip 'size' unused
+ input.skip(4);
+
+ int offset = input.readUInt32();
+
+ // Skip over any other icon descriptions
+ input.setPosition(offset);
+
+ // Skip over bitmap header; it is redundant
+ input.skip(40);
+
+ Array<Color4uint8> palette;
+ palette.resize(numColors, true);
+ for (int c = 0; c < numColors; ++c) {
+ palette[c].b = input.readUInt8();
+ palette[c].g = input.readUInt8();
+ palette[c].r = input.readUInt8();
+ palette[c].a = input.readUInt8();
+ }
+
+ // The actual image and mask follow
+
+ // The XOR Bitmap is stored as 1-bit, 4-bit or 8-bit uncompressed Bitmap
+ // using the same encoding as BMP files. The AND Bitmap is stored in as
+ // 1-bit uncompressed Bitmap.
+ //
+ // Pixels are stored bottom-up, left-to-right. Pixel lines are padded
+ // with zeros to end on a 32bit (4byte) boundary. Every line will have the
+ // same number of bytes. Color indices are zero based, meaning a pixel color
+ // of 0 represents the first color table entry, a pixel color of 255 (if there
+ // are that many) represents the 256th entry.
+/*
+ int bitsPerRow = width * bitsPerPixel;
+ int bytesPerRow = iCeil((double)bitsPerRow / 8);
+ // Rows are padded to 32-bit boundaries
+ bytesPerRow += bytesPerRow % 4;
+
+ // Read the XOR values into the color channel
+ for (int y = height - 1; y >= 0; --y) {
+ int x = 0;
+ // Read the row
+ for (int i = 0; i < bytesPerRow; ++i) {
+ uint8 byte = input.readUInt8();
+ for (int j = 0; (j < 8) && (x < width); ++x, j += bitsPerPixel) {
+ int bit = ((byte << j) >> (8 - bitsPerPixel)) & mask;
+ pixel4(x, y) = colorTable[bit];
+ }
+ }
+ }
+*/
+ int hStart = 0;
+ int hEnd = 0;
+ int hDir = 0;
+
+ if (m_height < 0) {
+ m_height = -m_height;
+ hStart = 0;
+ hEnd = m_height;
+ hDir = 1;
+ } else {
+ //height = height;
+ hStart = m_height - 1;
+ hEnd = -1;
+ hDir = -1;
+ }
+
+ int BMScanWidth;
+ uint8 BMGroup;
+ uint8 BMPixel8;
+ int currPixel;
+ int dest;
+
+ if (bitsPerPixel == 1) {
+ // Note that this file is not necessarily grayscale, since it's possible
+ // the palette is blue-and-white, or whatever. But of course most image
+ // programs only write 1-bit images if they're black-and-white.
+
+ // For bitmaps, each scanline is dword-aligned.
+ BMScanWidth = (m_width + 7) >> 3;
+ if (BMScanWidth & 3) {
+ BMScanWidth += 4 - (BMScanWidth & 3);
+ }
+
+ // Powers of 2
+ int pow2[8] = {0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80};
+
+ for (int h = hStart; h != hEnd; h += hDir) {
+
+ currPixel = 0;
+ dest = 3 * h * m_width;
+
+ for (int w = 0; w < BMScanWidth; ++w) {
+
+ BMGroup = input.readUInt8();
+
+ // Now we read the pixels. Usually there are eight pixels per byte,
+ // since each pixel is represented by one bit, but if the width
+ // is not a multiple of eight, the last byte will have some bits
+ // set, with the others just being extra. Plus there's the
+ // dword-alignment padding. So we keep checking to see if we've
+ // already read "width" number of pixels.
+ for (int i = 7; i >= 0; --i) {
+ if (currPixel < m_width) {
+ int src = ((BMGroup & pow2[i]) >> i);
+
+ m_byte[dest] = palette[src].r;
+ m_byte[dest + 1] = palette[src].g;
+ m_byte[dest + 2] = palette[src].b;
+
+ ++currPixel;
+ dest += 4;
+ }
+ }
+ }
+ }
+
+ } else if (bitsPerPixel == 4) {
+
+ // For bitmaps, each scanline is dword-aligned.
+ int BMScanWidth = (m_width + 1) >> 1;
+ if (BMScanWidth & 3) {
+ BMScanWidth += 4 - (BMScanWidth & 3);
+ }
+
+ for (int h = hStart; h != hEnd; h += hDir) {
+
+ currPixel = 0;
+ dest = 4 * h * m_width;
+
+ for (int w = 0; w < BMScanWidth; w++) {
+
+ BMGroup = input.readUInt8();
+ int src[2];
+ src[0] = ((BMGroup & 0xF0) >> 4);
+ src[1] = (BMGroup & 0x0F);
+
+ // Now we read the pixels. Usually there are two pixels per byte,
+ // since each pixel is represented by four bits, but if the width
+ // is not a multiple of two, the last byte will have only four bits
+ // set, with the others just being extra. Plus there's the
+ // dword-alignment padding. So we keep checking to see if we've
+ // already read "Width" number of pixels.
+
+ for (int i = 0; i < 2; ++i) {
+ if (currPixel < m_width) {
+ int tsrc = src[i];
+
+ m_byte[dest] = palette[tsrc].r;
+ m_byte[dest + 1] = palette[tsrc].g;
+ m_byte[dest + 2] = palette[tsrc].b;
+
+ ++currPixel;
+ dest += 4;
+ }
+ }
+ }
+ }
+
+ } else if (bitsPerPixel == 8) {
+
+ // For bitmaps, each scanline is dword-aligned.
+ BMScanWidth = m_width;
+ if (BMScanWidth & 3) {
+ BMScanWidth += 4 - (BMScanWidth & 3);
+ }
+
+ for (int h = hStart; h != hEnd; h += hDir) {
+
+ currPixel = 0;
+
+ for (int w = 0; w < BMScanWidth; ++w) {
+
+ BMPixel8 = input.readUInt8();
+
+ if (currPixel < m_width) {
+ dest = 4 * ((h * m_width) + currPixel);
+ int src = BMPixel8;
+
+ m_byte[dest] = palette[src].r;
+ m_byte[dest + 1] = palette[src].g;
+ m_byte[dest + 2] = palette[src].b;
+
+ ++currPixel;
+ }
+ }
+ }
+ }
+
+ // Read the mask into the alpha channel
+ int bitsPerRow = m_width;
+ int bytesPerRow = iCeil((double)bitsPerRow / 8);
+
+ // For bitmaps, each scanline is dword-aligned.
+ //BMScanWidth = (width + 1) >> 1;
+ if (bytesPerRow & 3) {
+ bytesPerRow += 4 - (bytesPerRow & 3);
+ }
+
+ for (int y = m_height - 1; y >= 0; --y) {
+ int x = 0;
+ // Read the row
+ for (int i = 0; i < bytesPerRow; ++i) {
+ uint8 byte = input.readUInt8();
+ for (int j = 0; (j < 8) && (x < m_width); ++x, ++j) {
+ int bit = (byte >> (7 - j)) & 0x01;
+ pixel4(x, y).a = (1 - bit) * 0xFF;
+ }
+ }
+ }
+
+}
+
+
+}
diff --git a/dep/src/g3dlite/GImage_jpeg.cpp b/dep/src/g3dlite/GImage_jpeg.cpp
new file mode 100644
index 00000000000..0b0521f31e7
--- /dev/null
+++ b/dep/src/g3dlite/GImage_jpeg.cpp
@@ -0,0 +1,446 @@
+/**
+ @file GImage_jpeg.cpp
+ @author Morgan McGuire, http://graphics.cs.williams.edu
+ @created 2002-05-27
+ @edited 2009-04-20
+ */
+#include "G3D/platform.h"
+#include "G3D/GImage.h"
+#include "G3D/BinaryInput.h"
+#include "G3D/BinaryOutput.h"
+
+#include <cstring>
+
+extern "C" {
+#ifdef G3D_LINUX
+# include <jconfig.h>
+# include <jpeglib.h>
+#else
+# include "jconfig.h"
+# include "jpeglib.h"
+#endif
+}
+
+namespace G3D {
+
+
+const int jpegQuality = 96;
+
+/**
+ The IJG library needs special setup for compress/decompressing
+ from memory. These classes provide them.
+
+ The format of this class is defined by the IJG library; do not
+ change it.
+ */
+class memory_destination_mgr {
+public:
+ struct jpeg_destination_mgr pub;
+ JOCTET* buffer;
+ int size;
+ int count;
+};
+
+typedef memory_destination_mgr* mem_dest_ptr;
+
+/**
+ Signature dictated by IJG.
+ */
+static void init_destination (
+ j_compress_ptr cinfo) {
+
+ mem_dest_ptr dest = (mem_dest_ptr) cinfo->dest;
+
+ dest->pub.next_output_byte = dest->buffer;
+ dest->pub.free_in_buffer = dest->size;
+ dest->count=0;
+}
+
+/**
+ Signature dictated by IJG.
+ */
+static boolean empty_output_buffer (
+ j_compress_ptr cinfo) {
+
+ mem_dest_ptr dest = (mem_dest_ptr) cinfo->dest;
+
+ dest->pub.next_output_byte = dest->buffer;
+ dest->pub.free_in_buffer = dest->size;
+
+ return TRUE;
+}
+
+/**
+ Signature dictated by IJG.
+ */
+static void term_destination (
+ j_compress_ptr cinfo) {
+
+ mem_dest_ptr dest = (mem_dest_ptr) cinfo->dest;
+ dest->count = dest->size - dest->pub.free_in_buffer;
+}
+
+/**
+ Signature dictated by IJG.
+ */
+static void jpeg_memory_dest (
+ j_compress_ptr cinfo,
+ JOCTET* buffer,
+ int size) {
+
+ mem_dest_ptr dest;
+
+ if (cinfo->dest == NULL) {
+ // First time for this JPEG object; call the
+ // IJG allocator to get space.
+ cinfo->dest = (struct jpeg_destination_mgr*)
+ (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo,
+ JPOOL_PERMANENT,
+ sizeof(memory_destination_mgr));
+ }
+
+ dest = (mem_dest_ptr) cinfo->dest;
+ dest->size = size;
+ dest->buffer = buffer;
+ dest->pub.init_destination = init_destination;
+ dest->pub.empty_output_buffer = empty_output_buffer;
+ dest->pub.term_destination = term_destination;
+}
+
+////////////////////////////////////////////////////////////////////////////////////////
+
+#define INPUT_BUF_SIZE 4096
+
+/**
+ Structure dictated by IJG.
+ */
+class memory_source_mgr {
+public:
+ struct jpeg_source_mgr pub;
+ int source_size;
+ unsigned char* source_data;
+ boolean start_of_data;
+ JOCTET* buffer;
+};
+
+
+typedef memory_source_mgr* mem_src_ptr;
+
+
+/**
+ Signature dictated by IJG.
+ */
+static void init_source(
+ j_decompress_ptr cinfo) {
+
+ mem_src_ptr src = (mem_src_ptr) cinfo->src;
+
+ src->start_of_data = TRUE;
+}
+
+
+/**
+ Signature dictated by IJG.
+ */
+static boolean fill_input_buffer(
+ j_decompress_ptr cinfo) {
+
+ mem_src_ptr src = (mem_src_ptr) cinfo->src;
+
+ size_t bytes_read = 0;
+
+ if (src->source_size > INPUT_BUF_SIZE)
+ bytes_read = INPUT_BUF_SIZE;
+ else
+ bytes_read = src->source_size;
+
+ memcpy (src->buffer, src->source_data, bytes_read);
+
+ src->source_data += bytes_read;
+ src->source_size -= bytes_read;
+
+ src->pub.next_input_byte = src->buffer;
+ src->pub.bytes_in_buffer = bytes_read;
+ src->start_of_data = FALSE;
+
+
+ return TRUE;
+}
+
+
+/**
+ Signature dictated by IJG.
+ */
+static void skip_input_data(
+ j_decompress_ptr cinfo,
+ long num_bytes) {
+
+ mem_src_ptr src = (mem_src_ptr)cinfo->src;
+
+ if (num_bytes > 0) {
+ while (num_bytes > (long) src->pub.bytes_in_buffer) {
+ num_bytes -= (long) src->pub.bytes_in_buffer;
+ boolean s = fill_input_buffer(cinfo);
+ debugAssert(s); (void)s;
+ }
+
+ src->pub.next_input_byte += (size_t) num_bytes;
+ src->pub.bytes_in_buffer -= (size_t) num_bytes;
+ }
+}
+
+
+/**
+ Signature dictated by IJG.
+ */
+static void term_source (
+ j_decompress_ptr cinfo) {
+ (void)cinfo;
+ // Intentionally empty
+}
+
+
+/**
+ Signature dictated by IJG.
+ */
+static void jpeg_memory_src (
+ j_decompress_ptr cinfo,
+ JOCTET* buffer,
+ int size) {
+
+ mem_src_ptr src;
+
+ if (cinfo->src == NULL) {
+ // First time for this JPEG object
+ cinfo->src = (struct jpeg_source_mgr*)
+ (*cinfo->mem->alloc_small)(
+ (j_common_ptr) cinfo,
+ JPOOL_PERMANENT,
+ sizeof(memory_source_mgr));
+
+ src = (mem_src_ptr)cinfo->src;
+
+ src->buffer = (JOCTET*)
+ (*cinfo->mem->alloc_small)(
+ (j_common_ptr) cinfo,
+ JPOOL_PERMANENT,
+ INPUT_BUF_SIZE * sizeof(JOCTET));
+ }
+
+ src = (mem_src_ptr)cinfo->src;
+ src->pub.init_source = init_source;
+ src->pub.fill_input_buffer = fill_input_buffer;
+ src->pub.skip_input_data = skip_input_data;
+
+ // use default method
+ src->pub.resync_to_restart = jpeg_resync_to_restart;
+ src->pub.term_source = term_source;
+ src->source_data = buffer;
+ src->source_size = size;
+
+ // forces fill_input_buffer on first read
+ src->pub.bytes_in_buffer = 0;
+
+ // until buffer loaded
+ src->pub.next_input_byte = NULL;
+}
+
+
+void GImage::encodeJPEG(
+ BinaryOutput& out) const {
+
+ if (m_channels != 3) {
+ // Convert to three channel
+ GImage tmp = *this;
+ tmp.convertToRGB();
+ tmp.encodeJPEG(out);
+ return;
+ }
+
+ debugAssert(m_channels == 3);
+ out.setEndian(G3D_LITTLE_ENDIAN);
+
+ // Allocate and initialize a compression object
+ jpeg_compress_struct cinfo;
+ jpeg_error_mgr jerr;
+
+ cinfo.err = jpeg_std_error(&jerr);
+ jpeg_create_compress(&cinfo);
+
+ // Specify the destination for the compressed data.
+ // (Overestimate the size)
+ int buffer_size = m_width * m_height * 3 + 200;
+ JOCTET* compressed_data = (JOCTET*)System::malloc(buffer_size);
+ jpeg_memory_dest(&cinfo, compressed_data, buffer_size);
+
+
+ cinfo.image_width = m_width;
+ cinfo.image_height = m_height;
+
+ // # of color components per pixel
+ cinfo.input_components = 3;
+
+ // colorspace of input image
+ cinfo.in_color_space = JCS_RGB;
+ cinfo.input_gamma = 1.0;
+
+ // Set parameters for compression, including image size & colorspace
+ jpeg_set_defaults(&cinfo);
+ jpeg_set_quality(&cinfo, jpegQuality, false);
+ cinfo.smoothing_factor = 0;
+ cinfo.optimize_coding = TRUE;
+// cinfo.dct_method = JDCT_FLOAT;
+ cinfo.dct_method = JDCT_ISLOW;
+ cinfo.jpeg_color_space = JCS_YCbCr;
+
+ // Initialize the compressor
+ jpeg_start_compress(&cinfo, TRUE);
+
+ // Iterate over all scanlines from top to bottom
+ // pointer to a single row
+ JSAMPROW row_pointer[1];
+
+ // JSAMPLEs per row in image_buffer
+ int row_stride = cinfo.image_width * 3;
+ while (cinfo.next_scanline < cinfo.image_height) {
+ row_pointer[0] = &(m_byte[cinfo.next_scanline * row_stride]);
+ jpeg_write_scanlines(&cinfo, row_pointer, 1);
+ }
+
+ // Shut down the compressor
+ jpeg_finish_compress(&cinfo);
+
+ // Figure out how big the result was.
+ int outLength = ((mem_dest_ptr)cinfo.dest)->count;
+
+ // Release the JPEG compression object
+ jpeg_destroy_compress(&cinfo);
+
+ // Copy into an appropriately sized output buffer.
+ out.writeBytes(compressed_data, outLength);
+
+ // Free the conservative buffer.
+ System::free(compressed_data);
+ compressed_data = NULL;
+}
+
+
+void GImage::decodeJPEG(
+ BinaryInput& input) {
+
+ struct jpeg_decompress_struct cinfo;
+ struct jpeg_error_mgr jerr;
+ int loc = 0;
+
+ m_channels = 3;
+ // We have to set up the error handler, in case initialization fails.
+ cinfo.err = jpeg_std_error(&jerr);
+
+ // Initialize the JPEG decompression object.
+ jpeg_create_decompress(&cinfo);
+
+ // Specify data source (eg, a file, for us, memory)
+ jpeg_memory_src(&cinfo, const_cast<uint8*>(input.getCArray()), input.size());
+
+ // Read the parameters with jpeg_read_header()
+ jpeg_read_header(&cinfo, TRUE);
+
+ // Set parameters for decompression
+ // (We do nothing here since the defaults are fine)
+
+ // Start decompressor
+ jpeg_start_decompress(&cinfo);
+
+ // Get and set the values of interest to this object
+ m_width = cinfo.output_width;
+ m_height = cinfo.output_height;
+
+ // Prepare the pointer object for the pixel data
+ m_byte = (uint8*)m_memMan->alloc(m_width * m_height * 3);
+
+ // JSAMPLEs per row in output buffer
+ int bpp = cinfo.output_components;
+ int row_stride = cinfo.output_width * bpp;
+
+ // Make a one-row-high sample array that will go away when done with image
+ JSAMPARRAY temp = (*cinfo.mem->alloc_sarray)
+ ((j_common_ptr) &cinfo, JPOOL_IMAGE, row_stride, 1);
+
+ // Read data on a scanline by scanline basis
+ while (cinfo.output_scanline < cinfo.output_height) {
+
+ // We may need to adjust the output based on the
+ // number of channels it has.
+ switch (bpp) {
+ case 1:
+ // Grayscale; decompress to temp.
+ jpeg_read_scanlines(&cinfo, temp, 1);
+
+ // Expand to three channels
+ {
+ uint8* scan = &(m_byte[loc * 3]);
+ uint8* endScan = scan + (m_width * 3);
+ uint8* t = *temp;
+
+ while (scan < endScan) {
+ uint8 value = t[0];
+
+ // Spread the value 3x.
+ scan[0] = value;
+ scan[1] = value;
+ scan[2] = value;
+
+ scan += 3;
+ t += 1;
+ }
+ }
+ break;
+
+ case 3:
+ // Read directly into the array
+ {
+ // Need one extra level of indirection.
+ uint8* scan = m_byte + loc;
+ JSAMPARRAY ptr = &scan;
+ jpeg_read_scanlines(&cinfo, ptr, 1);
+ }
+ break;
+
+ case 4:
+ // RGBA; decompress to temp.
+ jpeg_read_scanlines(&cinfo, temp, 1);
+
+ // Drop the 3rd channel
+ {
+ uint8* scan = &(m_byte[loc * 3]);
+ uint8* endScan = scan + m_width * 3;
+ uint8* t = *temp;
+
+ while (scan < endScan) {
+ scan[0] = t[0];
+ scan[1] = t[1];
+ scan[2] = t[2];
+
+ scan += 3;
+ t += 4;
+ }
+ }
+ break;
+
+ default:
+ throw Error("Unexpected number of channels.", input.getFilename());
+ }
+
+ loc += row_stride;
+ }
+
+ // Finish decompression
+ jpeg_finish_decompress(&cinfo);
+
+ alwaysAssertM(this, "Corrupt GImage");
+ // Release JPEG decompression object
+ jpeg_destroy_decompress(&cinfo);
+}
+
+
+}
diff --git a/dep/src/g3dlite/GImage_png.cpp b/dep/src/g3dlite/GImage_png.cpp
new file mode 100644
index 00000000000..0a515bf7ed2
--- /dev/null
+++ b/dep/src/g3dlite/GImage_png.cpp
@@ -0,0 +1,266 @@
+/**
+ @file GImage_png.cpp
+ @author Morgan McGuire, http://graphics.cs.williams.edu
+ @created 2002-05-27
+ @edited 2009-04-20
+ */
+#include "G3D/platform.h"
+#include "G3D/GImage.h"
+#include "G3D/BinaryInput.h"
+#include "G3D/BinaryOutput.h"
+#include "G3D/Log.h"
+#include <png.h>
+
+namespace G3D {
+
+
+//libpng required function signature
+static void png_read_data(
+ png_structp png_ptr,
+ png_bytep data,
+ png_size_t length) {
+
+
+ debugAssert( png_ptr->io_ptr != NULL );
+ debugAssert( length >= 0 );
+ debugAssert( data != NULL );
+
+ ((BinaryInput*)png_ptr->io_ptr)->readBytes(data, length);
+}
+
+//libpng required function signature
+static void png_write_data(png_structp png_ptr,
+ png_bytep data,
+ png_size_t length) {
+
+ debugAssert( png_ptr->io_ptr != NULL );
+ debugAssert( data != NULL );
+
+ ((BinaryOutput*)png_ptr->io_ptr)->writeBytes(data, length);
+}
+
+//libpng required function signature
+static void png_flush_data(
+ png_structp png_ptr) {
+ (void)png_ptr;
+ //Do nothing.
+}
+
+//libpng required function signature
+static void png_error(
+ png_structp png_ptr,
+ png_const_charp error_msg) {
+
+ (void)png_ptr;
+ debugAssert( error_msg != NULL );
+ throw GImage::Error(error_msg, "PNG");
+}
+
+
+//libpng required function signature
+void png_warning(
+ png_structp png_ptr,
+ png_const_charp warning_msg) {
+
+ (void)png_ptr;
+ debugAssert( warning_msg != NULL );
+ Log::common()->println(warning_msg);
+}
+
+
+void GImage::encodePNG(
+ BinaryOutput& out) const {
+
+ debugAssert( m_channels == 1 || m_channels == 3 || m_channels == 4 );
+
+ if (m_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);
+ png_color_8_struct sig_bit;
+
+ switch (m_channels) {
+ case 1:
+ png_set_IHDR(png_ptr, info_ptr, m_width, m_height, 8, PNG_COLOR_TYPE_GRAY,
+ PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_BASE, PNG_FILTER_TYPE_BASE);
+ sig_bit.red = 0;
+ sig_bit.green = 0;
+ sig_bit.blue = 0;
+ sig_bit.alpha = 0;
+ sig_bit.gray = 8;
+ break;
+
+ case 3:
+ png_set_IHDR(png_ptr, info_ptr, m_width, m_height, 8, PNG_COLOR_TYPE_RGB,
+ PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_BASE, PNG_FILTER_TYPE_BASE);
+
+ sig_bit.red = 8;
+ sig_bit.green = 8;
+ sig_bit.blue = 8;
+ sig_bit.alpha = 0;
+ sig_bit.gray = 0;
+ break;
+
+ case 4:
+ png_set_IHDR(png_ptr, info_ptr, m_width, m_height, 8, PNG_COLOR_TYPE_RGBA,
+ PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_BASE, PNG_FILTER_TYPE_BASE);
+ sig_bit.red = 8;
+ sig_bit.green = 8;
+ sig_bit.blue = 8;
+ sig_bit.alpha = 8;
+ sig_bit.gray = 0;
+ break;
+
+ default:
+ png_destroy_write_struct(&png_ptr, &info_ptr);
+ throw GImage::Error("Unsupported number of channels for PNG.", out.getFilename());
+ }
+
+
+ png_set_sBIT(png_ptr, info_ptr, &sig_bit);
+
+ //write the png header
+ png_write_info(png_ptr, info_ptr);
+
+ png_bytepp row_pointers = new png_bytep[m_height];
+
+ for (int i=0; i < m_height; ++i) {
+ row_pointers[i] = (png_bytep)&m_byte[m_width * m_channels * i];
+ }
+
+ png_write_image(png_ptr, row_pointers);
+
+ png_write_end(png_ptr, info_ptr);
+
+ delete[] row_pointers;
+
+ png_destroy_write_struct(&png_ptr, &info_ptr);
+}
+
+
+void GImage::decodePNG(
+ BinaryInput& input) {
+
+ png_structp png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, png_error, png_warning);
+ if (png_ptr == NULL) {
+ throw GImage::Error("Unable to initialize PNG decoder.", input.getFilename());
+ }
+
+ png_infop info_ptr = png_create_info_struct(png_ptr);
+ if (info_ptr == NULL) {
+ png_destroy_read_struct(&png_ptr, (png_infopp)NULL, (png_infopp)NULL);
+ throw GImage::Error("Unable to initialize PNG decoder.", input.getFilename());
+ }
+
+ png_infop end_info = png_create_info_struct(png_ptr);
+ if (end_info == NULL) {
+ png_destroy_read_struct(&png_ptr, &info_ptr, (png_infopp)NULL);
+ throw GImage::Error("Unable to initialize PNG decoder.", input.getFilename());
+ }
+
+ // now that the libpng structures are setup, change the error handlers and read routines
+ // to use G3D functions so that BinaryInput can be used.
+
+ png_set_read_fn(png_ptr, (png_voidp)&input, png_read_data);
+
+ // read in sequentially so that three copies of the file are not in memory at once
+ png_read_info(png_ptr, info_ptr);
+
+ png_uint_32 png_width, png_height;
+ int bit_depth, color_type, interlace_type;
+ // this will validate the data it extracts from info_ptr
+ png_get_IHDR(png_ptr, info_ptr, &png_width, &png_height, &bit_depth, &color_type,
+ &interlace_type, 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());
+ }
+
+ m_width = static_cast<uint32>(png_width);
+ m_height = static_cast<uint32>(png_height);
+
+ //swap bytes of 16 bit files to least significant byte first
+ png_set_swap(png_ptr);
+
+ png_set_strip_16(png_ptr);
+
+ //Expand paletted colors into true RGB triplets
+ if (color_type == PNG_COLOR_TYPE_PALETTE) {
+ png_set_palette_to_rgb(png_ptr);
+ }
+
+ //Expand grayscale images to the full 8 bits from 1, 2, or 4 bits/pixel
+ if (color_type == PNG_COLOR_TYPE_GRAY && bit_depth < 8) {
+ png_set_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)) ) {
+
+ m_channels = 4;
+ m_byte = (uint8*)m_memMan->alloc(m_width * m_height * 4);
+
+ } else if ((color_type == PNG_COLOR_TYPE_RGB) ||
+ (color_type == PNG_COLOR_TYPE_PALETTE)) {
+
+ m_channels = 3;
+ m_byte = (uint8*)System::malloc(m_width * m_height * 3);
+
+ } else if (color_type == PNG_COLOR_TYPE_GRAY) {
+
+ m_channels = 1;
+
+ // Round up to the nearest 8 rows to avoid a bug in the PNG decoder
+ int h = iCeil(m_height / 8) * 8;
+ int sz = m_width * h;
+ m_byte = (uint8*)m_memMan->alloc(sz);
+
+ } else {
+ throw GImage::Error("Unsupported PNG bit-depth or type.", input.getFilename());
+ }
+
+ //since we are reading row by row, required to handle interlacing
+ uint32 number_passes = png_set_interlace_handling(png_ptr);
+
+ png_read_update_info(png_ptr, info_ptr);
+
+ for (uint32 pass = 0; pass < number_passes; ++pass) {
+ for (uint32 y = 0; y < (uint32)m_height; ++y) {
+ png_bytep rowPointer = &m_byte[m_width * m_channels * y];
+ png_read_rows(png_ptr, &rowPointer, 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/dep/src/g3dlite/GImage_ppm.cpp b/dep/src/g3dlite/GImage_ppm.cpp
new file mode 100644
index 00000000000..28f8cdf9ab0
--- /dev/null
+++ b/dep/src/g3dlite/GImage_ppm.cpp
@@ -0,0 +1,217 @@
+/**
+ @file GImage_ppm.cpp
+ @author Morgan McGuire, http://graphics.cs.williams.edu
+ @created 2002-05-27
+ @edited 2006-05-10
+ */
+#include "G3D/platform.h"
+#include "G3D/GImage.h"
+#include "G3D/BinaryInput.h"
+#include "G3D/BinaryOutput.h"
+#include "G3D/TextInput.h"
+#include "G3D/TextOutput.h"
+#include "G3D/Log.h"
+
+namespace G3D {
+
+void GImage::encodePPMASCII(
+ BinaryOutput& out) const {
+
+ TextOutput::Settings ppmOptions;
+ ppmOptions.convertNewlines = false;
+ ppmOptions.numColumns = 70;
+ ppmOptions.wordWrap = TextOutput::Settings::WRAP_WITHOUT_BREAKING;
+ TextOutput ppm(ppmOptions);
+
+ switch (m_channels) {
+ case 1:
+ {
+ ppm.printf("P2\n%d %d\n255\n", m_width, m_height);
+
+ const Color1uint8* c = this->pixel1();
+ // Insert newlines every 70 characters max
+ for (uint32 i = 0; i < (uint32)(m_width * m_height); ++i) {
+ ppm.printf("%d%c", c[i].value, (i % (70/4) == 0) ? '\n' : ' ');
+ }
+ }
+ break;
+
+ case 3:
+ {
+ ppm.printf("P3\n%d %d\n255\n", m_width, m_height);
+
+ const Color3uint8* c = this->pixel3();
+ // Insert newlines every 70 characters max
+ for (uint32 i = 0; i < (uint32)(m_width * m_height); ++i) {
+ ppm.printf("%d %d %d%c", c[i].r, c[i].g, c[i].b,
+ (i % (70/12) == 0) ?
+ '\n' : ' ');
+ }
+ }
+ break;
+ default:
+ alwaysAssertM(false, "PPM requires either 1 or 3 channels exactly.");
+ }
+
+ const std::string& s = ppm.commitString();
+ out.writeBytes(s.c_str(), s.length());
+}
+
+
+void GImage::encodePPM(
+ BinaryOutput& out) const {
+
+ // http://netpbm.sourceforge.net/doc/ppm.html
+ if (m_channels == 3) {
+ std::string header = format("P6 %d %d 255 ", m_width, m_height);
+ out.writeBytes(header.c_str(), header.size());
+ out.writeBytes(this->pixel3(), m_width * m_height * 3);
+ } else if (m_channels == 1) {
+ std::string header = format("P5 %d %d 255 ", m_width, m_height);
+ out.writeBytes(header.c_str(), header.size());
+ out.writeBytes(this->pixel1(), m_width * m_height);
+ } else {
+ alwaysAssertM(false, "PPM requires either 1 or 3 channels exactly.");
+ }
+}
+
+
+void GImage::decodePPMASCII(
+ BinaryInput& input) {
+
+ int ppmWidth;
+ int ppmHeight;
+
+ double maxColor;
+
+ // Create a TextInput object to parse ascii format
+ // Mixed binary/ascii formats will require more
+
+ const std::string inputStr = input.readString();
+
+ TextInput::Settings ppmOptions;
+ ppmOptions.cppLineComments = false;
+ ppmOptions.otherCommentCharacter = '#';
+ ppmOptions.signedNumbers = true;
+ ppmOptions.singleQuotedStrings = false;
+
+ TextInput ppmInput(TextInput::FROM_STRING, inputStr, ppmOptions);
+
+ //Skip first line in header P#
+ std::string ppmType = ppmInput.readSymbol();
+
+ ppmWidth = (int)ppmInput.readNumber();
+ ppmHeight = (int)ppmInput.readNumber();
+
+ // Everything but a PBM will have a max color value
+ if (ppmType != "P2") {
+ maxColor = ppmInput.readNumber();
+ } else {
+ maxColor = 255;
+ }
+
+ if ((ppmWidth < 0) ||
+ (ppmHeight < 0) ||
+ (maxColor <= 0)) {
+ throw GImage::Error("Invalid PPM Header.", input.getFilename());
+ }
+
+ // I don't think it's proper to scale values less than 255
+ if (maxColor <= 255.0) {
+ maxColor = 255.0;
+ }
+
+ m_width = ppmWidth;
+ m_height = ppmHeight;
+ m_channels = 3;
+ // always scale down to 1 byte per channel
+ m_byte = (uint8*)m_memMan->alloc(m_width * m_height * 3);
+
+ // Read in the image data. I am not validating if the values match the maxColor
+ // requirements. I only scale if needed to fit within the byte available.
+ for (uint32 i = 0; i < (uint32)(m_width * m_height); ++i) {
+ // read in color and scale to max pixel defined in header
+ // A max color less than 255 might need to be left alone and not scaled.
+ Color3uint8& curPixel = *(pixel3() + i);
+
+ if (ppmType == "P3") {
+ curPixel.r = (uint8)(ppmInput.readNumber() * (255.0 / maxColor));
+ curPixel.g = (uint8)(ppmInput.readNumber() * (255.0 / maxColor));
+ curPixel.b = (uint8)(ppmInput.readNumber() * (255.0 / maxColor));
+ } else if (ppmType == "P2") {
+ uint8 pixel = (uint8)(ppmInput.readNumber() * (255.0 / maxColor));
+ curPixel.r = pixel;
+ curPixel.g = pixel;
+ curPixel.b = pixel;
+ } else if (ppmType == "P1") {
+ int pixel = (uint8)(ppmInput.readNumber() * maxColor);
+ curPixel.r = pixel;
+ curPixel.g = pixel;
+ curPixel.b = pixel;
+ }
+ }
+}
+
+/** Consumes whitespace up to and including a number, but not the following character */
+static int scanUInt(BinaryInput& input) {
+ char c = input.readUInt8();
+ while (isWhiteSpace(c)) {
+ c = input.readUInt8();
+ }
+
+ std::string s;
+ s += c;
+ c = input.readUInt8();
+ while (!isWhiteSpace(c)) {
+ s += c;
+ c = input.readUInt8();
+ }
+
+ // Back up one to avoid consuming the last character
+ input.setPosition(input.getPosition() - 1);
+
+ int x;
+ sscanf(s.c_str(), "%d", &x);
+ return x;
+}
+
+
+void GImage::decodePPM(
+ BinaryInput& input) {
+
+ char head[2];
+ int w, h;
+
+ input.readBytes(head, 2);
+ if (head[0] != 'P' || (head[1] != '6') && (head[1] != '5')) {
+ throw GImage::Error("Invalid PPM Header.", input.getFilename());
+ }
+
+ w = scanUInt(input);
+ h = scanUInt(input);
+
+ // Skip the max color specifier
+ scanUInt(input);
+
+ if ((w < 0) ||
+ (h < 0) ||
+ (w > 100000) ||
+ (h > 100000)) {
+ throw GImage::Error("Invalid PPM size in header.", input.getFilename());
+ }
+
+ // Trailing whitespace
+ input.readUInt8();
+
+ if (head[1] == '6') {
+ // 3 channel
+ resize(w, h, 3);
+ input.readBytes(m_byte, m_width * m_height * 3);
+ } else if (head[1] == '5') {
+ // 1 channel
+ resize(w, h, 1);
+ input.readBytes(m_byte, m_width * m_height);
+ }
+}
+
+}
diff --git a/dep/src/g3dlite/GImage_tga.cpp b/dep/src/g3dlite/GImage_tga.cpp
new file mode 100644
index 00000000000..9785f879a1f
--- /dev/null
+++ b/dep/src/g3dlite/GImage_tga.cpp
@@ -0,0 +1,193 @@
+/**
+ @file GImage_tga.cpp
+ @author Morgan McGuire, http://graphics.cs.williams.edu
+ @created 2002-05-27
+ @edited 2009-05-10
+ */
+#include "G3D/platform.h"
+#include "G3D/GImage.h"
+#include "G3D/BinaryInput.h"
+#include "G3D/BinaryOutput.h"
+#include "G3D/Log.h"
+
+namespace G3D {
+
+void GImage::encodeTGA(
+ BinaryOutput& out) const {
+
+ out.setEndian(G3D_LITTLE_ENDIAN);
+
+ // ID length
+ out.writeUInt8(0);
+
+ // Color map Type
+ out.writeUInt8(0);
+
+ // Type
+ out.writeUInt8(2);
+
+ // Color map
+ out.skip(5);
+
+ // x, y offsets
+ out.writeUInt16(0);
+ out.writeUInt16(0);
+
+ // Width & height
+ out.writeUInt16(m_width);
+ out.writeUInt16(m_height);
+
+ // Color depth
+ if (m_channels == 1) {
+ // Force RGB mode
+ out.writeUInt8(8 * 3);
+ } else {
+ out.writeUInt8(8 * m_channels);
+ }
+
+ // Image descriptor
+ if (m_channels < 4) {
+ // 0 alpha bits
+ out.writeUInt8(0);
+ } else {
+ // 8 alpha bits
+ out.writeUInt8(8);
+ }
+
+ // Image ID (zero length)
+
+ if (m_channels == 1) {
+ // Pixels are upside down in BGR format.
+ for (int y = m_height - 1; y >= 0; --y) {
+ for (int x = 0; x < m_width; ++x) {
+ uint8 p = (m_byte[(y * m_width + x)]);
+ out.writeUInt8(p);
+ out.writeUInt8(p);
+ out.writeUInt8(p);
+ }
+ }
+ } else if (m_channels == 3) {
+ // Pixels are upside down in BGR format.
+ for (int y = m_height - 1; y >= 0; --y) {
+ for (int x = 0; x < m_width; ++x) {
+ uint8* p = &(m_byte[3 * (y * m_width + x)]);
+ out.writeUInt8(p[2]);
+ out.writeUInt8(p[1]);
+ out.writeUInt8(p[0]);
+ }
+ }
+ } else {
+ // Pixels are upside down in BGRA format.
+ for (int y = m_height - 1; y >= 0; --y) {
+ for (int x = 0; x < m_width; ++x) {
+ uint8* p = &(m_byte[4 * (y * m_width + x)]);
+ out.writeUInt8(p[2]);
+ out.writeUInt8(p[1]);
+ out.writeUInt8(p[0]);
+ out.writeUInt8(p[3]);
+ }
+ }
+ }
+
+ // Write "TRUEVISION-XFILE " 18 bytes from the end
+ // (with null termination)
+ out.writeString("TRUEVISION-XFILE ");
+}
+
+
+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);
+
+ m_width = input.readInt16();
+ m_height = input.readInt16();
+
+ int colorDepth = input.readUInt8();
+
+ if ((colorDepth != 24) && (colorDepth != 32)) {
+ throw Error("TGA files must be 24 or 32 bit.", input.getFilename());
+ }
+
+ if (colorDepth == 32) {
+ m_channels = 4;
+ } else {
+ m_channels = 3;
+ }
+
+ // Image descriptor contains overlay data as well
+ // as data indicating where the origin is
+ int imageDescriptor = input.readUInt8();
+ (void)imageDescriptor;
+
+ // Image ID
+ input.skip(IDLength);
+
+ m_byte = (uint8*)m_memMan->alloc(m_width * m_height * m_channels);
+ debugAssert(m_byte);
+
+ // Pixel data
+ int x;
+ int y;
+
+ if (m_channels == 3) {
+ for (y = m_height - 1; y >= 0; --y) {
+ for (x = 0; x < m_width; ++x) {
+ int b = input.readUInt8();
+ int g = input.readUInt8();
+ int r = input.readUInt8();
+
+ int i = (x + y * m_width) * 3;
+ m_byte[i + 0] = r;
+ m_byte[i + 1] = g;
+ m_byte[i + 2] = b;
+ }
+ }
+ } else {
+ for (y = m_height - 1; y >= 0; --y) {
+ for (x = 0; x < m_width; ++x) {
+ int b = input.readUInt8();
+ int g = input.readUInt8();
+ int r = input.readUInt8();
+ int a = input.readUInt8();
+
+ int i = (x + y * m_width) * 4;
+ m_byte[i + 0] = r;
+ m_byte[i + 1] = g;
+ m_byte[i + 2] = b;
+ m_byte[i + 3] = a;
+ }
+ }
+ }
+}
+
+}
diff --git a/dep/src/g3dlite/GLight.cpp b/dep/src/g3dlite/GLight.cpp
new file mode 100644
index 00000000000..37c8054ff3d
--- /dev/null
+++ b/dep/src/g3dlite/GLight.cpp
@@ -0,0 +1,267 @@
+/**
+ @file GLight.cpp
+
+ @maintainer Morgan McGuire, http://graphics.cs.williams.edu
+
+ @created 2003-11-12
+ @edited 2009-11-16
+*/
+#include "G3D/GLight.h"
+#include "G3D/Sphere.h"
+#include "G3D/CoordinateFrame.h"
+#include "G3D/Any.h"
+#include "G3D/stringutils.h"
+
+namespace G3D {
+
+GLight::GLight(const Any& any) {
+ any.verifyName("GLight");
+
+ if (any.type() == Any::TABLE) {
+ *this = GLight();
+ for (Any::AnyTable::Iterator it = any.table().begin(); it.hasMore(); ++it) {
+ const std::string& key = toLower(it->key);
+ if (key == "position") {
+ position = it->value;
+ } else if (key == "rightdirection") {
+ rightDirection = it->value;
+ } else if (key == "spotdirection") {
+ spotDirection = it->value;
+ } else if (key == "spotcutoff") {
+ spotCutoff = it->value.number();
+ } else if (key == "spotsquare") {
+ spotSquare = it->value.boolean();
+ } else if (key == "attenuation") {
+ attenuation[0] = it->value[0].number();
+ attenuation[1] = it->value[1].number();
+ attenuation[2] = it->value[2].number();
+ } else if (key == "color") {
+ color = it->value;
+ } else if (key == "enabled") {
+ enabled = it->value.boolean();
+ } else if (key == "specular") {
+ specular = it->value.boolean();
+ } else if (key == "diffuse") {
+ diffuse = it->value.boolean();
+ } else {
+ any.verify(false, "Illegal key: " + it->key);
+ }
+ }
+ } else if (toLower(any.name()) == "glight::directional") {
+
+ *this = directional(any[0], any[1],
+ (any.size() > 2) ? any[2] : Any(true),
+ (any.size() > 3) ? any[3] : Any(true));
+
+ } else if (toLower(any.name()) == "glight::point") {
+
+ *this = point(any[0], any[1],
+ (any.size() > 2) ? any[2] : Any(1),
+ (any.size() > 3) ? any[3] : Any(0),
+ (any.size() > 4) ? any[4] : Any(0.5f),
+ (any.size() > 5) ? any[5] : Any(true),
+ (any.size() > 6) ? any[6] : Any(true));
+
+ } else if (toLower(any.name()) == "glight::spot") {
+
+ *this = spot(any[0], any[1], any[2], any[3],
+ (any.size() > 4) ? any[4] : Any(1),
+ (any.size() > 5) ? any[5] : Any(0),
+ (any.size() > 6) ? any[6] : Any(0.5f),
+ (any.size() > 7) ? any[5] : Any(true),
+ (any.size() > 8) ? any[6] : Any(true));
+ } else {
+ any.verify(false, "Unrecognized name");
+ }
+}
+
+
+GLight::operator Any() const {
+ Any a(Any::TABLE, "GLight");
+ a.set("position", position.operator Any());
+ a.set("rightDirection", rightDirection.operator Any());
+ a.set("spotDirection", spotDirection.operator Any());
+ a.set("spotCutoff", spotCutoff);
+ a.set("spotSquare", spotSquare);
+
+ Any att(Any::ARRAY);
+ att.append(attenuation[0], attenuation[1], attenuation[2]);
+ a.set("attenuation", att);
+ a.set("color", color.operator Any());
+ a.set("enabled", enabled);
+ a.set("specular", specular);
+ a.set("diffuse", diffuse);
+ return a;
+}
+
+
+GLight::GLight() :
+ position(0, 0, 0, 0),
+ rightDirection(0,0,0),
+ spotDirection(0, 0, -1),
+ spotCutoff(180),
+ spotSquare(false),
+ color(Color3::white()),
+ enabled(false),
+ specular(true),
+ diffuse(true) {
+
+ attenuation[0] = 1.0;
+ attenuation[1] = 0.0;
+ attenuation[2] = 0.0;
+}
+
+
+GLight GLight::directional(const Vector3& toLight, const Color3& color, bool s, bool d) {
+ GLight L;
+ L.position = Vector4(toLight.direction(), 0);
+ L.color = color;
+ L.specular = s;
+ L.diffuse = d;
+ return L;
+}
+
+
+GLight GLight::point(const Vector3& pos, const Color3& color, float constAtt, float linAtt, float quadAtt, bool s, bool d) {
+ GLight L;
+ L.position = Vector4(pos, 1);
+ L.color = color;
+ L.attenuation[0] = constAtt;
+ L.attenuation[1] = linAtt;
+ L.attenuation[2] = quadAtt;
+ L.specular = s;
+ L.diffuse = d;
+ return L;
+}
+
+
+GLight GLight::spot(const Vector3& pos, const Vector3& pointDirection, float cutOffAngleDegrees, const Color3& color, float constAtt, float linAtt, float quadAtt, bool s, bool d) {
+ GLight L;
+ L.position = Vector4(pos, 1.0f);
+ L.spotDirection = pointDirection.direction();
+ debugAssert(cutOffAngleDegrees <= 90);
+ L.spotCutoff = cutOffAngleDegrees;
+ L.color = color;
+ L.attenuation[0] = constAtt;
+ L.attenuation[1] = linAtt;
+ L.attenuation[2] = quadAtt;
+ L.specular = s;
+ L.diffuse = d;
+ return L;
+}
+
+
+bool GLight::operator==(const GLight& other) const {
+ return (position == other.position) &&
+ (rightDirection == other.rightDirection) &&
+ (spotDirection == other.spotDirection) &&
+ (spotCutoff == other.spotCutoff) &&
+ (spotSquare == other.spotSquare) &&
+ (attenuation[0] == other.attenuation[0]) &&
+ (attenuation[1] == other.attenuation[1]) &&
+ (attenuation[2] == other.attenuation[2]) &&
+ (color == other.color) &&
+ (enabled == other.enabled) &&
+ (specular == other.specular) &&
+ (diffuse == other.diffuse);
+}
+
+
+bool GLight::operator!=(const GLight& other) const {
+ return !(*this == other);
+}
+
+
+Sphere GLight::effectSphere(float cutoff) const {
+ if (position.w == 0) {
+ // Directional light
+ return Sphere(Vector3::zero(), finf());
+ } else {
+ // Avoid divide by zero
+ cutoff = max(cutoff, 0.00001f);
+ float maxIntensity = max(color.r, max(color.g, color.b));
+
+ float radius = finf();
+
+ if (attenuation[2] != 0) {
+
+ // Solve I / attenuation.dot(1, r, r^2) < cutoff for r
+ //
+ // a[0] + a[1] r + a[2] r^2 > I/cutoff
+ //
+
+ float a = attenuation[2];
+ float b = attenuation[1];
+ float c = attenuation[0] - maxIntensity / cutoff;
+
+ float discrim = square(b) - 4 * a * c;
+
+ if (discrim >= 0) {
+ discrim = sqrt(discrim);
+
+ float r1 = (-b + discrim) / (2 * a);
+ float r2 = (-b - discrim) / (2 * a);
+
+ if (r1 < 0) {
+ if (r2 > 0) {
+ radius = r2;
+ }
+ } else if (r2 > 0) {
+ radius = min(r1, r2);
+ } else {
+ radius = r1;
+ }
+ }
+
+ } else if (attenuation[1] != 0) {
+
+ // Solve I / attenuation.dot(1, r) < cutoff for r
+ //
+ // r * a[1] + a[0] = I / cutoff
+ // r = (I / cutoff - a[0]) / a[1]
+
+ float radius = (maxIntensity / cutoff - attenuation[0]) / attenuation[1];
+ radius = max(radius, 0.0f);
+ }
+
+ return Sphere(position.xyz(), radius);
+
+ }
+}
+
+
+CoordinateFrame GLight::frame() const {
+ CoordinateFrame f;
+ if (rightDirection == Vector3::zero()) {
+ // No specified right direction; choose one automatically
+ if (position.w == 0) {
+ // Directional light
+ f.lookAt(-position.xyz());
+ } else {
+ // Spot light
+ f.lookAt(spotDirection);
+ }
+ } else {
+ const Vector3& Z = -spotDirection.direction();
+ Vector3 X = rightDirection.direction();
+
+ // Ensure the vectors are not too close together
+ while (abs(X.dot(Z)) > 0.9f) {
+ X = Vector3::random();
+ }
+
+ // Ensure perpendicular
+ X -= Z * Z.dot(X);
+ const Vector3& Y = Z.cross(X);
+
+ f.rotation.setColumn(Vector3::X_AXIS, X);
+ f.rotation.setColumn(Vector3::Y_AXIS, Y);
+ f.rotation.setColumn(Vector3::Z_AXIS, Z);
+ }
+ f.translation = position.xyz();
+
+ return f;
+}
+
+
+} // G3D
diff --git a/dep/src/g3dlite/GThread.cpp b/dep/src/g3dlite/GThread.cpp
new file mode 100644
index 00000000000..607e4b3572f
--- /dev/null
+++ b/dep/src/g3dlite/GThread.cpp
@@ -0,0 +1,229 @@
+/**
+ @file GThread.cpp
+
+ GThread class.
+
+ @created 2005-09-24
+ @edited 2005-10-22
+ */
+
+#include "G3D/GThread.h"
+#include "G3D/System.h"
+#include "G3D/debugAssert.h"
+#include "G3D/GMutex.h"
+
+namespace G3D {
+
+namespace _internal {
+
+class BasicThread: public GThread {
+public:
+ BasicThread(const std::string& name, void (*proc)(void*), void* param):
+ GThread(name), m_wrapperProc(proc), m_param(param) { }
+protected:
+ virtual void threadMain() {
+ m_wrapperProc(m_param);
+ }
+
+private:
+ void (*m_wrapperProc)(void*);
+
+ void* m_param;
+};
+
+} // namespace _internal
+
+
+GThread::GThread(const std::string& name):
+ m_status(STATUS_CREATED),
+ m_name(name) {
+
+#ifdef G3D_WIN32
+ m_event = NULL;
+#endif
+
+ // system-independent clear of handle
+ System::memset(&m_handle, 0, sizeof(m_handle));
+}
+
+GThread::~GThread() {
+#ifdef _MSC_VER
+# pragma warning( push )
+# pragma warning( disable : 4127 )
+#endif
+ alwaysAssertM(m_status != STATUS_RUNNING, "Deleting thread while running.");
+#ifdef _MSC_VER
+# pragma warning( pop )
+#endif
+
+#ifdef G3D_WIN32
+ if (m_event) {
+ ::CloseHandle(m_event);
+ }
+#endif
+}
+
+GThreadRef GThread::create(const std::string& name, void (*proc)(void*), void* param) {
+ return new _internal::BasicThread(name, proc, param);
+}
+
+
+bool GThread::started() const {
+ return m_status != STATUS_CREATED;
+}
+
+bool GThread::start(SpawnBehavior behavior) {
+
+ debugAssertM(! started(), "Thread has already executed.");
+ if (started()) {
+ return false;
+ }
+
+ m_status = STATUS_STARTED;
+
+ if (behavior == USE_CURRENT_THREAD) {
+ // Run on this thread
+ m_status = STATUS_RUNNING;
+ threadMain();
+ m_status = STATUS_COMPLETED;
+ return true;
+ }
+
+# ifdef G3D_WIN32
+ DWORD threadId;
+
+ m_event = ::CreateEvent(NULL, TRUE, FALSE, NULL);
+ debugAssert(m_event);
+
+ m_handle = ::CreateThread(NULL, 0, &internalThreadProc, this, 0, &threadId);
+
+ if (m_handle == NULL) {
+ ::CloseHandle(m_event);
+ m_event = NULL;
+ }
+
+ return (m_handle != NULL);
+# else
+ if (!pthread_create(&m_handle, NULL, &internalThreadProc, this)) {
+ return true;
+ } else {
+ // system-independent clear of handle
+ System::memset(&m_handle, 0, sizeof(m_handle));
+
+ return false;
+ }
+# endif
+}
+
+void GThread::terminate() {
+ if (m_handle) {
+# ifdef G3D_WIN32
+ ::TerminateThread(m_handle, 0);
+# else
+ pthread_kill(m_handle, SIGSTOP);
+# endif
+ // system-independent clear of handle
+ System::memset(&m_handle, 0, sizeof(m_handle));
+ }
+}
+
+
+bool GThread::running() const{
+ return (m_status == STATUS_RUNNING);
+}
+
+
+bool GThread::completed() const {
+ return (m_status == STATUS_COMPLETED);
+}
+
+
+void GThread::waitForCompletion() {
+ if (m_status == STATUS_COMPLETED) {
+ // Must be done
+ return;
+ }
+
+# ifdef G3D_WIN32
+ debugAssert(m_event);
+ ::WaitForSingleObject(m_event, INFINITE);
+# else
+ debugAssert(m_handle);
+ pthread_join(m_handle, NULL);
+# endif
+}
+
+
+#ifdef G3D_WIN32
+DWORD WINAPI GThread::internalThreadProc(LPVOID param) {
+ GThread* current = reinterpret_cast<GThread*>(param);
+ debugAssert(current->m_event);
+ current->m_status = STATUS_RUNNING;
+ current->threadMain();
+ current->m_status = STATUS_COMPLETED;
+ ::SetEvent(current->m_event);
+ return 0;
+}
+#else
+void* GThread::internalThreadProc(void* param) {
+ GThread* current = reinterpret_cast<GThread*>(param);
+ current->m_status = STATUS_RUNNING;
+ current->threadMain();
+ current->m_status = STATUS_COMPLETED;
+ return (void*)NULL;
+}
+#endif
+
+
+
+//GMutex implementation
+GMutex::GMutex() {
+#ifdef G3D_WIN32
+ ::InitializeCriticalSection(&m_handle);
+#else
+ int ret = pthread_mutexattr_init(&m_attr);
+ debugAssert(ret == 0);
+ ret = pthread_mutexattr_settype(&m_attr, PTHREAD_MUTEX_RECURSIVE);
+ debugAssert(ret == 0);
+ ret = pthread_mutex_init(&m_handle, &m_attr);
+ debugAssert(ret == 0);
+#endif
+}
+
+GMutex::~GMutex() {
+ //TODO: Debug check for locked
+#ifdef G3D_WIN32
+ ::DeleteCriticalSection(&m_handle);
+#else
+ int ret = pthread_mutex_destroy(&m_handle);
+ debugAssert(ret == 0);
+ ret = pthread_mutexattr_destroy(&m_attr);
+ debugAssert(ret == 0);
+#endif
+}
+
+bool GMutex::tryLock() {
+#ifdef G3D_WIN32
+ return (::TryEnterCriticalSection(&m_handle) != 0);
+#else
+ return (pthread_mutex_trylock(&m_handle) == 0);
+#endif
+}
+
+void GMutex::lock() {
+#ifdef G3D_WIN32
+ ::EnterCriticalSection(&m_handle);
+#else
+ pthread_mutex_lock(&m_handle);
+#endif
+}
+
+void GMutex::unlock() {
+#ifdef G3D_WIN32
+ ::LeaveCriticalSection(&m_handle);
+#else
+ pthread_mutex_unlock(&m_handle);
+#endif
+}
+
+} // namespace G3D
diff --git a/dep/src/g3dlite/GUniqueID.cpp b/dep/src/g3dlite/GUniqueID.cpp
new file mode 100644
index 00000000000..84c853e0e31
--- /dev/null
+++ b/dep/src/g3dlite/GUniqueID.cpp
@@ -0,0 +1,78 @@
+/**
+ @file GUniqueID.cpp
+ @author Morgan McGuire, http://graphics.cs.williams.edu
+ */
+#include "G3D/GUniqueID.h"
+#include "G3D/BinaryInput.h"
+#include "G3D/TextInput.h"
+#include "G3D/BinaryOutput.h"
+#include "G3D/TextOutput.h"
+#include "G3D/NetworkDevice.h"
+
+namespace G3D {
+
+void GUniqueID::serialize(BinaryOutput& b) const {
+ b.writeUInt64(id);
+}
+
+
+void GUniqueID::deserialize(BinaryInput& b) {
+ id = b.readUInt64();
+}
+
+void GUniqueID::serialize(TextOutput& t) const {
+ t.writeSymbol("(");
+ t.writeNumber((double)(id >> 32));
+ t.writeNumber((double)(id & 0xFFFFFFFF));
+ t.writeSymbol(")");
+}
+
+void GUniqueID::deserialize(TextInput& t) {
+ t.readSymbol("(");
+ id = (((uint64)t.readNumber()) << 32) + (uint64)t.readNumber();
+ t.readSymbol(")");
+}
+
+
+GUniqueID GUniqueID::create(uint16 tag) {
+ static uint64 counter = 0;
+ static uint64 systemID = 0;
+
+ if (systemID == 0) {
+ // Create a unique ID for this machine/program instance
+
+ // TODO: see ioctl(skfd, SIOCGIFHWADDR, &if_hwaddr)
+ Array<NetAddress> addr;
+ NetworkDevice::instance()->localHostAddresses(addr);
+ if (addr.size() > 0) {
+ systemID |= addr[0].ip();
+ }
+
+ union {
+ float64 ft;
+ uint64 ut;
+ };
+ ft = System::time();
+ systemID = ut << 22;
+ systemID ^= ((uint64)iRandom(0, 32768)) << 8;
+
+ systemID &= ~((uint64)1023 << 54);
+
+ // Ensure that the systemID is non-zero (vanishingly small probability)
+ if (systemID == 0) {
+ systemID = 1;
+ }
+ }
+
+ // No need for modulo; we'll all be dead before this counter
+ // overflows 54 bits
+ ++counter;
+
+ GUniqueID i;
+
+ i.id = (((uint64)(tag & 1023)) << 54) | (counter ^ systemID);
+
+ return i;
+}
+
+} // G3D
diff --git a/dep/src/g3dlite/Image1.cpp b/dep/src/g3dlite/Image1.cpp
new file mode 100644
index 00000000000..a61f7faa633
--- /dev/null
+++ b/dep/src/g3dlite/Image1.cpp
@@ -0,0 +1,224 @@
+/**
+ @file Image1.cpp
+
+ @maintainer Morgan McGuire, http://graphics.cs.williams.edu
+
+ @created 2007-01-31
+ @edited 2007-01-31
+*/
+
+
+#include "G3D/Image1.h"
+#include "G3D/Image1uint8.h"
+#include "G3D/GImage.h"
+#include "G3D/Color4.h"
+#include "G3D/Color4uint8.h"
+#include "G3D/Color1.h"
+#include "G3D/Color1uint8.h"
+#include "G3D/ImageFormat.h"
+
+namespace G3D {
+
+Image1::Image1(int w, int h, WrapMode wrap) : Map2D<Color1, Color1>(w, h, wrap) {
+ setAll(Color1(0.0f));
+}
+
+
+Image1::Ref Image1::fromGImage(const GImage& im, WrapMode wrap) {
+ switch (im.channels()) {
+ case 1:
+ return fromArray(im.pixel1(), im.width(), im.height(), wrap);
+
+ case 3:
+ return fromArray(im.pixel3(), im.width(), im.height(), wrap);
+
+ case 4:
+ return fromArray(im.pixel4(), im.width(), im.height(), wrap);
+
+ default:
+ debugAssertM(false, "Input GImage must have 1, 3, or 4 channels.");
+ return NULL;
+ }
+}
+
+
+Image1::Ref Image1::fromImage1uint8(const ReferenceCountedPointer<Image1uint8>& im) {
+ Ref out = createEmpty(static_cast<WrapMode>(im->wrapMode()));
+ out->resize(im->width(), im->height());
+
+ int N = im->width() * im->height();
+ const Color1uint8* src = reinterpret_cast<Color1uint8*>(im->getCArray());
+ for (int i = 0; i < N; ++i) {
+ out->data[i] = Color1(src[i]);
+ }
+
+ return out;
+}
+
+
+Image1::Ref Image1::createEmpty(int width, int height, WrapMode wrap) {
+ return new Type(width, height, wrap);
+}
+
+
+Image1::Ref Image1::createEmpty(WrapMode wrap) {
+ return createEmpty(0, 0, wrap);
+}
+
+
+Image1::Ref Image1::fromFile(const std::string& filename, WrapMode wrap, GImage::Format fmt) {
+ Ref out = createEmpty(wrap);
+ out->load(filename, fmt);
+ return out;
+}
+
+
+void Image1::load(const std::string& filename, GImage::Format fmt) {
+ copyGImage(GImage(filename, fmt));
+ setChanged(true);
+}
+
+
+Image1::Ref Image1::fromArray(const class Color3uint8* ptr, int w, int h, WrapMode wrap) {
+ Ref out = createEmpty(wrap);
+ out->copyArray(ptr, w, h);
+ return out;
+}
+
+
+Image1::Ref Image1::fromArray(const class Color1* ptr, int w, int h, WrapMode wrap) {
+ Ref out = createEmpty(wrap);
+ out->copyArray(ptr, w, h);
+ return out;
+}
+
+
+Image1::Ref Image1::fromArray(const class Color1uint8* ptr, int w, int h, WrapMode wrap) {
+ Ref out = createEmpty(wrap);
+ out->copyArray(ptr, w, h);
+ return out;
+}
+
+
+Image1::Ref Image1::fromArray(const class Color3* ptr, int w, int h, WrapMode wrap) {
+ Ref out = createEmpty(wrap);
+ out->copyArray(ptr, w, h);
+ return out;
+}
+
+
+Image1::Ref Image1::fromArray(const class Color4uint8* ptr, int w, int h, WrapMode wrap) {
+ Ref out = createEmpty(wrap);
+ out->copyArray(ptr, w, h);
+ return out;
+}
+
+
+Image1::Ref Image1::fromArray(const class Color4* ptr, int w, int h, WrapMode wrap) {
+ Ref out = createEmpty(wrap);
+ out->copyArray(ptr, w, h);
+ return out;
+}
+
+void Image1::copyGImage(const GImage& im) {
+ switch (im.channels()) {
+ case 1:
+ copyArray(im.pixel1(), im.width(), im.height());
+ break;
+
+ case 3:
+ copyArray(im.pixel3(), im.width(), im.height());
+ break;
+
+ case 4:
+ copyArray(im.pixel4(), im.width(), im.height());
+ break;
+ }
+}
+
+
+void Image1::copyArray(const Color3uint8* src, int w, int h) {
+ resize(w, h);
+
+ int N = w * h;
+ Color1* dst = data.getCArray();
+ // Convert int8 -> float
+ for (int i = 0; i < N; ++i) {
+ dst[i] = Color1(Color3(src[i]).average());
+ }
+}
+
+
+void Image1::copyArray(const Color4uint8* src, int w, int h) {
+ resize(w, h);
+
+ int N = w * h;
+ Color1* dst = data.getCArray();
+
+ // Strip alpha and convert
+ for (int i = 0; i < N; ++i) {
+ dst[i] = Color1(Color3(src[i].rgb()).average());
+ }
+}
+
+
+void Image1::copyArray(const Color1* src, int w, int h) {
+ resize(w, h);
+ System::memcpy(getCArray(), src, w * h * sizeof(Color1));
+}
+
+
+void Image1::copyArray(const Color4* src, int w, int h) {
+ resize(w, h);
+
+ int N = w * h;
+ Color1* dst = data.getCArray();
+
+ // Strip alpha
+ for (int i = 0; i < N; ++i) {
+ dst[i] = Color1(src[i].rgb().average());
+ }
+}
+
+
+void Image1::copyArray(const Color1uint8* src, int w, int h) {
+ resize(w, h);
+ int N = w * h;
+
+ Color1* dst = getCArray();
+ for (int i = 0; i < N; ++i) {
+ dst[i]= Color1(src[i]);
+ }
+}
+
+
+void Image1::copyArray(const Color3* src, int w, int h) {
+ resize(w, h);
+ int N = w * h;
+
+ Color1* dst = getCArray();
+ for (int i = 0; i < N; ++i) {
+ dst[i] = Color1(src[i].average());
+ }
+}
+
+
+/** Saves in any of the formats supported by G3D::GImage. */
+void Image1::save(const std::string& filename, GImage::Format fmt) {
+ GImage im(width(), height(), 1);
+
+ int N = im.width() * im.height();
+ Color1uint8* dst = im.pixel1();
+ for (int i = 0; i < N; ++i) {
+ dst[i] = Color1uint8(data[i]);
+ }
+
+ im.save(filename, fmt);
+}
+
+
+const ImageFormat* Image1::format() const {
+ return ImageFormat::L32F();
+}
+
+} // G3D
diff --git a/dep/src/g3dlite/Image1uint8.cpp b/dep/src/g3dlite/Image1uint8.cpp
new file mode 100644
index 00000000000..de2cbbc130b
--- /dev/null
+++ b/dep/src/g3dlite/Image1uint8.cpp
@@ -0,0 +1,212 @@
+/**
+ @file Image1uint8.cpp
+
+ @maintainer Morgan McGuire, http://graphics.cs.williams.edu
+
+ @created 2007-01-31
+ @edited 2008-01-13
+*/
+
+#include "G3D/Image1uint8.h"
+#include "G3D/Image3uint8.h"
+#include "G3D/Image1.h"
+#include "G3D/GImage.h"
+#include "G3D/Color1.h"
+#include "G3D/Color1uint8.h"
+#include "G3D/Color4.h"
+#include "G3D/Color4uint8.h"
+#include "G3D/ImageFormat.h"
+
+namespace G3D {
+
+Image1uint8::Image1uint8(int w, int h, WrapMode wrap) : Map2D<Color1uint8, Color1>(w, h, wrap) {
+ setAll(Color1uint8(0));
+}
+
+
+Image1uint8::Ref Image1uint8::fromImage3uint8(const ReferenceCountedPointer<class Image3uint8>& im) {
+ return fromArray(im->getCArray(), im->width(), im->height(), im->wrapMode());
+}
+
+
+Image1uint8::Ref Image1uint8::fromGImage(const GImage& im, WrapMode wrap) {
+ switch (im.channels()) {
+ case 1:
+ return fromArray(im.pixel1(), im.width(), im.height(), wrap);
+
+ case 3:
+ return fromArray(im.pixel3(), im.width(), im.height(), wrap);
+
+ case 4:
+ return fromArray(im.pixel4(), im.width(), im.height(), wrap);
+
+ default:
+ debugAssertM(false, "Input GImage must have 1, 3, or 4 channels.");
+ return NULL;
+ }
+}
+
+
+Image1uint8::Ref Image1uint8::fromImage1(const ReferenceCountedPointer<Image1>& im) {
+ Ref out = createEmpty(static_cast<WrapMode>(im->wrapMode()));
+ out->copyArray(im->getCArray(), im->width(), im->height());
+
+ return out;
+}
+
+
+Image1uint8::Ref Image1uint8::createEmpty(int width, int height, WrapMode wrap) {
+ return new Type(width, height, wrap);
+}
+
+
+Image1uint8::Ref Image1uint8::createEmpty(WrapMode wrap) {
+ return createEmpty(0, 0, wrap);
+}
+
+
+Image1uint8::Ref Image1uint8::fromFile(const std::string& filename, WrapMode wrap, GImage::Format fmt) {
+ Ref out = createEmpty(wrap);
+ out->load(filename, fmt);
+ return out;
+}
+
+
+Image1uint8::Ref Image1uint8::fromArray(const class Color3uint8* ptr, int w, int h, WrapMode wrap) {
+ Ref out = createEmpty(wrap);
+ out->copyArray(ptr, w, h);
+ return out;
+}
+
+
+Image1uint8::Ref Image1uint8::fromArray(const class Color1* ptr, int w, int h, WrapMode wrap) {
+ Ref out = createEmpty(wrap);
+ out->copyArray(ptr, w, h);
+ return out;
+}
+
+
+Image1uint8::Ref Image1uint8::fromArray(const class Color1uint8* ptr, int w, int h, WrapMode wrap) {
+ Ref out = createEmpty(wrap);
+ out->copyArray(ptr, w, h);
+ return out;
+}
+
+
+Image1uint8::Ref Image1uint8::fromArray(const class Color3* ptr, int w, int h, WrapMode wrap) {
+ Ref out = createEmpty(wrap);
+ out->copyArray(ptr, w, h);
+ return out;
+}
+
+
+Image1uint8::Ref Image1uint8::fromArray(const class Color4uint8* ptr, int w, int h, WrapMode wrap) {
+ Ref out = createEmpty(wrap);
+ out->copyArray(ptr, w, h);
+ return out;
+}
+
+
+Image1uint8::Ref Image1uint8::fromArray(const class Color4* ptr, int w, int h, WrapMode wrap) {
+ Ref out = createEmpty(wrap);
+ out->copyArray(ptr, w, h);
+ return out;
+}
+
+
+void Image1uint8::load(const std::string& filename, GImage::Format fmt) {
+ copyGImage(GImage(filename, fmt));
+ setChanged(true);
+}
+
+
+void Image1uint8::copyGImage(const GImage& im) {
+ switch (im.channels()) {
+ case 1:
+ copyArray(im.pixel1(), im.width(), im.height());
+ break;
+
+ case 3:
+ copyArray(im.pixel3(), im.width(), im.height());
+ break;
+
+ case 4:
+ copyArray(im.pixel4(), im.width(), im.height());
+ break;
+ }
+}
+
+
+void Image1uint8::copyArray(const Color3uint8* src, int w, int h) {
+ resize(w, h);
+ int N = w * h;
+
+ Color1uint8* dst = getCArray();
+ for (int i = 0; i < N; ++i) {
+ dst[i].value = (src[i].r + src[i].g + src[i].b) / 3;
+ }
+}
+
+void Image1uint8::copyArray(const Color3* src, int w, int h) {
+ resize(w, h);
+ int N = w * h;
+
+ Color1uint8* dst = getCArray();
+ for (int i = 0; i < N; ++i) {
+ dst[i] = Color1uint8(Color1(src[i].average()));
+ }
+}
+
+
+void Image1uint8::copyArray(const Color1uint8* ptr, int w, int h) {
+ resize(w, h);
+ System::memcpy(getCArray(), ptr, w * h);
+}
+
+
+void Image1uint8::copyArray(const Color1* src, int w, int h) {
+ resize(w, h);
+ int N = w * h;
+
+ Color1uint8* dst = getCArray();
+ for (int i = 0; i < N; ++i) {
+ dst[i] = Color1uint8(src[i]);
+ }
+}
+
+
+void Image1uint8::copyArray(const Color4uint8* ptr, int w, int h) {
+ resize(w, h);
+ int N = w * h;
+
+ Color1uint8* dst = getCArray();
+ for (int i = 0; i < N; ++i) {
+ dst[i].value = (ptr[i].r + ptr[i].g + ptr[i].b) / 3;
+ }
+}
+
+
+void Image1uint8::copyArray(const Color4* src, int w, int h) {
+ resize(w, h);
+ int N = w * h;
+
+ Color1uint8* dst = getCArray();
+ for (int i = 0; i < N; ++i) {
+ dst[i] = Color1uint8(Color1(src[i].rgb().average()));
+ }
+}
+
+
+/** Saves in any of the formats supported by G3D::GImage. */
+void Image1uint8::save(const std::string& filename, GImage::Format fmt) {
+ GImage im(width(), height(), 1);
+ System::memcpy(im.byte(), getCArray(), width() * height());
+ im.save(filename, fmt);
+}
+
+
+const ImageFormat* Image1uint8::format() const {
+ return ImageFormat::L8();
+}
+
+} // G3D
diff --git a/dep/src/g3dlite/Image3.cpp b/dep/src/g3dlite/Image3.cpp
new file mode 100644
index 00000000000..0d85bdf45da
--- /dev/null
+++ b/dep/src/g3dlite/Image3.cpp
@@ -0,0 +1,225 @@
+/**
+ @file Image3.cpp
+
+ @maintainer Morgan McGuire, http://graphics.cs.williams.edu
+
+ @created 2007-01-31
+ @edited 2007-01-31
+*/
+
+
+#include "G3D/Image3.h"
+#include "G3D/Image3uint8.h"
+#include "G3D/GImage.h"
+#include "G3D/Color4.h"
+#include "G3D/Color4uint8.h"
+#include "G3D/Color1.h"
+#include "G3D/Color1uint8.h"
+#include "G3D/ImageFormat.h"
+
+namespace G3D {
+
+Image3::Image3(int w, int h, WrapMode wrap) : Map2D<Color3, Color3>(w, h, wrap) {
+ setAll(Color3::black());
+}
+
+
+Image3::Ref Image3::fromGImage(const GImage& im, WrapMode wrap) {
+ switch (im.channels()) {
+ case 1:
+ return fromArray(im.pixel1(), im.width(), im.height(), wrap);
+
+ case 3:
+ return fromArray(im.pixel3(), im.width(), im.height(), wrap);
+
+ case 4:
+ return fromArray(im.pixel4(), im.width(), im.height(), wrap);
+
+ default:
+ debugAssertM(false, "Input GImage must have 1, 3, or 4 channels.");
+ return NULL;
+ }
+}
+
+
+Image3::Ref Image3::fromImage3uint8(const ReferenceCountedPointer<Image3uint8>& im) {
+ Ref out = createEmpty(im->wrapMode());
+ out->resize(im->width(), im->height());
+
+ int N = im->width() * im->height();
+ const Color3uint8* src = reinterpret_cast<Color3uint8*>(im->getCArray());
+ for (int i = 0; i < N; ++i) {
+ out->data[i] = Color3(src[i]);
+ }
+
+ return out;
+}
+
+
+Image3::Ref Image3::createEmpty(int width, int height, WrapMode wrap) {
+ return new Image3(width, height, wrap);
+}
+
+
+Image3::Ref Image3::createEmpty(WrapMode wrap) {
+ return createEmpty(0, 0, wrap);
+}
+
+
+Image3::Ref Image3::fromFile(const std::string& filename, WrapMode wrap, GImage::Format fmt) {
+ Ref out = createEmpty(wrap);
+ out->load(filename, fmt);
+ return out;
+}
+
+
+void Image3::load(const std::string& filename, GImage::Format fmt) {
+ copyGImage(GImage(filename, fmt));
+ setChanged(true);
+}
+
+
+Image3::Ref Image3::fromArray(const class Color3uint8* ptr, int w, int h, WrapMode wrap) {
+ Ref out = createEmpty(wrap);
+ out->copyArray(ptr, w, h);
+ return out;
+}
+
+
+Image3::Ref Image3::fromArray(const class Color1* ptr, int w, int h, WrapMode wrap) {
+ Ref out = createEmpty(wrap);
+ out->copyArray(ptr, w, h);
+ return out;
+}
+
+
+Image3::Ref Image3::fromArray(const class Color1uint8* ptr, int w, int h, WrapMode wrap) {
+ Ref out = createEmpty(wrap);
+ out->copyArray(ptr, w, h);
+ return out;
+}
+
+
+Image3::Ref Image3::fromArray(const class Color3* ptr, int w, int h, WrapMode wrap) {
+ Ref out = createEmpty(wrap);
+ out->copyArray(ptr, w, h);
+ return out;
+}
+
+
+Image3::Ref Image3::fromArray(const class Color4uint8* ptr, int w, int h, WrapMode wrap) {
+ Ref out = createEmpty(wrap);
+ out->copyArray(ptr, w, h);
+ return out;
+}
+
+
+Image3::Ref Image3::fromArray(const class Color4* ptr, int w, int h, WrapMode wrap) {
+ Ref out = createEmpty(wrap);
+ out->copyArray(ptr, w, h);
+ return out;
+}
+
+
+void Image3::copyGImage(const GImage& im) {
+ switch (im.channels()) {
+ case 1:
+ copyArray(im.pixel1(), im.width(), im.height());
+ break;
+
+ case 3:
+ copyArray(im.pixel3(), im.width(), im.height());
+ break;
+
+ case 4:
+ copyArray(im.pixel4(), im.width(), im.height());
+ break;
+ }
+}
+
+
+void Image3::copyArray(const Color3uint8* src, int w, int h) {
+ resize(w, h);
+
+ int N = w * h;
+ Color3* dst = data.getCArray();
+ // Convert int8 -> float
+ for (int i = 0; i < N; ++i) {
+ dst[i] = Color3(src[i]);
+ }
+}
+
+
+void Image3::copyArray(const Color4uint8* src, int w, int h) {
+ resize(w, h);
+
+ int N = w * h;
+ Color3* dst = data.getCArray();
+
+ // Strip alpha and convert
+ for (int i = 0; i < N; ++i) {
+ dst[i] = Color3(src[i].rgb());
+ }
+}
+
+
+void Image3::copyArray(const Color3* src, int w, int h) {
+ resize(w, h);
+ System::memcpy(getCArray(), src, w * h * sizeof(Color3));
+}
+
+
+void Image3::copyArray(const Color4* src, int w, int h) {
+ resize(w, h);
+
+ int N = w * h;
+ Color3* dst = data.getCArray();
+
+ // Strip alpha
+ for (int i = 0; i < N; ++i) {
+ dst[i] = src[i].rgb();
+ }
+}
+
+
+void Image3::copyArray(const Color1uint8* src, int w, int h) {
+ resize(w, h);
+ int N = w * h;
+
+ Color3* dst = getCArray();
+ for (int i = 0; i < N; ++i) {
+ dst[i].r = dst[i].g = dst[i].b = Color1(src[i]).value;
+ }
+}
+
+
+void Image3::copyArray(const Color1* src, int w, int h) {
+ resize(w, h);
+ int N = w * h;
+
+ Color3* dst = getCArray();
+ for (int i = 0; i < N; ++i) {
+ dst[i].r = dst[i].g = dst[i].b = src[i].value;
+ }
+}
+
+
+/** Saves in any of the formats supported by G3D::GImage. */
+void Image3::save(const std::string& filename, GImage::Format fmt) {
+ GImage im(width(), height(), 3);
+
+ int N = im.width() * im.height();
+ Color3uint8* dst = im.pixel3();
+ for (int i = 0; i < N; ++i) {
+ dst[i] = Color3uint8(data[i]);
+ }
+
+ im.save(filename, fmt);
+}
+
+
+const ImageFormat* Image3::format() const {
+ return ImageFormat::RGB32F();
+}
+
+} // G3D
diff --git a/dep/src/g3dlite/Image3uint8.cpp b/dep/src/g3dlite/Image3uint8.cpp
new file mode 100644
index 00000000000..86595bbd1f6
--- /dev/null
+++ b/dep/src/g3dlite/Image3uint8.cpp
@@ -0,0 +1,225 @@
+/**
+ @file Image3uint8.cpp
+
+ @maintainer Morgan McGuire, http://graphics.cs.williams.edu
+
+ @created 2007-01-31
+ @edited 2008-01-08
+*/
+
+#include "G3D/Image1uint8.h"
+#include "G3D/Image3uint8.h"
+#include "G3D/Image3.h"
+#include "G3D/GImage.h"
+#include "G3D/Color1.h"
+#include "G3D/Color1uint8.h"
+#include "G3D/Color4.h"
+#include "G3D/Color4uint8.h"
+#include "G3D/ImageFormat.h"
+
+namespace G3D {
+
+Image3uint8::Ref Image3uint8::fromImage1uint8(const ReferenceCountedPointer<class Image1uint8>& im) {
+ return fromArray(im->getCArray(), im->width(), im->height(), im->wrapMode());
+}
+
+
+Image3uint8::Image3uint8(int w, int h, WrapMode wrap) : Map2D<Color3uint8>(w, h, wrap) {
+ setAll(Color3::black());
+}
+
+
+Image3uint8::Ref Image3uint8::fromGImage(const GImage& im, WrapMode wrap) {
+ switch (im.channels()) {
+ case 1:
+ return fromArray(im.pixel1(), im.width(), im.height(), wrap);
+
+ case 3:
+ return fromArray(im.pixel3(), im.width(), im.height(), wrap);
+
+ case 4:
+ return fromArray(im.pixel4(), im.width(), im.height(), wrap);
+
+ default:
+ debugAssertM(false, "Input GImage must have 1, 3, or 4 channels.");
+ return NULL;
+ }
+}
+
+
+Image3uint8::Ref Image3uint8::fromImage3(const ReferenceCountedPointer<Image3>& im) {
+ Ref out = createEmpty(static_cast<WrapMode>(im->wrapMode()));
+ out->copyArray(im->getCArray(), im->width(), im->height());
+
+ return out;
+}
+
+
+Image3uint8::Ref Image3uint8::createEmpty(int width, int height, WrapMode wrap) {
+ return new Type(width, height, wrap);
+}
+
+
+Image3uint8::Ref Image3uint8::createEmpty(WrapMode wrap) {
+ return createEmpty(0, 0, wrap);
+}
+
+
+Image3uint8::Ref Image3uint8::fromFile(const std::string& filename, WrapMode wrap, GImage::Format fmt) {
+ Ref out = createEmpty(wrap);
+ out->load(filename, fmt);
+ return out;
+}
+
+
+Image3uint8::Ref Image3uint8::fromArray(const class Color3uint8* ptr, int w, int h, WrapMode wrap) {
+ Ref out = createEmpty(wrap);
+ out->copyArray(ptr, w, h);
+ return out;
+}
+
+
+Image3uint8::Ref Image3uint8::fromArray(const class Color1* ptr, int w, int h, WrapMode wrap) {
+ Ref out = createEmpty(wrap);
+ out->copyArray(ptr, w, h);
+ return out;
+}
+
+
+Image3uint8::Ref Image3uint8::fromArray(const class Color1uint8* ptr, int w, int h, WrapMode wrap) {
+ Ref out = createEmpty(wrap);
+ out->copyArray(ptr, w, h);
+ return out;
+}
+
+
+Image3uint8::Ref Image3uint8::fromArray(const class Color3* ptr, int w, int h, WrapMode wrap) {
+ Ref out = createEmpty(wrap);
+ out->copyArray(ptr, w, h);
+ return out;
+}
+
+
+Image3uint8::Ref Image3uint8::fromArray(const class Color4uint8* ptr, int w, int h, WrapMode wrap) {
+ Ref out = createEmpty(wrap);
+ out->copyArray(ptr, w, h);
+ return out;
+}
+
+
+Image3uint8::Ref Image3uint8::fromArray(const class Color4* ptr, int w, int h, WrapMode wrap) {
+ Ref out = createEmpty(wrap);
+ out->copyArray(ptr, w, h);
+ return out;
+}
+
+
+void Image3uint8::load(const std::string& filename, GImage::Format fmt) {
+ copyGImage(GImage(filename, fmt));
+ setChanged(true);
+}
+
+
+void Image3uint8::copyGImage(const GImage& im) {
+ switch (im.channels()) {
+ case 1:
+ copyArray(im.pixel1(), im.width(), im.height());
+ break;
+
+ case 3:
+ copyArray(im.pixel3(), im.width(), im.height());
+ break;
+
+ case 4:
+ copyArray(im.pixel4(), im.width(), im.height());
+ break;
+ }
+}
+
+
+void Image3uint8::copyArray(const Color1uint8* src, int w, int h) {
+ resize(w, h);
+ int N = w * h;
+
+ Color3uint8* dst = getCArray();
+ for (int i = 0; i < N; ++i) {
+ dst[i].r = dst[i].g = dst[i].b = src[i].value;
+ }
+}
+
+void Image3uint8::copyArray(const Color1* src, int w, int h) {
+ resize(w, h);
+ int N = w * h;
+
+ Color3uint8* dst = getCArray();
+ for (int i = 0; i < N; ++i) {
+ dst[i].r = dst[i].g = dst[i].b = Color1uint8(src[i]).value;
+ }
+}
+
+
+void Image3uint8::copyArray(const Color3uint8* ptr, int w, int h) {
+ resize(w, h);
+ System::memcpy(getCArray(), ptr, w * h * 3);
+}
+
+
+void Image3uint8::copyArray(const Color3* src, int w, int h) {
+ resize(w, h);
+ int N = w * h;
+
+ Color3uint8* dst = getCArray();
+ for (int i = 0; i < N; ++i) {
+ dst[i] = Color3uint8(src[i]);
+ }
+}
+
+
+void Image3uint8::copyArray(const Color4uint8* ptr, int w, int h) {
+ resize(w, h);
+
+ // Copy 3/4 bytes
+ GImage::RGBAtoRGB((const uint8*)ptr, (uint8*)getCArray(), w * h);
+}
+
+
+void Image3uint8::copyArray(const Color4* src, int w, int h) {
+ resize(w, h);
+ int N = w * h;
+
+ Color3uint8* dst = getCArray();
+ for (int i = 0; i < N; ++i) {
+ dst[i] = Color3uint8(src[i].rgb());
+ }
+}
+
+
+/** Saves in any of the formats supported by G3D::GImage. */
+void Image3uint8::save(const std::string& filename, GImage::Format fmt) {
+ GImage im(width(), height(), 3);
+ System::memcpy(im.byte(), getCArray(), width() * height() * 3);
+ im.save(filename, fmt);
+}
+
+
+ReferenceCountedPointer<class Image1uint8> Image3uint8::getChannel(int c) const {
+ debugAssert(c >= 0 && c <= 2);
+
+ Image1uint8Ref dst = Image1uint8::createEmpty(width(), height(), wrapMode());
+ const Color3uint8* srcArray = getCArray();
+ Color1uint8* dstArray = dst->getCArray();
+
+ const int N = width() * height();
+ for (int i = 0; i < N; ++i) {
+ dstArray[i] = Color1uint8(srcArray[i][c]);
+ }
+
+ return dst;
+}
+
+
+const ImageFormat* Image3uint8::format() const {
+ return ImageFormat::RGB8();
+}
+
+} // G3D
diff --git a/dep/src/g3dlite/Image4.cpp b/dep/src/g3dlite/Image4.cpp
new file mode 100644
index 00000000000..c6f2b10640d
--- /dev/null
+++ b/dep/src/g3dlite/Image4.cpp
@@ -0,0 +1,226 @@
+/**
+ @file Image4.cpp
+
+ @maintainer Morgan McGuire, http://graphics.cs.williams.edu
+
+ @created 2007-01-31
+ @edited 2008-07-27
+*/
+
+
+#include "G3D/Image4.h"
+#include "G3D/Image4uint8.h"
+#include "G3D/GImage.h"
+#include "G3D/Color3.h"
+#include "G3D/Color3uint8.h"
+#include "G3D/Color1.h"
+#include "G3D/Color1uint8.h"
+#include "G3D/ImageFormat.h"
+
+namespace G3D {
+
+Image4::Image4(int w, int h, WrapMode wrap) : Map2D<Color4, Color4>(w, h, wrap) {
+ setAll(Color4::zero());
+}
+
+
+Image4::Ref Image4::fromGImage(const GImage& im, WrapMode wrap) {
+ switch (im.channels()) {
+ case 1:
+ return fromArray(im.pixel1(), im.width(), im.height(), wrap);
+
+ case 3:
+ return fromArray(im.pixel3(), im.width(), im.height(), wrap);
+
+ case 4:
+ return fromArray(im.pixel4(), im.width(), im.height(), wrap);
+
+ default:
+ debugAssertM(false, "Input GImage must have 1, 3, or 4 channels.");
+ return NULL;
+ }
+}
+
+
+Image4::Ref Image4::fromImage4uint8(const ReferenceCountedPointer<Image4uint8>& im) {
+ Ref out = createEmpty(static_cast<WrapMode>(im->wrapMode()));
+ out->resize(im->width(), im->height());
+
+ int N = im->width() * im->height();
+ const Color4uint8* src = reinterpret_cast<Color4uint8*>(im->getCArray());
+ for (int i = 0; i < N; ++i) {
+ out->data[i] = Color4(src[i]);
+ }
+
+ return out;
+}
+
+
+Image4::Ref Image4::createEmpty(int width, int height, WrapMode wrap) {
+ return new Type(width, height, wrap);
+}
+
+
+Image4::Ref Image4::createEmpty(WrapMode wrap) {
+ return createEmpty(0, 0, wrap);
+}
+
+
+Image4::Ref Image4::fromFile(const std::string& filename, WrapMode wrap, GImage::Format fmt) {
+ Ref out = createEmpty(wrap);
+ out->load(filename);
+ return out;
+}
+
+
+void Image4::load(const std::string& filename, GImage::Format fmt) {
+ copyGImage(GImage(filename, fmt));
+ setChanged(true);
+}
+
+
+Image4::Ref Image4::fromArray(const class Color3uint8* ptr, int w, int h, WrapMode wrap) {
+ Ref out = createEmpty(wrap);
+ out->copyArray(ptr, w, h);
+ return out;
+}
+
+
+Image4::Ref Image4::fromArray(const class Color1* ptr, int w, int h, WrapMode wrap) {
+ Ref out = createEmpty(wrap);
+ out->copyArray(ptr, w, h);
+ return out;
+}
+
+
+Image4::Ref Image4::fromArray(const class Color1uint8* ptr, int w, int h, WrapMode wrap) {
+ Ref out = createEmpty(wrap);
+ out->copyArray(ptr, w, h);
+ return out;
+}
+
+
+Image4::Ref Image4::fromArray(const class Color3* ptr, int w, int h, WrapMode wrap) {
+ Ref out = createEmpty(wrap);
+ out->copyArray(ptr, w, h);
+ return out;
+}
+
+
+Image4::Ref Image4::fromArray(const class Color4uint8* ptr, int w, int h, WrapMode wrap) {
+ Ref out = createEmpty(wrap);
+ out->copyArray(ptr, w, h);
+ return out;
+}
+
+
+Image4::Ref Image4::fromArray(const class Color4* ptr, int w, int h, WrapMode wrap) {
+ Ref out = createEmpty(wrap);
+ out->copyArray(ptr, w, h);
+ return out;
+}
+
+
+void Image4::copyGImage(const GImage& im) {
+ switch (im.channels()) {
+ case 1:
+ copyArray(im.pixel1(), im.width(), im.height());
+ break;
+
+ case 3:
+ copyArray(im.pixel3(), im.width(), im.height());
+ break;
+
+ case 4:
+ copyArray(im.pixel4(), im.width(), im.height());
+ break;
+ }
+}
+
+
+void Image4::copyArray(const Color4uint8* src, int w, int h) {
+ resize(w, h);
+
+ int N = w * h;
+ Color4* dst = data.getCArray();
+ // Convert int8 -> float
+ for (int i = 0; i < N; ++i) {
+ dst[i] = Color4(src[i]);
+ }
+}
+
+
+void Image4::copyArray(const Color3uint8* src, int w, int h) {
+ resize(w, h);
+
+ int N = w * h;
+ Color4* dst = data.getCArray();
+
+ // Add alpha and convert
+ for (int i = 0; i < N; ++i) {
+ dst[i] = Color4(Color3(src[i]), 1.0f);
+ }
+}
+
+
+void Image4::copyArray(const Color4* src, int w, int h) {
+ resize(w, h);
+ System::memcpy(getCArray(), src, w * h * sizeof(Color4));
+}
+
+
+void Image4::copyArray(const Color3* src, int w, int h) {
+ resize(w, h);
+
+ int N = w * h;
+ Color4* dst = data.getCArray();
+
+ // Add alpha
+ for (int i = 0; i < N; ++i) {
+ dst[i] = Color4(src[i], 1.0f);
+ }
+}
+
+
+void Image4::copyArray(const Color1uint8* src, int w, int h) {
+ resize(w, h);
+ int N = w * h;
+
+ Color4* dst = getCArray();
+ for (int i = 0; i < N; ++i) {
+ dst[i].r = dst[i].g = dst[i].b = Color1(src[i]).value;
+ dst[i].a = 1.0f;
+ }
+}
+
+
+void Image4::copyArray(const Color1* src, int w, int h) {
+ resize(w, h);
+ int N = w * h;
+
+ Color4* dst = getCArray();
+ for (int i = 0; i < N; ++i) {
+ dst[i].r = dst[i].g = dst[i].b = src[i].value;
+ dst[i].a = 1.0f;
+ }
+}
+
+
+/** Saves in any of the formats supported by G3D::GImage. */
+void Image4::save(const std::string& filename, GImage::Format fmt) {
+ GImage im(width(), height(), 4);
+
+ int N = im.width() * im.height();
+ Color4uint8* dst = im.pixel4();
+ for (int i = 0; i < N; ++i) {
+ dst[i] = Color4uint8(data[i]);
+ }
+
+ im.save(filename, fmt);
+}
+
+const ImageFormat* Image4::format() const {
+ return ImageFormat::RGBA32F();
+}
+
+} // G3D
diff --git a/dep/src/g3dlite/Image4uint8.cpp b/dep/src/g3dlite/Image4uint8.cpp
new file mode 100644
index 00000000000..a94ddb12d03
--- /dev/null
+++ b/dep/src/g3dlite/Image4uint8.cpp
@@ -0,0 +1,222 @@
+/**
+ @file Image4uint8.cpp
+
+ @maintainer Morgan McGuire, http://graphics.cs.williams.edu
+
+ @created 2007-01-31
+ @edited 2008-07-31
+*/
+
+#include "G3D/Image4uint8.h"
+#include "G3D/Image4.h"
+#include "G3D/Image3uint8.h"
+#include "G3D/Image3.h"
+#include "G3D/GImage.h"
+#include "G3D/Color1.h"
+#include "G3D/Color1uint8.h"
+#include "G3D/Color4.h"
+#include "G3D/Color4uint8.h"
+#include "G3D/ImageFormat.h"
+
+namespace G3D {
+
+Image4uint8::Image4uint8(int w, int h, WrapMode wrap) : Map2D<Color4uint8, Color4>(w, h, wrap) {
+ setAll(Color4::zero());
+}
+
+
+Image4uint8::Ref Image4uint8::fromGImage(const GImage& im, WrapMode wrap) {
+ switch (im.channels()) {
+ case 1:
+ return fromArray(im.pixel1(), im.width(), im.height(), wrap);
+
+ case 3:
+ return fromArray(im.pixel3(), im.width(), im.height(), wrap);
+
+ case 4:
+ return fromArray(im.pixel4(), im.width(), im.height(), wrap);
+
+ default:
+ debugAssertM(false, "Input GImage must have 1, 3, or 4 channels.");
+ return NULL;
+ }
+}
+
+
+Image4uint8::Ref Image4uint8::fromImage4(const ReferenceCountedPointer<Image4>& im) {
+ Ref out = createEmpty(static_cast<WrapMode>(im->wrapMode()));
+ out->copyArray(im->getCArray(), im->width(), im->height());
+
+ return out;
+}
+
+
+Image4uint8::Ref Image4uint8::createEmpty(int width, int height, WrapMode wrap) {
+ return new Type(width, height, wrap);
+}
+
+
+Image4uint8::Ref Image4uint8::createEmpty(WrapMode wrap) {
+ return createEmpty(0, 0, wrap);
+}
+
+
+Image4uint8::Ref Image4uint8::fromFile(const std::string& filename, WrapMode wrap, GImage::Format fmt) {
+ Ref out = createEmpty(wrap);
+ out->load(filename, fmt);
+ return out;
+}
+
+
+Image4uint8::Ref Image4uint8::fromArray(const class Color3uint8* ptr, int w, int h, WrapMode wrap) {
+ Ref out = createEmpty(wrap);
+ out->copyArray(ptr, w, h);
+ return out;
+}
+
+
+Image4uint8::Ref Image4uint8::fromArray(const class Color1* ptr, int w, int h, WrapMode wrap) {
+ Ref out = createEmpty(wrap);
+ out->copyArray(ptr, w, h);
+ return out;
+}
+
+
+Image4uint8::Ref Image4uint8::fromArray(const class Color1uint8* ptr, int w, int h, WrapMode wrap) {
+ Ref out = createEmpty(wrap);
+ out->copyArray(ptr, w, h);
+ return out;
+}
+
+
+Image4uint8::Ref Image4uint8::fromArray(const class Color3* ptr, int w, int h, WrapMode wrap) {
+ Ref out = createEmpty(wrap);
+ out->copyArray(ptr, w, h);
+ return out;
+}
+
+
+Image4uint8::Ref Image4uint8::fromArray(const class Color4uint8* ptr, int w, int h, WrapMode wrap) {
+ Ref out = createEmpty(wrap);
+ out->copyArray(ptr, w, h);
+ return out;
+}
+
+
+Image4uint8::Ref Image4uint8::fromArray(const class Color4* ptr, int w, int h, WrapMode wrap) {
+ Ref out = createEmpty(wrap);
+ out->copyArray(ptr, w, h);
+ return out;
+}
+
+
+void Image4uint8::load(const std::string& filename, GImage::Format fmt) {
+ copyGImage(GImage(filename, fmt));
+ setChanged(true);
+}
+
+
+void Image4uint8::copyGImage(const GImage& im) {
+ switch (im.channels()) {
+ case 1:
+ copyArray(im.pixel1(), im.width(), im.height());
+ break;
+
+ case 3:
+ copyArray(im.pixel3(), im.width(), im.height());
+ break;
+
+ case 4:
+ copyArray(im.pixel4(), im.width(), im.height());
+ break;
+ }
+}
+
+
+void Image4uint8::copyArray(const Color1uint8* src, int w, int h) {
+ resize(w, h);
+ int N = w * h;
+
+ Color4uint8* dst = getCArray();
+ for (int i = 0; i < N; ++i) {
+ dst[i].r = dst[i].g = dst[i].b = src[i].value;
+ dst[i].a = 255;
+ }
+}
+
+void Image4uint8::copyArray(const Color1* src, int w, int h) {
+ resize(w, h);
+ int N = w * h;
+
+ Color4uint8* dst = getCArray();
+ for (int i = 0; i < N; ++i) {
+ dst[i].r = dst[i].g = dst[i].b = Color1uint8(src[i]).value;
+ dst[i].a = 255;
+ }
+}
+
+
+void Image4uint8::copyArray(const Color4uint8* ptr, int w, int h) {
+ resize(w, h);
+ System::memcpy(getCArray(), ptr, w * h * 4);
+}
+
+
+void Image4uint8::copyArray(const Color4* src, int w, int h) {
+ resize(w, h);
+ int N = w * h;
+
+ Color4uint8* dst = getCArray();
+ for (int i = 0; i < N; ++i) {
+ dst[i] = Color4uint8(src[i]);
+ }
+}
+
+
+void Image4uint8::copyArray(const Color3uint8* ptr, int w, int h) {
+ resize(w, h);
+
+ GImage::RGBtoRGBA((const uint8*)ptr, (uint8*)getCArray(), w * h);
+}
+
+
+void Image4uint8::copyArray(const Color3* src, int w, int h) {
+ resize(w, h);
+ int N = w * h;
+
+ Color4uint8* dst = getCArray();
+ for (int i = 0; i < N; ++i) {
+ dst[i] = Color4uint8(Color4(src[i], 1.0f));
+ }
+}
+
+
+/** Saves in any of the formats supported by G3D::GImage. */
+void Image4uint8::save(const std::string& filename, GImage::Format fmt) {
+ GImage im(width(), height(), 4);
+ System::memcpy(im.byte(), getCArray(), width() * height() * 4);
+ im.save(filename, fmt);
+}
+
+
+ReferenceCountedPointer<class Image1uint8> Image4uint8::getChannel(int c) const {
+ debugAssert(c >= 0 && c <= 3);
+
+ Image1uint8Ref dst = Image1uint8::createEmpty(width(), height(), wrapMode());
+ const Color4uint8* srcArray = getCArray();
+ Color1uint8* dstArray = dst->getCArray();
+
+ const int N = width() * height();
+ for (int i = 0; i < N; ++i) {
+ dstArray[i] = Color1uint8(srcArray[i][c]);
+ }
+
+ return dst;
+}
+
+
+const ImageFormat* Image4uint8::format() const {
+ return ImageFormat::RGBA8();
+}
+
+} // G3D
diff --git a/dep/src/g3dlite/ImageFormat.cpp b/dep/src/g3dlite/ImageFormat.cpp
new file mode 100644
index 00000000000..70de878c11e
--- /dev/null
+++ b/dep/src/g3dlite/ImageFormat.cpp
@@ -0,0 +1,567 @@
+/**
+ @file ImageFormat.cpp
+
+ @maintainer Morgan McGuire, http://graphics.cs.williams.edu
+
+ @created 2003-05-23
+ @edited 2009-12-10
+ */
+
+#include "GLG3D/glheaders.h"
+#include "G3D/ImageFormat.h"
+
+namespace G3D {
+
+ImageFormat::ImageFormat(
+ int _numComponents,
+ bool _compressed,
+ int _glFormat,
+ int _glBaseFormat,
+ int _luminanceBits,
+ int _alphaBits,
+ int _redBits,
+ int _greenBits,
+ int _blueBits,
+ int _depthBits,
+ int _stencilBits,
+ int _hardwareBitsPerTexel,
+ int _packedBitsPerTexel,
+ int glDataFormat,
+ bool _opaque,
+ bool _floatingPoint,
+ Code _code,
+ ColorSpace _colorSpace,
+ BayerPattern _bayerPattern) :
+
+ numComponents(_numComponents),
+ compressed(_compressed),
+ code(_code),
+ colorSpace(_colorSpace),
+ bayerPattern(_bayerPattern),
+ openGLFormat(_glFormat),
+ openGLBaseFormat(_glBaseFormat),
+ luminanceBits(_luminanceBits),
+ alphaBits(_alphaBits),
+ redBits(_redBits),
+ greenBits(_greenBits),
+ blueBits(_blueBits),
+ stencilBits(_stencilBits),
+ depthBits(_depthBits),
+ cpuBitsPerPixel(_packedBitsPerTexel),
+ packedBitsPerTexel(_packedBitsPerTexel),
+ openGLBitsPerPixel(_hardwareBitsPerTexel),
+ hardwareBitsPerTexel(_hardwareBitsPerTexel),
+ openGLDataFormat(glDataFormat),
+ opaque(_opaque),
+ floatingPoint(_floatingPoint) {
+
+ debugAssert(_packedBitsPerTexel <= _hardwareBitsPerTexel);
+}
+
+const ImageFormat* ImageFormat::depth(int depthBits) {
+
+ switch (depthBits) {
+ case 16:
+ return DEPTH16();
+
+ case 24:
+ return DEPTH24();
+
+ case 32:
+ return DEPTH32();
+
+ default:
+ debugAssertM(false, "Depth must be 16, 24, or 32.");
+ return DEPTH32();
+ }
+}
+
+
+const ImageFormat* ImageFormat::stencil(int bits) {
+ switch (bits) {
+ case 1:
+ return STENCIL1();
+
+ case 4:
+ return STENCIL4();
+
+ case 8:
+ return STENCIL8();
+
+ case 16:
+ return STENCIL16();
+
+ default:
+ debugAssertM(false, "Stencil must be 1, 4, 8 or 16.");
+ return STENCIL16();
+ }
+}
+
+
+ static const std::string nameArray[] =
+ {
+ "L8",
+ "L16",
+ "L16F",
+ "L32F",
+
+ "A8",
+ "A16",
+ "A16F",
+ "A32F",
+
+ "LA4",
+ "LA8",
+ "LA16",
+ "LA16F",
+ "LA32F",
+
+ "RGB5",
+ "RGB5A1",
+ "RGB8",
+ "RGB10",
+ "RGB10A2",
+ "RGB16",
+ "RGB16F",
+ "RGB32F",
+ "R11G11B10F",
+ "RGB9E10F",
+
+ "RGB8I",
+ "RGB8UI",
+
+ "ARGB8",
+ "BGR8",
+
+ "RG8",
+ "RG8I",
+ "RG8UI",
+
+ "RGBA8",
+ "RGBA16",
+ "RGBA16F",
+ "RGBA32F",
+
+ "RGBA32UI",
+
+ "BAYER_RGGB8",
+ "BAYER_GRBG8",
+ "BAYER_GBRG8",
+ "BAYER_BGGR8",
+ "BAYER_RGGB32F",
+ "BAYER_GRBG32F",
+ "BAYER_GBRG32F",
+ "BAYER_BGGR32F",
+
+ "HSV8",
+ "HSV32F",
+
+ "YUV420_PLANAR",
+ "YUV422",
+ "YUV444",
+
+ "RGB_DXT1",
+ "RGBA_DXT1",
+ "RGBA_DXT3",
+ "RGBA_DXT5",
+
+ "SRGB8",
+ "SRGBA8",
+
+ "SL8",
+ "SLA8",
+
+ "SRGB_DXT1",
+ "SRGBA_DXT1",
+ "SRGBA_DXT3",
+ "SRGBA_DXT5",
+
+ "DEPTH16",
+ "DEPTH24",
+ "DEPTH32",
+ "DEPTH32F",
+
+ "STENCIL1",
+ "STENCIL4",
+ "STENCIL8",
+ "STENCIL16",
+
+ "DEPTH24_STENCIL8",
+ ""
+ };
+
+const std::string& ImageFormat::name() const {
+ debugAssert(code < CODE_NUM);
+ return nameArray[code];
+}
+
+
+const ImageFormat* ImageFormat::fromString(const std::string& s) {
+
+ for (int i = 0; ! nameArray[i].empty(); ++i) {
+ if (s == nameArray[i]) {
+ return fromCode(ImageFormat::Code(i));
+ }
+ }
+ return NULL;
+}
+
+
+const ImageFormat* ImageFormat::fromCode(ImageFormat::Code code) {
+ switch (code) {
+ case ImageFormat::CODE_L8:
+ return ImageFormat::L8();
+
+ case ImageFormat::CODE_L16:
+ return ImageFormat::L16();
+
+ case ImageFormat::CODE_L16F:
+ return ImageFormat::L16F();
+
+ case ImageFormat::CODE_L32F:
+ return ImageFormat::L32F();
+
+ case ImageFormat::CODE_A8:
+ return ImageFormat::A8();
+
+ case ImageFormat::CODE_A16:
+ return ImageFormat::A16();
+
+ case ImageFormat::CODE_A16F:
+ return ImageFormat::A16F();
+
+ case ImageFormat::CODE_A32F:
+ return ImageFormat::A32F();
+
+ case ImageFormat::CODE_LA4:
+ return ImageFormat::LA4();
+
+ case ImageFormat::CODE_LA8:
+ return ImageFormat::LA8();
+
+ case ImageFormat::CODE_LA16:
+ return ImageFormat::LA16();
+
+ case ImageFormat::CODE_LA16F:
+ return ImageFormat::LA16F();
+ break;
+ case ImageFormat::CODE_LA32F:
+ return ImageFormat::LA32F();
+
+ case ImageFormat::CODE_RGB5:
+ return ImageFormat::RGB5();
+
+ case ImageFormat::CODE_RGB5A1:
+ return ImageFormat::RGB5A1();
+
+ case ImageFormat::CODE_RGB8:
+ return ImageFormat::RGB8();
+
+ case ImageFormat::CODE_RGB10:
+ return ImageFormat::RGB10();
+
+ case ImageFormat::CODE_RGB10A2:
+ return ImageFormat::RGB10A2();
+
+ case ImageFormat::CODE_RGB16:
+ return ImageFormat::RGB16();
+
+ case ImageFormat::CODE_RGB32F:
+ return ImageFormat::RGB32F();
+
+ case ImageFormat::CODE_R11G11B10F:
+ return ImageFormat::R11G11B10F();
+
+ case ImageFormat::CODE_RGB9E5F:
+ return ImageFormat::RGB9E5F();
+
+ case ImageFormat::CODE_RGB8I:
+ return ImageFormat::RGB8I();
+
+ case ImageFormat::CODE_RGB8UI:
+ return ImageFormat::RGB8UI();
+
+ case ImageFormat::CODE_ARGB8:
+ return NULL;
+
+ case ImageFormat::CODE_BGR8:
+ return ImageFormat::BGR8();
+
+ case ImageFormat::CODE_RG8:
+ return ImageFormat::RG8();
+
+ case ImageFormat::CODE_RG8I:
+ return ImageFormat::RG8I();
+
+ case ImageFormat::CODE_RG8UI:
+ return ImageFormat::RG8UI();
+
+ case ImageFormat::CODE_RGBA8:
+ return ImageFormat::RGBA8();
+
+ case ImageFormat::CODE_RGBA16:
+ return ImageFormat::RGBA16();
+
+ case ImageFormat::CODE_RGBA16F:
+ return ImageFormat::RGBA16F();
+
+ case ImageFormat::CODE_RGBA32F:
+ return ImageFormat::RGBA32F();
+
+ case ImageFormat::CODE_RGBA32UI:
+ return ImageFormat::RGBA32UI();
+
+ case ImageFormat::CODE_BAYER_RGGB8:
+ // TODO
+ case ImageFormat::CODE_BAYER_GRBG8:
+ // TODO
+ case ImageFormat::CODE_BAYER_GBRG8:
+ // TODO
+ case ImageFormat::CODE_BAYER_BGGR8:
+ // TODO
+ case ImageFormat::CODE_BAYER_RGGB32F:
+ // TODO
+ case ImageFormat::CODE_BAYER_GRBG32F:
+ // TODO
+ case ImageFormat::CODE_BAYER_GBRG32F:
+ // TODO
+ case ImageFormat::CODE_BAYER_BGGR32F:
+ // TODO
+
+ case ImageFormat::CODE_HSV8:
+ // TODO
+ case ImageFormat::CODE_HSV32F:
+ // TODO
+ return NULL;
+ break;
+
+ case ImageFormat::CODE_RGB_DXT1:
+ return ImageFormat::RGB_DXT1();
+ break;
+ case ImageFormat::CODE_RGBA_DXT1:
+ return ImageFormat::RGBA_DXT1();
+ break;
+ case ImageFormat::CODE_RGBA_DXT3:
+ return ImageFormat::RGBA_DXT3();
+ break;
+ case ImageFormat::CODE_RGBA_DXT5:
+ return ImageFormat::RGBA_DXT5();
+ break;
+
+ case ImageFormat::CODE_SRGB8:
+ return ImageFormat::SRGB8();
+ break;
+
+ case ImageFormat::CODE_SRGBA8:
+ return ImageFormat::SRGBA8();
+ break;
+
+ case ImageFormat::CODE_SL8:
+ return ImageFormat::SL8();
+ break;
+
+ case ImageFormat::CODE_SLA8:
+ return ImageFormat::SLA8();
+ break;
+
+ case ImageFormat::CODE_SRGB_DXT1:
+ return ImageFormat::SRGB_DXT1();
+ break;
+
+ case ImageFormat::CODE_SRGBA_DXT1:
+ return ImageFormat::SRGBA_DXT1();
+ break;
+
+ case ImageFormat::CODE_SRGBA_DXT3:
+ return ImageFormat::SRGBA_DXT3();
+ break;
+
+ case ImageFormat::CODE_SRGBA_DXT5:
+ return ImageFormat::SRGBA_DXT5();
+ break;
+
+ case ImageFormat::CODE_DEPTH16:
+ return ImageFormat::DEPTH16();
+ break;
+ case ImageFormat::CODE_DEPTH24:
+ return ImageFormat::DEPTH24();
+ break;
+ case ImageFormat::CODE_DEPTH32:
+ return ImageFormat::DEPTH32();
+ break;
+ case ImageFormat::CODE_DEPTH32F:
+ return ImageFormat::DEPTH32F();
+ break;
+
+ case ImageFormat::CODE_STENCIL1:
+ return ImageFormat::STENCIL1();
+ break;
+ case ImageFormat::CODE_STENCIL4:
+ return ImageFormat::STENCIL4();
+ break;
+ case ImageFormat::CODE_STENCIL8:
+ return ImageFormat::STENCIL8();
+ break;
+ case ImageFormat::CODE_STENCIL16:
+ return ImageFormat::STENCIL16();
+ break;
+
+ case ImageFormat::CODE_DEPTH24_STENCIL8:
+ return ImageFormat::DEPTH24_STENCIL8();
+ break;
+
+ case ImageFormat::CODE_YUV420_PLANAR:
+ return ImageFormat::YUV420_PLANAR();
+ break;
+
+ case ImageFormat::CODE_YUV422:
+ return ImageFormat::YUV422();
+ break;
+
+ case ImageFormat::CODE_YUV444:
+ return ImageFormat::YUV444();
+ break;
+
+ default:
+ return NULL;
+ }
+}
+
+// Helper variables for defining texture formats
+
+// Is floating point format
+static const bool FLOAT_FORMAT = true;
+static const bool INT_FORMAT = false;
+
+// Is opaque format (no alpha)
+static const bool OPAQUE_FORMAT = true;
+static const bool CLEAR_FORMAT = false;
+
+// Is compressed format (not raw component data)
+static const bool COMP_FORMAT = true;
+static const bool UNCOMP_FORMAT = false;
+
+#define DEFINE_TEXTUREFORMAT_METHOD(enumname, cmpnts, cmprssd, glf, glbf, lb, ab, rb, gb, bb, db, sb, hbpt, pbpt, gldf, opq, fp, code, cs) \
+ const ImageFormat* ImageFormat::enumname() { \
+ static const ImageFormat format(cmpnts, cmprssd, glf, glbf, lb, ab, rb, gb, bb, db, sb, hbpt, pbpt, gldf, opq, fp, code, cs); \
+ return &format; }
+
+DEFINE_TEXTUREFORMAT_METHOD(L8, 1, UNCOMP_FORMAT, GL_LUMINANCE8, GL_LUMINANCE, 8, 0, 0, 0, 0, 0, 0, 8, 8, GL_UNSIGNED_BYTE, OPAQUE_FORMAT, INT_FORMAT, CODE_L8, COLOR_SPACE_NONE);
+
+DEFINE_TEXTUREFORMAT_METHOD(L16, 1, UNCOMP_FORMAT, GL_LUMINANCE16, GL_LUMINANCE, 16, 0, 0, 0, 0, 0, 0, 16, 16,GL_UNSIGNED_SHORT, OPAQUE_FORMAT, INT_FORMAT, CODE_L16, COLOR_SPACE_NONE);
+
+DEFINE_TEXTUREFORMAT_METHOD(L16F, 1, UNCOMP_FORMAT, GL_LUMINANCE16F_ARB,GL_LUMINANCE, 16, 0, 0, 0, 0, 0, 0, 16, 16, GL_FLOAT, OPAQUE_FORMAT, FLOAT_FORMAT, CODE_L16F, COLOR_SPACE_NONE);
+
+DEFINE_TEXTUREFORMAT_METHOD(L32F, 1, UNCOMP_FORMAT, GL_LUMINANCE32F_ARB,GL_LUMINANCE, 32, 0, 0, 0, 0, 0, 0, 32, 32, GL_FLOAT, OPAQUE_FORMAT, FLOAT_FORMAT, CODE_L32F, COLOR_SPACE_NONE);
+
+DEFINE_TEXTUREFORMAT_METHOD(A8, 1, UNCOMP_FORMAT, GL_ALPHA8, GL_ALPHA, 0, 8, 0, 0, 0, 0, 0, 8, 8, GL_UNSIGNED_BYTE, CLEAR_FORMAT, INT_FORMAT, CODE_A8, COLOR_SPACE_NONE);
+
+DEFINE_TEXTUREFORMAT_METHOD(A16, 1, UNCOMP_FORMAT, GL_ALPHA16, GL_ALPHA, 0, 16, 0, 0, 0, 0, 0, 16, 16, GL_UNSIGNED_SHORT, CLEAR_FORMAT, INT_FORMAT, CODE_A16, COLOR_SPACE_NONE);
+
+DEFINE_TEXTUREFORMAT_METHOD(A16F, 1, UNCOMP_FORMAT, GL_ALPHA16F_ARB, GL_ALPHA, 0, 16, 0, 0, 0, 0, 0, 16, 16, GL_FLOAT, CLEAR_FORMAT, FLOAT_FORMAT, CODE_A16F, COLOR_SPACE_NONE);
+
+DEFINE_TEXTUREFORMAT_METHOD(A32F, 1, UNCOMP_FORMAT, GL_ALPHA32F_ARB, GL_ALPHA, 0, 32, 0, 0, 0, 0, 0, 32, 32, GL_FLOAT, CLEAR_FORMAT, FLOAT_FORMAT, CODE_A32F, COLOR_SPACE_NONE);
+
+DEFINE_TEXTUREFORMAT_METHOD(LA4, 2, UNCOMP_FORMAT, GL_LUMINANCE4_ALPHA4, GL_LUMINANCE_ALPHA, 4, 4, 0, 0, 0, 0, 0, 8, 8, GL_UNSIGNED_BYTE, CLEAR_FORMAT, INT_FORMAT, CODE_LA4, COLOR_SPACE_NONE);
+
+DEFINE_TEXTUREFORMAT_METHOD(LA8, 2, UNCOMP_FORMAT, GL_LUMINANCE8_ALPHA8, GL_LUMINANCE_ALPHA, 8, 8, 0, 0, 0, 0, 0, 16, 16, GL_UNSIGNED_BYTE, CLEAR_FORMAT, INT_FORMAT, CODE_LA8, COLOR_SPACE_NONE);
+
+DEFINE_TEXTUREFORMAT_METHOD(LA16, 2, UNCOMP_FORMAT, GL_LUMINANCE16_ALPHA16, GL_LUMINANCE_ALPHA, 16, 16, 0, 0, 0, 0, 0, 16*2, 16*2, GL_UNSIGNED_SHORT, CLEAR_FORMAT, INT_FORMAT, CODE_LA16, COLOR_SPACE_NONE);
+
+DEFINE_TEXTUREFORMAT_METHOD(LA16F, 2, UNCOMP_FORMAT, GL_LUMINANCE_ALPHA16F_ARB, GL_LUMINANCE_ALPHA, 16, 16, 0, 0, 0, 0, 0, 16*2, 16*2, GL_FLOAT, CLEAR_FORMAT, FLOAT_FORMAT, ImageFormat::CODE_LA16F, ImageFormat::COLOR_SPACE_NONE);
+
+DEFINE_TEXTUREFORMAT_METHOD(LA32F, 2, UNCOMP_FORMAT, GL_LUMINANCE_ALPHA32F_ARB, GL_LUMINANCE_ALPHA, 32, 32, 0, 0, 0, 0, 0, 32*2, 32*2, GL_FLOAT, CLEAR_FORMAT, FLOAT_FORMAT, ImageFormat::CODE_LA32F, ImageFormat::COLOR_SPACE_NONE);
+
+DEFINE_TEXTUREFORMAT_METHOD(BGR8, 3, UNCOMP_FORMAT, GL_RGB8, GL_BGR, 0, 0, 8, 8, 8, 0, 0, 32, 24, GL_UNSIGNED_BYTE, OPAQUE_FORMAT, INT_FORMAT, ImageFormat::CODE_BGR8, ImageFormat::COLOR_SPACE_RGB);
+
+DEFINE_TEXTUREFORMAT_METHOD(RG8, 2, UNCOMP_FORMAT, GL_RG8, GL_RG, 0, 0, 8, 8, 0, 0, 0, 16, 16, GL_UNSIGNED_BYTE, OPAQUE_FORMAT, INT_FORMAT, ImageFormat::CODE_RG8, ImageFormat::COLOR_SPACE_RGB);
+
+DEFINE_TEXTUREFORMAT_METHOD(RG8I, 2, UNCOMP_FORMAT, GL_RG8I, GL_RG, 0, 0, 8, 8, 0, 0, 0, 16, 16, GL_UNSIGNED_BYTE, OPAQUE_FORMAT, INT_FORMAT, ImageFormat::CODE_RG8I, ImageFormat::COLOR_SPACE_RGB);
+
+DEFINE_TEXTUREFORMAT_METHOD(RG8UI, 2, UNCOMP_FORMAT, GL_RG8UI, GL_RG, 0, 0, 8, 8, 0, 0, 0, 16, 16, GL_UNSIGNED_BYTE, OPAQUE_FORMAT, INT_FORMAT, ImageFormat::CODE_RG8UI, ImageFormat::COLOR_SPACE_RGB);
+
+DEFINE_TEXTUREFORMAT_METHOD(RGB5, 3, UNCOMP_FORMAT, GL_RGB5, GL_RGBA, 0, 0, 5, 5, 5, 0, 0, 16, 16, GL_UNSIGNED_BYTE, OPAQUE_FORMAT, INT_FORMAT, ImageFormat::CODE_RGB5, ImageFormat::COLOR_SPACE_RGB);
+
+DEFINE_TEXTUREFORMAT_METHOD(RGB5A1, 4, UNCOMP_FORMAT, GL_RGB5_A1, GL_RGBA, 0, 1, 5, 5, 5, 0, 0, 16, 16, GL_UNSIGNED_BYTE, OPAQUE_FORMAT, INT_FORMAT, ImageFormat::CODE_RGB5A1, ImageFormat::COLOR_SPACE_RGB);
+
+DEFINE_TEXTUREFORMAT_METHOD(RGB8, 3, UNCOMP_FORMAT, GL_RGB8, GL_RGB, 0, 0, 8, 8, 8, 0, 0, 32, 24, GL_UNSIGNED_BYTE, OPAQUE_FORMAT, INT_FORMAT, ImageFormat::CODE_RGB8, ImageFormat::COLOR_SPACE_RGB);
+
+DEFINE_TEXTUREFORMAT_METHOD(RGB10, 3, UNCOMP_FORMAT, GL_RGB10, GL_RGB, 0, 0, 10, 10, 10, 0, 0, 32, 10*3, GL_UNSIGNED_SHORT, OPAQUE_FORMAT, INT_FORMAT, ImageFormat::CODE_RGB10, ImageFormat::COLOR_SPACE_RGB);
+
+DEFINE_TEXTUREFORMAT_METHOD(RGB10A2, 4, UNCOMP_FORMAT, GL_RGB10_A2, GL_RGBA, 0, 2, 10, 10, 10, 0, 0, 32, 32, GL_UNSIGNED_INT_10_10_10_2, OPAQUE_FORMAT, INT_FORMAT, ImageFormat::CODE_RGB10A2, ImageFormat::COLOR_SPACE_RGB);
+
+DEFINE_TEXTUREFORMAT_METHOD(RGB16, 3, UNCOMP_FORMAT, GL_RGB16, GL_RGB, 0, 0, 16, 16, 16, 0, 0, 16*3, 16*3, GL_UNSIGNED_SHORT, OPAQUE_FORMAT, INT_FORMAT, ImageFormat::CODE_RGB16, ImageFormat::COLOR_SPACE_RGB);
+
+DEFINE_TEXTUREFORMAT_METHOD(RGB16F, 3, UNCOMP_FORMAT, GL_RGB16F_ARB, GL_RGB, 0, 0, 16, 16, 16, 0, 0, 16*3, 16*3, GL_FLOAT, OPAQUE_FORMAT, FLOAT_FORMAT, ImageFormat::CODE_RGB16F, ImageFormat::COLOR_SPACE_RGB);
+
+DEFINE_TEXTUREFORMAT_METHOD(RGB32F, 3, UNCOMP_FORMAT, GL_RGB32F_ARB, GL_RGB, 0, 0, 32, 32, 32, 0, 0, 32*3, 32*3, GL_FLOAT, OPAQUE_FORMAT, FLOAT_FORMAT, ImageFormat::CODE_RGB32F, ImageFormat::COLOR_SPACE_RGB);
+
+DEFINE_TEXTUREFORMAT_METHOD(RGBA8, 4, UNCOMP_FORMAT, GL_RGBA8, GL_RGBA, 0, 8, 8, 8, 8, 0, 0, 32, 32, GL_UNSIGNED_BYTE, CLEAR_FORMAT, INT_FORMAT, ImageFormat::CODE_RGBA8, ImageFormat::COLOR_SPACE_RGB);
+
+DEFINE_TEXTUREFORMAT_METHOD(RGBA16, 4, UNCOMP_FORMAT, GL_RGBA16, GL_RGBA, 0, 16, 16, 16, 16, 0, 0, 16*4, 16*4, GL_UNSIGNED_SHORT, CLEAR_FORMAT, INT_FORMAT, ImageFormat::CODE_RGBA16, ImageFormat::COLOR_SPACE_RGB);
+
+DEFINE_TEXTUREFORMAT_METHOD(RGBA16F, 4, UNCOMP_FORMAT, GL_RGBA16F_ARB, GL_RGBA, 0, 16, 16, 16, 16, 0, 0, 16*4, 16*4, GL_FLOAT, CLEAR_FORMAT, FLOAT_FORMAT, ImageFormat::CODE_RGBA16F, ImageFormat::COLOR_SPACE_RGB);
+
+DEFINE_TEXTUREFORMAT_METHOD(RGBA32F, 4, UNCOMP_FORMAT, GL_RGBA32F_ARB, GL_RGBA, 0, 32, 32, 32, 32, 0, 0, 32*4, 32*4, GL_FLOAT, CLEAR_FORMAT, FLOAT_FORMAT, ImageFormat::CODE_RGBA32F, ImageFormat::COLOR_SPACE_RGB);
+
+DEFINE_TEXTUREFORMAT_METHOD(RGBA32UI, 4, UNCOMP_FORMAT, GL_RGBA32UI, GL_RGBA, 0, 32, 32, 32, 32, 0, 0, 32*4, 32*4, GL_UNSIGNED_INT, CLEAR_FORMAT, INT_FORMAT, ImageFormat::CODE_RGBA32UI, ImageFormat::COLOR_SPACE_RGB);
+
+// Unsigned
+DEFINE_TEXTUREFORMAT_METHOD(R11G11B10F, 3, UNCOMP_FORMAT, GL_R11F_G11F_B10F_EXT, GL_RGB, 0, 0, 11, 11, 10, 0, 0, 32, 32, GL_FLOAT, OPAQUE_FORMAT, FLOAT_FORMAT, ImageFormat::CODE_R11G11B10F, ImageFormat::COLOR_SPACE_RGB);
+
+// Unsigned
+DEFINE_TEXTUREFORMAT_METHOD(RGB9E5F, 3, UNCOMP_FORMAT, GL_RGB9_E5_EXT, GL_RGB, 0, 0, 14, 14, 14, 0, 0, 32, 32, GL_FLOAT, OPAQUE_FORMAT, FLOAT_FORMAT, ImageFormat::CODE_RGB9E5F, ImageFormat::COLOR_SPACE_RGB);
+
+DEFINE_TEXTUREFORMAT_METHOD(RGB8I, 3, UNCOMP_FORMAT, GL_RGB8I_EXT, GL_RGB, 0, 0, 8, 8, 8, 0, 0, 32, 24, GL_UNSIGNED_BYTE, OPAQUE_FORMAT, INT_FORMAT, ImageFormat::CODE_RGB8I, ImageFormat::COLOR_SPACE_RGB);
+
+DEFINE_TEXTUREFORMAT_METHOD(RGB8UI, 3, UNCOMP_FORMAT, GL_RGB8UI_EXT, GL_RGB, 0, 0, 8, 8, 8, 0, 0, 32, 24, GL_UNSIGNED_BYTE, OPAQUE_FORMAT, INT_FORMAT, ImageFormat::CODE_RGB8UI, ImageFormat::COLOR_SPACE_RGB);
+
+
+DEFINE_TEXTUREFORMAT_METHOD(RGB_DXT1, 3, COMP_FORMAT, GL_COMPRESSED_RGB_S3TC_DXT1_EXT, GL_RGB, 0, 0, 0, 0, 0, 0, 0, 64, 64, GL_UNSIGNED_BYTE, OPAQUE_FORMAT, INT_FORMAT, ImageFormat::CODE_RGB_DXT1, ImageFormat::COLOR_SPACE_RGB);
+
+DEFINE_TEXTUREFORMAT_METHOD(RGBA_DXT1, 4, COMP_FORMAT, GL_COMPRESSED_RGBA_S3TC_DXT1_EXT, GL_RGBA, 0, 0, 0, 0, 0, 0, 0, 64, 64, GL_UNSIGNED_BYTE, CLEAR_FORMAT, INT_FORMAT, ImageFormat::CODE_RGBA_DXT1, ImageFormat::COLOR_SPACE_RGB);
+
+DEFINE_TEXTUREFORMAT_METHOD(RGBA_DXT3, 4, COMP_FORMAT, GL_COMPRESSED_RGBA_S3TC_DXT3_EXT, GL_RGBA, 0, 0, 0, 0, 0, 0, 0, 128, 128, GL_UNSIGNED_BYTE, CLEAR_FORMAT, INT_FORMAT, ImageFormat::CODE_RGBA_DXT3, ImageFormat::COLOR_SPACE_RGB);
+
+DEFINE_TEXTUREFORMAT_METHOD(RGBA_DXT5, 4, COMP_FORMAT, GL_COMPRESSED_RGBA_S3TC_DXT5_EXT, GL_RGBA, 0, 0, 0, 0, 0, 0, 0, 128, 128, GL_UNSIGNED_BYTE, CLEAR_FORMAT, INT_FORMAT, ImageFormat::CODE_RGBA_DXT5, ImageFormat::COLOR_SPACE_RGB);
+
+DEFINE_TEXTUREFORMAT_METHOD(SRGB8, 3, UNCOMP_FORMAT, GL_SRGB8, GL_RGB, 0, 0, 8, 8, 8, 0, 0, 32, 24, GL_UNSIGNED_BYTE, OPAQUE_FORMAT, INT_FORMAT, ImageFormat::CODE_SRGB8, ImageFormat::COLOR_SPACE_SRGB);
+
+DEFINE_TEXTUREFORMAT_METHOD(SRGBA8, 4, UNCOMP_FORMAT, GL_SRGB8_ALPHA8, GL_RGBA, 0, 8, 8, 8, 8, 0, 0, 32, 24, GL_UNSIGNED_BYTE, CLEAR_FORMAT, INT_FORMAT, ImageFormat::CODE_SRGBA8, ImageFormat::COLOR_SPACE_SRGB);
+
+DEFINE_TEXTUREFORMAT_METHOD(SL8, 1, UNCOMP_FORMAT, GL_SLUMINANCE8, GL_LUMINANCE, 8, 0, 0, 0, 0, 0, 0, 8, 8, GL_UNSIGNED_BYTE, OPAQUE_FORMAT, INT_FORMAT, ImageFormat::CODE_SL8, ImageFormat::COLOR_SPACE_SRGB);
+
+DEFINE_TEXTUREFORMAT_METHOD(SLA8, 2, UNCOMP_FORMAT, GL_SLUMINANCE8_ALPHA8, GL_LUMINANCE_ALPHA, 8, 8, 0, 0, 0, 0, 0, 16, 16, GL_UNSIGNED_BYTE, CLEAR_FORMAT, INT_FORMAT, ImageFormat::CODE_SLA8, ImageFormat::COLOR_SPACE_SRGB);
+
+DEFINE_TEXTUREFORMAT_METHOD(SRGB_DXT1, 3, COMP_FORMAT, GL_COMPRESSED_SRGB_S3TC_DXT1_EXT, GL_RGB, 0, 0, 0, 0, 0, 0, 0, 64, 64, GL_UNSIGNED_BYTE, OPAQUE_FORMAT, INT_FORMAT, ImageFormat::CODE_SRGB_DXT1, ImageFormat::COLOR_SPACE_SRGB);
+
+DEFINE_TEXTUREFORMAT_METHOD(SRGBA_DXT1, 4, COMP_FORMAT, GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT1_EXT, GL_RGBA, 0, 0, 0, 0, 0, 0, 0, 64, 64, GL_UNSIGNED_BYTE, CLEAR_FORMAT, INT_FORMAT, ImageFormat::CODE_SRGBA_DXT1, ImageFormat::COLOR_SPACE_SRGB);
+
+DEFINE_TEXTUREFORMAT_METHOD(SRGBA_DXT3, 4, COMP_FORMAT, GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT3_EXT, GL_RGBA, 0, 0, 0, 0, 0, 0, 0, 128, 128, GL_UNSIGNED_BYTE, CLEAR_FORMAT, INT_FORMAT, ImageFormat::CODE_SRGBA_DXT3, ImageFormat::COLOR_SPACE_SRGB);
+
+DEFINE_TEXTUREFORMAT_METHOD(SRGBA_DXT5, 4, COMP_FORMAT, GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT5_EXT, GL_RGBA, 0, 0, 0, 0, 0, 0, 0, 128, 128, GL_UNSIGNED_BYTE, CLEAR_FORMAT, INT_FORMAT, ImageFormat::CODE_SRGBA_DXT5, ImageFormat::COLOR_SPACE_SRGB);
+
+DEFINE_TEXTUREFORMAT_METHOD(DEPTH16, 1, UNCOMP_FORMAT, GL_DEPTH_COMPONENT16_ARB, GL_DEPTH_COMPONENT, 0, 0, 0, 0, 0, 16, 0, 16, 16, GL_UNSIGNED_SHORT, CLEAR_FORMAT, INT_FORMAT, ImageFormat::CODE_DEPTH16, ImageFormat::COLOR_SPACE_NONE);
+
+DEFINE_TEXTUREFORMAT_METHOD(DEPTH24, 1, UNCOMP_FORMAT, GL_DEPTH_COMPONENT24_ARB, GL_DEPTH_COMPONENT, 0, 0, 0, 0, 0, 24, 0, 32, 24, GL_UNSIGNED_INT, CLEAR_FORMAT, INT_FORMAT, ImageFormat::CODE_DEPTH24, ImageFormat::COLOR_SPACE_NONE);
+
+DEFINE_TEXTUREFORMAT_METHOD(DEPTH32, 1, UNCOMP_FORMAT, GL_DEPTH_COMPONENT32_ARB, GL_DEPTH_COMPONENT, 0, 0, 0, 0, 0, 32, 0, 32, 32, GL_UNSIGNED_INT, CLEAR_FORMAT, INT_FORMAT, ImageFormat::CODE_DEPTH32, ImageFormat::COLOR_SPACE_NONE);
+
+DEFINE_TEXTUREFORMAT_METHOD(DEPTH32F, 1, UNCOMP_FORMAT, GL_DEPTH_COMPONENT32_ARB, GL_DEPTH_COMPONENT, 0, 0, 0, 0, 0, 32, 0, 32, 32, GL_FLOAT, CLEAR_FORMAT, FLOAT_FORMAT, ImageFormat::CODE_DEPTH32F, ImageFormat::COLOR_SPACE_NONE);
+
+// These formats are for use with Renderbuffers only!
+DEFINE_TEXTUREFORMAT_METHOD(STENCIL1, 1, UNCOMP_FORMAT, GL_STENCIL_INDEX1_EXT, GL_STENCIL_INDEX, 0, 0, 0, 0, 0, 0, 1, 1, 1, GL_UNSIGNED_BYTE, CLEAR_FORMAT, INT_FORMAT, ImageFormat::CODE_STENCIL1, ImageFormat::COLOR_SPACE_NONE);
+
+DEFINE_TEXTUREFORMAT_METHOD(STENCIL4, 1, UNCOMP_FORMAT, GL_STENCIL_INDEX4_EXT, GL_STENCIL_INDEX, 0, 0, 0, 0, 0, 0, 4, 4, 4, GL_UNSIGNED_BYTE, CLEAR_FORMAT, INT_FORMAT, ImageFormat::CODE_STENCIL4, ImageFormat::COLOR_SPACE_NONE);
+
+DEFINE_TEXTUREFORMAT_METHOD(STENCIL8, 1, UNCOMP_FORMAT, GL_STENCIL_INDEX8_EXT, GL_STENCIL_INDEX, 0, 0, 0, 0, 0, 0, 8, 8, 8, GL_UNSIGNED_BYTE, CLEAR_FORMAT, INT_FORMAT, ImageFormat::CODE_STENCIL8, ImageFormat::COLOR_SPACE_NONE);
+
+DEFINE_TEXTUREFORMAT_METHOD(STENCIL16, 1, UNCOMP_FORMAT, GL_STENCIL_INDEX16_EXT, GL_STENCIL_INDEX, 0, 0, 0, 0, 0, 0, 16, 16, 16, GL_UNSIGNED_SHORT, CLEAR_FORMAT, INT_FORMAT, ImageFormat::CODE_STENCIL16, ImageFormat::COLOR_SPACE_NONE);
+
+DEFINE_TEXTUREFORMAT_METHOD(DEPTH24_STENCIL8, 2, UNCOMP_FORMAT, GL_DEPTH24_STENCIL8_EXT, GL_DEPTH_STENCIL_EXT,0, 0, 0, 0, 0, 24, 8, 32, 32, GL_UNSIGNED_INT, CLEAR_FORMAT, INT_FORMAT, ImageFormat::CODE_DEPTH24_STENCIL8, ImageFormat::COLOR_SPACE_NONE);
+
+DEFINE_TEXTUREFORMAT_METHOD(YUV420_PLANAR, 3, UNCOMP_FORMAT, GL_NONE, GL_NONE, 0, 0, 0, 0, 0, 0, 0, 12, 12, GL_UNSIGNED_BYTE, OPAQUE_FORMAT, INT_FORMAT, ImageFormat::CODE_YUV420_PLANAR, ImageFormat::COLOR_SPACE_YUV);
+DEFINE_TEXTUREFORMAT_METHOD(YUV422, 3, UNCOMP_FORMAT, GL_NONE, GL_NONE, 0, 0, 0, 0, 0, 0, 0, 16, 16, GL_UNSIGNED_BYTE, OPAQUE_FORMAT, INT_FORMAT, ImageFormat::CODE_YUV422, ImageFormat::COLOR_SPACE_YUV);
+DEFINE_TEXTUREFORMAT_METHOD(YUV444, 3, UNCOMP_FORMAT, GL_NONE, GL_NONE, 0, 0, 0, 0, 0, 0, 0, 24, 24, GL_UNSIGNED_BYTE, OPAQUE_FORMAT, INT_FORMAT, ImageFormat::CODE_YUV444, ImageFormat::COLOR_SPACE_YUV);
+
+}
diff --git a/dep/src/g3dlite/ImageFormat_convert.cpp b/dep/src/g3dlite/ImageFormat_convert.cpp
new file mode 100644
index 00000000000..ecefe6319c7
--- /dev/null
+++ b/dep/src/g3dlite/ImageFormat_convert.cpp
@@ -0,0 +1,1307 @@
+#include "G3D/ImageFormat.h"
+#include "G3D/Color1uint8.h"
+#include "G3D/Color3uint8.h"
+#include "G3D/Color4uint8.h"
+#include "G3D/Color1.h"
+#include "G3D/Color3.h"
+#include "G3D/Color4.h"
+
+
+namespace G3D {
+
+// this is the signature for all conversion routines (same parameters as ImageFormat::convert)
+typedef void (*ConvertFunc)(const Array<const void*>& srcBytes, int srcWidth, int srcHeight, const ImageFormat* srcFormat, int srcRowPadBits, const Array<void*>& dstBytes, const ImageFormat* dstFormat, int dstRowPadBits, bool invertY, ImageFormat::BayerAlgorithm bayerAlg);
+
+// this defines the conversion routines for converting between compatible formats
+static const int NUM_CONVERT_IMAGE_FORMATS = 5;
+struct ConvertAttributes {
+ ConvertFunc m_converter;
+ ImageFormat::Code m_sourceFormats[NUM_CONVERT_IMAGE_FORMATS];
+ ImageFormat::Code m_destFormats[NUM_CONVERT_IMAGE_FORMATS];
+ bool m_handlesSourcePadding;
+ bool m_handlesDestPadding;
+ bool m_handleInvertY;
+};
+
+// forward declare the converters we can use them below
+#define DECLARE_CONVERT_FUNC(name) static void name(const Array<const void*>& srcBytes, int srcWidth, int srcHeight, const ImageFormat* srcFormat, int srcRowPadBits, const Array<void*>& dstBytes, const ImageFormat* dstFormat, int dstRowPadBits, bool invertY, ImageFormat::BayerAlgorithm bayerAlg);
+
+DECLARE_CONVERT_FUNC(l8_to_rgb8);
+DECLARE_CONVERT_FUNC(l32f_to_rgb8);
+DECLARE_CONVERT_FUNC(rgb8_to_rgba8);
+DECLARE_CONVERT_FUNC(rgb8_to_bgr8);
+DECLARE_CONVERT_FUNC(rgb8_to_rgba32f);
+DECLARE_CONVERT_FUNC(bgr8_to_rgb8);
+DECLARE_CONVERT_FUNC(bgr8_to_rgba8);
+DECLARE_CONVERT_FUNC(bgr8_to_rgba32f);
+DECLARE_CONVERT_FUNC(rgba8_to_rgb8);
+DECLARE_CONVERT_FUNC(rgba8_to_bgr8);
+DECLARE_CONVERT_FUNC(rgba8_to_rgba32f);
+DECLARE_CONVERT_FUNC(rgb32f_to_rgba32f);
+DECLARE_CONVERT_FUNC(rgba32f_to_rgb8);
+DECLARE_CONVERT_FUNC(rgba32f_to_rgba8);
+DECLARE_CONVERT_FUNC(rgba32f_to_bgr8);
+DECLARE_CONVERT_FUNC(rgba32f_to_rgb32f);
+DECLARE_CONVERT_FUNC(rgba32f_to_bayer_rggb8);
+DECLARE_CONVERT_FUNC(rgba32f_to_bayer_gbrg8);
+DECLARE_CONVERT_FUNC(rgba32f_to_bayer_grbg8);
+DECLARE_CONVERT_FUNC(rgba32f_to_bayer_bggr8);
+DECLARE_CONVERT_FUNC(bayer_rggb8_to_rgba32f);
+DECLARE_CONVERT_FUNC(bayer_gbrg8_to_rgba32f);
+DECLARE_CONVERT_FUNC(bayer_grbg8_to_rgba32f);
+DECLARE_CONVERT_FUNC(bayer_bggr8_to_rgba32f);
+DECLARE_CONVERT_FUNC(rgb8_to_yuv420p);
+DECLARE_CONVERT_FUNC(rgb8_to_yuv422);
+DECLARE_CONVERT_FUNC(rgb8_to_yuv444);
+DECLARE_CONVERT_FUNC(yuv420p_to_rgb8);
+DECLARE_CONVERT_FUNC(yuv422_to_rgb8);
+DECLARE_CONVERT_FUNC(yuv444_to_rgb8);
+
+// this is the list of mappings between formats and the routines to perform them
+static const ConvertAttributes sConvertMappings[] = {
+
+ // RGB -> RGB color space
+ // L8 ->
+ {l8_to_rgb8, {ImageFormat::CODE_L8, ImageFormat::CODE_NONE}, {ImageFormat::CODE_RGB8, ImageFormat::CODE_NONE}, false, false, true},
+
+ // L32F ->
+ {l32f_to_rgb8, {ImageFormat::CODE_L32F, ImageFormat::CODE_NONE}, {ImageFormat::CODE_RGB8, ImageFormat::CODE_NONE}, false, false, true},
+
+ // RGB8 ->
+ {rgb8_to_rgba8, {ImageFormat::CODE_RGB8, ImageFormat::CODE_NONE}, {ImageFormat::CODE_RGBA8, ImageFormat::CODE_NONE}, false, false, true},
+ {rgb8_to_bgr8, {ImageFormat::CODE_RGB8, ImageFormat::CODE_NONE}, {ImageFormat::CODE_BGR8, ImageFormat::CODE_NONE}, false, false, true},
+ {rgb8_to_rgba32f, {ImageFormat::CODE_RGB8, ImageFormat::CODE_NONE}, {ImageFormat::CODE_RGBA32F, ImageFormat::CODE_NONE}, true, false, true},
+
+ // BGR8 ->
+ {bgr8_to_rgb8, {ImageFormat::CODE_BGR8, ImageFormat::CODE_NONE}, {ImageFormat::CODE_RGB8, ImageFormat::CODE_NONE}, false, false, true},
+ {bgr8_to_rgba8, {ImageFormat::CODE_BGR8, ImageFormat::CODE_NONE}, {ImageFormat::CODE_RGBA8, ImageFormat::CODE_NONE}, false, false, true},
+ {bgr8_to_rgba32f, {ImageFormat::CODE_BGR8, ImageFormat::CODE_NONE}, {ImageFormat::CODE_RGBA32F, ImageFormat::CODE_NONE}, true, false, true},
+
+ // RGBA8 ->
+ {rgba8_to_rgb8, {ImageFormat::CODE_RGBA8, ImageFormat::CODE_NONE}, {ImageFormat::CODE_RGB8, ImageFormat::CODE_NONE}, false, false, true},
+ {rgba8_to_bgr8, {ImageFormat::CODE_RGBA8, ImageFormat::CODE_NONE}, {ImageFormat::CODE_BGR8, ImageFormat::CODE_NONE}, false, false, true},
+ {rgba8_to_rgba32f, {ImageFormat::CODE_RGBA8, ImageFormat::CODE_NONE}, {ImageFormat::CODE_RGBA32F, ImageFormat::CODE_NONE}, true, false, true},
+
+ // RGB32F ->
+ {rgb32f_to_rgba32f, {ImageFormat::CODE_RGB32F, ImageFormat::CODE_NONE}, {ImageFormat::CODE_RGBA32F, ImageFormat::CODE_NONE}, true, false, true},
+
+ // RGBA32F ->
+ {rgba32f_to_rgb8, {ImageFormat::CODE_RGBA32F, ImageFormat::CODE_NONE}, {ImageFormat::CODE_RGB8, ImageFormat::CODE_NONE}, false, true, true},
+ {rgba32f_to_rgba8, {ImageFormat::CODE_RGBA32F, ImageFormat::CODE_NONE}, {ImageFormat::CODE_RGBA8, ImageFormat::CODE_NONE}, false, true, true},
+ {rgba32f_to_bgr8, {ImageFormat::CODE_RGBA32F, ImageFormat::CODE_NONE}, {ImageFormat::CODE_BGR8, ImageFormat::CODE_NONE}, false, true, true},
+ {rgba32f_to_rgb32f, {ImageFormat::CODE_RGBA32F, ImageFormat::CODE_NONE}, {ImageFormat::CODE_RGB32F, ImageFormat::CODE_NONE}, false, true, true},
+
+ // RGB -> BAYER color space
+ {rgba32f_to_bayer_rggb8, {ImageFormat::CODE_RGBA32F, ImageFormat::CODE_NONE}, {ImageFormat::CODE_BAYER_RGGB8, ImageFormat::CODE_NONE}, false, true, true},
+ {rgba32f_to_bayer_gbrg8, {ImageFormat::CODE_RGBA32F, ImageFormat::CODE_NONE}, {ImageFormat::CODE_BAYER_GBRG8, ImageFormat::CODE_NONE}, false, true, true},
+ {rgba32f_to_bayer_grbg8, {ImageFormat::CODE_RGBA32F, ImageFormat::CODE_NONE}, {ImageFormat::CODE_BAYER_GRBG8, ImageFormat::CODE_NONE}, false, true, true},
+ {rgba32f_to_bayer_bggr8, {ImageFormat::CODE_RGBA32F, ImageFormat::CODE_NONE}, {ImageFormat::CODE_BAYER_BGGR8, ImageFormat::CODE_NONE}, false, true, true},
+
+ // BAYER -> RGB color space
+ {bayer_rggb8_to_rgba32f, {ImageFormat::CODE_BAYER_RGGB8, ImageFormat::CODE_NONE}, {ImageFormat::CODE_RGBA32F, ImageFormat::CODE_NONE}, false, false, true},
+ {bayer_gbrg8_to_rgba32f, {ImageFormat::CODE_BAYER_GBRG8, ImageFormat::CODE_NONE}, {ImageFormat::CODE_RGBA32F, ImageFormat::CODE_NONE}, false, false, true},
+ {bayer_grbg8_to_rgba32f, {ImageFormat::CODE_BAYER_GRBG8, ImageFormat::CODE_NONE}, {ImageFormat::CODE_RGBA32F, ImageFormat::CODE_NONE}, false, false, true},
+ {bayer_bggr8_to_rgba32f, {ImageFormat::CODE_BAYER_BGGR8, ImageFormat::CODE_NONE}, {ImageFormat::CODE_RGBA32F, ImageFormat::CODE_NONE}, false, false, true},
+
+ // RGB <-> YUV color space
+ {rgb8_to_yuv420p, {ImageFormat::CODE_RGB8, ImageFormat::CODE_NONE}, {ImageFormat::CODE_YUV420_PLANAR, ImageFormat::CODE_NONE}, false, false, false},
+ {rgb8_to_yuv422, {ImageFormat::CODE_RGB8, ImageFormat::CODE_NONE}, {ImageFormat::CODE_YUV422, ImageFormat::CODE_NONE}, false, false, false},
+ {rgb8_to_yuv444, {ImageFormat::CODE_RGB8, ImageFormat::CODE_NONE}, {ImageFormat::CODE_YUV444, ImageFormat::CODE_NONE}, false, false, false},
+ {yuv420p_to_rgb8, {ImageFormat::CODE_YUV420_PLANAR, ImageFormat::CODE_NONE}, {ImageFormat::CODE_RGB8, ImageFormat::CODE_NONE}, false, false, false},
+ {yuv422_to_rgb8, {ImageFormat::CODE_YUV422, ImageFormat::CODE_NONE}, {ImageFormat::CODE_RGB8, ImageFormat::CODE_NONE}, false, false, false},
+ {yuv444_to_rgb8, {ImageFormat::CODE_YUV444, ImageFormat::CODE_NONE}, {ImageFormat::CODE_RGB8, ImageFormat::CODE_NONE}, false, false, false},
+};
+
+static ConvertFunc findConverter(TextureFormat::Code sourceCode, TextureFormat::Code destCode, bool needsSourcePadding, bool needsDestPadding, bool needsInvertY) {
+ int numRoutines = sizeof(sConvertMappings) / sizeof(ConvertAttributes);
+ for (int routineIndex = 0; routineIndex < numRoutines; ++routineIndex) {
+ int sourceIndex = 0;
+ ConvertAttributes routine = sConvertMappings[routineIndex];
+
+ while (routine.m_sourceFormats[sourceIndex] != ImageFormat::CODE_NONE) {
+ // check for matching source
+ if (routine.m_sourceFormats[sourceIndex] == sourceCode) {
+ int destIndex = 0;
+
+ // now check for matching dest to see if the routine fits
+ while (routine.m_destFormats[destIndex] != ImageFormat::CODE_NONE) {
+
+ // check if dest format matches and padding + invert rules match
+ if ((routine.m_destFormats[destIndex] == destCode) &&
+ (!needsSourcePadding || (routine.m_handlesSourcePadding == needsSourcePadding)) &&
+ (!needsDestPadding || (routine.m_handlesDestPadding == needsDestPadding)) &&
+ (!needsInvertY || (routine.m_handleInvertY == needsInvertY))) {
+
+ // found compatible converter
+ return routine.m_converter;
+ }
+ ++destIndex;
+ }
+ }
+ ++sourceIndex;
+ }
+ }
+
+ return NULL;
+}
+
+bool conversionAvailable(const ImageFormat* srcFormat, int srcRowPadBits, const ImageFormat* dstFormat, int dstRowPadBits, bool invertY = false) {
+ bool conversionAvailable = false;
+
+ // check if a conversion is available
+ if ( (srcFormat->code == dstFormat->code) && (srcRowPadBits == dstRowPadBits) && !invertY) {
+ conversionAvailable = true;
+ } else {
+ ConvertFunc directConverter = findConverter(srcFormat->code, dstFormat->code, srcRowPadBits > 0, dstRowPadBits > 0, invertY);
+
+ conversionAvailable = (directConverter != NULL);
+ }
+
+ return conversionAvailable;
+}
+
+bool ImageFormat::convert(const Array<const void*>& srcBytes, int srcWidth, int srcHeight, const ImageFormat* srcFormat, int srcRowPadBits,
+ const Array<void*>& dstBytes, const ImageFormat* dstFormat, int dstRowPadBits,
+ bool invertY, BayerAlgorithm bayerAlg) {
+
+ bool conversionAvailable = false;
+
+ // Handle direct copy of image to same format
+ if ( (srcFormat->code == dstFormat->code) && (srcRowPadBits == dstRowPadBits) && !invertY) {
+
+ System::memcpy(dstBytes[0], srcBytes[0], iCeil(((srcWidth * srcFormat->cpuBitsPerPixel + srcRowPadBits) * srcHeight) / 8.0f));
+ conversionAvailable = true;
+ } else {
+ // if no direct conversion routine exists,
+ // then look for conversion to intermediate
+ // and then from intermediate to dest.
+ // intermediate format is RGBA32F
+ ConvertFunc directConverter = findConverter(srcFormat->code, dstFormat->code, srcRowPadBits > 0, dstRowPadBits > 0, invertY);
+
+ // if we have a direct converter, use it, otherwise find intermdiate path
+ if (directConverter) {
+ directConverter(srcBytes, srcWidth, srcHeight, srcFormat, srcRowPadBits, dstBytes, dstFormat, dstRowPadBits, invertY, bayerAlg);
+ conversionAvailable = true;
+ } else {
+ ConvertFunc toInterConverter = findConverter(srcFormat->code, ImageFormat::CODE_RGBA32F, srcRowPadBits > 0, false, false);;
+ ConvertFunc fromInterConverter = findConverter(ImageFormat::CODE_RGBA32F, dstFormat->code, false, dstRowPadBits > 0, invertY);;
+
+ if (toInterConverter && fromInterConverter) {
+ Array<void*> tmp;
+ tmp.append(System::malloc(srcWidth * srcHeight * ImageFormat::RGBA32F()->cpuBitsPerPixel * 8));
+
+ toInterConverter(srcBytes, srcWidth, srcHeight, srcFormat, srcRowPadBits, tmp, ImageFormat::RGBA32F(), 0, false, bayerAlg);
+ fromInterConverter(reinterpret_cast<Array<const void*>&>(tmp), srcWidth, srcHeight, ImageFormat::RGBA32F(), 0, dstBytes, dstFormat, dstRowPadBits, invertY, bayerAlg);
+
+ System::free(tmp[0]);
+
+ conversionAvailable = true;
+ }
+ }
+ }
+
+ return conversionAvailable;
+}
+
+
+// *******************
+// RGB -> RGB color space conversions
+// *******************
+
+// L8 ->
+static void l8_to_rgb8(const Array<const void*>& srcBytes, int srcWidth, int srcHeight, const ImageFormat* srcFormat, int srcRowPadBits, const Array<void*>& dstBytes, const ImageFormat* dstFormat, int dstRowPadBits, bool invertY, ImageFormat::BayerAlgorithm bayerAlg) {
+ (void)bayerAlg;
+ (void)dstRowPadBits;
+ uint8* dst = static_cast<uint8*>(dstBytes[0]);
+ const uint8* src = static_cast<const uint8*>(srcBytes[0]);
+ for (int y = 0; y < srcHeight; ++y) {
+ for (int x = 0; x < srcWidth; ++x) {
+ int i = (invertY) ? ((srcHeight-1-y) * srcWidth +x) : (y * srcWidth + x);
+ int i3 = i * 3;
+
+ dst[i3 + 0] = src[i];
+ dst[i3 + 1] = src[i];
+ dst[i3 + 2] = src[i];
+ }
+ }
+}
+
+// L32F ->
+static void l32f_to_rgb8(const Array<const void*>& srcBytes, int srcWidth, int srcHeight, const ImageFormat* srcFormat, int srcRowPadBits, const Array<void*>& dstBytes, const ImageFormat* dstFormat, int dstRowPadBits, bool invertY, ImageFormat::BayerAlgorithm bayerAlg) {
+ int srcIndex = 0;
+ int dstByteOffset = 0;
+ uint8* dst = static_cast<uint8*>(dstBytes[0]);
+ const float* src = static_cast<const float*>(srcBytes[0]);
+
+ for (int y = 0; y < srcHeight; ++y) {
+ if (invertY) {
+ srcIndex = srcWidth * (srcHeight - y - 1);
+ }
+
+ for (int x = 0; x < srcWidth; ++x, ++srcIndex, dstByteOffset += 3) {
+ Color3uint8& d = *reinterpret_cast<Color3uint8*>(dst + dstByteOffset);
+ float s = src[srcIndex];
+
+ uint8 c = iMin(255, iFloor(s * 256));
+ d = Color3uint8(c, c, c);
+ }
+ }
+}
+
+// RGB8 ->
+static void rgb8_to_rgba8(const Array<const void*>& srcBytes, int srcWidth, int srcHeight, const ImageFormat* srcFormat, int srcRowPadBits, const Array<void*>& dstBytes, const ImageFormat* dstFormat, int dstRowPadBits, bool invertY, ImageFormat::BayerAlgorithm bayerAlg) {
+ uint8* dst = static_cast<uint8*>(dstBytes[0]);
+ const uint8* src = static_cast<const uint8*>(srcBytes[0]);
+ for (int y = 0; y < srcHeight; ++y) {
+ for (int x = 0; x < srcWidth; ++x) {
+ int i = (invertY) ? ((srcHeight-1-y) * srcWidth +x) : (y * srcWidth + x);
+ int i3 = i * 3;
+ int i4 = i3 + i;
+
+ dst[i4 + 0] = src[i3 + 0];
+ dst[i4 + 1] = src[i3 + 1];
+ dst[i4 + 2] = src[i3 + 2];
+ dst[i4 + 3] = 255;
+ }
+ }
+}
+
+static void rgb8_to_bgr8(const Array<const void*>& srcBytes, int srcWidth, int srcHeight, const ImageFormat* srcFormat, int srcRowPadBits, const Array<void*>& dstBytes, const ImageFormat* dstFormat, int dstRowPadBits, bool invertY, ImageFormat::BayerAlgorithm bayerAlg) {
+ uint8* dst = static_cast<uint8*>(dstBytes[0]);
+ const uint8* src = static_cast<const uint8*>(srcBytes[0]);
+ for (int y = 0; y < srcHeight; ++y) {
+ for (int x = 0; x < srcWidth; ++x) {
+ int i = (invertY) ? ((srcHeight-1-y) * srcWidth +x) : (y * srcWidth + x);
+ int i3 = i * 3;
+ dst[i3 + 0] = src[i3 + 2];
+ dst[i3 + 1] = src[i3 + 1];
+ dst[i3 + 2] = src[i3 + 0];
+ }
+ }
+}
+
+static void rgb8_to_rgba32f(const Array<const void*>& srcBytes, int srcWidth, int srcHeight, const ImageFormat* srcFormat, int srcRowPadBits, const Array<void*>& dstBytes, const ImageFormat* dstFormat, int dstRowPadBits, bool invertY, ImageFormat::BayerAlgorithm bayerAlg) {
+ debugAssertM(srcRowPadBits % 8 == 0, "Source row padding must be a multiple of 8 bits for this format");
+
+ int dstIndex = 0;
+ int srcByteOffset = 0;
+ int srcRowPadBytes = srcRowPadBits / 8;
+ Color4* dst = static_cast<Color4*>(dstBytes[0]);
+ const uint8* src = static_cast<const uint8*>(srcBytes[0]);
+
+ for (int y = 0; y < srcHeight; ++y) {
+ if (invertY) {
+ dstIndex = srcWidth * (srcHeight - 1 - y);
+ }
+ for (int x = 0; x < srcWidth; ++x, ++dstIndex, srcByteOffset += 3) {
+ const Color3uint8& s = *reinterpret_cast<const Color3uint8*>(src + srcByteOffset);
+ dst[dstIndex] = Color4(Color3(s), 1.0f);
+ }
+ srcByteOffset += srcRowPadBytes;
+ }
+}
+
+// BGR8 ->
+static void bgr8_to_rgb8(const Array<const void*>& srcBytes, int srcWidth, int srcHeight, const ImageFormat* srcFormat, int srcRowPadBits, const Array<void*>& dstBytes, const ImageFormat* dstFormat, int dstRowPadBits, bool invertY, ImageFormat::BayerAlgorithm bayerAlg) {
+ uint8* dst = static_cast<uint8*>(dstBytes[0]);
+ const uint8* src = static_cast<const uint8*>(srcBytes[0]);
+ for (int y = 0; y < srcHeight; ++y) {
+ for (int x = 0; x < srcWidth; ++x) {
+ int i = (invertY) ? ((srcHeight-1-y) * srcWidth +x) : (y * srcWidth + x);
+ int i3 = i * 3;
+ dst[i3 + 0] = src[i3 + 2];
+ dst[i3 + 1] = src[i3 + 1];
+ dst[i3 + 2] = src[i3 + 0];
+ }
+ }
+}
+
+static void bgr8_to_rgba8(const Array<const void*>& srcBytes, int srcWidth, int srcHeight, const ImageFormat* srcFormat, int srcRowPadBits, const Array<void*>& dstBytes, const ImageFormat* dstFormat, int dstRowPadBits, bool invertY, ImageFormat::BayerAlgorithm bayerAlg) {
+ uint8* dst = static_cast<uint8*>(dstBytes[0]);
+ const uint8* src = static_cast<const uint8*>(srcBytes[0]);
+ for (int y = 0; y < srcHeight; ++y) {
+ for (int x = 0; x < srcWidth; ++x) {
+ int i = (invertY) ? ((srcHeight-1-y) * srcWidth +x) : (y * srcWidth + x);
+ int i3 = i * 3;
+ int i4 = i3 + i;
+
+ dst[i4 + 0] = src[i3 + 2];
+ dst[i4 + 1] = src[i3 + 1];
+ dst[i4 + 2] = src[i3 + 0];
+ dst[i4 + 3] = 255;
+ }
+ }
+}
+
+static void bgr8_to_rgba32f(const Array<const void*>& srcBytes, int srcWidth, int srcHeight, const ImageFormat* srcFormat, int srcRowPadBits, const Array<void*>& dstBytes, const ImageFormat* dstFormat, int dstRowPadBits, bool invertY, ImageFormat::BayerAlgorithm bayerAlg) {
+ debugAssertM(srcRowPadBits % 8 == 0, "Source row padding must be a multiple of 8 bits for this format");
+
+ int dstIndex = 0;
+ int srcByteOffset = 0;
+ int srcRowPadBytes = srcRowPadBits / 8;
+ Color4* dst = static_cast<Color4*>(dstBytes[0]);
+ const uint8* src = static_cast<const uint8*>(srcBytes[0]);
+
+ for (int y = 0; y < srcHeight; ++y) {
+ if (invertY) {
+ dstIndex = srcWidth * (srcHeight - 1 - y);
+ }
+
+ for (int x = 0; x < srcWidth; ++x, ++dstIndex, srcByteOffset += 3) {
+ const Color3uint8& s = *reinterpret_cast<const Color3uint8*>(src + srcByteOffset);
+ dst[dstIndex] = Color4(Color3(s).bgr(), 1.0f);
+ }
+ srcByteOffset += srcRowPadBytes;
+ }
+}
+
+// RGBA8 ->
+static void rgba8_to_rgb8(const Array<const void*>& srcBytes, int srcWidth, int srcHeight, const ImageFormat* srcFormat, int srcRowPadBits, const Array<void*>& dstBytes, const ImageFormat* dstFormat, int dstRowPadBits, bool invertY, ImageFormat::BayerAlgorithm bayerAlg) {
+ uint8* dst = static_cast<uint8*>(dstBytes[0]);
+ const uint8* src = static_cast<const uint8*>(srcBytes[0]);
+ for (int y = 0; y < srcHeight; ++y) {
+ for (int x = 0; x < srcWidth; ++x) {
+ int i = (invertY) ? ((srcHeight-1-y) * srcWidth +x) : (y * srcWidth + x);
+ int i3 = i * 3;
+ int i4 = i3 + i;
+
+ dst[i3 + 0] = src[i4 + 0];
+ dst[i3 + 1] = src[i4 + 1];
+ dst[i3 + 2] = src[i4 + 2];
+ }
+ }
+}
+
+static void rgba8_to_bgr8(const Array<const void*>& srcBytes, int srcWidth, int srcHeight, const ImageFormat* srcFormat, int srcRowPadBits, const Array<void*>& dstBytes, const ImageFormat* dstFormat, int dstRowPadBits, bool invertY, ImageFormat::BayerAlgorithm bayerAlg) {
+ uint8* dst = static_cast<uint8*>(dstBytes[0]);
+ const uint8* src = static_cast<const uint8*>(srcBytes[0]);
+ for (int y = 0; y < srcHeight; ++y) {
+ for (int x = 0; x < srcWidth; ++x) {
+ int i = (invertY) ? ((srcHeight-1-y) * srcWidth +x) : (y * srcWidth + x);
+ int i3 = i * 3;
+ int i4 = i3 + i;
+
+ dst[i3 + 0] = src[i4 + 2];
+ dst[i3 + 1] = src[i4 + 1];
+ dst[i3 + 2] = src[i4 + 0];
+ }
+ }
+}
+
+static void rgba8_to_rgba32f(const Array<const void*>& srcBytes, int srcWidth, int srcHeight, const ImageFormat* srcFormat, int srcRowPadBits, const Array<void*>& dstBytes, const ImageFormat* dstFormat, int dstRowPadBits, bool invertY, ImageFormat::BayerAlgorithm bayerAlg) {
+ debugAssertM(srcRowPadBits % 8 == 0, "Source row padding must be a multiple of 8 bits for this format");
+
+ int dstIndex = 0;
+ int srcByteOffset = 0;
+ int srcRowPadBytes = srcRowPadBits / 8;
+ Color4* dst = static_cast<Color4*>(dstBytes[0]);
+ const uint8* src = static_cast<const uint8*>(srcBytes[0]);
+
+ for (int y = 0; y < srcHeight; ++y) {
+ if (invertY) {
+ dstIndex = srcWidth * (srcHeight - 1 - y);
+ }
+
+ for (int x = 0; x < srcWidth; ++x, ++dstIndex, srcByteOffset += 4) {
+ const Color4uint8& s = *reinterpret_cast<const Color4uint8*>(src + srcByteOffset);
+ dst[dstIndex] = Color4(s);
+ }
+ srcByteOffset += srcRowPadBytes;
+ }
+}
+
+// RGB32F ->
+static void rgb32f_to_rgba32f(const Array<const void*>& srcBytes, int srcWidth, int srcHeight, const ImageFormat* srcFormat, int srcRowPadBits, const Array<void*>& dstBytes, const ImageFormat* dstFormat, int dstRowPadBits, bool invertY, ImageFormat::BayerAlgorithm bayerAlg) {
+ debugAssertM(srcRowPadBits % 8 == 0, "Source row padding must be a multiple of 8 bits for this format");
+
+ int dstIndex = 0;
+ int srcByteOffset = 0;
+ int srcRowPadBytes = srcRowPadBits / 8;
+ Color4* dst = static_cast<Color4*>(dstBytes[0]);
+ const uint8* src = static_cast<const uint8*>(srcBytes[0]);
+
+ for (int y = 0; y < srcHeight; ++y) {
+ if (invertY) {
+ dstIndex = srcWidth * (srcHeight - 1 - y);
+ }
+
+ for (int x = 0; x < srcWidth; ++x, ++dstIndex, srcByteOffset += 3 * sizeof(float)) {
+ const Color3& s = *reinterpret_cast<const Color3*>(src + srcByteOffset);
+ dst[dstIndex] = Color4(Color3(s), 1.0f);
+ }
+ srcByteOffset += srcRowPadBytes;
+ }
+}
+
+// RGBA32F ->
+static void rgba32f_to_rgb8(const Array<const void*>& srcBytes, int srcWidth, int srcHeight, const ImageFormat* srcFormat, int srcRowPadBits, const Array<void*>& dstBytes, const ImageFormat* dstFormat, int dstRowPadBits, bool invertY, ImageFormat::BayerAlgorithm bayerAlg) {
+ debugAssertM(dstRowPadBits % 8 == 0, "Destination row padding must be a multiple of 8 bits for this format");
+
+ int srcIndex = 0;
+ int dstByteOffset = 0;
+ int dstRowPadBytes = dstRowPadBits / 8;
+ uint8* dst = static_cast<uint8*>(dstBytes[0]);
+ const Color4* src = static_cast<const Color4*>(srcBytes[0]);
+
+ for (int y = 0; y < srcHeight; ++y) {
+ if (invertY) {
+ srcIndex = srcWidth * (srcHeight - y - 1);
+ }
+
+ for (int x = 0; x < srcWidth; ++x, ++srcIndex, dstByteOffset += 3) {
+ Color3uint8& d = *reinterpret_cast<Color3uint8*>(dst + dstByteOffset);
+ const Color4& s = src[srcIndex];
+
+ d = Color3uint8(s.rgb());
+ }
+ dstByteOffset += dstRowPadBytes;
+ }
+}
+
+static void rgba32f_to_rgba8(const Array<const void*>& srcBytes, int srcWidth, int srcHeight, const ImageFormat* srcFormat, int srcRowPadBits, const Array<void*>& dstBytes, const ImageFormat* dstFormat, int dstRowPadBits, bool invertY, ImageFormat::BayerAlgorithm bayerAlg) {
+ debugAssertM(dstRowPadBits % 8 == 0, "Destination row padding must be a multiple of 8 bits for this format");
+
+ int srcIndex = 0;
+ int dstByteOffset = 0;
+ int dstRowPadBytes = dstRowPadBits / 8;
+ uint8* dst = static_cast<uint8*>(dstBytes[0]);
+ const Color4* src = static_cast<const Color4*>(srcBytes[0]);
+
+ for (int y = 0; y < srcHeight; ++y) {
+ if (invertY) {
+ srcIndex = srcWidth * (srcHeight - 1 - y);
+ }
+ for (int x = 0; x < srcWidth; ++x, ++srcIndex, dstByteOffset += 4) {
+ Color4uint8& d = *reinterpret_cast<Color4uint8*>(dst + dstByteOffset);
+ const Color4& s = src[srcIndex];
+
+ d = Color4uint8(s);
+ }
+ dstByteOffset += dstRowPadBytes;
+ }
+}
+
+static void rgba32f_to_bgr8(const Array<const void*>& srcBytes, int srcWidth, int srcHeight, const ImageFormat* srcFormat, int srcRowPadBits, const Array<void*>& dstBytes, const ImageFormat* dstFormat, int dstRowPadBits, bool invertY, ImageFormat::BayerAlgorithm bayerAlg) {
+ debugAssertM(dstRowPadBits % 8 == 0, "Destination row padding must be a multiple of 8 bits for this format");
+
+ int srcIndex = 0;
+ int dstByteOffset = 0;
+ int dstRowPadBytes = dstRowPadBits / 8;
+ uint8* dst = static_cast<uint8*>(dstBytes[0]);
+ const Color4* src = static_cast<const Color4*>(srcBytes[0]);
+
+ for (int y = 0; y < srcHeight; ++y) {
+ if (invertY) {
+ srcIndex = srcWidth * (srcHeight - y - 1);
+ }
+
+ for (int x = 0; x < srcWidth; ++x, ++srcIndex, dstByteOffset += 3) {
+ Color3uint8& d = *reinterpret_cast<Color3uint8*>(dst + dstByteOffset);
+ const Color4& s = src[srcIndex];
+
+ d = Color3uint8(s.rgb()).bgr();
+ }
+ dstByteOffset += dstRowPadBytes;
+ }
+}
+
+static void rgba32f_to_rgb32f(const Array<const void*>& srcBytes, int srcWidth, int srcHeight, const ImageFormat* srcFormat, int srcRowPadBits, const Array<void*>& dstBytes, const ImageFormat* dstFormat, int dstRowPadBits, bool invertY, ImageFormat::BayerAlgorithm bayerAlg) {
+ debugAssertM(dstRowPadBits % 8 == 0, "Destination row padding must be a multiple of 8 bits for this format");
+
+ int srcIndex = 0;
+ int dstByteOffset = 0;
+ int dstRowPadBytes = dstRowPadBits / 8;
+ uint8* dst = static_cast<uint8*>(dstBytes[0]);
+ const Color4* src = static_cast<const Color4*>(srcBytes[0]);
+
+ for (int y = 0; y < srcHeight; ++y) {
+ if (invertY) {
+ srcIndex = srcWidth * (srcHeight - 1 - y);
+ }
+ for (int x = 0; x < srcWidth; ++x, ++srcIndex, dstByteOffset += 3 * sizeof(float)) {
+ Color3& d = *reinterpret_cast<Color3*>(dst + dstByteOffset);
+ const Color4& s = src[srcIndex];
+ d = s.rgb();
+ }
+ dstByteOffset += dstRowPadBytes;
+ }
+}
+
+// *******************
+// RGB <-> YUV color space conversions
+// *******************
+
+static uint32 blendPixels(uint32 pixel1, uint32 pixel2) {
+ static const uint32 rbMask = 0x00FF00FF;
+ static const uint32 agMask = 0xFF00FF00;
+
+ // Compute two color channels at a time. Use >> 1 for fast division by two
+ // Using alternating color channels prevents overflow
+ const uint32 rb = ((pixel1 & rbMask) + (pixel2 & rbMask)) >> 1;
+
+ // Shift first to avoid overflow in alpha channel
+ const uint32 ag = (((pixel1 & agMask) >> 1) + ((pixel2 & agMask) >> 1));
+
+ return ((rb & rbMask) | (ag & agMask));
+}
+
+#define PIXEL_RGB8_TO_YUV_Y(r, g, b) static_cast<uint8>(iClamp(((66 * r + 129 * g + 25 * b + 128) >> 8) + 16, 0, 255))
+#define PIXEL_RGB8_TO_YUV_U(r, g, b) static_cast<uint8>(iClamp(((-38 * r - 74 * g + 112 * b + 128) >> 8) + 128, 0, 255))
+#define PIXEL_RGB8_TO_YUV_V(r, g, b) static_cast<uint8>(iClamp(((112 * r - 94 * g - 18 * b + 128) >> 8) + 128, 0, 255))
+
+static void rgb8_to_yuv420p(const Array<const void*>& srcBytes, int srcWidth, int srcHeight, const ImageFormat* srcFormat, int srcRowPadBits, const Array<void*>& dstBytes, const ImageFormat* dstFormat, int dstRowPadBits, bool invertY, ImageFormat::BayerAlgorithm bayerAlg) {
+ debugAssertM(srcRowPadBits == 0, "Source row padding must be 0 for this format");
+ debugAssertM((srcWidth % 2 == 0) && (srcHeight % 2 == 0), "Source width and height must be a multiple of two");
+
+ const Color3uint8* src = static_cast<const Color3uint8*>(srcBytes[0]);
+
+ uint8* dstY = static_cast<uint8*>(dstBytes[0]);
+ uint8* dstU = static_cast<uint8*>(dstBytes[1]);
+ uint8* dstV = static_cast<uint8*>(dstBytes[2]);
+
+ for (int y = 0; y < srcHeight; y += 2) {
+ for (int x = 0; x < srcWidth; x += 2) {
+
+ // convert 4-pixel block at a time
+ int srcPixelOffset0 = y * srcWidth + x;
+ int srcPixelOffset1 = srcPixelOffset0 + 1;
+ int srcPixelOffset2 = srcPixelOffset0 + srcWidth;
+ int srcPixelOffset3 = srcPixelOffset2 + 1;
+
+ int yIndex = y * srcWidth + x;
+
+ dstY[yIndex] = PIXEL_RGB8_TO_YUV_Y(src[srcPixelOffset0].r, src[srcPixelOffset0].g, src[srcPixelOffset0].b);
+ dstY[yIndex + 1] = PIXEL_RGB8_TO_YUV_Y(src[srcPixelOffset1].r, src[srcPixelOffset1].g, src[srcPixelOffset1].b);
+
+ yIndex += srcWidth;
+ dstY[yIndex] = PIXEL_RGB8_TO_YUV_Y(src[srcPixelOffset2].r, src[srcPixelOffset2].g, src[srcPixelOffset2].b);
+ dstY[yIndex + 1] = PIXEL_RGB8_TO_YUV_Y(src[srcPixelOffset3].r, src[srcPixelOffset3].g, src[srcPixelOffset3].b);
+
+ uint32 blendedPixel = blendPixels(src[srcPixelOffset0].asUInt32(), src[srcPixelOffset2].asUInt32());
+ Color3uint8 uvSrcColor = Color3uint8::fromARGB(blendedPixel);
+
+ int uvIndex = y / 2 * srcWidth / 2 + x / 2;
+ dstU[uvIndex] = PIXEL_RGB8_TO_YUV_U(uvSrcColor.r, uvSrcColor.g, uvSrcColor.b);
+ dstV[uvIndex] = PIXEL_RGB8_TO_YUV_V(uvSrcColor.r, uvSrcColor.g, uvSrcColor.b);
+ }
+ }
+}
+
+static void rgb8_to_yuv422(const Array<const void*>& srcBytes, int srcWidth, int srcHeight, const ImageFormat* srcFormat, int srcRowPadBits, const Array<void*>& dstBytes, const ImageFormat* dstFormat, int dstRowPadBits, bool invertY, ImageFormat::BayerAlgorithm bayerAlg) {
+ debugAssertM(srcRowPadBits == 0, "Source row padding must be 0 for this format");
+ debugAssertM((srcWidth % 2 == 0), "Source width must be a multiple of two");
+
+ const Color3uint8* src = static_cast<const Color3uint8*>(srcBytes[0]);
+
+ uint8* dst = static_cast<uint8*>(dstBytes[0]);
+
+ for (int y = 0; y < srcHeight; ++y) {
+ for (int x = 0; x < srcWidth; x += 2) {
+
+ // convert 2-pixel horizontal block at a time
+ int srcIndex = y * srcWidth + x;
+ int dstIndex = srcIndex * 2;
+
+ uint32 blendedPixel = blendPixels(src[srcIndex].asUInt32(), src[srcIndex + 1].asUInt32());
+ Color3uint8 uvSrcColor = Color3uint8::fromARGB(blendedPixel);
+
+ dst[dstIndex] = PIXEL_RGB8_TO_YUV_Y(src[srcIndex].r, src[srcIndex].g, src[srcIndex].b);
+
+ dst[dstIndex + 1] = PIXEL_RGB8_TO_YUV_U(uvSrcColor.r, uvSrcColor.g, uvSrcColor.b);
+
+ dst[dstIndex + 2] = PIXEL_RGB8_TO_YUV_Y(src[srcIndex + 1].r, src[srcIndex + 1].g, src[srcIndex + 1].b);
+
+ dst[dstIndex + 3] = PIXEL_RGB8_TO_YUV_V(uvSrcColor.r, uvSrcColor.g, uvSrcColor.b);
+
+ }
+ }
+}
+
+static void rgb8_to_yuv444(const Array<const void*>& srcBytes, int srcWidth, int srcHeight, const ImageFormat* srcFormat, int srcRowPadBits, const Array<void*>& dstBytes, const ImageFormat* dstFormat, int dstRowPadBits, bool invertY, ImageFormat::BayerAlgorithm bayerAlg) {
+ debugAssertM(srcRowPadBits == 0, "Source row padding must be 0 for this format");
+
+ const Color3uint8* src = static_cast<const Color3uint8*>(srcBytes[0]);
+
+ Color3uint8* dst = static_cast<Color3uint8*>(dstBytes[0]);
+
+ for (int y = 0; y < srcHeight; ++y) {
+ for (int x = 0; x < srcWidth; ++x) {
+
+ // convert 1-pixels at a time
+ int index = y * srcWidth + x;
+ uint8 y = PIXEL_RGB8_TO_YUV_Y(src[index].r, src[index].g, src[index].b);
+ uint8 u = PIXEL_RGB8_TO_YUV_U(src[index].r, src[index].g, src[index].b);
+ uint8 v = PIXEL_RGB8_TO_YUV_V(src[index].r, src[index].g, src[index].b);
+
+ dst[index].r = y;
+ dst[index].g = u;
+ dst[index].b = v;
+ }
+ }
+}
+
+
+#define PIXEL_YUV_TO_RGB8_R(y, u, v) static_cast<uint8>(iClamp((298 * (y - 16) + 409 * (v - 128) + 128) >> 8, 0, 255))
+#define PIXEL_YUV_TO_RGB8_G(y, u, v) static_cast<uint8>(iClamp((298 * (y - 16) - 100 * (u - 128) - 208 * (v - 128) + 128) >> 8, 0, 255))
+#define PIXEL_YUV_TO_RGB8_B(y, u, v) static_cast<uint8>(iClamp((298 * (y - 16) + 516 * (u - 128) + 128) >> 8, 0, 255))
+
+static void yuv420p_to_rgb8(const Array<const void*>& srcBytes, int srcWidth, int srcHeight, const ImageFormat* srcFormat, int srcRowPadBits, const Array<void*>& dstBytes, const ImageFormat* dstFormat, int dstRowPadBits, bool invertY, ImageFormat::BayerAlgorithm bayerAlg) {
+ debugAssertM(srcRowPadBits == 0, "Source row padding must be 0 for this format");
+ debugAssertM((srcWidth % 2 == 0) && (srcHeight % 2 == 0), "Source width and height must be a multiple of two");
+
+ const uint8* srcY = static_cast<const uint8*>(srcBytes[0]);
+ const uint8* srcU = static_cast<const uint8*>(srcBytes[1]);
+ const uint8* srcV = static_cast<const uint8*>(srcBytes[2]);
+
+ Color3uint8* dst = static_cast<Color3uint8*>(dstBytes[0]);
+
+ for (int y = 0; y < srcHeight; ++y) {
+ for (int x = 0; x < srcWidth; x += 2) {
+
+ // convert to two rgb pixels in a row
+ Color3uint8* rgb = &dst[y * srcWidth + x];
+
+ int yOffset = y * srcWidth + x;
+ int uvOffset = y / 2 * srcWidth / 2 + x / 2;
+
+ rgb->r = PIXEL_YUV_TO_RGB8_R(srcY[yOffset], srcU[uvOffset], srcV[uvOffset]);
+ rgb->g = PIXEL_YUV_TO_RGB8_G(srcY[yOffset], srcU[uvOffset], srcV[uvOffset]);
+ rgb->b = PIXEL_YUV_TO_RGB8_B(srcY[yOffset], srcU[uvOffset], srcV[uvOffset]);
+
+ rgb += 1;
+ rgb->r = PIXEL_YUV_TO_RGB8_R(srcY[yOffset + 1], srcU[uvOffset], srcV[uvOffset]);
+ rgb->g = PIXEL_YUV_TO_RGB8_G(srcY[yOffset + 1], srcU[uvOffset], srcV[uvOffset]);
+ rgb->b = PIXEL_YUV_TO_RGB8_B(srcY[yOffset + 1], srcU[uvOffset], srcV[uvOffset]);
+ }
+ }
+}
+
+static void yuv422_to_rgb8(const Array<const void*>& srcBytes, int srcWidth, int srcHeight, const ImageFormat* srcFormat, int srcRowPadBits, const Array<void*>& dstBytes, const ImageFormat* dstFormat, int dstRowPadBits, bool invertY, ImageFormat::BayerAlgorithm bayerAlg) {
+ debugAssertM(srcRowPadBits == 0, "Source row padding must be 0 for this format");
+ debugAssertM((srcWidth % 2 == 0), "Source width must be a multiple of two");
+
+ const uint8* src = static_cast<const uint8*>(srcBytes[0]);
+
+ Color3uint8* dst = static_cast<Color3uint8*>(dstBytes[0]);
+
+ for (int y = 0; y < srcHeight; ++y) {
+ for (int x = 0; x < srcWidth; x += 2) {
+
+ // convert to two rgb pixels in a row
+ Color3uint8* rgb = &dst[y * srcWidth + x];
+
+ int srcIndex = (y * srcWidth + x) * 2;
+ uint8 y = src[srcIndex];
+ uint8 u = src[srcIndex + 1];
+ uint8 y2 = src[srcIndex + 2];
+ uint8 v = src[srcIndex + 3];
+
+ rgb->r = PIXEL_YUV_TO_RGB8_R(y, u, v);
+ rgb->g = PIXEL_YUV_TO_RGB8_G(y, u, v);
+ rgb->b = PIXEL_YUV_TO_RGB8_B(y, u, v);
+
+ rgb += 1;
+ rgb->r = PIXEL_YUV_TO_RGB8_R(y2, u, v);
+ rgb->g = PIXEL_YUV_TO_RGB8_G(y2, u, v);
+ rgb->b = PIXEL_YUV_TO_RGB8_B(y2, u, v);
+ }
+ }
+}
+
+static void yuv444_to_rgb8(const Array<const void*>& srcBytes, int srcWidth, int srcHeight, const ImageFormat* srcFormat, int srcRowPadBits, const Array<void*>& dstBytes, const ImageFormat* dstFormat, int dstRowPadBits, bool invertY, ImageFormat::BayerAlgorithm bayerAlg) {
+ debugAssertM(srcRowPadBits == 0, "Source row padding must be 0 for this format");
+
+ const Color3uint8* src = static_cast<const Color3uint8*>(srcBytes[0]);
+
+ Color3uint8* dst = static_cast<Color3uint8*>(dstBytes[0]);
+
+ for (int y = 0; y < srcHeight; ++y) {
+ for (int x = 0; x < srcWidth; ++x) {
+
+ // convert to one rgb pixels at a time
+ int index = y * srcWidth + x;
+ Color3uint8* rgb = &dst[index];
+
+ rgb->r = PIXEL_YUV_TO_RGB8_R(src[index].r, src[index].g, src[index].b);
+ rgb->g = PIXEL_YUV_TO_RGB8_G(src[index].r, src[index].g, src[index].b);
+ rgb->b = PIXEL_YUV_TO_RGB8_B(src[index].r, src[index].g, src[index].b);
+ }
+ }
+}
+
+////////////////////////////////////////////////////////////////////////////////////////////////
+//
+// Bayer conversions
+//
+
+// There are two kinds of rows (GR and BG).
+// In each row, there are two kinds of pixels (G/R, B/G).
+// We express the four kinds of INPUT pixels as:
+// GRG, GRG, BGB, BGG
+//
+// There are three kinds of OUTPUT pixels: R, G, B.
+// Thus there are nominally 12 different I/O combinations,
+// but several are impulses because needed output at that
+// location *is* the input (e.g., G_GRG and G_BGG).
+//
+// The following 5x5 row-major filters are named as output_input.
+
+// Green
+static const float G_GRR[5][5] =
+ {{ 0.0f, 0.0f, -1.0f, 0.0f, 0.0f},
+ { 0.0f, 0.0f, 2.0f, 0.0f, 0.0f},
+ { -1.0f, 2.0f, 4.0f, 2.0f, -1.0f},
+ { 0.0f, 0.0f, 2.0f, 0.0f, 0.0f},
+ { 0.0f, 0.0f, -1.0f, 0.0f, 0.0f}};
+
+static const float G_BGB[5][5] =
+ {{ 0.0f, 0.0f, -1.0f, 0.0f, 0.0f},
+ { 0.0f, 0.0f, 2.0f, 0.0f, 0.0f},
+ { -1.0f, 2.0f, 4.0f, 2.0f, -1.0f},
+ { 0.0f, 0.0f, 2.0f, 0.0f, 0.0f},
+ { 0.0f, 0.0f, -1.0f, 0.0f, 0.0f}};
+
+// Red
+//(the caption in the paper is wrong for this case:
+// "R row B column really means R row G column"
+static const float R_GRG[5][5] =
+ {{ 0.0f, 0.0f, 0.5f, 0.0f, 0.0f},
+ { 0.0f, -1.0f, 0.0f, -1.0f, 0.0f},
+ { -1.0f, 4.0f, 5.0f, 4.0f, -1.0f},
+ { 0.0f, -1.0f, 0.0f, -1.0f, 0.0f},
+ { 0.0f, 0.0f, 0.5f, 0.0f, 0.0f}};
+
+static const float R_BGG[5][5] =
+ {{ 0.0f, 0.0f, -1.0f, 0.0f, 0.0f},
+ { 0.0f, -1.0f, 4.0f, -1.0f, 0.0f},
+ { 0.5f, 0.0f, 5.0f, 0.0f, 0.5f},
+ { 0.0f, -1.0f, 4.0f, -1.0f, 0.0f},
+ { 0.0f, 0.0f, -1.0f, 0.0f, 0.0f}};
+
+static const float R_BGB[5][5] =
+ {{ 0.0f, 0.0f, -3.0f/2.0f, 0.0f, 0.0f},
+ { 0.0f, 2.0f, 0.0f, 2.0f, 0.0f},
+ {-3.0f/2.0f, 0.0f, 6.0f, 0.0f, -3.0f/2.0f},
+ { 0.0f, 2.0f, 0.0f, 2.0f, 0.0f},
+ { 0.0f, 0.0f, -3.0f/2.0f, 0.0f, 0.0f}};
+
+
+// Blue
+//(the caption in the paper is wrong for this case:
+// "B row R column really means B row G column")
+#define B_BGG R_GRG
+#define B_GRG R_BGG
+#define B_GRR R_BGB
+
+// =====================================================================
+// Helper methods
+// =====================================================================
+
+
+/** Applies a 5x5 filter to monochrome image I (wrapping at the boundaries) */
+static uint8 applyFilter(const uint8* I,
+ int x,
+ int y,
+ int w,
+ int h,
+ const float filter[5][5]) {
+
+ debugAssert(isEven(w));
+ debugAssert(isEven(h));
+
+ float sum = 0.0f;
+ float denom = 0.0f;
+
+ for (int dy = 0; dy < 5; ++dy) {
+ int offset = ((y + dy + h - 2) % h) * w;
+
+ for (int dx = 0; dx < 5; ++dx) {
+ float f = filter[dy][dx];
+ sum += f * I[((x + dx + w - 2) % w) + offset];
+ denom += f;
+ }
+ }
+
+ return (uint8)iClamp(iRound(sum / denom), 0, 255);
+}
+
+/** Helper method for Bayer grbg and bggr --> rgb8 */
+static void swapRedAndBlue(int N, Color3uint8* out) {
+ for (int i = N - 1; i >= 0; --i) {
+ uint8 tmp = out[i].r;
+ out[i].r = out[i].b;
+ out[i].b = tmp;
+ }
+}
+
+// RGB -> BAYER color space
+
+// =====================================================================
+// rgb8 --> bayer helpers
+// =====================================================================
+static void rgb8_to_bayer_rggb8(const int w, const int h,
+ const uint8* src, uint8* dst) {
+ Color3uint8* srcColor = (Color3uint8*)src;
+ Color1uint8* dstColor = (Color1uint8*)dst;
+
+ // Top row pixels
+ for (int y = 0; y < h - 1; y += 2) {
+ int offset = y * w;
+
+ // Top left pixels
+ for(int x = 0; x < w - 1; x += 2) {
+ dstColor[x + offset] = Color1(srcColor[x + offset].r);
+ }
+
+ // Top right pixels
+ for(int x = 1; x < w - 1; x += 2) {
+ dstColor[x + offset] = Color1(srcColor[x + offset].g);
+ }
+ }
+
+ // Bottom row pixels
+ for (int y = 1; y < h - 1; y += 2) {
+ int offset = y * w;
+
+ // Bottom left pixels
+ for (int x = 0; x < w - 1; x += 2) {
+ dstColor[x + offset] = Color1(srcColor[x + offset].g);
+ }
+
+ // Bottom right pixels
+ for (int x = 1; x < w - 1; x += 2) {
+ dstColor[x + offset] = Color1(srcColor[x + offset].b);
+ }
+ }
+}
+
+
+static void rgb8_to_bayer_grbg8(const int w, const int h,
+ const uint8* src, uint8* dst) {
+ Color3uint8* srcColor = (Color3uint8*)src;
+ Color1uint8* dstColor = (Color1uint8*)dst;
+
+ // Top row pixels
+ for (int y = 0; y < h - 1; y += 2) {
+ int offset = y * w;
+
+ // Top left pixels
+ for (int x = 0; x < w - 1; x += 2) {
+ dstColor[x + offset] = Color1(srcColor[x + offset].g);
+ }
+
+ // Top right pixels
+ for (int x = 1; x < w - 1; x += 2) {
+ dstColor[x + offset] = Color1(srcColor[x + offset].r);
+ }
+ }
+
+ // Bottom row pixels
+ for (int y = 1; y < h - 1; y += 2) {
+ int offset = y * w;
+
+ // Bottom left pixels
+ for (int x = 0; x < w - 1; x += 2) {
+ dstColor[x + offset] = Color1(srcColor[x + offset].b);
+ }
+
+ // Bottom right pixels
+ for (int x = 1; x < w - 1; x += 2) {
+ dstColor[x + offset] = Color1(srcColor[x + offset].g);
+ }
+ }
+}
+
+
+static void rgb8_to_bayer_bggr8(const int w, const int h,
+ const uint8* src, uint8* dst) {
+ Color3uint8* srcColor = (Color3uint8*)src;
+ Color1uint8* dstColor = (Color1uint8*)dst;
+
+ // Top row pixels
+ for (int y = 0; y < h - 1; y += 2) {
+ int offset = y * w;
+
+ // Top left pixels
+ for (int x = 0; x < w - 1; x += 2) {
+ dstColor[x + offset] = Color1(srcColor[x + offset].b);
+ }
+
+ // Top right pixels
+ for (int x = 1; x < w - 1; x += 2) {
+ dstColor[x + offset] = Color1(srcColor[x + offset].g);
+ }
+ }
+
+ // Bottom row pixels
+ for (int y = 1; y < h - 1; y += 2) {
+ int offset = y * w;
+
+ // Bottom left pixels
+ for(int x = 0; x < w - 1; x += 2) {
+ dstColor[x + offset] = Color1(srcColor[x + offset].g);
+ }
+
+ // Bottom right pixels
+ for(int x = 1; x < w - 1; x += 2) {
+ dstColor[x + offset] = Color1(srcColor[x + offset].r);
+ }
+ }
+}
+
+
+static void rgb8_to_bayer_gbrg8(const int w, const int h,
+ const uint8* src, uint8* dst) {
+ Color3uint8* srcColor = (Color3uint8*)src;
+ Color1uint8* dstColor = (Color1uint8*)dst;
+
+ // Top row pixels
+ for(int y = 0; y < h - 1; y += 2) {
+ int offset = y * w;
+
+ // Top left pixels
+ for(int x = 0; x < w - 1; x += 2) {
+ dstColor[x + offset] = Color1(srcColor[x + offset].g);
+ }
+
+ // Top right pixels
+ for(int x = 1; x < w - 1; x += 2) {
+ dstColor[x + offset] = Color1(srcColor[x + offset].b);
+ }
+ }
+
+ // Bottom row pixels
+ for(int y = 1; y < h - 1; y += 2) {
+ int offset = y * w;
+
+ // Bottom left pixels
+ for(int x = 0; x < w - 1; x += 2) {
+ dstColor[x + offset] = Color1(srcColor[x + offset].r);
+ }
+
+ // Bottom right pixels
+ for(int x = 1; x < w - 1; x += 2) {
+ dstColor[x + offset] = Color1(srcColor[x + offset].g);
+ }
+ }
+}
+
+// =====================================================================
+// rgba32f (-->rgb8) --> bayer converter implementations
+// =====================================================================
+static void rgba32f_to_bayer_rggb8(const Array<const void*>& srcBytes, int srcWidth, int srcHeight, const ImageFormat* srcFormat, int srcRowPadBits, const Array<void*>& dstBytes, const ImageFormat* dstFormat, int dstRowPadBits, bool invertY, ImageFormat::BayerAlgorithm bayerAlg) {
+ Array<void*> tmp;
+ tmp.append(System::malloc(srcWidth * srcHeight * sizeof(Color3uint8)));
+
+ rgba32f_to_rgb8(srcBytes, srcWidth, srcHeight, ImageFormat::RGBA32F(), 0, tmp, ImageFormat::RGB8(), 0, invertY, bayerAlg);
+ rgb8_to_bayer_rggb8(srcWidth, srcHeight, static_cast<uint8*>(tmp[0]), static_cast<uint8*>(dstBytes[0]));
+
+ System::free(tmp[0]);
+}
+
+static void rgba32f_to_bayer_gbrg8(const Array<const void*>& srcBytes, int srcWidth, int srcHeight, const ImageFormat* srcFormat, int srcRowPadBits, const Array<void*>& dstBytes, const ImageFormat* dstFormat, int dstRowPadBits, bool invertY, ImageFormat::BayerAlgorithm bayerAlg) {
+ Array<void*> tmp;
+ tmp.append(System::malloc(srcWidth * srcHeight * sizeof(Color3uint8)));
+
+ rgba32f_to_rgb8(srcBytes, srcWidth, srcHeight, ImageFormat::RGBA32F(), 0, tmp, ImageFormat::RGB8(), 0, invertY, bayerAlg);
+ rgb8_to_bayer_grbg8(srcWidth, srcHeight, static_cast<uint8*>(tmp[0]), static_cast<uint8*>(dstBytes[0]));
+
+ System::free(tmp[0]);
+}
+
+static void rgba32f_to_bayer_grbg8(const Array<const void*>& srcBytes, int srcWidth, int srcHeight, const ImageFormat* srcFormat, int srcRowPadBits, const Array<void*>& dstBytes, const ImageFormat* dstFormat, int dstRowPadBits, bool invertY, ImageFormat::BayerAlgorithm bayerAlg) {
+ Array<void*> tmp;
+ tmp.append(System::malloc(srcWidth * srcHeight * sizeof(Color3uint8)));
+
+ rgba32f_to_rgb8(srcBytes, srcWidth, srcHeight, ImageFormat::RGBA32F(), 0, tmp, ImageFormat::RGB8(), 0, invertY, bayerAlg);
+ rgb8_to_bayer_gbrg8(srcWidth, srcHeight, static_cast<uint8*>(tmp[0]), static_cast<uint8*>(dstBytes[0]));
+
+ System::free(tmp[0]);
+}
+
+static void rgba32f_to_bayer_bggr8(const Array<const void*>& srcBytes, int srcWidth, int srcHeight, const ImageFormat* srcFormat, int srcRowPadBits, const Array<void*>& dstBytes, const ImageFormat* dstFormat, int dstRowPadBits, bool invertY, ImageFormat::BayerAlgorithm bayerAlg) {
+ Array<void*> tmp;
+ tmp.append(System::malloc(srcWidth * srcHeight * sizeof(Color3uint8)));
+
+ rgba32f_to_rgb8(srcBytes, srcWidth, srcHeight, ImageFormat::RGBA32F(), 0, tmp, ImageFormat::RGB8(), 0, invertY, bayerAlg);
+ rgb8_to_bayer_bggr8(srcWidth, srcHeight, static_cast<uint8*>(tmp[0]), static_cast<uint8*>(dstBytes[0]));
+
+ System::free(tmp[0]);
+}
+
+// BAYER -> RGB color space
+
+// =====================================================================
+// bayer --> rgb8 helpers
+// =====================================================================
+static void bayer_rggb8_to_rgb8_mhc(int w, int h,
+ const uint8* in, uint8* _out) {
+ debugAssert(in != _out);
+
+ Color3uint8* out = (Color3uint8*)_out;
+
+ for (int y = 0; y < h; ++y) {
+
+ // Row beginning in the input array.
+ int offset = y * w;
+
+ // RG row
+ for (int x = 0; x < w; ++x, ++out) {
+ // R pixel
+ {
+ out->r = in[x + offset];
+ out->g = applyFilter(in, x, y, w, h, G_GRR);
+ out->b = applyFilter(in, x, y, w, h, B_GRR);
+ }
+ ++x; ++out;
+
+ // G pixel
+ {
+ out->r = applyFilter(in, x, y, w, h, R_GRG);
+ out->g = in[x + offset];
+ out->b = applyFilter(in, x, y, w, h, B_GRG);
+ }
+ }
+
+ ++y;
+ offset += w;
+
+ // GB row
+ for (int x = 0; x < w; ++x, ++out) {
+ // G pixel
+ {
+ out->r = applyFilter(in, x, y, w, h, R_BGG);
+ out->g = in[x + offset];
+ out->b = applyFilter(in, x, y, w, h, B_BGG);
+ }
+ ++x; ++out;
+
+ // B pixel
+ {
+ out->r = applyFilter(in, x, y, w, h, R_BGB);
+ out->g = applyFilter(in, x, y, w, h, G_BGB);
+ out->b = in[x + offset];
+ }
+ }
+ }
+}
+
+
+
+static void bayer_gbrg8_to_rgb8_mhc(int w, int h,
+ const uint8* in, uint8* _out) {
+
+ debugAssert(in != _out);
+
+ Color3uint8* out = (Color3uint8*)_out;
+
+ for (int y = 0; y < h; ++y) {
+
+ // Row beginning in the input array.
+ int offset = y * w;
+
+ // GB row
+ for (int x = 0; x < w; ++x, ++out) {
+ // G pixel
+ {
+ out->r = applyFilter(in, x, y, w, h, R_BGG);
+ out->g = in[x + offset];
+ out->b = applyFilter(in, x, y, w, h, B_BGG);
+ }
+ ++x; ++out;
+
+ // B pixel
+ {
+ out->r = applyFilter(in, x, y, w, h, R_BGB);
+ out->g = applyFilter(in, x, y, w, h, G_BGB);
+ out->b = in[x + offset];
+ }
+ }
+ }
+}
+
+
+static void bayer_grbg8_to_rgb8_mhc(int w, int h,
+ const uint8* in, uint8* _out) {
+ // Run the equivalent function for red
+ bayer_gbrg8_to_rgb8_mhc(w, h, in, _out);
+
+ // Now swap red and blue
+ swapRedAndBlue(w * h, (Color3uint8*)_out);
+}
+
+
+static void bayer_bggr8_to_rgb8_mhc(int w, int h,
+ const uint8* in, uint8* _out) {
+ // Run the equivalent function for red
+ bayer_rggb8_to_rgb8_mhc(w, h, in, _out);
+
+ // Now swap red and blue
+ swapRedAndBlue(w * h, (Color3uint8*)_out);
+}
+
+// =====================================================================
+// bayer (--> rgb8) --> rgba32f converter implementations
+// =====================================================================
+static void bayer_rggb8_to_rgba32f(const Array<const void*>& srcBytes, int srcWidth, int srcHeight, const ImageFormat* srcFormat, int srcRowPadBits, const Array<void*>& dstBytes, const ImageFormat* dstFormat, int dstRowPadBits, bool invertY, ImageFormat::BayerAlgorithm bayerAlg) {
+ Array<void*> tmp;
+ tmp.append(System::malloc(srcWidth * srcHeight * sizeof(Color3uint8)));
+
+ bayer_rggb8_to_rgb8_mhc(srcWidth, srcHeight, static_cast<const uint8*>(srcBytes[0]), static_cast<uint8*>(tmp[0]));
+ rgb8_to_rgba32f(reinterpret_cast<Array<const void*>&>(tmp), srcWidth, srcHeight, ImageFormat::RGB8(), 0, dstBytes, ImageFormat::RGBA32F(), 0, invertY, bayerAlg);
+
+ System::free(tmp[0]);
+}
+
+static void bayer_gbrg8_to_rgba32f(const Array<const void*>& srcBytes, int srcWidth, int srcHeight, const ImageFormat* srcFormat, int srcRowPadBits, const Array<void*>& dstBytes, const ImageFormat* dstFormat, int dstRowPadBits, bool invertY, ImageFormat::BayerAlgorithm bayerAlg) {
+ Array<void*> tmp;
+ tmp.append(System::malloc(srcWidth * srcHeight * sizeof(Color3uint8)));
+
+ bayer_grbg8_to_rgb8_mhc(srcWidth, srcHeight, static_cast<const uint8*>(srcBytes[0]), static_cast<uint8*>(tmp[0]));
+ rgb8_to_rgba32f(reinterpret_cast<Array<const void*>&>(tmp), srcWidth, srcHeight, ImageFormat::RGB8(), 0, dstBytes, ImageFormat::RGBA32F(), 0, invertY, bayerAlg);
+
+ System::free(tmp[0]);
+}
+
+static void bayer_grbg8_to_rgba32f(const Array<const void*>& srcBytes, int srcWidth, int srcHeight, const ImageFormat* srcFormat, int srcRowPadBits, const Array<void*>& dstBytes, const ImageFormat* dstFormat, int dstRowPadBits, bool invertY, ImageFormat::BayerAlgorithm bayerAlg) {
+ Array<void*> tmp;
+ tmp.append(System::malloc(srcWidth * srcHeight * sizeof(Color3uint8)));
+
+ bayer_gbrg8_to_rgb8_mhc(srcWidth, srcHeight, static_cast<const uint8*>(srcBytes[0]), static_cast<uint8*>(tmp[0]));
+ rgb8_to_rgba32f(reinterpret_cast<Array<const void*>&>(tmp), srcWidth, srcHeight, ImageFormat::RGB8(), 0, dstBytes, ImageFormat::RGBA32F(), 0, invertY, bayerAlg);
+
+ System::free(tmp[0]);
+}
+
+static void bayer_bggr8_to_rgba32f(const Array<const void*>& srcBytes, int srcWidth, int srcHeight, const ImageFormat* srcFormat, int srcRowPadBits, const Array<void*>& dstBytes, const ImageFormat* dstFormat, int dstRowPadBits, bool invertY, ImageFormat::BayerAlgorithm bayerAlg) {
+ Array<void*> tmp;
+ tmp.append(System::malloc(srcWidth * srcHeight * sizeof(Color3uint8)));
+
+ bayer_bggr8_to_rgb8_mhc(srcWidth, srcHeight, static_cast<const uint8*>(srcBytes[0]), static_cast<uint8*>(tmp[0]));
+ rgb8_to_rgba32f(reinterpret_cast<Array<const void*>&>(tmp), srcWidth, srcHeight, ImageFormat::RGB8(), 0, dstBytes, ImageFormat::RGBA32F(), 0, invertY, bayerAlg);
+
+ System::free(tmp[0]);
+}
+
+
+
+
+
+ // TODO: The following region is commented out because so far
+ // those conversions are not used anywhere else. Until it is
+ // decided that such conversions are not needed, this region
+ // remains commented out.
+
+
+// // =====================================================================
+// // bayer --> bgr8
+// // =====================================================================
+
+// static void bayer_rggb8_to_bgr8_mhc(int w, int h,
+// const uint8* in, uint8* _out) {
+// debugAssert(in != _out);
+
+// Color3uint8* out = (Color3uint8*)_out;
+
+// for (int y = 0; y < h; ++y) {
+
+// // Row beginning in the input array.
+// int offset = y * w;
+
+// // RG row
+// for (int x = 0; x < w; ++x, ++out) {
+// // R pixel
+// {
+// out->b = in[x + offset];
+// out->g = applyFilter(in, x, y, w, h, G_GRR);
+// out->r = applyFilter(in, x, y, w, h, B_GRR);
+// }
+// ++x; ++out;
+
+// // G pixel
+// {
+// out->b = applyFilter(in, x, y, w, h, R_GRG);
+// out->g = in[x + offset];
+// out->r = applyFilter(in, x, y, w, h, B_GRG);
+// }
+// }
+
+// ++y;
+// offset += w;
+
+// // GB row
+// for (int x = 0; x < w; ++x, ++out) {
+// // G pixel
+// {
+// out->b = applyFilter(in, x, y, w, h, R_BGG);
+// out->g = in[x + offset];
+// out->r = applyFilter(in, x, y, w, h, B_BGG);
+// }
+// ++x; ++out;
+
+// // B pixel
+// {
+// out->b = applyFilter(in, x, y, w, h, R_BGB);
+// out->g = applyFilter(in, x, y, w, h, G_BGB);
+// out->r = in[x + offset];
+// }
+// }
+// }
+// }
+
+
+// static void bayer_gbrg8_to_bgr8_mhc(int w, int h,
+// const uint8* in, uint8* _out) {
+
+// debugAssert(in != _out);
+
+// Color3uint8* out = (Color3uint8*)_out;
+
+// for (int y = 0; y < h; ++y) {
+
+// // Row beginning in the input array.
+// int offset = y * w;
+
+// // GB row
+// for (int x = 0; x < srcWidth; ++x, ++out) {
+// // G pixel
+// {
+// out->b = applyFilter(in, x, y, w, h, R_BGG);
+// out->g = in[x + offset];
+// out->r = applyFilter(in, x, y, w, h, B_BGG);
+// }
+// ++x; ++out;
+
+// // B pixel
+// {
+// out->b = applyFilter(in, x, y, w, h, R_BGB);
+// out->g = applyFilter(in, x, y, w, h, G_BGB);
+// out->r = in[x + offset];
+// }
+// }
+// }
+// }
+
+// static void bayer_grbg8_to_bgr8_mhc(int w, int h,
+// const uint8* in, uint8* _out) {
+// // Run the equivalent function for red
+// bayer_gbrg8_to_bgr8_mhc(w, h, in, _out);
+
+// // Now swap red and blue
+// swapRedAndBlue(srcWidth * h, (Color3uint8*)_out);
+// }
+
+// static void bayer_bggr8_to_bgr8_mhc(int w, int h,
+// const uint8* in, uint8* _out) {
+// // Run the equivalent function for red
+// bayer_rggb8_to_bgr8_mhc(w, h, in, _out);
+
+// // Now swap red and blue
+// swapRedAndBlue(srcWidth * h, (Color3uint8*)_out);
+// }
+
+
+
+///////////////////////////////////////////////////
+
+} // namespace G3D
diff --git a/dep/src/g3dlite/Intersect.cpp b/dep/src/g3dlite/Intersect.cpp
new file mode 100644
index 00000000000..929a2e4e670
--- /dev/null
+++ b/dep/src/g3dlite/Intersect.cpp
@@ -0,0 +1,844 @@
+/**
+ @file Intersect.cpp
+
+ @maintainer Morgan McGuire, http://graphics.cs.williams.edu
+
+ @created 2009-06-29
+ @edited 2009-06-29
+
+ Copyright 2000-2009, Morgan McGuire.
+ All rights reserved.
+
+ From the G3D Innovation Engine
+ http://g3d.sf.net
+ */
+#include "G3D/Intersect.h"
+
+namespace G3D {
+
+#ifdef _MSC_VER
+// Turn on fast floating-point optimizations
+#pragma float_control( push )
+#pragma fp_contract( on )
+#pragma fenv_access( off )
+#pragma float_control( except, off )
+#pragma float_control( precise, off )
+#endif
+
+bool __fastcall Intersect::rayAABox(const Ray& ray, const AABox& box) {
+ switch (ray.classification) {
+ case Ray::MMM:
+
+ if ((ray.m_origin.x < box.lo.x) || (ray.m_origin.y < box.lo.y) || (ray.m_origin.z < box.lo.z)
+ || (ray.jbyi * box.lo.x - box.hi.y + ray.c_xy > 0)
+ || (ray.ibyj * box.lo.y - box.hi.x + ray.c_yx > 0)
+ || (ray.jbyk * box.lo.z - box.hi.y + ray.c_zy > 0)
+ || (ray.kbyj * box.lo.y - box.hi.z + ray.c_yz > 0)
+ || (ray.kbyi * box.lo.x - box.hi.z + ray.c_xz > 0)
+ || (ray.ibyk * box.lo.z - box.hi.x + ray.c_zx > 0)
+ )
+ return false;
+
+ return true;
+
+ case Ray::MMP:
+
+ if ((ray.m_origin.x < box.lo.x) || (ray.m_origin.y < box.lo.y) || (ray.m_origin.z > box.hi.z)
+ || (ray.jbyi * box.lo.x - box.hi.y + ray.c_xy > 0)
+ || (ray.ibyj * box.lo.y - box.hi.x + ray.c_yx > 0)
+ || (ray.jbyk * box.hi.z - box.hi.y + ray.c_zy > 0)
+ || (ray.kbyj * box.lo.y - box.lo.z + ray.c_yz < 0)
+ || (ray.kbyi * box.lo.x - box.lo.z + ray.c_xz < 0)
+ || (ray.ibyk * box.hi.z - box.hi.x + ray.c_zx > 0)
+ )
+ return false;
+
+ return true;
+
+ case Ray::MPM:
+
+ if ((ray.m_origin.x < box.lo.x) || (ray.m_origin.y > box.hi.y) || (ray.m_origin.z < box.lo.z)
+ || (ray.jbyi * box.lo.x - box.lo.y + ray.c_xy < 0)
+ || (ray.ibyj * box.hi.y - box.hi.x + ray.c_yx > 0)
+ || (ray.jbyk * box.lo.z - box.lo.y + ray.c_zy < 0)
+ || (ray.kbyj * box.hi.y - box.hi.z + ray.c_yz > 0)
+ || (ray.kbyi * box.lo.x - box.hi.z + ray.c_xz > 0)
+ || (ray.ibyk * box.lo.z - box.hi.x + ray.c_zx > 0)
+ )
+ return false;
+
+ return true;
+
+ case Ray::MPP:
+
+ if ((ray.m_origin.x < box.lo.x) || (ray.m_origin.y > box.hi.y) || (ray.m_origin.z > box.hi.z)
+ || (ray.jbyi * box.lo.x - box.lo.y + ray.c_xy < 0)
+ || (ray.ibyj * box.hi.y - box.hi.x + ray.c_yx > 0)
+ || (ray.jbyk * box.hi.z - box.lo.y + ray.c_zy < 0)
+ || (ray.kbyj * box.hi.y - box.lo.z + ray.c_yz < 0)
+ || (ray.kbyi * box.lo.x - box.lo.z + ray.c_xz < 0)
+ || (ray.ibyk * box.hi.z - box.hi.x + ray.c_zx > 0)
+ )
+ return false;
+
+ return true;
+
+ case Ray::PMM:
+
+ if ((ray.m_origin.x > box.hi.x) || (ray.m_origin.y < box.lo.y) || (ray.m_origin.z < box.lo.z)
+ || (ray.jbyi * box.hi.x - box.hi.y + ray.c_xy > 0)
+ || (ray.ibyj * box.lo.y - box.lo.x + ray.c_yx < 0)
+ || (ray.jbyk * box.lo.z - box.hi.y + ray.c_zy > 0)
+ || (ray.kbyj * box.lo.y - box.hi.z + ray.c_yz > 0)
+ || (ray.kbyi * box.hi.x - box.hi.z + ray.c_xz > 0)
+ || (ray.ibyk * box.lo.z - box.lo.x + ray.c_zx < 0)
+ )
+ return false;
+
+ return true;
+
+ case Ray::PMP:
+
+ if ((ray.m_origin.x > box.hi.x) || (ray.m_origin.y < box.lo.y) || (ray.m_origin.z > box.hi.z)
+ || (ray.jbyi * box.hi.x - box.hi.y + ray.c_xy > 0)
+ || (ray.ibyj * box.lo.y - box.lo.x + ray.c_yx < 0)
+ || (ray.jbyk * box.hi.z - box.hi.y + ray.c_zy > 0)
+ || (ray.kbyj * box.lo.y - box.lo.z + ray.c_yz < 0)
+ || (ray.kbyi * box.hi.x - box.lo.z + ray.c_xz < 0)
+ || (ray.ibyk * box.hi.z - box.lo.x + ray.c_zx < 0)
+ )
+ return false;
+
+ return true;
+
+ case Ray::PPM:
+
+ if ((ray.m_origin.x > box.hi.x) || (ray.m_origin.y > box.hi.y) || (ray.m_origin.z < box.lo.z)
+ || (ray.jbyi * box.hi.x - box.lo.y + ray.c_xy < 0)
+ || (ray.ibyj * box.hi.y - box.lo.x + ray.c_yx < 0)
+ || (ray.jbyk * box.lo.z - box.lo.y + ray.c_zy < 0)
+ || (ray.kbyj * box.hi.y - box.hi.z + ray.c_yz > 0)
+ || (ray.kbyi * box.hi.x - box.hi.z + ray.c_xz > 0)
+ || (ray.ibyk * box.lo.z - box.lo.x + ray.c_zx < 0)
+ )
+ return false;
+
+ return true;
+
+ case Ray::PPP:
+
+ if ((ray.m_origin.x > box.hi.x) || (ray.m_origin.y > box.hi.y) || (ray.m_origin.z > box.hi.z)
+ || (ray.jbyi * box.hi.x - box.lo.y + ray.c_xy < 0)
+ || (ray.ibyj * box.hi.y - box.lo.x + ray.c_yx < 0)
+ || (ray.jbyk * box.hi.z - box.lo.y + ray.c_zy < 0)
+ || (ray.kbyj * box.hi.y - box.lo.z + ray.c_yz < 0)
+ || (ray.kbyi * box.hi.x - box.lo.z + ray.c_xz < 0)
+ || (ray.ibyk * box.hi.z - box.lo.x + ray.c_zx < 0)) {
+ return false;
+ }
+
+ return true;
+
+ case Ray::OMM:
+
+ if((ray.m_origin.x < box.lo.x) || (ray.m_origin.x > box.hi.x)
+ || (ray.m_origin.y < box.lo.y) || (ray.m_origin.z < box.lo.z)
+ || (ray.jbyk * box.lo.z - box.hi.y + ray.c_zy > 0)
+ || (ray.kbyj * box.lo.y - box.hi.z + ray.c_yz > 0)
+ )
+ return false;
+
+ return true;
+
+ case Ray::OMP:
+
+ if((ray.m_origin.x < box.lo.x) || (ray.m_origin.x > box.hi.x)
+ || (ray.m_origin.y < box.lo.y) || (ray.m_origin.z > box.hi.z)
+ || (ray.jbyk * box.hi.z - box.hi.y + ray.c_zy > 0)
+ || (ray.kbyj * box.lo.y - box.lo.z + ray.c_yz < 0)
+ )
+ return false;
+
+ return true;
+
+ case Ray::OPM:
+
+ if((ray.m_origin.x < box.lo.x) || (ray.m_origin.x > box.hi.x)
+ || (ray.m_origin.y > box.hi.y) || (ray.m_origin.z < box.lo.z)
+ || (ray.jbyk * box.lo.z - box.lo.y + ray.c_zy < 0)
+ || (ray.kbyj * box.hi.y - box.hi.z + ray.c_yz > 0)
+ )
+ return false;
+
+ return true;
+
+ case Ray::OPP:
+
+ if((ray.m_origin.x < box.lo.x) || (ray.m_origin.x > box.hi.x)
+ || (ray.m_origin.y > box.hi.y) || (ray.m_origin.z > box.hi.z)
+ || (ray.jbyk * box.hi.z - box.lo.y + ray.c_zy < 0)
+ || (ray.kbyj * box.hi.y - box.lo.z + ray.c_yz < 0)
+ )
+ return false;
+
+ return true;
+
+ case Ray::MOM:
+
+ if((ray.m_origin.y < box.lo.y) || (ray.m_origin.y > box.hi.y)
+ || (ray.m_origin.x < box.lo.x) || (ray.m_origin.z < box.lo.z)
+ || (ray.kbyi * box.lo.x - box.hi.z + ray.c_xz > 0)
+ || (ray.ibyk * box.lo.z - box.hi.x + ray.c_zx > 0)
+ )
+ return false;
+
+ return true;
+
+ case Ray::MOP:
+
+ if((ray.m_origin.y < box.lo.y) || (ray.m_origin.y > box.hi.y)
+ || (ray.m_origin.x < box.lo.x) || (ray.m_origin.z > box.hi.z)
+ || (ray.kbyi * box.lo.x - box.lo.z + ray.c_xz < 0)
+ || (ray.ibyk * box.hi.z - box.hi.x + ray.c_zx > 0)
+ )
+ return false;
+
+ return true;
+
+ case Ray::POM:
+
+ if((ray.m_origin.y < box.lo.y) || (ray.m_origin.y > box.hi.y)
+ || (ray.m_origin.x > box.hi.x) || (ray.m_origin.z < box.lo.z)
+ || (ray.kbyi * box.hi.x - box.hi.z + ray.c_xz > 0)
+ || (ray.ibyk * box.lo.z - box.lo.x + ray.c_zx < 0)
+ )
+ return false;
+
+ return true;
+
+ case Ray::POP:
+
+ if((ray.m_origin.y < box.lo.y) || (ray.m_origin.y > box.hi.y)
+ || (ray.m_origin.x > box.hi.x) || (ray.m_origin.z > box.hi.z)
+ || (ray.kbyi * box.hi.x - box.lo.z + ray.c_xz < 0)
+ || (ray.ibyk * box.hi.z - box.lo.x + ray.c_zx < 0)
+ )
+ return false;
+
+ return true;
+
+ case Ray::MMO:
+
+ if((ray.m_origin.z < box.lo.z) || (ray.m_origin.z > box.hi.z)
+ || (ray.m_origin.x < box.lo.x) || (ray.m_origin.y < box.lo.y)
+ || (ray.jbyi * box.lo.x - box.hi.y + ray.c_xy > 0)
+ || (ray.ibyj * box.lo.y - box.hi.x + ray.c_yx > 0)
+ )
+ return false;
+
+ return true;
+
+ case Ray::MPO:
+
+ if((ray.m_origin.z < box.lo.z) || (ray.m_origin.z > box.hi.z)
+ || (ray.m_origin.x < box.lo.x) || (ray.m_origin.y > box.hi.y)
+ || (ray.jbyi * box.lo.x - box.lo.y + ray.c_xy < 0)
+ || (ray.ibyj * box.hi.y - box.hi.x + ray.c_yx > 0)
+ )
+ return false;
+
+ return true;
+
+ case Ray::PMO:
+
+ if((ray.m_origin.z < box.lo.z) || (ray.m_origin.z > box.hi.z)
+ || (ray.m_origin.x > box.hi.x) || (ray.m_origin.y < box.lo.y)
+ || (ray.jbyi * box.hi.x - box.hi.y + ray.c_xy > 0)
+ || (ray.ibyj * box.lo.y - box.lo.x + ray.c_yx < 0)
+ )
+ return false;
+
+ return true;
+
+ case Ray::PPO:
+
+ if((ray.m_origin.z < box.lo.z) || (ray.m_origin.z > box.hi.z)
+ || (ray.m_origin.x > box.hi.x) || (ray.m_origin.y > box.hi.y)
+ || (ray.jbyi * box.hi.x - box.lo.y + ray.c_xy < 0)
+ || (ray.ibyj * box.hi.y - box.lo.x + ray.c_yx < 0)
+ )
+ return false;
+
+ return true;
+
+ case Ray::MOO:
+
+ if((ray.m_origin.x < box.lo.x)
+ || (ray.m_origin.y < box.lo.y) || (ray.m_origin.y > box.hi.y)
+ || (ray.m_origin.z < box.lo.z) || (ray.m_origin.z > box.hi.z)
+ )
+ return false;
+
+ return true;
+
+ case Ray::POO:
+
+ if((ray.m_origin.x > box.hi.x)
+ || (ray.m_origin.y < box.lo.y) || (ray.m_origin.y > box.hi.y)
+ || (ray.m_origin.z < box.lo.z) || (ray.m_origin.z > box.hi.z)
+ )
+ return false;
+
+ return true;
+
+ case Ray::OMO:
+
+ if((ray.m_origin.y < box.lo.y)
+ || (ray.m_origin.x < box.lo.x) || (ray.m_origin.x > box.hi.x)
+ || (ray.m_origin.z < box.lo.z) || (ray.m_origin.z > box.hi.z)
+ )
+ return false;
+
+ case Ray::OPO:
+
+ if((ray.m_origin.y > box.hi.y)
+ || (ray.m_origin.x < box.lo.x) || (ray.m_origin.x > box.hi.x)
+ || (ray.m_origin.z < box.lo.z) || (ray.m_origin.z > box.hi.z)
+ )
+ return false;
+
+ case Ray::OOM:
+
+ if((ray.m_origin.z < box.lo.z)
+ || (ray.m_origin.x < box.lo.x) || (ray.m_origin.x > box.hi.x)
+ || (ray.m_origin.y < box.lo.y) || (ray.m_origin.y > box.hi.y)
+ )
+ return false;
+
+ case Ray::OOP:
+
+ if((ray.m_origin.z > box.hi.z)
+ || (ray.m_origin.x < box.lo.x) || (ray.m_origin.x > box.hi.x)
+ || (ray.m_origin.y < box.lo.y) || (ray.m_origin.y > box.hi.y)
+ )
+ return false;
+
+ return true;
+
+ }
+
+ return false;
+}
+
+
+bool __fastcall Intersect::rayAABox(const Ray& ray, const AABox& box, float& time) {
+
+ switch (ray.classification) {
+ case Ray::MMM:
+ {
+ if ((ray.m_origin.x < box.lo.x) || (ray.m_origin.y < box.lo.y) || (ray.m_origin.z < box.lo.z)
+ || (ray.jbyi * box.lo.x - box.hi.y + ray.c_xy > 0)
+ || (ray.ibyj * box.lo.y - box.hi.x + ray.c_yx > 0)
+ || (ray.jbyk * box.lo.z - box.hi.y + ray.c_zy > 0)
+ || (ray.kbyj * box.lo.y - box.hi.z + ray.c_yz > 0)
+ || (ray.kbyi * box.lo.x - box.hi.z + ray.c_xz > 0)
+ || (ray.ibyk * box.lo.z - box.hi.x + ray.c_zx > 0)) {
+ return false;
+ }
+
+ // compute the intersection distance
+
+ time = (box.hi.x - ray.m_origin.x) * ray.m_invDirection.x;
+ float t1 = (box.hi.y - ray.m_origin.y) * ray.m_invDirection.y;
+ if (t1 > time) {
+ time = t1;
+ }
+
+ float t2 = (box.hi.z - ray.m_origin.z) * ray.m_invDirection.z;
+ if (t2 > time) {
+ time = t2;
+ }
+
+ return true;
+ }
+
+ case Ray::MMP:
+ {
+ if ((ray.m_origin.x < box.lo.x) || (ray.m_origin.y < box.lo.y) || (ray.m_origin.z > box.hi.z)
+ || (ray.jbyi * box.lo.x - box.hi.y + ray.c_xy > 0)
+ || (ray.ibyj * box.lo.y - box.hi.x + ray.c_yx > 0)
+ || (ray.jbyk * box.hi.z - box.hi.y + ray.c_zy > 0)
+ || (ray.kbyj * box.lo.y - box.lo.z + ray.c_yz < 0)
+ || (ray.kbyi * box.lo.x - box.lo.z + ray.c_xz < 0)
+ || (ray.ibyk * box.hi.z - box.hi.x + ray.c_zx > 0)) {
+ return false;
+ }
+
+ time = (box.hi.x - ray.m_origin.x) * ray.m_invDirection.x;
+ float t1 = (box.hi.y - ray.m_origin.y) * ray.m_invDirection.y;
+ if (t1 > time) {
+ time = t1;
+ }
+ float t2 = (box.lo.z - ray.m_origin.z) * ray.m_invDirection.z;
+ if (t2 > time) {
+ time = t2;
+ }
+
+ return true;
+ }
+
+ case Ray::MPM:
+ {
+ if ((ray.m_origin.x < box.lo.x) || (ray.m_origin.y > box.hi.y) || (ray.m_origin.z < box.lo.z)
+ || (ray.jbyi * box.lo.x - box.lo.y + ray.c_xy < 0)
+ || (ray.ibyj * box.hi.y - box.hi.x + ray.c_yx > 0)
+ || (ray.jbyk * box.lo.z - box.lo.y + ray.c_zy < 0)
+ || (ray.kbyj * box.hi.y - box.hi.z + ray.c_yz > 0)
+ || (ray.kbyi * box.lo.x - box.hi.z + ray.c_xz > 0)
+ || (ray.ibyk * box.lo.z - box.hi.x + ray.c_zx > 0)) {
+ return false;
+ }
+
+ time = (box.hi.x - ray.m_origin.x) * ray.m_invDirection.x;
+ float t1 = (box.lo.y - ray.m_origin.y) * ray.m_invDirection.y;
+ if (t1 > time) {
+ time = t1;
+ }
+ float t2 = (box.hi.z - ray.m_origin.z) * ray.m_invDirection.z;
+ if (t2 > time) {
+ time = t2;
+ }
+
+ return true;
+ }
+
+ case Ray::MPP:
+ {
+ if ((ray.m_origin.x < box.lo.x) || (ray.m_origin.y > box.hi.y) || (ray.m_origin.z > box.hi.z)
+ || (ray.jbyi * box.lo.x - box.lo.y + ray.c_xy < 0)
+ || (ray.ibyj * box.hi.y - box.hi.x + ray.c_yx > 0)
+ || (ray.jbyk * box.hi.z - box.lo.y + ray.c_zy < 0)
+ || (ray.kbyj * box.hi.y - box.lo.z + ray.c_yz < 0)
+ || (ray.kbyi * box.lo.x - box.lo.z + ray.c_xz < 0)
+ || (ray.ibyk * box.hi.z - box.hi.x + ray.c_zx > 0)) {
+ return false;
+ }
+
+ time = (box.hi.x - ray.m_origin.x) * ray.m_invDirection.x;
+ float t1 = (box.lo.y - ray.m_origin.y) * ray.m_invDirection.y;
+ if (t1 > time) {
+ time = t1;
+ }
+ float t2 = (box.lo.z - ray.m_origin.z) * ray.m_invDirection.z;
+ if (t2 > time) {
+ time = t2;
+ }
+
+ return true;
+ }
+
+ case Ray::PMM:
+ {
+ if ((ray.m_origin.x > box.hi.x) || (ray.m_origin.y < box.lo.y) || (ray.m_origin.z < box.lo.z)
+ || (ray.jbyi * box.hi.x - box.hi.y + ray.c_xy > 0)
+ || (ray.ibyj * box.lo.y - box.lo.x + ray.c_yx < 0)
+ || (ray.jbyk * box.lo.z - box.hi.y + ray.c_zy > 0)
+ || (ray.kbyj * box.lo.y - box.hi.z + ray.c_yz > 0)
+ || (ray.kbyi * box.hi.x - box.hi.z + ray.c_xz > 0)
+ || (ray.ibyk * box.lo.z - box.lo.x + ray.c_zx < 0)) {
+ return false;
+ }
+
+ time = (box.lo.x - ray.m_origin.x) * ray.m_invDirection.x;
+ float t1 = (box.hi.y - ray.m_origin.y) * ray.m_invDirection.y;
+ if (t1 > time) {
+ time = t1;
+ }
+ float t2 = (box.hi.z - ray.m_origin.z) * ray.m_invDirection.z;
+ if (t2 > time) {
+ time = t2;
+ }
+
+ return true;
+ }
+
+
+ case Ray::PMP:
+ {
+ if ((ray.m_origin.x > box.hi.x) || (ray.m_origin.y < box.lo.y) || (ray.m_origin.z > box.hi.z)
+ || (ray.jbyi * box.hi.x - box.hi.y + ray.c_xy > 0)
+ || (ray.ibyj * box.lo.y - box.lo.x + ray.c_yx < 0)
+ || (ray.jbyk * box.hi.z - box.hi.y + ray.c_zy > 0)
+ || (ray.kbyj * box.lo.y - box.lo.z + ray.c_yz < 0)
+ || (ray.kbyi * box.hi.x - box.lo.z + ray.c_xz < 0)
+ || (ray.ibyk * box.hi.z - box.lo.x + ray.c_zx < 0)) {
+ return false;
+ }
+
+ time = (box.lo.x - ray.m_origin.x) * ray.m_invDirection.x;
+ float t1 = (box.hi.y - ray.m_origin.y) * ray.m_invDirection.y;
+ if (t1 > time) {
+ time = t1;
+ }
+ float t2 = (box.lo.z - ray.m_origin.z) * ray.m_invDirection.z;
+ if (t2 > time) {
+ time = t2;
+ }
+
+ return true;
+ }
+
+ case Ray::PPM:
+ {
+ if ((ray.m_origin.x > box.hi.x) || (ray.m_origin.y > box.hi.y) || (ray.m_origin.z < box.lo.z)
+ || (ray.jbyi * box.hi.x - box.lo.y + ray.c_xy < 0)
+ || (ray.ibyj * box.hi.y - box.lo.x + ray.c_yx < 0)
+ || (ray.jbyk * box.lo.z - box.lo.y + ray.c_zy < 0)
+ || (ray.kbyj * box.hi.y - box.hi.z + ray.c_yz > 0)
+ || (ray.kbyi * box.hi.x - box.hi.z + ray.c_xz > 0)
+ || (ray.ibyk * box.lo.z - box.lo.x + ray.c_zx < 0)) {
+ return false;
+ }
+
+ time = (box.lo.x - ray.m_origin.x) * ray.m_invDirection.x;
+ float t1 = (box.lo.y - ray.m_origin.y) * ray.m_invDirection.y;
+ if (t1 > time) {
+ time = t1;
+ }
+ float t2 = (box.hi.z - ray.m_origin.z) * ray.m_invDirection.z;
+ if (t2 > time) {
+ time = t2;
+ }
+
+ return true;
+ }
+
+ case Ray::PPP:
+ {
+ if ((ray.m_origin.x > box.hi.x) || (ray.m_origin.y > box.hi.y) || (ray.m_origin.z > box.hi.z)
+ || (ray.jbyi * box.hi.x - box.lo.y + ray.c_xy < 0)
+ || (ray.ibyj * box.hi.y - box.lo.x + ray.c_yx < 0)
+ || (ray.jbyk * box.hi.z - box.lo.y + ray.c_zy < 0)
+ || (ray.kbyj * box.hi.y - box.lo.z + ray.c_yz < 0)
+ || (ray.kbyi * box.hi.x - box.lo.z + ray.c_xz < 0)
+ || (ray.ibyk * box.hi.z - box.lo.x + ray.c_zx < 0)) {
+ return false;
+ }
+
+ time = (box.lo.x - ray.m_origin.x) * ray.m_invDirection.x;
+ float t1 = (box.lo.y - ray.m_origin.y) * ray.m_invDirection.y;
+ if (t1 > time) {
+ time = t1;
+ }
+ float t2 = (box.lo.z - ray.m_origin.z) * ray.m_invDirection.z;
+ if (t2 > time) {
+ time = t2;
+ }
+
+ return true;
+ }
+
+ case Ray::OMM:
+ {
+ if((ray.m_origin.x < box.lo.x) || (ray.m_origin.x > box.hi.x)
+ || (ray.m_origin.y < box.lo.y) || (ray.m_origin.z < box.lo.z)
+ || (ray.jbyk * box.lo.z - box.hi.y + ray.c_zy > 0)
+ || (ray.kbyj * box.lo.y - box.hi.z + ray.c_yz > 0)) {
+ return false;
+ }
+
+ time = (box.hi.y - ray.m_origin.y) * ray.m_invDirection.y;
+ float t2 = (box.hi.z - ray.m_origin.z) * ray.m_invDirection.z;
+ if (t2 > time) {
+ time = t2;
+ }
+
+ return true;
+ }
+
+ case Ray::OMP:
+ {
+ if((ray.m_origin.x < box.lo.x) || (ray.m_origin.x > box.hi.x)
+ || (ray.m_origin.y < box.lo.y) || (ray.m_origin.z > box.hi.z)
+ || (ray.jbyk * box.hi.z - box.hi.y + ray.c_zy > 0)
+ || (ray.kbyj * box.lo.y - box.lo.z + ray.c_yz < 0)) {
+ return false;
+ }
+
+ time = (box.hi.y - ray.m_origin.y) * ray.m_invDirection.y;
+ float t2 = (box.lo.z - ray.m_origin.z) * ray.m_invDirection.z;
+ if (t2 > time) {
+ time = t2;
+ }
+
+ return true;
+ }
+
+ case Ray::OPM:
+ {
+ if((ray.m_origin.x < box.lo.x) || (ray.m_origin.x > box.hi.x)
+ || (ray.m_origin.y > box.hi.y) || (ray.m_origin.z < box.lo.z)
+ || (ray.jbyk * box.lo.z - box.lo.y + ray.c_zy < 0)
+ || (ray.kbyj * box.hi.y - box.hi.z + ray.c_yz > 0)) {
+ return false;
+ }
+
+ time = (box.lo.y - ray.m_origin.y) * ray.m_invDirection.y;
+ float t2 = (box.hi.z - ray.m_origin.z) * ray.m_invDirection.z;
+ if (t2 > time) {
+ time = t2;
+ }
+
+ return true;
+ }
+
+ case Ray::OPP:
+ {
+ if((ray.m_origin.x < box.lo.x) || (ray.m_origin.x > box.hi.x)
+ || (ray.m_origin.y > box.hi.y) || (ray.m_origin.z > box.hi.z)
+ || (ray.jbyk * box.hi.z - box.lo.y + ray.c_zy < 0)
+ || (ray.kbyj * box.hi.y - box.lo.z + ray.c_yz < 0)) {
+ return false;
+ }
+
+ time = (box.lo.y - ray.m_origin.y) * ray.m_invDirection.y;
+ float t2 = (box.lo.z - ray.m_origin.z) * ray.m_invDirection.z;
+ if (t2 > time) {
+ time = t2;
+ }
+
+ return true;
+ }
+
+
+ case Ray::MOM:
+ {
+ if((ray.m_origin.y < box.lo.y) || (ray.m_origin.y > box.hi.y)
+ || (ray.m_origin.x < box.lo.x) || (ray.m_origin.z < box.lo.z)
+ || (ray.kbyi * box.lo.x - box.hi.z + ray.c_xz > 0)
+ || (ray.ibyk * box.lo.z - box.hi.x + ray.c_zx > 0)) {
+ return false;
+ }
+
+ time = (box.hi.x - ray.m_origin.x) * ray.m_invDirection.x;
+ float t2 = (box.hi.z - ray.m_origin.z) * ray.m_invDirection.z;
+ if (t2 > time) {
+ time = t2;
+ }
+
+ return true;
+ }
+
+
+ case Ray::MOP:
+ {
+ if((ray.m_origin.y < box.lo.y) || (ray.m_origin.y > box.hi.y)
+ || (ray.m_origin.x < box.lo.x) || (ray.m_origin.z > box.hi.z)
+ || (ray.kbyi * box.lo.x - box.lo.z + ray.c_xz < 0)
+ || (ray.ibyk * box.hi.z - box.hi.x + ray.c_zx > 0)) {
+ return false;
+ }
+
+ time = (box.hi.x - ray.m_origin.x) * ray.m_invDirection.x;
+ float t2 = (box.lo.z - ray.m_origin.z) * ray.m_invDirection.z;
+ if (t2 > time) {
+ time = t2;
+ }
+
+ return true;
+ }
+
+ case Ray::POM:
+ {
+ if((ray.m_origin.y < box.lo.y) || (ray.m_origin.y > box.hi.y)
+ || (ray.m_origin.x > box.hi.x) || (ray.m_origin.z < box.lo.z)
+ || (ray.kbyi * box.hi.x - box.hi.z + ray.c_xz > 0)
+ || (ray.ibyk * box.lo.z - box.lo.x + ray.c_zx < 0)) {
+ return false;
+ }
+
+ time = (box.lo.x - ray.m_origin.x) * ray.m_invDirection.x;
+ float t2 = (box.hi.z - ray.m_origin.z) * ray.m_invDirection.z;
+ if (t2 > time) {
+ time = t2;
+ }
+
+ return true;
+ }
+
+
+ case Ray::POP:
+ {
+ if((ray.m_origin.y < box.lo.y) || (ray.m_origin.y > box.hi.y)
+ || (ray.m_origin.x > box.hi.x) || (ray.m_origin.z > box.hi.z)
+ || (ray.kbyi * box.hi.x - box.lo.z + ray.c_xz < 0)
+ || (ray.ibyk * box.hi.z - box.lo.x + ray.c_zx < 0)) {
+ return false;
+ }
+
+ time = (box.lo.x - ray.m_origin.x) * ray.m_invDirection.x;
+ float t2 = (box.lo.z - ray.m_origin.z) * ray.m_invDirection.z;
+ if (t2 > time) {
+ time = t2;
+ }
+
+ return true;
+ }
+
+ case Ray::MMO:
+ {
+ if((ray.m_origin.z < box.lo.z) || (ray.m_origin.z > box.hi.z)
+ || (ray.m_origin.x < box.lo.x) || (ray.m_origin.y < box.lo.y)
+ || (ray.jbyi * box.lo.x - box.hi.y + ray.c_xy > 0)
+ || (ray.ibyj * box.lo.y - box.hi.x + ray.c_yx > 0)) {
+ return false;
+ }
+
+ time = (box.hi.x - ray.m_origin.x) * ray.m_invDirection.x;
+ float t1 = (box.hi.y - ray.m_origin.y) * ray.m_invDirection.y;
+ if (t1 > time) {
+ time = t1;
+ }
+
+ return true;
+ }
+
+ case Ray::MPO:
+ {
+ if((ray.m_origin.z < box.lo.z) || (ray.m_origin.z > box.hi.z)
+ || (ray.m_origin.x < box.lo.x) || (ray.m_origin.y > box.hi.y)
+ || (ray.jbyi * box.lo.x - box.lo.y + ray.c_xy < 0)
+ || (ray.ibyj * box.hi.y - box.hi.x + ray.c_yx > 0)) {
+ return false;
+ }
+
+ time = (box.hi.x - ray.m_origin.x) * ray.m_invDirection.x;
+ float t1 = (box.lo.y - ray.m_origin.y) * ray.m_invDirection.y;
+ if (t1 > time) {
+ time = t1;
+ }
+
+ return true;
+ }
+
+
+ case Ray::PMO:
+ {
+ if((ray.m_origin.z < box.lo.z) || (ray.m_origin.z > box.hi.z)
+ || (ray.m_origin.x > box.hi.x) || (ray.m_origin.y < box.lo.y)
+ || (ray.jbyi * box.hi.x - box.hi.y + ray.c_xy > 0)
+ || (ray.ibyj * box.lo.y - box.lo.x + ray.c_yx < 0)) {
+ return false;
+ }
+
+ time = (box.lo.x - ray.m_origin.x) * ray.m_invDirection.x;
+ float t1 = (box.hi.y - ray.m_origin.y) * ray.m_invDirection.y;
+ if (t1 > time) {
+ time = t1;
+ }
+
+ return true;
+ }
+
+ case Ray::PPO:
+ {
+ if((ray.m_origin.z < box.lo.z) || (ray.m_origin.z > box.hi.z)
+ || (ray.m_origin.x > box.hi.x) || (ray.m_origin.y > box.hi.y)
+ || (ray.jbyi * box.hi.x - box.lo.y + ray.c_xy < 0)
+ || (ray.ibyj * box.hi.y - box.lo.x + ray.c_yx < 0)) {
+ return false;
+ }
+
+ time = (box.lo.x - ray.m_origin.x) * ray.m_invDirection.x;
+ float t1 = (box.lo.y - ray.m_origin.y) * ray.m_invDirection.y;
+ if (t1 > time) {
+ time = t1;
+ }
+
+ return true;
+ }
+
+
+ case Ray::MOO:
+ {
+ if((ray.m_origin.x < box.lo.x)
+ || (ray.m_origin.y < box.lo.y) || (ray.m_origin.y > box.hi.y)
+ || (ray.m_origin.z < box.lo.z) || (ray.m_origin.z > box.hi.z)) {
+ return false;
+ }
+
+ time = (box.hi.x - ray.m_origin.x) * ray.m_invDirection.x;
+ return true;
+ }
+
+ case Ray::POO:
+ {
+ if ((ray.m_origin.x > box.hi.x)
+ || (ray.m_origin.y < box.lo.y) || (ray.m_origin.y > box.hi.y)
+ || (ray.m_origin.z < box.lo.z) || (ray.m_origin.z > box.hi.z)) {
+ return false;
+ }
+
+ time = (box.lo.x - ray.m_origin.x) * ray.m_invDirection.x;
+ return true;
+ }
+
+ case Ray::OMO:
+ {
+ if ((ray.m_origin.y < box.lo.y)
+ || (ray.m_origin.x < box.lo.x) || (ray.m_origin.x > box.hi.x)
+ || (ray.m_origin.z < box.lo.z) || (ray.m_origin.z > box.hi.z)) {
+ return false;
+ }
+
+ time = (box.hi.y - ray.m_origin.y) * ray.m_invDirection.y;
+ return true;
+ }
+
+ case Ray::OPO:
+ {
+ if ((ray.m_origin.y > box.hi.y)
+ || (ray.m_origin.x < box.lo.x) || (ray.m_origin.x > box.hi.x)
+ || (ray.m_origin.z < box.lo.z) || (ray.m_origin.z > box.hi.z)) {
+ return false;
+ }
+
+ time = (box.lo.y - ray.m_origin.y) * ray.m_invDirection.y;
+ return true;
+ }
+
+
+ case Ray::OOM:
+ {
+ if ((ray.m_origin.z < box.lo.z)
+ || (ray.m_origin.x < box.lo.x) || (ray.m_origin.x > box.hi.x)
+ || (ray.m_origin.y < box.lo.y) || (ray.m_origin.y > box.hi.y)) {
+ return false;
+ }
+
+ time = (box.hi.z - ray.m_origin.z) * ray.m_invDirection.z;
+ return true;
+ }
+
+ case Ray::OOP:
+ {
+ if ((ray.m_origin.z > box.hi.z)
+ || (ray.m_origin.x < box.lo.x) || (ray.m_origin.x > box.hi.x)
+ || (ray.m_origin.y < box.lo.y) || (ray.m_origin.y > box.hi.y)) {
+ return false;
+ }
+
+ time = (box.lo.z - ray.m_origin.z) * ray.m_invDirection.z;
+ return true;
+ }
+ }
+
+ return false;
+}
+
+#ifdef _MSC_VER
+// Turn off fast floating-point optimizations
+#pragma float_control( pop )
+#endif
+
+}
diff --git a/dep/src/g3dlite/Line.cpp b/dep/src/g3dlite/Line.cpp
new file mode 100644
index 00000000000..195ae7197f2
--- /dev/null
+++ b/dep/src/g3dlite/Line.cpp
@@ -0,0 +1,89 @@
+/**
+ @file Line.cpp
+
+ Line class
+
+ @maintainer Morgan McGuire, graphics3d.com
+
+ @created 2001-06-02
+ @edited 2006-01-28
+ */
+
+#include "G3D/Line.h"
+#include "G3D/Plane.h"
+
+namespace G3D {
+
+Vector3 Line::intersection(const Plane& plane) const {
+ float d;
+ Vector3 normal = plane.normal();
+ plane.getEquation(normal, d);
+ float rate = _direction.dot(normal);
+
+ if (rate == 0) {
+
+ return Vector3::inf();
+
+ } else {
+ float t = -(d + _point.dot(normal)) / rate;
+
+ return _point + _direction * t;
+ }
+}
+
+
+Line::Line(class BinaryInput& b) {
+ deserialize(b);
+}
+
+
+void Line::serialize(class BinaryOutput& b) const {
+ _point.serialize(b);
+ _direction.serialize(b);
+}
+
+
+void Line::deserialize(class BinaryInput& b) {
+ _point.deserialize(b);
+ _direction.deserialize(b);
+}
+
+
+Vector3 Line::closestPoint(const Vector3& pt) const {
+ float t = _direction.dot(pt - _point);
+ return _point + _direction * t;
+}
+
+
+Vector3 Line::point() const {
+ return _point;
+}
+
+
+Vector3 Line::direction() const {
+ return _direction;
+}
+
+
+Vector3 Line::closestPoint(const Line& B, float& minDist) const {
+ const Vector3& P1 = _point;
+ const Vector3& U1 = _direction;
+
+ Vector3 P2 = B.point();
+ Vector3 U2 = B.direction();
+
+ const Vector3& P21 = P2 - P1;
+ const Vector3& M = U2.cross(U1);
+ float m2 = M.length();
+
+ Vector3 R = P21.cross(M) / m2;
+
+ float t1 = R.dot(U2);
+
+ minDist = abs(P21.dot(M)) / sqrt(m2);
+
+ return P1 + t1 * U1;
+}
+
+}
+
diff --git a/dep/src/g3dlite/LineSegment.cpp b/dep/src/g3dlite/LineSegment.cpp
new file mode 100644
index 00000000000..754600ad554
--- /dev/null
+++ b/dep/src/g3dlite/LineSegment.cpp
@@ -0,0 +1,236 @@
+/**
+ @file LineSegment.cpp
+
+ @maintainer Morgan McGuire, http://graphics.cs.williams.edu
+
+ @created 2003-02-08
+ @edited 2008-02-02
+ */
+
+#include "G3D/platform.h"
+#include "G3D/LineSegment.h"
+#include "G3D/Sphere.h"
+#include "G3D/debug.h"
+
+namespace G3D {
+
+
+Vector3 LineSegment::closestPoint(const Vector3& p) const {
+
+ // The vector from the end of the capsule to the point in question.
+ Vector3 v(p - _point);
+
+ // Projection of v onto the line segment scaled by
+ // the length of direction.
+ float t = direction.dot(v);
+
+ // Avoid some square roots. Derivation:
+ // t/direction.length() <= direction.length()
+ // t <= direction.squaredLength()
+
+ if ((t >= 0) && (t <= direction.squaredMagnitude())) {
+
+ // The point falls within the segment. Normalize direction,
+ // divide t by the length of direction.
+ return _point + direction * t / direction.squaredMagnitude();
+
+ } else {
+
+ // The point does not fall within the segment; see which end is closer.
+
+ // Distance from 0, squared
+ float d0Squared = v.squaredMagnitude();
+
+ // Distance from 1, squared
+ float d1Squared = (v - direction).squaredMagnitude();
+
+ if (d0Squared < d1Squared) {
+
+ // Point 0 is closer
+ return _point;
+
+ } else {
+
+ // Point 1 is closer
+ return _point + direction;
+
+ }
+ }
+
+}
+
+Vector3 LineSegment::point(int i) const {
+ switch (i) {
+ case 0:
+ return _point;
+
+ case 1:
+ return _point + direction;
+
+ default:
+ debugAssertM(i == 0 || i == 1, "Argument to point must be 0 or 1");
+ return _point;
+ }
+}
+
+
+bool LineSegment::intersectsSolidSphere(const class Sphere& s) const {
+ return distanceSquared(s.center) <= square(s.radius);
+}
+
+
+LineSegment::LineSegment(class BinaryInput& b) {
+ deserialize(b);
+}
+
+
+void LineSegment::serialize(class BinaryOutput& b) const {
+ _point.serialize(b);
+ direction.serialize(b);
+}
+
+
+void LineSegment::deserialize(class BinaryInput& b) {
+ _point.deserialize(b);
+ direction.deserialize(b);
+}
+
+
+Vector3 LineSegment::randomPoint() const {
+ return _point + uniformRandom(0, 1) * direction;
+}
+
+
+/////////////////////////////////////////////////////////////////////////////////////
+
+LineSegment2D LineSegment2D::fromTwoPoints(const Vector2& p0, const Vector2& p1) {
+ LineSegment2D s;
+ s.m_origin = p0;
+ s.m_direction = p1 - p0;
+ s.m_length = s.m_direction.length();
+ return s;
+}
+
+
+Vector2 LineSegment2D::point(int i) const {
+ debugAssert(i == 0 || i == 1);
+ if (i == 0) {
+ return m_origin;
+ } else {
+ return m_direction + m_origin;
+ }
+}
+
+
+Vector2 LineSegment2D::closestPoint(const Vector2& Q) const {
+ // Two constants that appear in the result
+ const Vector2 k1(m_origin - Q);
+ const Vector2& k2 = m_direction;
+
+ if (fuzzyEq(m_length, 0)) {
+ // This line segment has no length
+ return m_origin;
+ }
+
+ // Time [0, 1] at which we hit the closest point travelling from p0 to p1.
+ // Derivation can be obtained by minimizing the expression
+ // ||P0 + (P1 - P0)t - Q||.
+ const float t = -k1.dot(k2) / (m_length * m_length);
+
+ if (t < 0) {
+ // Clipped to low end point
+ return m_origin;
+ } else if (t > 1) {
+ // Clipped to high end point
+ return m_origin + m_direction;
+ } else {
+ // Subsitute into the line equation to find
+ // the point on the segment.
+ return m_origin + k2 * t;
+ }
+}
+
+
+float LineSegment2D::distance(const Vector2& p) const {
+ Vector2 closest = closestPoint(p);
+ return (closest - p).length();
+}
+
+
+float LineSegment2D::length() const {
+ return m_length;
+}
+
+
+Vector2 LineSegment2D::intersection(const LineSegment2D& other) const {
+
+ if ((m_origin == other.m_origin) ||
+ (m_origin == other.m_origin + other.m_direction)) {
+ return m_origin;
+ }
+
+ if (m_origin + m_direction == other.m_origin) {
+ return other.m_origin;
+ }
+
+ // Note: Now that we've checked the endpoints, all other parallel lines can now be assumed
+ // to not intersect (within numerical precision)
+
+ Vector2 dir1 = m_direction;
+ Vector2 dir2 = other.m_direction;
+ Vector2 origin1 = m_origin;
+ Vector2 origin2 = other.m_origin;
+
+ if (dir1.x == 0) {
+ // Avoid an upcoming divide by zero
+ dir1 = dir1.yx();
+ dir2 = dir2.yx();
+ origin1 = origin1.yx();
+ origin2 = origin2.yx();
+ }
+
+ // t1 = ((other.m_origin.x - m_origin.x) + other.m_direction.x * t2) / m_direction.x
+ //
+ // ((other.m_origin.x - m_origin.x) + other.m_direction.x * t2) * m_direction.y / m_direction.x =
+ // (other.m_origin.y - m_origin.y) + other.m_direction.y * t2
+ //
+ // m = m_direction.y / m_direction.x
+ // d = other.m_origin - m_origin
+ //
+ // (d.x + other.m_direction.x * t2) * m = d.y + other.m_direction.y * t2
+ //
+ // d.x * m + other.m_direction.x * m * t2 = d.y + other.m_direction.y * t2
+ //
+ // d.x * m - d.y = (other.m_direction.y - other.m_direction.x * m) * t2
+ //
+ // (d.x * m - d.y) / (other.m_direction.y - other.m_direction.x * m) = t2
+ //
+
+ Vector2 d = origin2 - origin1;
+ float m = dir1.y / dir1.x;
+
+ float t2 = (d.x * m - d.y) / (dir2.y - dir2.x * m);
+ if (! isFinite(t2)) {
+ // Parallel lines: no intersection
+ return Vector2::inf();
+ }
+
+ if ((t2 < 0.0f) || (t2 > 1.0f)) {
+ // Intersection occurs past the end of the line segments
+ return Vector2::inf();
+ }
+
+ float t1 = (d.x + dir2.x * t2) / dir1.x;
+ if ((t1 < 0.0f) || (t1 > 1.0f)) {
+ // Intersection occurs past the end of the line segments
+ return Vector2::inf();
+ }
+
+ // Return the intersection point (computed from non-transposed
+ // variables even if we flipped above)
+ return m_origin + m_direction * t1;
+
+}
+
+}
+
diff --git a/dep/src/g3dlite/Log.cpp b/dep/src/g3dlite/Log.cpp
new file mode 100644
index 00000000000..07614fcf563
--- /dev/null
+++ b/dep/src/g3dlite/Log.cpp
@@ -0,0 +1,146 @@
+/**
+ @file Log.cpp
+
+ @maintainer Morgan McGuire, http://graphics.cs.williams.edu
+ @created 2001-08-04
+ @edited 2009-01-15
+ */
+
+#include "G3D/platform.h"
+#include "G3D/Log.h"
+#include "G3D/format.h"
+#include "G3D/Array.h"
+#include "G3D/fileutils.h"
+#include <time.h>
+
+#ifdef G3D_WIN32
+ #include <imagehlp.h>
+#else
+ #include <stdarg.h>
+#endif
+
+namespace G3D {
+
+void logPrintf(const char* fmt, ...) {
+ va_list arg_list;
+ va_start(arg_list, fmt);
+ Log::common()->vprintf(fmt, arg_list);
+ va_end(arg_list);
+}
+
+
+void logLazyPrintf(const char* fmt, ...) {
+ va_list arg_list;
+ va_start(arg_list, fmt);
+ Log::common()->lazyvprintf(fmt, arg_list);
+ va_end(arg_list);
+}
+
+Log* Log::commonLog = NULL;
+
+Log::Log(const std::string& filename, int stripFromStackBottom) :
+ stripFromStackBottom(stripFromStackBottom) {
+
+ this->filename = filename;
+
+ logFile = fopen(filename.c_str(), "w");
+
+ if (logFile == NULL) {
+ std::string drive, base, ext;
+ Array<std::string> path;
+ parseFilename(filename, drive, path, base, ext);
+ std::string logName = base + ((ext != "") ? ("." + ext) : "");
+
+ // Write time is greater than 1ms. This may be a network drive.... try another file.
+ #ifdef G3D_WIN32
+ logName = std::string(std::getenv("TEMP")) + logName;
+ #else
+ logName = std::string("/tmp/") + logName;
+ #endif
+
+ logFile = fopen(logName.c_str(), "w");
+ }
+
+ // Use a large buffer (although we flush in logPrintf)
+ setvbuf(logFile, NULL, _IOFBF, 2048);
+
+ fprintf(logFile, "Application Log\n");
+ time_t t;
+ time(&t);
+ fprintf(logFile, "Start: %s\n", ctime(&t));
+ fflush(logFile);
+
+ if (commonLog == NULL) {
+ commonLog = this;
+ }
+}
+
+
+Log::~Log() {
+ section("Shutdown");
+ println("Closing log file");
+
+ // Make sure we don't leave a dangling pointer
+ if (Log::commonLog == this) {
+ Log::commonLog = NULL;
+ }
+
+ fclose(logFile);
+}
+
+
+FILE* Log::getFile() const {
+ return logFile;
+}
+
+
+Log* Log::common() {
+ if (commonLog == NULL) {
+ commonLog = new Log();
+ }
+ return commonLog;
+}
+
+
+std::string Log::getCommonLogFilename() {
+ return common()->filename;
+}
+
+
+void Log::section(const std::string& s) {
+ fprintf(logFile, "_____________________________________________________\n");
+ fprintf(logFile, "\n ### %s ###\n\n", s.c_str());
+}
+
+
+void __cdecl Log::printf(const char* fmt, ...) {
+ va_list arg_list;
+ va_start(arg_list, fmt);
+ print(vformat(fmt, arg_list));
+ va_end(arg_list);
+}
+
+
+void __cdecl Log::vprintf(const char* fmt, va_list argPtr) {
+ vfprintf(logFile, fmt, argPtr);
+ fflush(logFile);
+}
+
+
+void __cdecl Log::lazyvprintf(const char* fmt, va_list argPtr) {
+ vfprintf(logFile, fmt, argPtr);
+}
+
+
+void Log::print(const std::string& s) {
+ fprintf(logFile, "%s", s.c_str());
+ fflush(logFile);
+}
+
+
+void Log::println(const std::string& s) {
+ fprintf(logFile, "%s\n", s.c_str());
+ fflush(logFile);
+}
+
+}
diff --git a/dep/src/g3dlite/Makefile.am b/dep/src/g3dlite/Makefile.am
new file mode 100644
index 00000000000..d37528b4c72
--- /dev/null
+++ b/dep/src/g3dlite/Makefile.am
@@ -0,0 +1,69 @@
+## Modified for MaNGOS project <http://getmangos.com>
+##
+## Permission is hereby granted, free of charge, to any person obtaining a copy
+## of this software and associated documentation files (the "Software"), to deal
+## in the Software without restriction, including without limitation the rights
+## to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+## copies of the Software, and to permit persons to whom the Software is furnished
+## to do so, subject to the following conditions:
+##
+## The above copyright notice and this permission notice shall be included in all
+## copies or substantial portions of the Software.
+##
+## THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+## IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+## FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+## AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+## WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+## CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+## Process this file with automake to produce Makefile.in
+
+## CPP flags for includes, defines, etc.
+AM_CPPFLAGS = -I$(srcdir) -I$(srcdir)/../../include -I$(srcdir)/../../include/g3dlite
+
+noinst_LIBRARIES = libg3dlite.a
+libg3dlite_a_SOURCES = \
+ AABox.cpp \
+ Box.cpp \
+ Crypto.cpp \
+ format.cpp \
+ Matrix3.cpp \
+ Plane.cpp \
+ System.cpp \
+ Triangle.cpp \
+ Vector3.cpp \
+ Vector4.cpp \
+ debugAssert.cpp \
+ fileutils.cpp \
+ g3dmath.cpp \
+ g3dfnmatch.cpp \
+ prompt.cpp \
+ stringutils.cpp \
+ Any.cpp \
+ BinaryFormat.cpp \
+ BinaryInput.cpp \
+ BinaryOutput.cpp \
+ Capsule.cpp \
+ CollisionDetection.cpp \
+ CoordinateFrame.cpp \
+ Cylinder.cpp \
+ Line.cpp \
+ LineSegment.cpp \
+ Log.cpp \
+ Matrix4.cpp \
+ MemoryManager.cpp \
+ Quat.cpp \
+ Random.cpp \
+ Ray.cpp \
+ ReferenceCount.cpp \
+ Sphere.cpp \
+ TextInput.cpp \
+ TextOutput.cpp \
+ UprightFrame.cpp \
+ Vector2.cpp
+
+EXTRA_DIST = \
+ license.html
+
+
diff --git a/dep/src/g3dlite/Matrix.cpp b/dep/src/g3dlite/Matrix.cpp
new file mode 100644
index 00000000000..7a668e59e2c
--- /dev/null
+++ b/dep/src/g3dlite/Matrix.cpp
@@ -0,0 +1,1802 @@
+/**
+ @file Matrix.cpp
+ @author Morgan McGuire, http://graphics.cs.williams.edu
+ */
+#include "G3D/Matrix.h"
+#include "G3D/TextOutput.h"
+
+static inline G3D::Matrix::T negate(G3D::Matrix::T x) {
+ return -x;
+}
+
+namespace G3D {
+
+int Matrix::debugNumCopyOps = 0;
+int Matrix::debugNumAllocOps = 0;
+
+void Matrix::serialize(TextOutput& t) const {
+ t.writeSymbol("%");
+ t.writeNumber(rows());
+ t.writeSymbol("x");
+ t.writeNumber(cols());
+ t.pushIndent();
+ t.writeNewline();
+
+ t.writeSymbol("[");
+ for (int r = 0; r < rows(); ++r) {
+ for (int c = 0; c < cols(); ++c) {
+ t.writeNumber(impl->get(r, c));
+ if (c < cols() - 1) {
+ t.writeSymbol(",");
+ } else {
+ if (r < rows() - 1) {
+ t.writeSymbol(";");
+ t.writeNewline();
+ }
+ }
+ }
+ }
+ t.writeSymbol("]");
+ t.popIndent();
+ t.writeNewline();
+}
+
+
+std::string Matrix::toString(const std::string& name) const {
+ std::string s;
+
+ if (name != "") {
+ s += format("%s = \n", name.c_str());
+ }
+
+ s += "[";
+ for (int r = 0; r < rows(); ++r) {
+ for (int c = 0; c < cols(); ++c) {
+ double v = impl->get(r, c);
+
+ if (::fabs(v) < 0.00001) {
+ // Don't print "negative zero"
+ s += format("% 10.04g", 0.0);
+ } else if (v == iRound(v)) {
+ // Print integers nicely
+ s += format("% 10.04g", v);
+ } else {
+ s += format("% 10.04f", v);
+ }
+
+ if (c < cols() - 1) {
+ s += ",";
+ } else if (r < rows() - 1) {
+ s += ";\n ";
+ } else {
+ s += "]\n";
+ }
+ }
+ }
+ return s;
+}
+
+
+#define INPLACE(OP)\
+ ImplRef A = impl;\
+\
+ if (! A.isLastReference()) {\
+ impl = new Impl(A->R, A->C);\
+ }\
+\
+ A->OP(B, *impl);
+
+Matrix& Matrix::operator*=(const T& B) {
+ INPLACE(mul)
+ return *this;
+}
+
+
+Matrix& Matrix::operator-=(const T& B) {
+ INPLACE(sub)
+ return *this;
+}
+
+
+Matrix& Matrix::operator+=(const T& B) {
+ INPLACE(add)
+ return *this;
+}
+
+
+Matrix& Matrix::operator/=(const T& B) {
+ INPLACE(div)
+ return *this;
+}
+
+
+Matrix& Matrix::operator*=(const Matrix& B) {
+ // We can't optimize this one
+ *this = *this * B;
+ return *this;
+}
+
+
+Matrix& Matrix::operator-=(const Matrix& _B) {
+ const Impl& B = *_B.impl;
+ INPLACE(sub)
+ return *this;
+}
+
+
+Matrix& Matrix::operator+=(const Matrix& _B) {
+ const Impl& B = *_B.impl;
+ INPLACE(add)
+ return *this;
+}
+
+
+void Matrix::arrayMulInPlace(const Matrix& _B) {
+ const Impl& B = *_B.impl;
+ INPLACE(arrayMul)
+}
+
+
+void Matrix::arrayDivInPlace(const Matrix& _B) {
+ const Impl& B = *_B.impl;
+ INPLACE(arrayDiv)
+}
+
+#undef INPLACE
+
+Matrix Matrix::fromDiagonal(const Matrix& d) {
+ debugAssert((d.rows() == 1) || (d.cols() == 1));
+
+ int n = d.numElements();
+ Matrix D = zero(n, n);
+ for (int i = 0; i < n; ++i) {
+ D.set(i, i, d.impl->data[i]);
+ }
+
+ return D;
+}
+
+void Matrix::set(int r, int c, T v) {
+ if (! impl.isLastReference()) {
+ // Copy the data before mutating; this object is shared
+ impl = new Impl(*impl);
+ }
+ impl->set(r, c, v);
+}
+
+
+void Matrix::setRow(int r, const Matrix& vec) {
+ debugAssertM(vec.cols() == cols(),
+ "A row must be set to a vector of the same size.");
+ debugAssertM(vec.rows() == 1,
+ "A row must be set to a row vector.");
+
+ debugAssert(r >= 0);
+ debugAssert(r < rows());
+
+ if (! impl.isLastReference()) {
+ // Copy the data before mutating; this object is shared
+ impl = new Impl(*impl);
+ }
+ impl->setRow(r, vec.impl->data);
+}
+
+
+void Matrix::setCol(int c, const Matrix& vec) {
+ debugAssertM(vec.rows() == rows(),
+ "A column must be set to a vector of the same size.");
+ debugAssertM(vec.cols() == 1,
+ "A column must be set to a column vector.");
+
+ debugAssert(c >= 0);
+
+ debugAssert(c < cols());
+
+ if (! impl.isLastReference()) {
+ // Copy the data before mutating; this object is shared
+ impl = new Impl(*impl);
+ }
+ impl->setCol(c, vec.impl->data);
+}
+
+
+Matrix::T Matrix::get(int r, int c) const {
+ return impl->get(r, c);
+}
+
+
+Matrix Matrix::row(int r) const {
+ debugAssert(r >= 0);
+ debugAssert(r < rows());
+ Matrix out(1, cols());
+ out.impl->setRow(1, impl->elt[r]);
+ return out;
+}
+
+
+Matrix Matrix::col(int c) const {
+ debugAssert(c >= 0);
+ debugAssert(c < cols());
+ Matrix out(rows(), 1);
+
+ T* outData = out.impl->data;
+ // Get a pointer to the first element in the column
+ const T* inElt = &(impl->elt[0][c]);
+ int R = rows();
+ int C = cols();
+ for (int r = 0; r < R; ++r) {
+ outData[r] = *inElt;
+ // Skip around to the next row
+ inElt += C;
+ }
+
+ return out;
+}
+
+
+Matrix Matrix::zero(int R, int C) {
+ Impl* A = new Impl(R, C);
+ A->setZero();
+ return Matrix(A);
+}
+
+
+Matrix Matrix::one(int R, int C) {
+ Impl* A = new Impl(R, C);
+ for (int i = R * C - 1; i >= 0; --i) {
+ A->data[i] = 1.0;
+ }
+ return Matrix(A);
+}
+
+
+Matrix Matrix::random(int R, int C) {
+ Impl* A = new Impl(R, C);
+ for (int i = R * C - 1; i >= 0; --i) {
+ A->data[i] = G3D::uniformRandom(0.0, 1.0);
+ }
+ return Matrix(A);
+}
+
+
+Matrix Matrix::identity(int N) {
+ Impl* m = new Impl(N, N);
+ m->setZero();
+ for (int i = 0; i < N; ++i) {
+ m->elt[i][i] = 1.0;
+ }
+ return Matrix(m);
+}
+
+
+// Implement an explicit-output unary method by trampolining to the impl
+#define TRAMPOLINE_EXPLICIT_1(method)\
+void Matrix::method(Matrix& out) const {\
+ if ((out.impl == impl) && impl.isLastReference()) {\
+ impl->method(*out.impl);\
+ } else {\
+ out = this->method();\
+ }\
+}
+
+TRAMPOLINE_EXPLICIT_1(abs)
+TRAMPOLINE_EXPLICIT_1(negate)
+TRAMPOLINE_EXPLICIT_1(arrayLog)
+TRAMPOLINE_EXPLICIT_1(arrayExp)
+TRAMPOLINE_EXPLICIT_1(arrayCos)
+TRAMPOLINE_EXPLICIT_1(arraySin)
+
+void Matrix::mulRow(int r, const T& v) {
+ debugAssert(r >= 0 && r < rows());
+
+ if (! impl.isLastReference()) {
+ impl = new Impl(*impl);
+ }
+
+ impl->mulRow(r, v);
+}
+
+
+void Matrix::transpose(Matrix& out) const {
+ if ((out.impl == impl) && impl.isLastReference() && (impl->R == impl->C)) {
+ // In place
+ impl->transpose(*out.impl);
+ } else {
+ out = this->transpose();
+ }
+}
+
+
+Matrix3 Matrix::toMatrix3() const {
+ debugAssert(impl->R == 3);
+ debugAssert(impl->C == 3);
+ return Matrix3(
+ impl->get(0,0), impl->get(0,1), impl->get(0,2),
+ impl->get(1,0), impl->get(1,1), impl->get(1,2),
+ impl->get(2,0), impl->get(2,1), impl->get(2,2));
+}
+
+
+Matrix4 Matrix::toMatrix4() const {
+ debugAssert(impl->R == 4);
+ debugAssert(impl->C == 4);
+ return Matrix4(
+ impl->get(0,0), impl->get(0,1), impl->get(0,2), impl->get(0,3),
+ impl->get(1,0), impl->get(1,1), impl->get(1,2), impl->get(1,3),
+ impl->get(2,0), impl->get(2,1), impl->get(2,2), impl->get(2,3),
+ impl->get(3,0), impl->get(3,1), impl->get(3,2), impl->get(3,3));
+}
+
+
+Vector2 Matrix::toVector2() const {
+ debugAssert(impl->R * impl->C == 2);
+ if (impl->R > impl->C) {
+ return Vector2(impl->get(0,0), impl->get(1,0));
+ } else {
+ return Vector2(impl->get(0,0), impl->get(0,1));
+ }
+}
+
+
+Vector3 Matrix::toVector3() const {
+ debugAssert(impl->R * impl->C == 3);
+ if (impl->R > impl->C) {
+ return Vector3(impl->get(0,0), impl->get(1,0), impl->get(2, 0));
+ } else {
+ return Vector3(impl->get(0,0), impl->get(0,1), impl->get(0, 2));
+ }
+}
+
+
+Vector4 Matrix::toVector4() const {
+ debugAssert(
+ ((impl->R == 4) && (impl->C == 1)) ||
+ ((impl->R == 1) && (impl->C == 4)));
+
+ if (impl->R > impl->C) {
+ return Vector4(impl->get(0,0), impl->get(1,0), impl->get(2, 0), impl->get(3,0));
+ } else {
+ return Vector4(impl->get(0,0), impl->get(0,1), impl->get(0, 2), impl->get(0,3));
+ }
+}
+
+
+void Matrix::swapRows(int r0, int r1) {
+ debugAssert(r0 >= 0 && r0 < rows());
+ debugAssert(r1 >= 0 && r1 < rows());
+
+ if (r0 == r1) {
+ return;
+ }
+
+ if (! impl.isLastReference()) {
+ impl = new Impl(*impl);
+ }
+
+ impl->swapRows(r0, r1);
+}
+
+
+void Matrix::swapAndNegateCols(int c0, int c1) {
+ debugAssert(c0 >= 0 && c0 < cols());
+ debugAssert(c1 >= 0 && c1 < cols());
+
+ if (c0 == c1) {
+ return;
+ }
+
+ if (! impl.isLastReference()) {
+ impl = new Impl(*impl);
+ }
+
+ impl->swapAndNegateCols(c0, c1);
+}
+
+Matrix Matrix::subMatrix(int r1, int r2, int c1, int c2) const {
+ debugAssert(r2>=r1);
+ debugAssert(c2>=c1);
+ debugAssert(c2<cols());
+ debugAssert(r2<rows());
+ debugAssert(r1>=0);
+ debugAssert(c1>=0);
+
+ Matrix X(r2 - r1 + 1, c2 - c1 + 1);
+
+ for (int r = 0; r < X.rows(); ++r) {
+ for (int c = 0; c < X.cols(); ++c) {
+ X.set(r, c, get(r + r1, c + c1));
+ }
+ }
+
+ return X;
+}
+
+
+bool Matrix::anyNonZero() const {
+ return impl->anyNonZero();
+}
+
+
+bool Matrix::allNonZero() const {
+ return impl->allNonZero();
+}
+
+
+void Matrix::svd(Matrix& U, Array<T>& d, Matrix& V, bool sort) const {
+ debugAssert(rows() >= cols());
+ debugAssertM(&U != &V, "Arguments to SVD must be different matrices");
+ debugAssertM(&U != this, "Arguments to SVD must be different matrices");
+ debugAssertM(&V != this, "Arguments to SVD must be different matrices");
+
+ int R = rows();
+ int C = cols();
+
+ // Make sure we don't overwrite a shared matrix
+ if (! V.impl.isLastReference()) {
+ V = Matrix::zero(C, C);
+ } else {
+ V.impl->setSize(C, C);
+ }
+
+ if (&U != this || ! impl.isLastReference()) {
+ // Make a copy of this for in-place SVD
+ U.impl = new Impl(*impl);
+ }
+
+ d.resize(C);
+ const char* ret = svdCore(U.impl->elt, R, C, d.getCArray(), V.impl->elt);
+
+ debugAssertM(ret == NULL, ret);
+ (void)ret;
+
+ if (sort) {
+ // Sort the singular values from greatest to least
+
+ Array<SortRank> rank;
+ rank.resize(C);
+ for (int c = 0; c < C; ++c) {
+ rank[c].col = c;
+ rank[c].value = d[c];
+ }
+
+ rank.sort(SORT_INCREASING);
+
+ Matrix Uold = U;
+ Matrix Vold = V;
+
+ U = Matrix(U.rows(), U.cols());
+ V = Matrix(V.rows(), V.cols());
+
+ // Now permute U, d, and V appropriately
+ for (int c0 = 0; c0 < C; ++c0) {
+ const int c1 = rank[c0].col;
+
+ d[c0] = rank[c0].value;
+ U.setCol(c0, Uold.col(c1));
+ V.setCol(c0, Vold.col(c1));
+ }
+
+ }
+}
+
+
+#define COMPARE_SCALAR(OP)\
+Matrix Matrix::operator OP (const T& scalar) const {\
+ int R = rows();\
+ int C = cols();\
+ int N = R * C;\
+ Matrix out = Matrix::zero(R, C);\
+\
+ const T* raw = impl->data;\
+ T* outRaw = out.impl->data;\
+ for (int i = 0; i < N; ++i) {\
+ outRaw[i] = raw[i] OP scalar;\
+ }\
+\
+ return out;\
+}
+
+COMPARE_SCALAR(<)
+COMPARE_SCALAR(<=)
+COMPARE_SCALAR(>)
+COMPARE_SCALAR(>=)
+COMPARE_SCALAR(==)
+COMPARE_SCALAR(!=)
+
+#undef COMPARE_SCALAR
+
+double Matrix::normSquared() const {
+ int R = rows();
+ int C = cols();
+ int N = R * C;
+
+ double sum = 0.0;
+
+ const T* raw = impl->data;
+ for (int i = 0; i < N; ++i) {
+ sum += square(raw[i]);
+ }
+
+ return sum;
+}
+
+double Matrix::norm() const {
+ return sqrt(normSquared());
+}
+
+///////////////////////////////////////////////////////////
+
+Matrix::Impl::Impl(const Matrix3& M) : elt(NULL), data(NULL), R(0), C(0), dataSize(0){
+ setSize(3, 3);
+ for (int r = 0; r < 3; ++r) {
+ for (int c = 0; c < 3; ++c) {
+ set(r, c, M[r][c]);
+ }
+ }
+
+}
+
+
+Matrix::Impl::Impl(const Matrix4& M): elt(NULL), data(NULL), R(0), C(0), dataSize(0) {
+ setSize(4, 4);
+ for (int r = 0; r < 4; ++r) {
+ for (int c = 0; c < 4; ++c) {
+ set(r, c, M[r][c]);
+ }
+ }
+}
+
+
+void Matrix::Impl::setSize(int newRows, int newCols) {
+ if ((R == newRows) && (C == newCols)) {
+ // Nothing to do
+ return;
+ }
+
+ int newSize = newRows * newCols;
+
+ R = newRows; C = newCols;
+
+ // Only allocate if we need more space
+ // or the size difference is ridiculous
+ if ((newSize > dataSize) || (newSize < dataSize / 4)) {
+ System::alignedFree(data);
+ data = (float*)System::alignedMalloc(R * C * sizeof(T), 16);
+ ++Matrix::debugNumAllocOps;
+ dataSize = newSize;
+ }
+
+ // Construct the row pointers
+ //delete[] elt;
+ System::free(elt);
+ elt = (T**)System::malloc(R * sizeof(T*));// new T*[R];
+
+ for (int r = 0; r < R; ++ r) {
+ elt[r] = data + r * C;
+ }
+}
+
+
+Matrix::Impl::~Impl() {
+ //delete[] elt;
+ System::free(elt);
+ System::alignedFree(data);
+}
+
+
+Matrix::Impl& Matrix::Impl::operator=(const Impl& m) {
+ setSize(m.R, m.C);
+ System::memcpy(data, m.data, R * C * sizeof(T));
+ ++Matrix::debugNumCopyOps;
+ return *this;
+}
+
+
+void Matrix::Impl::setZero() {
+ System::memset(data, 0, R * C * sizeof(T));
+}
+
+
+void Matrix::Impl::swapRows(int r0, int r1) {
+ T* R0 = elt[r0];
+ T* R1 = elt[r1];
+
+ for (int c = 0; c < C; ++c) {
+ T temp = R0[c];
+ R0[c] = R1[c];
+ R1[c] = temp;
+ }
+}
+
+
+void Matrix::Impl::swapAndNegateCols(int c0, int c1) {
+ for (int r = 0; r < R; ++r) {
+ T* row = elt[r];
+
+ const T temp = -row[c0];
+ row[c0] = -row[c1];
+ row[c1] = temp;
+ }
+}
+
+
+void Matrix::Impl::mulRow(int r, const T& v) {
+ T* row = elt[r];
+
+ for (int c = 0; c < C; ++c) {
+ row[c] *= v;
+ }
+}
+
+
+void Matrix::Impl::mul(const Impl& B, Impl& out) const {
+ const Impl& A = *this;
+
+ debugAssertM(
+ (this != &out) && (&B != &out),
+ "Output argument to mul cannot be the same as an input argument.");
+
+ debugAssert(A.C == B.R);
+ debugAssert(A.R == out.R);
+ debugAssert(B.C == out.C);
+
+ for (int r = 0; r < out.R; ++r) {
+ for (int c = 0; c < out.C; ++c) {
+ T sum = 0.0;
+ for (int i = 0; i < A.C; ++i) {
+ sum += A.get(r, i) * B.get(i, c);
+ }
+ out.set(r, c, sum);
+ }
+ }
+}
+
+
+// We're about to define several similar methods,
+// so use a macro to share implementations. This
+// must be a macro because the difference between
+// the macros is the operation in the inner loop.
+#define IMPLEMENT_ARRAY_2(method, OP)\
+void Matrix::Impl::method(const Impl& B, Impl& out) const {\
+ const Impl& A = *this;\
+ \
+ debugAssert(A.C == B.C);\
+ debugAssert(A.R == B.R);\
+ debugAssert(A.C == out.C);\
+ debugAssert(A.R == out.R);\
+ \
+ for (int i = R * C - 1; i >= 0; --i) {\
+ out.data[i] = A.data[i] OP B.data[i];\
+ }\
+}
+
+
+#define IMPLEMENT_ARRAY_1(method, f)\
+void Matrix::Impl::method(Impl& out) const {\
+ const Impl& A = *this;\
+ \
+ debugAssert(A.C == out.C);\
+ debugAssert(A.R == out.R);\
+ \
+ for (int i = R * C - 1; i >= 0; --i) {\
+ out.data[i] = f(A.data[i]);\
+ }\
+}
+
+
+#define IMPLEMENT_ARRAY_SCALAR(method, OP)\
+void Matrix::Impl::method(Matrix::T B, Impl& out) const {\
+ const Impl& A = *this;\
+ \
+ debugAssert(A.C == out.C);\
+ debugAssert(A.R == out.R);\
+ \
+ for (int i = R * C - 1; i >= 0; --i) {\
+ out.data[i] = A.data[i] OP B;\
+ }\
+}
+
+IMPLEMENT_ARRAY_2(add, +)
+IMPLEMENT_ARRAY_2(sub, -)
+IMPLEMENT_ARRAY_2(arrayMul, *)
+IMPLEMENT_ARRAY_2(arrayDiv, /)
+
+IMPLEMENT_ARRAY_SCALAR(add, +)
+IMPLEMENT_ARRAY_SCALAR(sub, -)
+IMPLEMENT_ARRAY_SCALAR(mul, *)
+IMPLEMENT_ARRAY_SCALAR(div, /)
+
+IMPLEMENT_ARRAY_1(abs, ::fabs)
+IMPLEMENT_ARRAY_1(negate, ::negate)
+IMPLEMENT_ARRAY_1(arrayLog, ::log)
+IMPLEMENT_ARRAY_1(arraySqrt, ::sqrt)
+IMPLEMENT_ARRAY_1(arrayExp, ::exp)
+IMPLEMENT_ARRAY_1(arrayCos, ::cos)
+IMPLEMENT_ARRAY_1(arraySin, ::sin)
+
+#undef IMPLEMENT_ARRAY_SCALAR
+#undef IMPLEMENT_ARRAY_1
+#undef IMPLEMENT_ARRAY_2
+
+// lsub is special because the argument order is reversed
+void Matrix::Impl::lsub(Matrix::T B, Impl& out) const {
+ const Impl& A = *this;
+
+ debugAssert(A.C == out.C);
+ debugAssert(A.R == out.R);
+
+ for (int i = R * C - 1; i >= 0; --i) {
+ out.data[i] = B - A.data[i];
+ }
+}
+
+
+void Matrix::Impl::inverseViaAdjoint(Impl& out) const {
+ debugAssert(&out != this);
+
+ // Inverse = adjoint / determinant
+
+ adjoint(out);
+
+ // Don't call the determinant method when we already have an
+ // adjoint matrix; there's a faster way of computing it: the dot
+ // product of the first row and the adjoint's first col.
+ double det = 0.0;
+ for (int r = R - 1; r >= 0; --r) {
+ det += elt[0][r] * out.elt[r][0];
+ }
+
+ out.div(Matrix::T(det), out);
+}
+
+
+void Matrix::Impl::transpose(Impl& out) const {
+ debugAssert(out.R == C);
+ debugAssert(out.C == R);
+
+ if (&out == this) {
+ // Square matrix in place
+ for (int r = 0; r < R; ++r) {
+ for (int c = r + 1; c < C; ++c) {
+ T temp = get(r, c);
+ out.set(r, c, get(c, r));
+ out.set(c, r, temp);
+ }
+ }
+ } else {
+ for (int r = 0; r < R; ++r) {
+ for (int c = 0; c < C; ++c) {
+ out.set(c, r, get(r, c));
+ }
+ }
+ }
+}
+
+
+void Matrix::Impl::adjoint(Impl& out) const {
+ cofactor(out);
+ // Transpose is safe to perform in place
+ out.transpose(out);
+}
+
+
+void Matrix::Impl::cofactor(Impl& out) const {
+ debugAssert(&out != this);
+ for(int r = 0; r < R; ++r) {
+ for(int c = 0; c < C; ++c) {
+ out.set(r, c, cofactor(r, c));
+ }
+ }
+}
+
+
+Matrix::T Matrix::Impl::cofactor(int r, int c) const {
+ // Strang p. 217
+ float s = isEven(r + c) ? 1.0f : -1.0f;
+
+ return s * determinant(r, c);
+}
+
+
+Matrix::T Matrix::Impl::determinant(int nr, int nc) const {
+ debugAssert(R > 0);
+ debugAssert(C > 0);
+ Impl A(R - 1, C - 1);
+ withoutRowAndCol(nr, nc, A);
+ return A.determinant();
+}
+
+
+void Matrix::Impl::setRow(int r, const T* vals) {
+ debugAssert(r >= 0);
+ System::memcpy(elt[r], vals, sizeof(T) * C);
+}
+
+
+void Matrix::Impl::setCol(int c, const T* vals) {
+ for (int r = 0; r < R; ++r) {
+ elt[r][c] = vals[r];
+ }
+}
+
+
+Matrix::T Matrix::Impl::determinant() const {
+
+ debugAssert(R == C);
+
+ // Compute using cofactors
+ switch(R) {
+ case 0:
+ return 0;
+
+ case 1:
+ // Determinant of a 1x1 is the element
+ return elt[0][0];
+
+ case 2:
+ // Determinant of a 2x2 is ad-bc
+ return elt[0][0] * elt[1][1] - elt[0][1] * elt[1][0];
+
+ case 3:
+ {
+ // Determinant of an nxn matrix is the dot product of the first
+ // row with the first row of cofactors. The base cases of this
+ // method get called a lot, so we spell out the implementation
+ // for the 3x3 case.
+
+ float cofactor00 = elt[1][1] * elt[2][2] - elt[1][2] * elt[2][1];
+ float cofactor10 = elt[1][2] * elt[2][0] - elt[1][0] * elt[2][2];
+ float cofactor20 = elt[1][0] * elt[2][1] - elt[1][1] * elt[2][0];
+
+ return Matrix::T(
+ elt[0][0] * cofactor00 +
+ elt[0][1] * cofactor10 +
+ elt[0][2] * cofactor20);
+ }
+
+ default:
+ {
+ // Determinant of an n x n matrix is the dot product of the first
+ // row with the first row of cofactors
+ T det = 0;
+
+ for (int c = 0; c < C; ++c) {
+ det += elt[0][c] * cofactor(0, c);
+ }
+
+ return det;
+ }
+ }
+}
+
+
+void Matrix::Impl::withoutRowAndCol(int excludeRow, int excludeCol, Impl& out) const {
+ debugAssert(out.R == R - 1);
+ debugAssert(out.C == C - 1);
+
+ for (int r = 0; r < out.R; ++r) {
+ for (int c = 0; c < out.C; ++c) {
+ out.elt[r][c] = elt[r + ((r >= excludeRow) ? 1 : 0)][c + ((c >= excludeCol) ? 1 : 0)];
+ }
+ }
+}
+
+
+Matrix Matrix::pseudoInverse(float tolerance) const {
+ if ((cols() == 1) || (rows() == 1)) {
+ return vectorPseudoInverse();
+ } else if ((cols() <= 4) || (rows() <= 4)) {
+ return partitionPseudoInverse();
+ } else {
+ return svdPseudoInverse(tolerance);
+ }
+}
+
+/*
+ Public function for testing purposes only. Use pseudoInverse(), as it contains optimizations for
+ nonsingular matrices with at least one small (<5) dimension.
+*/
+Matrix Matrix::svdPseudoInverse(float tolerance) const {
+ if (cols() > rows()) {
+ return transpose().svdPseudoInverse(tolerance).transpose();
+ }
+
+ // Matrices from SVD
+ Matrix U, V;
+
+ // Diagonal elements
+ Array<T> d;
+
+ svd(U, d, V);
+
+ if (rows() == 1) {
+ d.resize(1, false);
+ }
+
+ if (tolerance < 0) {
+ // TODO: Should be eps(d[0]), which is the largest diagonal
+ tolerance = G3D::max(rows(), cols()) * 0.0001f;
+ }
+
+ Matrix X;
+
+ int r = 0;
+ for (int i = 0; i < d.size(); ++i) {
+ if (d[i] > tolerance) {
+ d[i] = Matrix::T(1) / d[i];
+ ++r;
+ }
+ }
+
+ if (r == 0) {
+ // There were no non-zero elements
+ X = zero(cols(), rows());
+ } else {
+ // Use the first r columns
+
+ // Test code (the rest is below)
+ /*
+ d.resize(r);
+ Matrix testU = U.subMatrix(0, U.rows() - 1, 0, r - 1);
+ Matrix testV = V.subMatrix(0, V.rows() - 1, 0, r - 1);
+ Matrix testX = testV * Matrix::fromDiagonal(d) * testU.transpose();
+ X = testX;
+ */
+
+
+ // We want to do this:
+ //
+ // d.resize(r);
+ // U = U.subMatrix(0, U.rows() - 1, 0, r - 1);
+ // X = V * Matrix::fromDiagonal(d) * U.transpose();
+ //
+ // but creating a large diagonal matrix and then
+ // multiplying by it is wasteful. So we instead
+ // explicitly perform A = (D * U')' = U * D, and
+ // then multiply X = V * A'.
+
+ Matrix A = Matrix(U.rows(), r);
+
+ const T* dPtr = d.getCArray();
+ for (int i = 0; i < A.rows(); ++i) {
+ const T* Urow = U.impl->elt[i];
+ T* Arow = A.impl->elt[i];
+ const int Acols = A.cols();
+ for (int j = 0; j < Acols; ++j) {
+ // A(i,j) = U(i,:) * D(:,j)
+ // This is non-zero only at j = i because D is diagonal
+ // A(i,j) = U(i,j) * D(j,j)
+ Arow[j] = Urow[j] * dPtr[j];
+ }
+ }
+
+ //
+ // Compute X = V.subMatrix(0, V.rows() - 1, 0, r - 1) * A.transpose()
+ //
+ // Avoid the explicit subMatrix call, and by storing A' instead of A, avoid
+ // both the transpose and the memory incoherence of striding across memory
+ // in big steps.
+
+ alwaysAssertM(A.cols() == r,
+ "Internal dimension mismatch during pseudoInverse()");
+ alwaysAssertM(V.cols() >= r,
+ "Internal dimension mismatch during pseudoInverse()");
+
+ X = Matrix(V.rows(), A.rows());
+ T** Xelt = X.impl->elt;
+ for (int i = 0; i < X.rows(); ++i) {
+ const T* Vrow = V.impl->elt[i];
+ for (int j = 0; j < X.cols(); ++j) {
+ const T* Arow = A.impl->elt[j];
+ T sum = 0;
+ for (int k = 0; k < r; ++k) {
+ sum += Vrow[k] * Arow[k];
+ }
+ Xelt[i][j] = sum;
+ }
+ }
+
+ /*
+ // Test that results are the same after optimizations:
+ Matrix diff = X - testX;
+ T n = diff.norm();
+ debugAssert(n < 0.0001);
+ */
+ }
+ return X;
+}
+
+// Computes pseudoinverse for a vector
+Matrix Matrix::vectorPseudoInverse() const {
+ // If vector A has nonzero elements: transpose A, then divide each elt. by the squared norm
+ // If A is zero vector: transpose A
+ double x = 0.0;
+
+ if (anyNonZero()) {
+ x = 1.0 / normSquared();
+ }
+
+ Matrix A(cols(), rows());
+ T** Aelt = A.impl->elt;
+ for (int r = 0; r < rows(); ++r) {
+ const T* MyRow = impl->elt[r];
+ for (int c = 0; c < cols(); ++c) {
+ Aelt[c][r] = T(MyRow[c] * x);
+ }
+ }
+ return Matrix(A);
+}
+
+
+Matrix Matrix::rowPartPseudoInverse() const{
+ int m = rows();
+ int n = cols();
+ alwaysAssertM((m<=n),"Row-partitioned block matrix pseudoinverse requires R<C");
+
+ // B = A * A'
+ Matrix A = *this;
+ Matrix B = Matrix(m,m);
+
+ T** Aelt = A.impl->elt;
+ T** Belt = B.impl->elt;
+ for (int i = 0; i < m; ++i) {
+ const T* Arow = Aelt[i];
+ for (int j = 0; j < m; ++j) {
+ const T* Brow = Aelt[j];
+ T sum = 0;
+ for (int k = 0; k < n; ++k) {
+ sum += Arow[k] * Brow[k];
+ }
+ Belt[i][j] = sum;
+ }
+ }
+
+ // B has size m x m
+ switch (m) {
+ case 2:
+ return row2PseudoInverse(B);
+
+ case 3:
+ return row3PseudoInverse(B);
+
+ case 4:
+ return row4PseudoInverse(B);
+
+ default:
+ alwaysAssertM(false, "G3D internal error: Should have used the vector or general case!");
+ return Matrix();
+ }
+}
+
+Matrix Matrix::colPartPseudoInverse() const{
+ int m = rows();
+ int n = cols();
+ alwaysAssertM((m>=n),"Column-partitioned block matrix pseudoinverse requires R>C");
+ // TODO: Put each of the individual cases in its own helper function
+ // TODO: Push the B computation down into the individual cases
+ // B = A' * A
+ Matrix A = *this;
+ Matrix B = Matrix(n, n);
+ T** Aelt = A.impl->elt;
+ T** Belt = B.impl->elt;
+ for (int i = 0; i < n; ++i) {
+ for (int j = 0; j < n; ++j) {
+ T sum = 0;
+ for (int k = 0; k < m; ++k) {
+ sum += Aelt[k][i] * Aelt[k][j];
+ }
+ Belt[i][j] = sum;
+ }
+ }
+
+ // B has size n x n
+ switch (n) {
+ case 2:
+ return col2PseudoInverse(B);
+
+ case 3:
+ return col3PseudoInverse(B);
+
+ case 4:
+ return col4PseudoInverse(B);
+
+ default:
+ alwaysAssertM(false, "G3D internal error: Should have used the vector or general case!");
+ return Matrix();
+ }
+}
+
+Matrix Matrix::col2PseudoInverse(const Matrix& B) const {
+
+ Matrix A = *this;
+ int m = rows();
+ int n = cols();
+ (void)n;
+
+ // Row-major 2x2 matrix
+ const float B2[2][2] =
+ {{B.get(0,0), B.get(0,1)},
+ {B.get(1,0), B.get(1,1)}};
+
+ float det = (B2[0][0]*B2[1][1]) - (B2[0][1]*B2[1][0]);
+
+ if (fuzzyEq(det, T(0))) {
+
+ // Matrix was singular; the block matrix pseudo-inverse can't
+ // handle that, so fall back to the old case
+ return svdPseudoInverse();
+
+ } else {
+ // invert using formula at http://www.netsoc.tcd.ie/~jgilbert/maths_site/applets/algebra/matrix_inversion.html
+
+ // Multiply by Binv * A'
+ Matrix X(cols(), rows());
+
+ T** Xelt = X.impl->elt;
+ T** Aelt = A.impl->elt;
+ float binv00 = B2[1][1]/det, binv01 = -B2[1][0]/det;
+ float binv10 = -B2[0][1]/det, binv11 = B2[0][0]/det;
+ for (int j = 0; j < m; ++j) {
+ const T* Arow = Aelt[j];
+ float a0 = Arow[0];
+ float a1 = Arow[1];
+ Xelt[0][j] = binv00 * a0 + binv01 * a1;
+ Xelt[1][j] = binv10 * a0 + binv11 * a1;
+ }
+ return X;
+ }
+}
+
+Matrix Matrix::col3PseudoInverse(const Matrix& B) const {
+ Matrix A = *this;
+ int m = rows();
+ int n = cols();
+
+ Matrix3 B3 = B.toMatrix3();
+ if (fuzzyEq(B3.determinant(), (T)0.0)) {
+
+ // Matrix was singular; the block matrix pseudo-inverse can't
+ // handle that, so fall back to the old case
+ return svdPseudoInverse();
+
+ } else {
+ Matrix3 B3inv = B3.inverse();
+
+ // Multiply by Binv * A'
+ Matrix X(cols(), rows());
+
+ T** Xelt = X.impl->elt;
+ T** Aelt = A.impl->elt;
+ for (int i = 0; i < n; ++i) {
+ T* Xrow = Xelt[i];
+ for (int j = 0; j < m; ++j) {
+ const T* Arow = Aelt[j];
+ T sum = 0;
+ const float* Binvrow = B3inv[i];
+ for (int k = 0; k < n; ++k) {
+ sum += Binvrow[k] * Arow[k];
+ }
+ Xrow[j] = sum;
+ }
+ }
+ return X;
+ }
+}
+
+Matrix Matrix::col4PseudoInverse(const Matrix& B) const {
+ Matrix A = *this;
+ int m = rows();
+ int n = cols();
+
+ Matrix4 B4 = B.toMatrix4();
+ if (fuzzyEq(B4.determinant(), (T)0.0)) {
+
+ // Matrix was singular; the block matrix pseudo-inverse can't
+ // handle that, so fall back to the old case
+ return svdPseudoInverse();
+
+ } else {
+ Matrix4 B4inv = B4.inverse();
+
+ // Multiply by Binv * A'
+ Matrix X(cols(), rows());
+
+ T** Xelt = X.impl->elt;
+ T** Aelt = A.impl->elt;
+ for (int i = 0; i < n; ++i) {
+ T* Xrow = Xelt[i];
+ for (int j = 0; j < m; ++j) {
+ const T* Arow = Aelt[j];
+ T sum = 0;
+ const float* Binvrow = B4inv[i];
+ for (int k = 0; k < n; ++k) {
+ sum += Binvrow[k] * Arow[k];
+ }
+ Xrow[j] = sum;
+ }
+ }
+ return X;
+ }
+}
+
+Matrix Matrix::row2PseudoInverse(const Matrix& B) const {
+
+ Matrix A = *this;
+ int m = rows();
+ int n = cols();
+ (void)m;
+
+ // Row-major 2x2 matrix
+ const float B2[2][2] =
+ {{B.get(0,0), B.get(0,1)},
+ {B.get(1,0), B.get(1,1)}};
+
+ float det = (B2[0][0]*B2[1][1]) - (B2[0][1]*B2[1][0]);
+
+ if (fuzzyEq(det, T(0))) {
+
+ // Matrix was singular; the block matrix pseudo-inverse can't
+ // handle that, so fall back to the old case
+ return svdPseudoInverse();
+
+ } else {
+ // invert using formula at http://www.netsoc.tcd.ie/~jgilbert/maths_site/applets/algebra/matrix_inversion.html
+
+ // Multiply by Binv * A'
+ Matrix X(cols(), rows());
+
+ T** Xelt = X.impl->elt;
+ T** Aelt = A.impl->elt;
+ float binv00 = B2[1][1]/det, binv01 = -B2[1][0]/det;
+ float binv10 = -B2[0][1]/det, binv11 = B2[0][0]/det;
+ for (int j = 0; j < n; ++j) {
+ Xelt[j][0] = Aelt[0][j] * binv00 + Aelt[1][j] * binv10;
+ Xelt[j][1] = Aelt[0][j] * binv01 + Aelt[1][j] * binv11;
+ }
+ return X;
+ }
+}
+
+Matrix Matrix::row3PseudoInverse(const Matrix& B) const {
+
+ Matrix A = *this;
+ int m = rows();
+ int n = cols();
+
+ Matrix3 B3 = B.toMatrix3();
+ if (fuzzyEq(B3.determinant(), (T)0.0)) {
+
+ // Matrix was singular; the block matrix pseudo-inverse can't
+ // handle that, so fall back to the old case
+ return svdPseudoInverse();
+
+ } else {
+ Matrix3 B3inv = B3.inverse();
+
+ // Multiply by Binv * A'
+ Matrix X(cols(), rows());
+
+ T** Xelt = X.impl->elt;
+ T** Aelt = A.impl->elt;
+ for (int i = 0; i < n; ++i) {
+ T* Xrow = Xelt[i];
+ for (int j = 0; j < m; ++j) {
+ T sum = 0;
+ for (int k = 0; k < m; ++k) {
+ sum += Aelt[k][i] * B3inv[j][k];
+ }
+ Xrow[j] = sum;
+ }
+ }
+ return X;
+ }
+}
+
+Matrix Matrix::row4PseudoInverse(const Matrix& B) const {
+
+ Matrix A = *this;
+ int m = rows();
+ int n = cols();
+
+ Matrix4 B4 = B.toMatrix4();
+ if (fuzzyEq(B4.determinant(), (T)0.0)) {
+
+ // Matrix was singular; the block matrix pseudo-inverse can't
+ // handle that, so fall back to the old case
+ return svdPseudoInverse();
+
+ } else {
+ Matrix4 B4inv = B4.inverse();
+
+ // Multiply by Binv * A'
+ Matrix X(cols(), rows());
+
+ T** Xelt = X.impl->elt;
+ T** Aelt = A.impl->elt;
+ for (int i = 0; i < n; ++i) {
+ T* Xrow = Xelt[i];
+ for (int j = 0; j < m; ++j) {
+ T sum = 0;
+ for (int k = 0; k < m; ++k) {
+ sum += Aelt[k][i] * B4inv[j][k];
+ }
+ Xrow[j] = sum;
+ }
+ }
+ return X;
+ }
+}
+
+// Uses the block matrix pseudoinverse to compute the pseudoinverse of a full-rank mxn matrix with m >= n
+// http://en.wikipedia.org/wiki/Block_matrix_pseudoinverse
+Matrix Matrix::partitionPseudoInverse() const {
+
+ // Logic:
+ // A^-1 = (A'A)^-1 A'
+ // A has few (n) columns, so A'A is small (n x n) and fast to invert
+
+ int m = rows();
+ int n = cols();
+
+ if (m < n) {
+ // TODO: optimize by pushing through the transpose
+ //return transpose().partitionPseudoInverse().transpose();
+ return rowPartPseudoInverse();
+
+ } else {
+ return colPartPseudoInverse();
+ }
+}
+
+void Matrix::Impl::inverseInPlaceGaussJordan() {
+ debugAssertM(R == C,
+ format(
+ "Cannot perform Gauss-Jordan inverse on a non-square matrix."
+ " (Argument was %dx%d)",
+ R, C));
+
+ // Exchange to float elements
+# define SWAP(x, y) {float temp = x; x = y; y = temp;}
+
+ // The integer arrays pivot, rowIndex, and colIndex are
+ // used for bookkeeping on the pivoting
+ static Array<int> colIndex, rowIndex, pivot;
+
+ int col = 0, row = 0;
+
+ colIndex.resize(R);
+ rowIndex.resize(R);
+ pivot.resize(R);
+
+ static const int NO_PIVOT = -1;
+
+ // Initialize the pivot array to default values.
+ for (int i = 0; i < R; ++i) {
+ pivot[i] = NO_PIVOT;
+ }
+
+ // This is the main loop over the columns to be reduced
+ // Loop over the columns.
+ for (int c = 0; c < R; ++c) {
+
+ // Find the largest element and use that as a pivot
+ float largestMagnitude = 0.0;
+
+ // This is the outer loop of the search for a pivot element
+ for (int r = 0; r < R; ++r) {
+
+ // Unless we've already found the pivot, keep going
+ if (pivot[r] != 0) {
+
+ // Find the largest pivot
+ for (int k = 0; k < R; ++k) {
+ if (pivot[k] == NO_PIVOT) {
+ const float mag = fabs(elt[r][k]);
+
+ if (mag >= largestMagnitude) {
+ largestMagnitude = mag;
+ row = r; col = k;
+ }
+ }
+ }
+ }
+ }
+
+ pivot[col] += 1;
+
+ // Interchange columns so that the pivot element is on the diagonal (we'll have to undo this
+ // at the end)
+ if (row != col) {
+ for (int k = 0; k < R; ++k) {
+ SWAP(elt[row][k], elt[col][k])
+ }
+ }
+
+ // The pivot is now at [row, col]
+ rowIndex[c] = row;
+ colIndex[c] = col;
+
+ double piv = elt[col][col];
+
+ debugAssertM(piv != 0.0, "Matrix is singular");
+
+ // Divide everything by the pivot (avoid computing the division
+ // multiple times).
+ const double pivotInverse = 1.0 / piv;
+ elt[col][col] = 1.0;
+
+ for (int k = 0; k < R; ++k) {
+ elt[col][k] *= Matrix::T(pivotInverse);
+ }
+
+ // Reduce all rows
+ for (int r = 0; r < R; ++r) {
+ // Skip over the pivot row
+ if (r != col) {
+
+ double oldValue = elt[r][col];
+ elt[r][col] = 0.0;
+
+ for (int k = 0; k < R; ++k) {
+ elt[r][k] -= Matrix::T(elt[col][k] * oldValue);
+ }
+ }
+ }
+ }
+
+
+ // Put the columns back in the correct locations
+ for (int i = R - 1; i >= 0; --i) {
+ if (rowIndex[i] != colIndex[i]) {
+ for (int k = 0; k < R; ++k) {
+ SWAP(elt[k][rowIndex[i]], elt[k][colIndex[i]]);
+ }
+ }
+ }
+
+# undef SWAP
+}
+
+
+bool Matrix::Impl::anyNonZero() const {
+ int N = R * C;
+ for (int i = 0; i < N; ++i) {
+ if (data[i] != 0.0) {
+ return true;
+ }
+ }
+ return false;
+}
+
+
+bool Matrix::Impl::allNonZero() const {
+ int N = R * C;
+ for (int i = 0; i < N; ++i) {
+ if (data[i] == 0.0) {
+ return false;
+ }
+ }
+ return true;
+}
+
+
+/** Helper for svdCore */
+static double pythag(double a, double b) {
+
+ double at = fabs(a), bt = fabs(b), ct, result;
+
+ if (at > bt) {
+ ct = bt / at;
+ result = at * sqrt(1.0 + square(ct));
+ } else if (bt > 0.0) {
+ ct = at / bt;
+ result = bt * sqrt(1.0 + square(ct));
+ } else {
+ result = 0.0;
+ }
+
+ return result;
+}
+
+#define SIGN(a, b) ((b) >= 0.0 ? fabs(a) : -fabs(a))
+
+const char* Matrix::svdCore(float** U, int rows, int cols, float* D, float** V) {
+ const int MAX_ITERATIONS = 30;
+
+ int flag, i, its, j, jj, k, l = 0, nm = 0;
+ double c, f, h, s, x, y, z;
+ double anorm = 0.0, g = 0.0, scale = 0.0;
+
+ // Temp row vector
+ double* rv1;
+
+ debugAssertM(rows >= cols, "Must have more rows than columns");
+
+ rv1 = (double*)System::alignedMalloc(cols * sizeof(double), 16);
+ debugAssert(rv1);
+
+ // Householder reduction to bidiagonal form
+ for (i = 0; i < cols; ++i) {
+
+ // Left-hand reduction
+ l = i + 1;
+ rv1[i] = scale * g;
+ g = s = scale = 0.0;
+
+ if (i < rows) {
+
+ for (k = i; k < rows; ++k) {
+ scale += fabs((double)U[k][i]);
+ }
+
+ if (scale) {
+ for (k = i; k < rows; ++k) {
+ U[k][i] = (float)((double)U[k][i]/scale);
+ s += ((double)U[k][i] * (double)U[k][i]);
+ }
+
+ f = (double)U[i][i];
+
+ // TODO: what is this 2-arg sign function?
+ g = -SIGN(sqrt(s), f);
+ h = f * g - s;
+ U[i][i] = (float)(f - g);
+
+ if (i != cols - 1) {
+ for (j = l; j < cols; j++) {
+
+ for (s = 0.0, k = i; k < rows; ++k) {
+ s += ((double)U[k][i] * (double)U[k][j]);
+ }
+
+ f = s / h;
+ for (k = i; k < rows; ++k) {
+ U[k][j] += (float)(f * (double)U[k][i]);
+ }
+ }
+ }
+ for (k = i; k < rows; ++k) {
+ U[k][i] = (float)((double)U[k][i]*scale);
+ }
+ }
+ }
+ D[i] = (float)(scale * g);
+
+ // right-hand reduction
+ g = s = scale = 0.0;
+ if (i < rows && i != cols - 1) {
+ for (k = l; k < cols; ++k) {
+ scale += fabs((double)U[i][k]);
+ }
+
+ if (scale) {
+ for (k = l; k < cols; ++k) {
+ U[i][k] = (float)((double)U[i][k]/scale);
+ s += ((double)U[i][k] * (double)U[i][k]);
+ }
+
+ f = (double)U[i][l];
+ g = -SIGN(sqrt(s), f);
+ h = f * g - s;
+ U[i][l] = (float)(f - g);
+
+ for (k = l; k < cols; ++k) {
+ rv1[k] = (double)U[i][k] / h;
+ }
+
+ if (i != rows - 1) {
+
+ for (j = l; j < rows; ++j) {
+ for (s = 0.0, k = l; k < cols; ++k) {
+ s += ((double)U[j][k] * (double)U[i][k]);
+ }
+
+ for (k = l; k < cols; ++k) {
+ U[j][k] += (float)(s * rv1[k]);
+ }
+ }
+ }
+
+ for (k = l; k < cols; ++k) {
+ U[i][k] = (float)((double)U[i][k]*scale);
+ }
+ }
+ }
+
+ anorm = max(anorm, fabs((double)D[i]) + fabs(rv1[i]));
+ }
+
+ // accumulate the right-hand transformation
+ for (i = cols - 1; i >= 0; --i) {
+ if (i < cols - 1) {
+ if (g) {
+ for (j = l; j < cols; j++) {
+ V[j][i] = (float)(((double)U[i][j] / (double)U[i][l]) / g);
+ }
+
+ // double division to avoid underflow
+ for (j = l; j < cols; ++j) {
+ for (s = 0.0, k = l; k < cols; k++) {
+ s += ((double)U[i][k] * (double)V[k][j]);
+ }
+
+ for (k = l; k < cols; ++k) {
+ V[k][j] += (float)(s * (double)V[k][i]);
+ }
+ }
+ }
+
+ for (j = l; j < cols; ++j) {
+ V[i][j] = V[j][i] = 0.0;
+ }
+ }
+
+ V[i][i] = 1.0;
+ g = rv1[i];
+ l = i;
+ }
+
+ // accumulate the left-hand transformation
+ for (i = cols - 1; i >= 0; --i) {
+ l = i + 1;
+ g = (double)D[i];
+ if (i < cols - 1) {
+ for (j = l; j < cols; ++j) {
+ U[i][j] = 0.0;
+ }
+ }
+
+ if (g) {
+ g = 1.0 / g;
+ if (i != cols - 1) {
+ for (j = l; j < cols; ++j) {
+ for (s = 0.0, k = l; k < rows; ++k) {
+ s += ((double)U[k][i] * (double)U[k][j]);
+ }
+
+ f = (s / (double)U[i][i]) * g;
+
+ for (k = i; k < rows; ++k) {
+ U[k][j] += (float)(f * (double)U[k][i]);
+ }
+ }
+ }
+
+ for (j = i; j < rows; ++j) {
+ U[j][i] = (float)((double)U[j][i]*g);
+ }
+
+ } else {
+ for (j = i; j < rows; ++j) {
+ U[j][i] = 0.0;
+ }
+ }
+ ++U[i][i];
+ }
+
+ // diagonalize the bidiagonal form
+ for (k = cols - 1; k >= 0; --k) {
+ // loop over singular values
+ for (its = 0; its < MAX_ITERATIONS; ++its) {
+ // loop over allowed iterations
+ flag = 1;
+
+ for (l = k; l >= 0; --l) {
+ // test for splitting
+ nm = l - 1;
+ if (fabs(rv1[l]) + anorm == anorm) {
+ flag = 0;
+ break;
+ }
+
+ if (fabs((double)D[nm]) + anorm == anorm) {
+ break;
+ }
+ }
+
+ if (flag) {
+ c = 0.0;
+ s = 1.0;
+ for (i = l; i <= k; ++i) {
+ f = s * rv1[i];
+ if (fabs(f) + anorm != anorm) {
+ g = (double)D[i];
+ h = pythag(f, g);
+ D[i] = (float)h;
+ h = 1.0 / h;
+ c = g * h;
+ s = (- f * h);
+ for (j = 0; j < rows; ++j) {
+ y = (double)U[j][nm];
+ z = (double)U[j][i];
+ U[j][nm] = (float)(y * c + z * s);
+ U[j][i] = (float)(z * c - y * s);
+ }
+ }
+ }
+ }
+
+ z = (double)D[k];
+ if (l == k) {
+ // convergence
+ if (z < 0.0) {
+ // make singular value nonnegative
+ D[k] = (float)(-z);
+
+ for (j = 0; j < cols; ++j) {
+ V[j][k] = (-V[j][k]);
+ }
+ }
+ break;
+ }
+
+ if (its >= MAX_ITERATIONS) {
+ free(rv1);
+ rv1 = NULL;
+ return "Failed to converge.";
+ }
+
+ // shift from bottom 2 x 2 minor
+ x = (double)D[l];
+ nm = k - 1;
+ y = (double)D[nm];
+ g = rv1[nm];
+ h = rv1[k];
+ f = ((y - z) * (y + z) + (g - h) * (g + h)) / (2.0 * h * y);
+ g = pythag(f, 1.0);
+ f = ((x - z) * (x + z) + h * ((y / (f + SIGN(g, f))) - h)) / x;
+
+ // next QR transformation
+ c = s = 1.0;
+ for (j = l; j <= nm; ++j) {
+ i = j + 1;
+ g = rv1[i];
+ y = (double)D[i];
+ h = s * g;
+ g = c * g;
+ z = pythag(f, h);
+ rv1[j] = z;
+ c = f / z;
+ s = h / z;
+ f = x * c + g * s;
+ g = g * c - x * s;
+ h = y * s;
+ y = y * c;
+
+ for (jj = 0; jj < cols; ++jj) {
+ x = (double)V[jj][j];
+ z = (double)V[jj][i];
+ V[jj][j] = (float)(x * c + z * s);
+ V[jj][i] = (float)(z * c - x * s);
+ }
+ z = pythag(f, h);
+ D[j] = (float)z;
+ if (z) {
+ z = 1.0 / z;
+ c = f * z;
+ s = h * z;
+ }
+ f = (c * g) + (s * y);
+ x = (c * y) - (s * g);
+ for (jj = 0; jj < rows; jj++) {
+ y = (double)U[jj][j];
+ z = (double)U[jj][i];
+ U[jj][j] = (float)(y * c + z * s);
+ U[jj][i] = (float)(z * c - y * s);
+ }
+ }
+ rv1[l] = 0.0;
+ rv1[k] = f;
+ D[k] = (float)x;
+ }
+ }
+
+ System::alignedFree(rv1);
+ rv1 = NULL;
+
+ return NULL;
+}
+
+#undef SIGN
+
+}
diff --git a/dep/src/g3dlite/Matrix3.cpp b/dep/src/g3dlite/Matrix3.cpp
index 76864e1b60c..b32d938f0f9 100644
--- a/dep/src/g3dlite/Matrix3.cpp
+++ b/dep/src/g3dlite/Matrix3.cpp
@@ -6,21 +6,51 @@
@author Morgan McGuire, graphics3d.com
@created 2001-06-02
- @edited 2006-04-06
+ @edited 2009-11-15
+
+ Copyright 2000-2009, Morgan McGuire.
+ All rights reserved.
*/
#include "G3D/platform.h"
-#include "G3D/format.h"
#include <memory.h>
#include <assert.h>
#include "G3D/Matrix3.h"
#include "G3D/g3dmath.h"
+#include "G3D/BinaryInput.h"
+#include "G3D/BinaryOutput.h"
#include "G3D/Quat.h"
+#include "G3D/Any.h"
namespace G3D {
const float Matrix3::EPSILON = 1e-06f;
+Matrix3::Matrix3(const Any& any) {
+ any.verifyName("Matrix3");
+ any.verifyType(Any::ARRAY);
+ any.verifySize(9);
+
+ for (int r = 0; r < 3; ++r) {
+ for (int c = 0; c < 3; ++c) {
+ elt[r][c] = any[r * 3 + c];
+ }
+ }
+}
+
+
+Matrix3::operator Any() const {
+ Any any(Any::ARRAY, "Matrix3");
+ any.resize(9);
+ for (int r = 0; r < 3; ++r) {
+ for (int c = 0; c < 3; ++c) {
+ any[r * 3 + c] = elt[r][c];
+ }
+ }
+
+ return any;
+}
+
const Matrix3& Matrix3::zero() {
static Matrix3 m(0, 0, 0, 0, 0, 0, 0, 0, 0);
return m;
@@ -31,13 +61,14 @@ const Matrix3& Matrix3::identity() {
return m;
}
-// Deprecated.
-const Matrix3 Matrix3::ZERO(0, 0, 0, 0, 0, 0, 0, 0, 0);
-const Matrix3 Matrix3::IDENTITY(1, 0, 0, 0, 1, 0, 0, 0, 1);
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) {
@@ -49,12 +80,25 @@ bool Matrix3::fuzzyEq(const Matrix3& b) const {
return true;
}
+
+bool Matrix3::isRightHanded() const{
+
+ const Vector3& X = column(0);
+ const Vector3& Y = column(1);
+ const Vector3& Z = column(2);
+
+ const Vector3& W = X.cross(Y);
+
+ return W.dot(Z) > 0.0f;
+}
+
+
bool Matrix3::isOrthonormal() const {
- Vector3 X = getColumn(0);
- Vector3 Y = getColumn(1);
- Vector3 Z = getColumn(2);
+ const Vector3& X = column(0);
+ const Vector3& Y = column(1);
+ const Vector3& Z = column(2);
- return
+ return
(G3D::fuzzyEq(X.dot(Y), 0.0f) &&
G3D::fuzzyEq(Y.dot(Z), 0.0f) &&
G3D::fuzzyEq(X.dot(Z), 0.0f) &&
@@ -66,8 +110,9 @@ bool Matrix3::isOrthonormal() const {
//----------------------------------------------------------------------------
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.unitize();
+ // 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;
@@ -108,7 +153,7 @@ Matrix3::Matrix3(
void Matrix3::set(
float fEntry00, float fEntry01, float fEntry02,
- float fEntry10, float fEntry11, float fEntry12,
+ float fEntry10, float fEntry11, float fEntry12,
float fEntry20, float fEntry21, float fEntry22) {
elt[0][0] = fEntry00;
@@ -122,17 +167,41 @@ void Matrix3::set(
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::getColumn (int iCol) const {
+Vector3 Matrix3::column (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]);
+
+const Vector3& Matrix3::row (int iRow) const {
+ assert((0 <= iRow) && (iRow < 3));
+ return *reinterpret_cast<const Vector3*>(elt[iRow]);
}
+
void Matrix3::setColumn(int iCol, const Vector3 &vector) {
debugAssert((iCol >= 0) && (iCol < 3));
elt[0][iCol] = vector.x;
@@ -140,6 +209,7 @@ void Matrix3::setColumn(int iCol, const Vector3 &vector) {
elt[2][iCol] = vector.z;
}
+
void Matrix3::setRow(int iRow, const Vector3 &vector) {
debugAssert((iRow >= 0) && (iRow < 3));
elt[iRow][0] = vector.x;
@@ -147,6 +217,7 @@ void Matrix3::setRow(int iRow, const Vector3 &vector) {
elt[iRow][2] = vector.z;
}
+
//----------------------------------------------------------------------------
bool Matrix3::operator== (const Matrix3& rkMatrix) const {
for (int iRow = 0; iRow < 3; iRow++) {
@@ -269,6 +340,21 @@ Matrix3 Matrix3::operator* (float fScalar) const {
return kProd;
}
+Matrix3& Matrix3::operator/= (float fScalar) {
+ return *this *= (1.0f / fScalar);
+}
+
+Matrix3& Matrix3::operator*= (float fScalar) {
+
+ for (int iRow = 0; iRow < 3; iRow++) {
+ for (int iCol = 0; iCol < 3; iCol++) {
+ elt[iRow][iCol] *= fScalar;
+ }
+ }
+
+ return *this;
+}
+
//----------------------------------------------------------------------------
Matrix3 operator* (double fScalar, const Matrix3& rkMatrix) {
Matrix3 kProd;
@@ -286,6 +372,7 @@ Matrix3 operator* (float fScalar, const Matrix3& rkMatrix) {
return (double)fScalar * rkMatrix;
}
+
Matrix3 operator* (int fScalar, const Matrix3& rkMatrix) {
return (double)fScalar * rkMatrix;
}
@@ -914,6 +1001,97 @@ void Matrix3::qDUDecomposition (Matrix3& kQ,
}
//----------------------------------------------------------------------------
+void Matrix3::polarDecomposition(Matrix3 &R, Matrix3 &S) const{
+ /*
+ Polar decomposition of a matrix. Based on pseudocode from
+ Nicholas J Higham, "Computing the Polar Decomposition -- with
+ Applications Siam Journal of Science and Statistical Computing, Vol 7, No. 4,
+ October 1986.
+
+ Decomposes A into R*S, where R is orthogonal and S is symmetric.
+
+ Ken Shoemake's "Matrix animation and polar decomposition"
+ in Proceedings of the conference on Graphics interface '92
+ seems to be better known in the world of graphics, but Higham's version
+ uses a scaling constant that can lead to faster convergence than
+ Shoemake's when the initial matrix is far from orthogonal.
+ */
+
+ Matrix3 X = *this;
+ Matrix3 tmp = X.inverse();
+ Matrix3 Xit = tmp.transpose();
+ int iter = 0;
+
+ const int MAX_ITERS = 100;
+
+ const double eps = 50 * std::numeric_limits<float>::epsilon();
+ const float BigEps = 50 * eps;
+
+ /* Higham suggests using OneNorm(Xit-X) < eps * OneNorm(X)
+ * as the convergence criterion, but OneNorm(X) should quickly
+ * settle down to something between 1 and 1.7, so just comparing
+ * with eps seems sufficient.
+ *--------------------------------------------------------------- */
+
+ double resid = X.diffOneNorm(Xit);
+ while (resid > eps && iter < MAX_ITERS) {
+
+ tmp = X.inverse();
+ Xit = tmp.transpose();
+
+ if (resid < BigEps) {
+ // close enough use simple iteration
+ X += Xit;
+ X *= 0.5f;
+ }
+ else {
+ // not close to convergence, compute acceleration factor
+ float gamma = sqrt( sqrt(
+ (Xit.l1Norm()* Xit.lInfNorm())/(X.l1Norm()*X.lInfNorm()) ) );
+
+ X *= 0.5f * gamma;
+ tmp = Xit;
+ tmp *= 0.5f / gamma;
+ X += tmp;
+ }
+
+ resid = X.diffOneNorm(Xit);
+ iter++;
+ }
+
+ R = X;
+ tmp = R.transpose();
+
+ S = tmp * (*this);
+
+ // S := (S + S^t)/2 one more time to make sure it is symmetric
+ tmp = S.transpose();
+
+ S += tmp;
+ S *= 0.5f;
+
+#ifdef G3D_DEBUG
+ // Check iter limit
+ assert(iter < MAX_ITERS);
+
+ // Check A = R*S
+ tmp = R*S;
+ resid = tmp.diffOneNorm(*this);
+ assert(resid < eps);
+
+ // Check R is orthogonal
+ tmp = R*R.transpose();
+ resid = tmp.diffOneNorm(Matrix3::identity());
+ assert(resid < eps);
+
+ // Check that S is symmetric
+ tmp = S.transpose();
+ resid = tmp.diffOneNorm(S);
+ assert(resid < eps);
+#endif
+}
+
+//----------------------------------------------------------------------------
float Matrix3::maxCubicRoot (float afCoeff[3]) {
// Spectral norm is for A^T*A, so characteristic polynomial
// P(x) = c[0]+c[1]*x+c[2]*x^2+x^3 has three positive float roots.
@@ -1006,9 +1184,74 @@ float Matrix3::spectralNorm () const {
}
//----------------------------------------------------------------------------
+float Matrix3::squaredFrobeniusNorm() const {
+ float norm2 = 0;
+ const float* e = &elt[0][0];
+
+ for (int i = 0; i < 9; ++i){
+ norm2 += (*e) * (*e);
+ }
+
+ return norm2;
+}
+
+//----------------------------------------------------------------------------
+float Matrix3::frobeniusNorm() const {
+ return sqrtf(squaredFrobeniusNorm());
+}
+
+//----------------------------------------------------------------------------
+float Matrix3::l1Norm() const {
+ // The one norm of a matrix is the max column sum in absolute value.
+ float oneNorm = 0;
+ for (int c = 0; c < 3; ++c) {
+
+ float f = fabs(elt[0][c])+ fabs(elt[1][c]) + fabs(elt[2][c]);
+
+ if (f > oneNorm) {
+ oneNorm = f;
+ }
+ }
+ return oneNorm;
+}
+
+//----------------------------------------------------------------------------
+float Matrix3::lInfNorm() const {
+ // The infinity norm of a matrix is the max row sum in absolute value.
+ float infNorm = 0;
+
+ for (int r = 0; r < 3; ++r) {
+
+ float f = fabs(elt[r][0]) + fabs(elt[r][1])+ fabs(elt[r][2]);
+
+ if (f > infNorm) {
+ infNorm = f;
+ }
+ }
+ return infNorm;
+}
+
+//----------------------------------------------------------------------------
+float Matrix3::diffOneNorm(const Matrix3 &y) const{
+ float oneNorm = 0;
+
+ for (int c = 0; c < 3; ++c){
+
+ float f = fabs(elt[0][c] - y[0][c]) + fabs(elt[1][c] - y[1][c])
+ + fabs(elt[2][c] - y[2][c]);
+
+ if (f > oneNorm) {
+ oneNorm = f;
+ }
+ }
+ return oneNorm;
+}
+
+//----------------------------------------------------------------------------
void Matrix3::toAxisAngle (Vector3& rkAxis, float& rfRadians) const {
+ //
// Let (x,y,z) be the unit-length axis and let A be an angle of rotation.
- // The rotation matrix is R = I + sin(A)*P + (1-cos(A))*P^2 where
+ // The rotation matrix is R = I + sin(A)*P + (1-cos(A))*P^2 (Rodrigues' formula) where
// I is the identity and
//
// +- -+
@@ -1030,11 +1273,11 @@ void Matrix3::toAxisAngle (Vector3& rkAxis, float& rfRadians) const {
// 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.5 * (fTrace - 1.0);
+ float fCos = 0.5f * (fTrace - 1.0f);
rfRadians = G3D::aCos(fCos); // in [0,PI]
if ( rfRadians > 0.0 ) {
- if ( rfRadians < G3D_PI ) {
+ 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];
@@ -1089,28 +1332,31 @@ void Matrix3::toAxisAngle (Vector3& rkAxis, float& rfRadians) const {
}
//----------------------------------------------------------------------------
-Matrix3 Matrix3::fromAxisAngle (const Vector3& rkAxis, float fRadians) {
- Matrix3 m;
+Matrix3 Matrix3::fromAxisAngle (const Vector3& _axis, float fRadians) {
+ Vector3 axis = _axis.direction();
- float fCos = cos(fRadians);
- float fSin = sin(fRadians);
+ Matrix3 m;
+ float fCos = cos(fRadians);
+ float fSin = sin(fRadians);
float fOneMinusCos = 1.0 - fCos;
- float fX2 = rkAxis.x * rkAxis.x;
- float fY2 = rkAxis.y * rkAxis.y;
- float fZ2 = rkAxis.z * rkAxis.z;
- float fXYM = rkAxis.x * rkAxis.y * fOneMinusCos;
- float fXZM = rkAxis.x * rkAxis.z * fOneMinusCos;
- float fYZM = rkAxis.y * rkAxis.z * fOneMinusCos;
- float fXSin = rkAxis.x * fSin;
- float fYSin = rkAxis.y * fSin;
- float fZSin = rkAxis.z * fSin;
+ 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;
@@ -1134,14 +1380,14 @@ bool Matrix3::toEulerAnglesXYZ (float& rfXAngle, float& rfYAngle,
} else {
// WARNING. Not unique. XA - ZA = -atan2(r10,r11)
rfXAngle = -G3D::aTan2(elt[1][0], elt[1][1]);
- rfYAngle = -(float)G3D_HALF_PI;
+ 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)G3D_HALF_PI;
+ rfYAngle = (float)halfPi();
rfZAngle = 0.0f;
return false;
}
@@ -1163,14 +1409,14 @@ bool Matrix3::toEulerAnglesXZY (float& rfXAngle, float& rfZAngle,
} else {
// WARNING. Not unique. XA - YA = atan2(r20,r22)
rfXAngle = G3D::aTan2(elt[2][0], elt[2][2]);
- rfZAngle = (float)G3D_HALF_PI;
+ 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)G3D_HALF_PI;
+ rfZAngle = -(float)halfPi();
rfYAngle = 0.0f;
return false;
}
@@ -1192,14 +1438,14 @@ bool Matrix3::toEulerAnglesYXZ (float& rfYAngle, float& rfXAngle,
} else {
// WARNING. Not unique. YA - ZA = atan2(r01,r00)
rfYAngle = G3D::aTan2(elt[0][1], elt[0][0]);
- rfXAngle = (float)G3D_HALF_PI;
+ 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)G3D_HALF_PI;
+ rfXAngle = -(float)halfPi();
rfZAngle = 0.0f;
return false;
}
@@ -1221,14 +1467,14 @@ bool Matrix3::toEulerAnglesYZX (float& rfYAngle, float& rfZAngle,
} else {
// WARNING. Not unique. YA - XA = -atan2(r21,r22);
rfYAngle = -G3D::aTan2(elt[2][1], elt[2][2]);
- rfZAngle = -(float)G3D_HALF_PI;
+ 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)G3D_HALF_PI;
+ rfZAngle = (float)halfPi();
rfXAngle = 0.0f;
return false;
}
@@ -1250,14 +1496,14 @@ bool Matrix3::toEulerAnglesZXY (float& rfZAngle, float& rfXAngle,
} else {
// WARNING. Not unique. ZA - YA = -atan(r02,r00)
rfZAngle = -G3D::aTan2(elt[0][2], elt[0][0]);
- rfXAngle = -(float)G3D_HALF_PI;
+ 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)G3D_HALF_PI;
+ rfXAngle = (float)halfPi();
rfYAngle = 0.0f;
return false;
}
@@ -1279,14 +1525,14 @@ bool Matrix3::toEulerAnglesZYX (float& rfZAngle, float& rfYAngle,
} else {
// WARNING. Not unique. ZA - XA = -atan2(r01,r02)
rfZAngle = -G3D::aTan2(elt[0][1], elt[0][2]);
- rfYAngle = (float)G3D_HALF_PI;
+ 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)G3D_HALF_PI;
+ rfYAngle = -(float)halfPi();
rfXAngle = 0.0f;
return false;
}
@@ -1335,10 +1581,10 @@ Matrix3 Matrix3::fromEulerAnglesXZY (float fYAngle, float fPAngle,
//----------------------------------------------------------------------------
Matrix3 Matrix3::fromEulerAnglesYXZ(
- float fYAngle,
+ float fYAngle,
float fPAngle,
float fRAngle) {
-
+
float fCos, fSin;
fCos = cos(fYAngle);
@@ -1358,7 +1604,7 @@ Matrix3 Matrix3::fromEulerAnglesYXZ(
//----------------------------------------------------------------------------
Matrix3 Matrix3::fromEulerAnglesYZX(
- float fYAngle,
+ float fYAngle,
float fPAngle,
float fRAngle) {
@@ -1600,9 +1846,9 @@ void Matrix3::tensorProduct (const Vector3& rkU, const Vector3& rkV,
// Runs in 52 cycles on AMD, 76 cycles on Intel Centrino
//
-// The loop unrolling is necessary for performance.
+// 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.
+// into float*'s instead of 2D arrays.
//
// -morgan
void Matrix3::_mul(const Matrix3& A, const Matrix3& B, Matrix3& out) {
@@ -1669,12 +1915,13 @@ void Matrix3::_transpose(const Matrix3& A, Matrix3& out) {
//-----------------------------------------------------------------------------
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]);
+ 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
+} // namespace
+
diff --git a/dep/src/g3dlite/Matrix4.cpp b/dep/src/g3dlite/Matrix4.cpp
new file mode 100644
index 00000000000..cd38a1a3602
--- /dev/null
+++ b/dep/src/g3dlite/Matrix4.cpp
@@ -0,0 +1,523 @@
+/**
+ @file Matrix4.cpp
+
+
+ @maintainer Morgan McGuire, http://graphics.cs.williams.edu
+
+ @created 2003-10-02
+ @edited 2010-01-29
+ */
+
+#include "G3D/platform.h"
+#include "G3D/Matrix4.h"
+#include "G3D/Matrix3.h"
+#include "G3D/Vector4.h"
+#include "G3D/Vector3.h"
+#include "G3D/BinaryInput.h"
+#include "G3D/BinaryOutput.h"
+#include "G3D/CoordinateFrame.h"
+#include "G3D/Rect2D.h"
+#include "G3D/Any.h"
+#include "G3D/stringutils.h"
+
+namespace G3D {
+
+
+Matrix4::Matrix4(const Any& any) {
+ any.verifyName("Matrix4");
+ any.verifyType(Any::ARRAY);
+
+ const std::string& name = toLower(any.name());
+ if (name == "matrix4") {
+ any.verifySize(16);
+
+ for (int r = 0; r < 4; ++r) {
+ for (int c = 0; c < 4; ++c) {
+ elt[r][c] = any[r * 4 + c];
+ }
+ }
+ } else if (name == "matrix4::scale") {
+ if (any.size() == 1) {
+ *this = scale(any[0].number());
+ } else if (any.size() == 3) {
+ *this = scale(any[0], any[1], any[2]);
+ } else {
+ any.verify(false, "Matrix4::scale() takes either 1 or 3 arguments");
+ }
+ } else {
+ any.verify(false, "Expected Matrix4 constructor");
+ }
+}
+
+
+Matrix4::operator Any() const {
+ Any any(Any::ARRAY, "Matrix4");
+ any.resize(16);
+ for (int r = 0; r < 4; ++r) {
+ for (int c = 0; c < 4; ++c) {
+ any[r * 4 + c] = elt[r][c];
+ }
+ }
+
+ return any;
+}
+
+const Matrix4& Matrix4::identity() {
+ static Matrix4 m(
+ 1, 0, 0, 0,
+ 0, 1, 0, 0,
+ 0, 0, 1, 0,
+ 0, 0, 0, 1);
+ return m;
+}
+
+
+const Matrix4& Matrix4::zero() {
+ static Matrix4 m(
+ 0, 0, 0, 0,
+ 0, 0, 0, 0,
+ 0, 0, 0, 0,
+ 0, 0, 0, 0);
+ return m;
+}
+
+
+Matrix4::Matrix4(const class CoordinateFrame& cframe) {
+ for (int r = 0; r < 3; ++r) {
+ for (int c = 0; c < 3; ++c) {
+ elt[r][c] = cframe.rotation[r][c];
+ }
+ elt[r][3] = cframe.translation[r];
+ }
+ elt[3][0] = 0.0f;
+ elt[3][1] = 0.0f;
+ elt[3][2] = 0.0f;
+ elt[3][3] = 1.0f;
+}
+
+Matrix4::Matrix4(const Matrix3& upper3x3, const Vector3& lastCol) {
+ for (int r = 0; r < 3; ++r) {
+ for (int c = 0; c < 3; ++c) {
+ elt[r][c] = upper3x3[r][c];
+ }
+ elt[r][3] = lastCol[r];
+ }
+ elt[3][0] = 0.0f;
+ elt[3][1] = 0.0f;
+ elt[3][2] = 0.0f;
+ elt[3][3] = 1.0f;
+}
+
+
+Matrix3 Matrix4::upper3x3() const {
+ return Matrix3(elt[0][0], elt[0][1], elt[0][2],
+ elt[1][0], elt[1][1], elt[1][2],
+ elt[2][0], elt[2][1], elt[2][2]);
+}
+
+
+Matrix4 Matrix4::orthogonalProjection(
+ const class Rect2D& rect,
+ float nearval,
+ float farval,
+ float upDirection) {
+ return Matrix4::orthogonalProjection(rect.x0(), rect.x1(), rect.y1(), rect.y0(), nearval, farval, upDirection);
+}
+
+
+Matrix4 Matrix4::orthogonalProjection(
+ float left,
+ float right,
+ float bottom,
+ float top,
+ float nearval,
+ float farval,
+ float upDirection) {
+
+ // Adapted from Mesa. Note that Microsoft (http://msdn.microsoft.com/library/default.asp?url=/library/en-us/opengl/glfunc03_8qnj.asp)
+ // and Linux (http://www.xfree86.org/current/glOrtho.3.html) have different matrices shown in their documentation.
+
+ float x, y, z;
+ float tx, ty, tz;
+
+ x = 2.0f / (right-left);
+ y = 2.0f / (top-bottom);
+ z = -2.0f / (farval-nearval);
+ tx = -(right+left) / (right-left);
+ ty = -(top+bottom) / (top-bottom);
+ tz = -(farval+nearval) / (farval-nearval);
+
+ y *= upDirection;
+ ty *= upDirection;
+
+ return
+ Matrix4( x , 0.0f, 0.0f, tx,
+ 0.0f, y , 0.0f, ty,
+ 0.0f, 0.0f, z , tz,
+ 0.0f, 0.0f, 0.0f, 1.0f);
+}
+
+
+Matrix4 Matrix4::perspectiveProjection(
+ float left,
+ float right,
+ float bottom,
+ float top,
+ float nearval,
+ float farval,
+ float upDirection) {
+
+ float x, y, a, b, c, d;
+
+ x = (2.0f*nearval) / (right-left);
+ y = (2.0f*nearval) / (top-bottom);
+ a = (right+left) / (right-left);
+ b = (top+bottom) / (top-bottom);
+
+ if (farval >= finf()) {
+ // Infinite view frustum
+ c = -1.0f;
+ d = -2.0f * nearval;
+ } else {
+ c = -(farval+nearval) / (farval-nearval);
+ d = -(2.0f*farval*nearval) / (farval-nearval);
+ }
+
+ debugAssertM(abs(upDirection) == 1.0f, "upDirection must be -1 or +1");
+ y *= upDirection;
+ b *= upDirection;
+
+ return Matrix4(
+ x, 0, a, 0,
+ 0, y, b, 0,
+ 0, 0, c, d,
+ 0, 0, -1, 0);
+}
+
+
+void Matrix4::getPerspectiveProjectionParameters(
+ float& left,
+ float& right,
+ float& bottom,
+ float& top,
+ float& nearval,
+ float& farval,
+ float upDirection) const {
+
+ debugAssertM(abs(upDirection) == 1.0f, "upDirection must be -1 or +1");
+
+ float x = elt[0][0];
+ float y = elt[1][1] * upDirection;
+ float a = elt[0][2];
+ float b = elt[1][2] * upDirection;
+ float c = elt[2][2];
+ float d = elt[2][3];
+
+ // Verify that this really is a projection matrix
+ debugAssertM(elt[3][2] == -1, "Not a projection matrix");
+ debugAssertM(elt[0][1] == 0, "Not a projection matrix");
+ debugAssertM(elt[0][3] == 0, "Not a projection matrix");
+ debugAssertM(elt[1][3] == 0, "Not a projection matrix");
+ debugAssertM(elt[3][3] == 0, "Not a projection matrix");
+ debugAssertM(elt[1][0] == 0, "Not a projection matrix");
+ debugAssertM(elt[2][0] == 0, "Not a projection matrix");
+ debugAssertM(elt[2][1] == 0, "Not a projection matrix");
+ debugAssertM(elt[3][0] == 0, "Not a projection matrix");
+ debugAssertM(elt[3][1] == 0, "Not a projection matrix");
+
+ if (c == -1) {
+ farval = finf();
+ nearval = -d / 2.0f;
+ } else {
+ nearval = d * ((c - 1.0f) / (c + 1.0f) - 1.0f) / (-2.0f * (c - 1.0f) / (c + 1.0f));
+ farval = nearval * ((c - 1.0f) / (c + 1.0f));
+ }
+
+
+ left = (a - 1.0f) * nearval / x;
+ right = 2.0f * nearval / x + left;
+
+ bottom = (b - 1.0f) * nearval / y;
+ top = 2.0f * nearval / y + bottom;
+}
+
+
+Matrix4::Matrix4(
+ float r1c1, float r1c2, float r1c3, float r1c4,
+ float r2c1, float r2c2, float r2c3, float r2c4,
+ float r3c1, float r3c2, float r3c3, float r3c4,
+ float r4c1, float r4c2, float r4c3, float r4c4) {
+ elt[0][0] = r1c1; elt[0][1] = r1c2; elt[0][2] = r1c3; elt[0][3] = r1c4;
+ elt[1][0] = r2c1; elt[1][1] = r2c2; elt[1][2] = r2c3; elt[1][3] = r2c4;
+ elt[2][0] = r3c1; elt[2][1] = r3c2; elt[2][2] = r3c3; elt[2][3] = r3c4;
+ elt[3][0] = r4c1; elt[3][1] = r4c2; elt[3][2] = r4c3; elt[3][3] = r4c4;
+}
+
+/**
+ init should be <B>row major</B>.
+ */
+Matrix4::Matrix4(const float* init) {
+ for (int r = 0; r < 4; ++r) {
+ for (int c = 0; c < 4; ++c) {
+ elt[r][c] = init[r * 4 + c];
+ }
+ }
+}
+
+
+Matrix4::Matrix4(const double* init) {
+ for (int r = 0; r < 4; ++r) {
+ for (int c = 0; c < 4; ++c) {
+ elt[r][c] = (float)init[r * 4 + c];
+ }
+ }
+}
+
+
+Matrix4::Matrix4() {
+ for (int r = 0; r < 4; ++r) {
+ for (int c = 0; c < 4; ++c) {
+ elt[r][c] = 0;
+ }
+ }
+}
+
+
+void Matrix4::setRow(int r, const Vector4& v) {
+ for (int c = 0; c < 4; ++c) {
+ elt[r][c] = v[c];
+ }
+}
+
+
+void Matrix4::setColumn(int c, const Vector4& v) {
+ for (int r = 0; r < 4; ++r) {
+ elt[r][c] = v[r];
+ }
+}
+
+
+const Vector4& Matrix4::row(int r) const {
+ return reinterpret_cast<const Vector4*>(elt[r])[0];
+}
+
+
+Vector4 Matrix4::column(int c) const {
+ Vector4 v;
+ for (int r = 0; r < 4; ++r) {
+ v[r] = elt[r][c];
+ }
+ return v;
+}
+
+
+Matrix4 Matrix4::operator*(const Matrix4& other) const {
+ Matrix4 result;
+ for (int r = 0; r < 4; ++r) {
+ for (int c = 0; c < 4; ++c) {
+ for (int i = 0; i < 4; ++i) {
+ result.elt[r][c] += elt[r][i] * other.elt[i][c];
+ }
+ }
+ }
+
+ return result;
+}
+
+
+Matrix4 Matrix4::operator*(const float s) const {
+ Matrix4 result;
+ for (int r = 0; r < 4; ++r) {
+ for (int c = 0; c < 4; ++c) {
+ result.elt[r][c] = elt[r][c] * s;
+ }
+ }
+
+ return result;
+}
+
+
+Vector3 Matrix4::homoMul(const class Vector3& v, float w) const {
+ Vector4 r = (*this) * Vector4(v, w);
+ return r.xyz() * (1.0f / r.w);
+}
+
+
+Vector4 Matrix4::operator*(const Vector4& vector) const {
+ Vector4 result(0,0,0,0);
+ for (int r = 0; r < 4; ++r) {
+ for (int c = 0; c < 4; ++c) {
+ result[r] += elt[r][c] * vector[c];
+ }
+ }
+
+ return result;
+}
+
+
+Matrix4 Matrix4::transpose() const {
+ Matrix4 result;
+ for (int r = 0; r < 4; ++r) {
+ for (int c = 0; c < 4; ++c) {
+ result.elt[c][r] = elt[r][c];
+ }
+ }
+
+ return result;
+}
+
+
+bool Matrix4::operator!=(const Matrix4& other) const {
+ return ! (*this == other);
+}
+
+
+bool Matrix4::operator==(const Matrix4& other) const {
+
+ // If the bit patterns are identical, they must be
+ // the same matrix. If not, they *might* still have
+ // equal elements due to floating point weirdness.
+ if (memcmp(this, &other, sizeof(Matrix4) == 0)) {
+ return true;
+ }
+
+ for (int r = 0; r < 4; ++r) {
+ for (int c = 0; c < 4; ++c) {
+ if (elt[r][c] != other.elt[r][c]) {
+ return false;
+ }
+ }
+ }
+
+ return true;
+}
+
+
+float Matrix4::determinant() const {
+ // Determinant is the dot product of the first row and the first row
+ // of cofactors (i.e. the first col of the adjoint matrix)
+ return cofactor().row(0).dot(row(0));
+}
+
+
+Matrix4 Matrix4::adjoint() const {
+ return cofactor().transpose();
+}
+
+
+Matrix4 Matrix4::inverse() const {
+ // Inverse = adjoint / determinant
+
+ Matrix4 A = adjoint();
+
+ // Determinant is the dot product of the first row and the first row
+ // of cofactors (i.e. the first col of the adjoint matrix)
+ float det = A.column(0).dot(row(0));
+
+ return A * (1.0f / det);
+}
+
+
+Matrix4 Matrix4::cofactor() const {
+ Matrix4 out;
+
+ // We'll use i to incrementally compute -1 ^ (r+c)
+ int i = 1;
+
+ for (int r = 0; r < 4; ++r) {
+ for (int c = 0; c < 4; ++c) {
+ // Compute the determinant of the 3x3 submatrix
+ float det = subDeterminant(r, c);
+ out.elt[r][c] = i * det;
+ i = -i;
+ }
+ i = -i;
+ }
+
+ return out;
+}
+
+
+float Matrix4::subDeterminant(int excludeRow, int excludeCol) const {
+ // Compute non-excluded row and column indices
+ int row[3];
+ int col[3];
+
+ for (int i = 0; i < 3; ++i) {
+ row[i] = i;
+ col[i] = i;
+
+ if (i >= excludeRow) {
+ ++row[i];
+ }
+ if (i >= excludeCol) {
+ ++col[i];
+ }
+ }
+
+ // Compute the first row of cofactors
+ float cofactor00 =
+ elt[row[1]][col[1]] * elt[row[2]][col[2]] -
+ elt[row[1]][col[2]] * elt[row[2]][col[1]];
+
+ float cofactor10 =
+ elt[row[1]][col[2]] * elt[row[2]][col[0]] -
+ elt[row[1]][col[0]] * elt[row[2]][col[2]];
+
+ float cofactor20 =
+ elt[row[1]][col[0]] * elt[row[2]][col[1]] -
+ elt[row[1]][col[1]] * elt[row[2]][col[0]];
+
+ // Product of the first row and the cofactors along the first row
+ return
+ elt[row[0]][col[0]] * cofactor00 +
+ elt[row[0]][col[1]] * cofactor10 +
+ elt[row[0]][col[2]] * cofactor20;
+}
+
+
+CoordinateFrame Matrix4::approxCoordinateFrame() const {
+ CoordinateFrame cframe;
+
+ for (int r = 0; r < 3; ++r) {
+ for (int c = 0; c < 3; ++c) {
+ cframe.rotation[r][c] = elt[r][c];
+ }
+ cframe.translation[r] = elt[r][3];
+ }
+
+ // Ensure that the rotation matrix is orthonormal
+ cframe.rotation.orthonormalize();
+
+ return cframe;
+}
+
+
+void Matrix4::serialize(class BinaryOutput& b) const {
+ for (int r = 0; r < 4; ++r) {
+ for (int c = 0; c < 4; ++c) {
+ b.writeFloat32(elt[r][c]);
+ }
+ }
+}
+
+
+void Matrix4::deserialize(class BinaryInput& b) {
+ for (int r = 0; r < 4; ++r) {
+ for (int c = 0; c < 4; ++c) {
+ elt[r][c] = b.readFloat32();
+ }
+ }
+}
+
+std::string Matrix4::toString() const {
+ return G3D::format("[%g, %g, %g, %g; %g, %g, %g, %g; %g, %g, %g, %g; %g, %g, %g, %g]",
+ elt[0][0], elt[0][1], elt[0][2], elt[0][3],
+ elt[1][0], elt[1][1], elt[1][2], elt[1][3],
+ elt[2][0], elt[2][1], elt[2][2], elt[2][3],
+ elt[3][0], elt[3][1], elt[3][2], elt[3][3]);
+}
+
+} // namespace
+
+
diff --git a/dep/src/g3dlite/MemoryManager.cpp b/dep/src/g3dlite/MemoryManager.cpp
new file mode 100644
index 00000000000..240188a1f0e
--- /dev/null
+++ b/dep/src/g3dlite/MemoryManager.cpp
@@ -0,0 +1,91 @@
+/**
+ @file MemoryManager.cpp
+
+ @maintainer Morgan McGuire, http://graphics.cs.williams.edu
+ @created 2009-04-20
+ @edited 2009-05-29
+
+ Copyright 2000-2009, Morgan McGuire.
+ All rights reserved.
+ */
+
+#include "G3D/MemoryManager.h"
+#include "G3D/System.h"
+
+namespace G3D {
+
+MemoryManager::MemoryManager() {}
+
+
+void* MemoryManager::alloc(size_t s) {
+ return System::malloc(s);
+}
+
+
+void MemoryManager::free(void* ptr) {
+ System::free(ptr);
+}
+
+
+bool MemoryManager::isThreadsafe() const {
+ return true;
+}
+
+
+MemoryManager::Ref MemoryManager::create() {
+ static MemoryManager::Ref m = new MemoryManager();
+ return m;
+}
+
+
+///////////////////////////////////////////////////
+
+AlignedMemoryManager::AlignedMemoryManager() {}
+
+
+void* AlignedMemoryManager::alloc(size_t s) {
+ return System::alignedMalloc(s, 16);
+}
+
+
+void AlignedMemoryManager::free(void* ptr) {
+ System::alignedFree(ptr);
+}
+
+
+bool AlignedMemoryManager::isThreadsafe() const {
+ return true;
+}
+
+
+AlignedMemoryManager::Ref AlignedMemoryManager::create() {
+ static AlignedMemoryManager::Ref m = new AlignedMemoryManager();
+ return m;
+}
+
+
+///////////////////////////////////////////////////
+
+CRTMemoryManager::CRTMemoryManager() {}
+
+
+void* CRTMemoryManager::alloc(size_t s) {
+ return ::malloc(s);
+}
+
+
+void CRTMemoryManager::free(void* ptr) {
+ return ::free(ptr);
+}
+
+
+bool CRTMemoryManager::isThreadsafe() const {
+ return true;
+}
+
+
+CRTMemoryManager::Ref CRTMemoryManager::create() {
+ static CRTMemoryManager::Ref m = new CRTMemoryManager();
+ return m;
+}
+}
diff --git a/dep/src/g3dlite/MeshAlg.cpp b/dep/src/g3dlite/MeshAlg.cpp
new file mode 100644
index 00000000000..626fed92920
--- /dev/null
+++ b/dep/src/g3dlite/MeshAlg.cpp
@@ -0,0 +1,637 @@
+/**
+ @file MeshAlg.cpp
+
+ @maintainer Morgan McGuire, http://graphics.cs.williams.edu
+ @created 2003-09-14
+ @edited 2008-09-03
+
+ Copyright 2000-2009, Morgan McGuire.
+ All rights reserved.
+
+ */
+
+#include "G3D/MeshAlg.h"
+#include "G3D/Table.h"
+#include "G3D/Set.h"
+#include "G3D/Box.h"
+#include "G3D/Sphere.h"
+#include "G3D/vectorMath.h"
+#include "G3D/AABox.h"
+#include "G3D/Image1.h"
+
+#include <climits>
+
+namespace G3D {
+
+const int MeshAlg::Face::NONE = INT_MIN;
+
+void MeshAlg::generateGrid(
+ Array<Vector3>& vertex,
+ Array<Vector2>& texCoord,
+ Array<int>& index,
+ int wCells,
+ int hCells,
+ const Vector2& textureScale,
+ bool spaceCentered,
+ bool twoSided,
+ const CoordinateFrame& xform,
+ const Image1::Ref& height) {
+
+ vertex.fastClear();
+ texCoord.fastClear();
+ index.fastClear();
+
+ // Generate vertices
+ for (int z = 0; z <= hCells; ++z) {
+ for (int x = 0; x <= wCells; ++x) {
+ Vector3 v(x / (float)wCells, 0, z / (float)hCells);
+
+ Vector2 t = v.xz() * textureScale;
+
+ texCoord.append(t);
+
+ if (height.notNull()) {
+ v.y = height->nearest(v.x * (height->width() - 1), v.z * (height->height() - 1)).value;
+ }
+ if (spaceCentered) {
+ v -= Vector3(0.5f, 0, 0.5f);
+ }
+ v = xform.pointToWorldSpace(v);
+ vertex.append(v);
+ }
+ }
+
+ // Generate indices
+ for (int z = 0; z < hCells; ++z) {
+ for (int x = 0; x < wCells; ++x) {
+ int A = x + z * (wCells + 1);
+ int B = A + 1;
+ int C = A + (wCells + 1);
+ int D = C + 1;
+
+ // A B
+ // *-----*
+ // | \ |
+ // | \ |
+ // *-----*
+ // C D
+
+ index.append(A, D, B);
+ index.append(A, C, D);
+ }
+ }
+
+ if (twoSided) {
+ // The index array needs to have reversed winding for the bottom
+ // and offset by the original number of vertices
+ Array<int> ti = index;
+ ti.reverse();
+ for (int i = 0; i < ti.size(); ++i) {
+ ti[i] += vertex.size();
+ }
+ index.append(ti);
+
+ // Duplicate the arrays
+ vertex.append(Array<Vector3>(vertex));
+ texCoord.append(Array<Vector2>(texCoord));
+ }
+}
+
+MeshAlg::Face::Face() {
+ for (int i = 0; i < 3; ++i) {
+ edgeIndex[i] = 0;
+ vertexIndex[i] = 0;
+ }
+}
+
+
+MeshAlg::Edge::Edge() {
+ for (int i = 0; i < 2; ++i) {
+ vertexIndex[i] = 0;
+ // Negative face indices are faces that don't exist
+ faceIndex[i] = -1;
+ }
+}
+
+
+MeshAlg::Geometry& MeshAlg::Geometry::operator=(const MeshAlg::Geometry& src) {
+ vertexArray.resize(src.vertexArray.size());
+ normalArray.resize(src.vertexArray.size());
+
+ System::memcpy(vertexArray.getCArray(), src.vertexArray.getCArray(), sizeof(Vector3)*vertexArray.size());
+ System::memcpy(normalArray.getCArray(), src.normalArray.getCArray(), sizeof(Vector3)*normalArray.size());
+
+ return *this;
+}
+
+
+void MeshAlg::computeNormals(
+ Geometry& geometry,
+ const Array<int>& indexArray) {
+
+ Array<Face> faceArray;
+ Array<Vertex> vertexArray;
+ Array<Edge> edgeArray;
+ Array<Vector3> faceNormalArray;
+
+ computeAdjacency(geometry.vertexArray, indexArray, faceArray, edgeArray, vertexArray);
+
+ computeNormals(geometry.vertexArray, faceArray, vertexArray,
+ geometry.normalArray, faceNormalArray);
+}
+
+
+void MeshAlg::computeNormals(
+ const Array<Vector3>& vertexGeometry,
+ const Array<Face>& faceArray,
+ const Array< Array<int> >& adjacentFaceArray,
+ Array<Vector3>& vertexNormalArray,
+ Array<Vector3>& faceNormalArray) {
+
+ // Construct a fake vertex array for backwards compatibility
+ Array<Vertex> fakeVertexArray;
+ fakeVertexArray.resize(adjacentFaceArray.size());
+
+ for (int v = 0; v < adjacentFaceArray.size(); ++v) {
+ fakeVertexArray[v].faceIndex.resize(adjacentFaceArray[v].size());
+ for (int i = 0; i < fakeVertexArray[v].faceIndex.size(); ++i) {
+ fakeVertexArray[v].faceIndex[i] = adjacentFaceArray[v][i];
+ }
+ // We leave out the edges because they aren't used to compute normals
+ }
+
+ computeNormals(vertexGeometry, faceArray, fakeVertexArray,
+ vertexNormalArray, faceNormalArray);
+}
+
+
+void MeshAlg::computeNormals(
+ const Array<Vector3>& vertexGeometry,
+ const Array<Face>& faceArray,
+ const Array<Vertex>& vertexArray,
+ Array<Vector3>& vertexNormalArray,
+ Array<Vector3>& faceNormalArray) {
+
+ // Face normals (not unit length)
+ faceNormalArray.resize(faceArray.size());
+ for (int f = 0; f < faceArray.size(); ++f) {
+ const Face& face = faceArray[f];
+
+ Vector3 vertex[3];
+ for (int j = 0; j < 3; ++j) {
+ vertex[j] = vertexGeometry[face.vertexIndex[j]];
+ debugAssert(vertex[j].isFinite());
+ }
+
+ faceNormalArray[f] = (vertex[1] - vertex[0]).cross(vertex[2] - vertex[0]);
+# ifdef G3D_DEBUG
+ const Vector3& N = faceNormalArray[f];
+ debugAssert(N.isFinite());
+# endif
+ }
+
+ // Per-vertex normals, computed by averaging
+ vertexNormalArray.resize(vertexGeometry.size());
+ for (int v = 0; v < vertexNormalArray.size(); ++v) {
+ Vector3 sum = Vector3::zero();
+ for (int k = 0; k < vertexArray[v].faceIndex.size(); ++k) {
+ const int f = vertexArray[v].faceIndex[k];
+ sum += faceNormalArray[f];
+ }
+ vertexNormalArray[v] = sum.directionOrZero();
+# ifdef G3D_DEBUG
+ const Vector3& N = vertexNormalArray[v];
+ debugAssert(N.isUnit() || N.isZero());
+# endif
+ }
+
+
+ for (int f = 0; f < faceArray.size(); ++f) {
+ faceNormalArray[f] = faceNormalArray[f].directionOrZero();
+# ifdef G3D_DEBUG
+ const Vector3& N = faceNormalArray[f];
+ debugAssert(N.isUnit() || N.isZero());
+# endif
+ }
+
+}
+
+
+void MeshAlg::computeFaceNormals(
+ const Array<Vector3>& vertexArray,
+ const Array<MeshAlg::Face>& faceArray,
+ Array<Vector3>& faceNormals,
+ bool normalize) {
+
+ faceNormals.resize(faceArray.size());
+
+ for (int f = 0; f < faceArray.size(); ++f) {
+ const MeshAlg::Face& face = faceArray[f];
+
+ const Vector3& v0 = vertexArray[face.vertexIndex[0]];
+ const Vector3& v1 = vertexArray[face.vertexIndex[1]];
+ const Vector3& v2 = vertexArray[face.vertexIndex[2]];
+
+ faceNormals[f] = (v1 - v0).cross(v2 - v0);
+ }
+
+ if (normalize) {
+ for (int f = 0; f < faceArray.size(); ++f) {
+ faceNormals[f] = faceNormals[f].direction();
+ }
+ }
+}
+
+
+void MeshAlg::identifyBackfaces(
+ const Array<Vector3>& vertexArray,
+ const Array<MeshAlg::Face>& faceArray,
+ const Vector4& HP,
+ Array<bool>& backface) {
+
+ Vector3 P = HP.xyz();
+
+ backface.resize(faceArray.size());
+
+ if (fuzzyEq(HP.w, 0.0)) {
+ // Infinite case
+ for (int f = faceArray.size() - 1; f >= 0; --f) {
+ const MeshAlg::Face& face = faceArray[f];
+
+ const Vector3& v0 = vertexArray[face.vertexIndex[0]];
+ const Vector3& v1 = vertexArray[face.vertexIndex[1]];
+ const Vector3& v2 = vertexArray[face.vertexIndex[2]];
+
+ const Vector3 N = (v1 - v0).cross(v2 - v0);
+
+ backface[f] = N.dot(P) < 0;
+ }
+ } else {
+ // Finite case
+ for (int f = faceArray.size() - 1; f >= 0; --f) {
+ const MeshAlg::Face& face = faceArray[f];
+
+ const Vector3& v0 = vertexArray[face.vertexIndex[0]];
+ const Vector3& v1 = vertexArray[face.vertexIndex[1]];
+ const Vector3& v2 = vertexArray[face.vertexIndex[2]];
+
+ const Vector3 N = (v1 - v0).cross(v2 - v0);
+
+ backface[f] = N.dot(P - v0) < 0;
+ }
+ }
+}
+
+
+void MeshAlg::identifyBackfaces(
+ const Array<Vector3>& vertexArray,
+ const Array<MeshAlg::Face>& faceArray,
+ const Vector4& HP,
+ Array<bool>& backface,
+ const Array<Vector3>& faceNormals) {
+
+ Vector3 P = HP.xyz();
+
+ backface.resize(faceArray.size());
+
+ if (fuzzyEq(HP.w, 0.0)) {
+ // Infinite case
+ for (int f = faceArray.size() - 1; f >= 0; --f) {
+ const Vector3& N = faceNormals[f];
+ backface[f] = N.dot(P) < 0;
+ }
+ } else {
+ // Finite case
+ for (int f = faceArray.size() - 1; f >= 0; --f) {
+ const MeshAlg::Face& face = faceArray[f];
+ const Vector3& v0 = vertexArray[face.vertexIndex[0]];
+ const Vector3& N = faceNormals[f];
+
+ backface[f] = N.dot(P - v0) < 0;
+ }
+ }
+}
+
+
+void MeshAlg::createIndexArray(int n, Array<int>& array, int start, int run, int skip) {
+ debugAssert(skip >= 0);
+ debugAssert(run >= 0);
+
+ array.resize(n);
+ if (skip == 0) {
+ for (int i = 0; i < n; ++i) {
+ array[i] = start + i;
+ }
+ } else {
+ int rcount = 0;
+ int j = start;
+ for (int i = 0; i < n; ++i) {
+ array[i] = j;
+
+ ++j;
+ ++rcount;
+
+ if (rcount == run) {
+ rcount = 0;
+ j += skip;
+ }
+ }
+ }
+}
+
+
+void MeshAlg::computeAreaStatistics(
+ const Array<Vector3>& vertexArray,
+ const Array<int>& indexArray,
+ double& minEdgeLength,
+ double& meanEdgeLength,
+ double& medianEdgeLength,
+ double& maxEdgeLength,
+ double& minFaceArea,
+ double& meanFaceArea,
+ double& medianFaceArea,
+ double& maxFaceArea) {
+
+ debugAssert(indexArray.size() % 3 == 0);
+
+ Array<double> area;
+ area.resize(indexArray.size() / 3);
+ Array<double> magnitude;
+ magnitude.resize(indexArray.size());
+
+ for (int i = 0; i < indexArray.size(); i += 3) {
+ const Vector3& v0 = vertexArray[indexArray[i]];
+ const Vector3& v1 = vertexArray[indexArray[i + 1]];
+ const Vector3& v2 = vertexArray[indexArray[i + 2]];
+
+ area[i / 3] = (v1 - v0).cross(v2 - v0).magnitude() / 2.0;
+ magnitude[i] = (v1 - v0).magnitude();
+ magnitude[i + 1] = (v2 - v1).magnitude();
+ magnitude[i + 2] = (v0 - v2).magnitude();
+ }
+
+ area.sort();
+ magnitude.sort();
+
+ minEdgeLength = max(0.0, magnitude[0]);
+ maxEdgeLength = max(0.0, magnitude.last());
+ medianEdgeLength = max(0.0, magnitude[magnitude.size() / 2]);
+ meanEdgeLength = 0;
+ for (int i = 0; i < magnitude.size(); ++i) {
+ meanEdgeLength += magnitude[i];
+ }
+ meanEdgeLength /= magnitude.size();
+
+ minFaceArea = max(0.0, area[0]);
+ maxFaceArea = max(0.0, area.last());
+ medianFaceArea = max(0.0, area[area.size() / 2]);
+ meanFaceArea = 0;
+ for (int i = 0; i < area.size(); ++i) {
+ meanFaceArea += area[i];
+ }
+ meanFaceArea /= area.size();
+
+
+ // Make sure round-off hasn't pushed values less than zero
+ meanFaceArea = max(0.0, meanFaceArea);
+ meanEdgeLength = max(0.0, meanEdgeLength);
+}
+
+
+int MeshAlg::countBoundaryEdges(const Array<MeshAlg::Edge>& edgeArray) {
+ int b = 0;
+
+ for (int i = 0; i < edgeArray.size(); ++i) {
+ if ((edgeArray[i].faceIndex[0] == MeshAlg::Face::NONE) !=
+ (edgeArray[i].faceIndex[1] == MeshAlg::Face::NONE)) {
+ ++b;
+ }
+ }
+
+ return b;
+}
+
+void MeshAlg::computeBounds(
+ const Array<Vector3>& vertexArray,
+ const Array<int>& indexArray,
+ AABox& box,
+ Sphere& sphere) {
+
+ Array<Vector3> newArray;
+ newArray.resize(indexArray.size());
+ for (int i = 0; i < indexArray.size(); ++i) {
+ newArray[i] = vertexArray[indexArray[i]];
+ }
+ computeBounds(newArray, box, sphere);
+}
+
+
+void MeshAlg::computeBounds(
+ const Array<Vector3>& vertexArray,
+ AABox& box,
+ Sphere& sphere) {
+
+ Vector3 xmin, xmax, ymin, ymax, zmin, zmax;
+
+ // FIRST PASS: find 6 minima/maxima points
+ xmin.x = ymin.y = zmin.z = finf();
+ xmax.x = ymax.y = zmax.z = -finf();
+
+ for (int v = 0; v < vertexArray.size(); ++v) {
+ const Vector3& vertex = vertexArray[v];
+
+ if (vertex.x < xmin.x) {
+ xmin = vertex;
+ }
+
+ if (vertex.x > xmax.x) {
+ xmax = vertex;
+ }
+
+ if (vertex.y < ymin.y) {
+ ymin = vertex;
+ }
+
+ if (vertex.y > ymax.y) {
+ ymax = vertex;
+ }
+
+ if (vertex.z < zmin.z) {
+ zmin = vertex;
+ }
+
+ if (vertex.z > zmax.z) {
+ zmax = vertex;
+ }
+ }
+
+ // Set points dia1 & dia2 to the maximally separated pair
+ Vector3 dia1 = xmin;
+ Vector3 dia2 = xmax;
+ {
+ // Set xspan = distance between the 2 points xmin & xmax (squared)
+ double xspan = (xmax - xmin).squaredMagnitude();
+
+ // Same for y & z spans
+ double yspan = (ymax - ymin).squaredMagnitude();
+ double zspan = (zmax - zmin).squaredMagnitude();
+
+ double maxspan = xspan;
+
+ if (yspan > maxspan) {
+ maxspan = yspan;
+ dia1 = ymin;
+ dia2 = ymax;
+ }
+
+ if (zspan > maxspan) {
+ maxspan = zspan;
+ dia1 = zmin;
+ dia2 = zmax;
+ }
+ }
+
+
+ // dia1, dia2 is a diameter of initial sphere
+
+ // calc initial center
+ Vector3 center = (dia1 + dia2) / 2.0;
+
+ // calculate initial radius^2 and radius
+ Vector3 d = dia2 - sphere.center;
+
+ double radSq = d.squaredMagnitude();
+ double rad = sqrt(radSq);
+
+ // SECOND PASS: increment current sphere
+ double old_to_p, old_to_new;
+
+ for (int v = 0; v < vertexArray.size(); ++v) {
+ const Vector3& vertex = vertexArray[v];
+
+ d = vertex - center;
+
+ double old_to_p_sq = d.squaredMagnitude();
+
+ // do r^2 test first
+ if (old_to_p_sq > radSq) {
+ // this point is outside of current sphere
+ old_to_p = sqrt(old_to_p_sq);
+
+ // calc radius of new sphere
+ rad = (rad + old_to_p) / 2.0;
+
+ // for next r^2 compare
+ radSq = rad * rad;
+ old_to_new = old_to_p - rad;
+
+ // calc center of new sphere
+ center = (rad * center + old_to_new * vertex) / old_to_p;
+ }
+ }
+
+ const Vector3 min(xmin.x, ymin.y, zmin.z);
+ const Vector3 max(xmax.x, ymax.y, zmax.z);
+
+ box = AABox(min, max);
+
+ const float boxRadSq = (max - min).squaredMagnitude() * 0.25f;
+
+ if (boxRadSq >= radSq){
+ if (isNaN(center.x) || ! isFinite(rad)) {
+ sphere = Sphere(Vector3::zero(), finf());
+ } else {
+ sphere = Sphere(center, rad);
+ }
+ } else {
+ sphere = Sphere((max + min) * 0.5f, sqrt(boxRadSq));
+ }
+}
+
+void MeshAlg::computeTangentSpaceBasis(
+ const Array<Vector3>& vertexArray,
+ const Array<Vector2>& texCoordArray,
+ const Array<Vector3>& vertexNormalArray,
+ const Array<Face>& faceArray,
+ Array<Vector3>& tangent,
+ Array<Vector3>& binormal) {
+
+ debugAssertM(faceArray.size() != 0, "Unable to calculate valid tangent space without faces.");
+
+ tangent.resize(vertexArray.size());
+ binormal.resize(vertexArray.size());
+
+ // Zero the output arrays.
+ System::memset(tangent.getCArray(), 0, sizeof(Vector3) * tangent.size());
+ System::memset(binormal.getCArray(), 0, sizeof(Vector3) * binormal.size());
+
+ // Iterate over faces, computing the tangent vectors for each
+ // vertex. Accumulate those into the tangent and binormal arrays
+ // and then orthonormalize at the end.
+
+ for (int f = 0; f < faceArray.size(); ++f) {
+ const Face& face = faceArray[f];
+
+ const int i0 = face.vertexIndex[0];
+ const int i1 = face.vertexIndex[1];
+ const int i2 = face.vertexIndex[2];
+
+ const Vector3& v0 = vertexArray[i0];
+ const Vector3& v1 = vertexArray[i1];
+ const Vector3& v2 = vertexArray[i2];
+
+ const Vector2& t0 = texCoordArray[i0];
+ const Vector2& t1 = texCoordArray[i1];
+ const Vector2& t2 = texCoordArray[i2];
+
+ // See http://www.terathon.com/code/tangent.html for a derivation of the following code
+
+ // vertex edges
+ Vector3 ve1 = v1 - v0;
+ Vector3 ve2 = v2 - v0;
+
+ // texture edges
+ Vector2 te1 = t1 - t0;
+ Vector2 te2 = t2 - t0;
+
+ Vector3 n(ve1.cross(ve2).direction());
+ Vector3 t, b;
+
+ float r = te1.x * te2.y - te1.y * te2.x;
+ if (r == 0.0) {
+ // degenerate case
+ Vector3::generateOrthonormalBasis(t, b, n, true);
+ } else {
+ r = 1.0f / r;
+ t = (te2.y * ve1 - te1.y * ve2) * r;
+ b = (te2.x * ve1 - te1.x * ve2) * r;
+ }
+
+ for (int v = 0; v < 3; ++v) {
+ int i = face.vertexIndex[v];
+ tangent[i] += t;
+ binormal[i] += b;
+ }
+ }
+
+ // Normalize the basis vectors
+ for (int v = 0; v < vertexArray.size(); ++v) {
+ // Remove the component parallel to the normal
+ const Vector3& N = vertexNormalArray[v];
+ Vector3& T = tangent[v];
+ Vector3& B = binormal[v];
+
+ debugAssertM(N.isUnit() || N.isZero(), "Input normals must have unit length");
+
+ T -= T.dot(N) * N;
+ B -= B.dot(N) * N;
+
+ // Normalize
+ T = T.directionOrZero();
+ B = B.directionOrZero();
+ }
+}
+
+
+
+} // G3D namespace
diff --git a/dep/src/g3dlite/MeshAlgAdjacency.cpp b/dep/src/g3dlite/MeshAlgAdjacency.cpp
new file mode 100644
index 00000000000..24b5d207c88
--- /dev/null
+++ b/dep/src/g3dlite/MeshAlgAdjacency.cpp
@@ -0,0 +1,739 @@
+/**
+ @file MeshAlgAdjacency.cpp
+
+ @maintainer Morgan McGuire, http://graphics.cs.williams.edu
+ @created 2003-09-14
+ @edited 2009-04-26
+
+ Copyright 2000-2009, Morgan McGuire.
+ All rights reserved.
+
+ */
+
+#include "G3D/Table.h"
+#include "G3D/MeshAlg.h"
+#include "G3D/Set.h"
+#include "G3D/Stopwatch.h"
+#include "G3D/SmallArray.h"
+
+namespace G3D {
+
+/** Two-level table mapping index 0 -> index 1 -> list of face indices */
+class MeshEdgeTable {
+public:
+
+ /** We expect 2 faces per edge. */
+ typedef SmallArray<int, 2> FaceIndexArray;
+
+ class Edge {
+ public:
+ int i1;
+
+ FaceIndexArray faceIndexArray;
+ };
+
+ /** We expect at most 6 edges per vertex; that matches a typical regular grid mesh */
+ typedef SmallArray<Edge, 6> EdgeArray;
+
+ typedef Array< EdgeArray > ET;
+
+private:
+
+ ET table;
+
+public:
+
+ void clear() {
+ table.clear();
+ }
+
+ void resize(int maxV) {
+ table.resize(maxV);
+ }
+
+ /**
+ Inserts the faceIndex into the edge's face list.
+ The index may be a negative number indicating a backface.
+
+ \param v0 Vertex index 0
+ \param v1 Vertex index 1
+ */
+ void insert(int v0, int v1, int faceIndex) {
+
+ debugAssert(v0 <= v1);
+ EdgeArray& edgeArray = table[v0];
+ for (int i = 0; i < edgeArray.size(); ++i) {
+ if (edgeArray[i].i1 == v1) {
+ edgeArray[i].faceIndexArray.push(faceIndex);
+ return;
+ }
+ }
+
+ Edge& p = edgeArray.next();
+ p.i1 = v1;
+ p.faceIndexArray.push(faceIndex);
+ }
+
+ class Iterator {
+ friend class MeshEdgeTable;
+ private:
+
+ int m_i0;
+ /** Pair index */
+ int m_p;
+ ET& m_array;
+ EdgeArray* m_edgeArray;
+ bool m_end;
+
+ public:
+
+ int i0() const {
+ return m_i0;
+ }
+
+ int i1() const {
+ return (*m_edgeArray)[m_p].i1;
+ }
+
+ FaceIndexArray& faceIndex() {
+ return (*m_edgeArray)[m_p].faceIndexArray;
+ }
+
+ Iterator& operator++() {
+ if ((m_i0 >= 0) && (m_p < m_edgeArray->size() - 1)) {
+ ++m_p;
+ } else {
+ // Skip over elements with no face array
+ do {
+ ++m_i0;
+ if (m_i0 == m_array.size()) {
+ m_end = true;
+ return *this;
+ } else {
+ m_edgeArray = &m_array[m_i0];
+ m_p = 0;
+ }
+ } while (m_edgeArray->size() == 0);
+ }
+
+ return *this;
+ }
+
+ bool hasMore() const {
+ return ! m_end;
+ }
+
+ private:
+
+ Iterator(ET& a) : m_i0(-1), m_p(-1), m_array(a), m_edgeArray(NULL), m_end(false) {
+ ++(*this);
+ }
+
+ };
+
+ Iterator begin() {
+ return Iterator(table);
+ }
+};
+
+
+/**
+ Assigns the edge index into the next unassigned edge
+ index. The edge index may be negative, indicating
+ a reverse edge.
+ */
+static void assignEdgeIndex(MeshAlg::Face& face, int e) {
+ for (int i = 0; i < 3; ++i) {
+ if (face.edgeIndex[i] == MeshAlg::Face::NONE) {
+ face.edgeIndex[i] = e;
+ return;
+ }
+ }
+
+ debugAssertM(false, "Face has already been assigned 3 edges");
+}
+
+
+void MeshAlg::computeAdjacency(
+ const Array<Vector3>& vertexGeometry,
+ const Array<int>& indexArray,
+ Array<Face>& faceArray,
+ Array<Edge>& edgeArray,
+ Array< Array<int> >& adjacentFaceArray) {
+
+ Array<Vertex> vertexArray;
+
+ computeAdjacency(vertexGeometry, indexArray, faceArray, edgeArray, vertexArray);
+
+ // Convert the vertexArray into adjacentFaceArray
+ adjacentFaceArray.clear();
+ adjacentFaceArray.resize(vertexArray.size());
+ for (int v = 0; v < adjacentFaceArray.size(); ++v) {
+ const SmallArray<int, 6>& src = vertexArray[v].faceIndex;
+ Array<int>& dst = adjacentFaceArray[v];
+ dst.resize(src.size());
+ for (int f = 0; f < dst.size(); ++f) {
+ dst[f] = src[f];
+ }
+ }
+}
+
+
+void MeshAlg::computeAdjacency(
+ const Array<Vector3>& vertexGeometry,
+ const Array<int>& indexArray,
+ Array<Face>& faceArray,
+ Array<Edge>& edgeArray,
+ Array<Vertex>& vertexArray) {
+
+ MeshEdgeTable edgeTable;
+
+ edgeArray.clear();
+ vertexArray.clear();
+ faceArray.clear();
+
+ // Face normals
+ Array<Vector3> faceNormal;
+ faceNormal.resize(indexArray.size() / 3);
+ faceArray.resize(faceNormal.size());
+
+ // This array has the same size as the vertex array
+ vertexArray.resize(vertexGeometry.size());
+
+ edgeTable.resize(vertexArray.size());
+
+ // Iterate through the triangle list
+ for (int q = 0, f = 0; q < indexArray.size(); ++f, q += 3) {
+
+ Vector3 vertex[3];
+ MeshAlg::Face& face = faceArray[f];
+
+ // Construct the face
+ for (int j = 0; j < 3; ++j) {
+ int v = indexArray[q + j];
+ face.vertexIndex[j] = v;
+ face.edgeIndex[j] = Face::NONE;
+
+ // Store back pointers in the vertices
+ vertexArray[v].faceIndex.append(f);
+
+ // We'll need these vertices to find the face normal
+ vertex[j] = vertexGeometry[v];
+ }
+
+ // Compute the face normal
+ const Vector3& N = (vertex[1] - vertex[0]).cross(vertex[2] - vertex[0]);
+ faceNormal[f] = N.directionOrZero();
+
+ static const int nextIndex[] = {1, 2, 0};
+
+ // Add each edge to the edge table.
+ for (int j = 0; j < 3; ++j) {
+ const int i0 = indexArray[q + j];
+ const int i1 = indexArray[q + nextIndex[j]];
+
+ if (i0 < i1) {
+ // The edge was directed in the same manner as in the face
+ edgeTable.insert(i0, i1, f);
+ } else {
+ // The edge was directed in the opposite manner as in the face
+ edgeTable.insert(i1, i0, ~f);
+ }
+ }
+ }
+
+ // For each edge in the edge table, create an edge in the edge array.
+ // Collapse every 2 edges from adjacent faces.
+
+ MeshEdgeTable::Iterator cur = edgeTable.begin();
+
+ Array<Edge> tempEdgeArray;
+ while (cur.hasMore()) {
+ MeshEdgeTable::FaceIndexArray& faceIndexArray = cur.faceIndex();
+
+ // Process this edge
+ while (faceIndexArray.size() > 0) {
+
+ // Remove the last index
+ int f0 = faceIndexArray.pop();
+
+ // Find the normal to that face
+ const Vector3& n0 = faceNormal[(f0 >= 0) ? f0 : ~f0];
+
+ bool found = false;
+
+ // We try to find the matching face with the closest
+ // normal. This ensures that we don't introduce a lot
+ // of artificial ridges into flat parts of a mesh.
+ float ndotn = -2;
+ int f1 = -1, i1 = -1;
+
+ // Try to find the face with the matching edge
+ for (int i = faceIndexArray.size() - 1; i >= 0; --i) {
+ int f = faceIndexArray[i];
+
+ if ((f >= 0) != (f0 >= 0)) {
+ // This face contains the oppositely oriented edge
+ // and has not been assigned too many edges
+
+ const Vector3& n1 = faceNormal[(f >= 0) ? f : ~f];
+ float d = n1.dot(n0);
+
+ if (found) {
+ // We previously found a good face; see if this
+ // one is better.
+ if (d > ndotn) {
+ // This face is better.
+ ndotn = d;
+ f1 = f;
+ i1 = i;
+ }
+ } else {
+ // This is the first face we've found
+ found = true;
+ ndotn = d;
+ f1 = f;
+ i1 = i;
+ }
+ }
+ }
+
+ // Create the new edge
+ int e = tempEdgeArray.size();
+ Edge& edge = tempEdgeArray.next();
+
+ edge.vertexIndex[0] = cur.i0();
+ edge.vertexIndex[1] = cur.i1();
+
+ if (f0 >= 0) {
+ edge.faceIndex[0] = f0;
+ edge.faceIndex[1] = Face::NONE;
+ assignEdgeIndex(faceArray[f0], e);
+ } else {
+ // The face indices above are two's complemented.
+ // this code restores them to regular indices.
+ debugAssert((~f0) >= 0);
+ edge.faceIndex[1] = ~f0;
+ edge.faceIndex[0] = Face::NONE;
+
+ // The edge index *does* need to be inverted, however.
+ assignEdgeIndex(faceArray[~f0], ~e);
+ }
+
+ if (found) {
+ // We found a matching face; remove both
+ // faces from the active list.
+ faceIndexArray.fastRemove(i1);
+
+ if (f1 >= 0) {
+ edge.faceIndex[0] = f1;
+ assignEdgeIndex(faceArray[f1], e);
+ } else {
+ edge.faceIndex[1] = ~f1;
+ assignEdgeIndex(faceArray[~f1], ~e);
+ }
+ }
+ }
+
+ ++cur;
+ }
+
+ edgeTable.clear();
+
+ // Move boundary edges to the end of the list and then
+ // clean up the face references into them
+ {
+ // Map old edge indices to new edge indices
+ Array<int> newIndex;
+ newIndex.resize(tempEdgeArray.size());
+
+ // Index of the start and end of the edge array
+ int i = 0;
+ int j = tempEdgeArray.size() - 1;
+
+ edgeArray.resize(tempEdgeArray.size());
+ for (int e = 0; e < tempEdgeArray.size(); ++e) {
+ if (tempEdgeArray[e].boundary()) {
+ newIndex[e] = j;
+ --j;
+ } else {
+ newIndex[e] = i;
+ ++i;
+ }
+ edgeArray[newIndex[e]] = tempEdgeArray[e];
+ }
+
+ debugAssertM(i == j + 1, "Counting from front and back of array did not match");
+
+ // Fix the faces
+ for (int f = 0; f < faceArray.size(); ++f) {
+ Face& face = faceArray[f];
+ for (int q = 0; q < 3; ++q) {
+ int e = face.edgeIndex[q];
+ if (e < 0) {
+ // Backwards edge; twiddle before and after conversion
+ face.edgeIndex[q] = ~newIndex[~e];
+ } else {
+ // Regular edge; remap the index
+ face.edgeIndex[q] = newIndex[e];
+ }
+ }
+ }
+ }
+
+ // Now order the edge indices inside the faces correctly.
+ for (int f = 0; f < faceArray.size(); ++f) {
+ Face& face = faceArray[f];
+ int e0 = face.edgeIndex[0];
+ int e1 = face.edgeIndex[1];
+ int e2 = face.edgeIndex[2];
+
+ // e0 will always remain first. The only
+ // question is whether e1 and e2 should be swapped.
+
+ // See if e1 begins at the vertex where e1 ends.
+ const int e0End = (e0 < 0) ?
+ edgeArray[~e0].vertexIndex[0] :
+ edgeArray[e0].vertexIndex[1];
+
+ const int e1Begin = (e1 < 0) ?
+ edgeArray[~e1].vertexIndex[1] :
+ edgeArray[e1].vertexIndex[0];
+
+ if (e0End != e1Begin) {
+ // We must swap e1 and e2
+ face.edgeIndex[1] = e2;
+ face.edgeIndex[2] = e1;
+ }
+ }
+
+ // Fill out the edge adjacency information in the vertex array
+ for (int e = 0; e < edgeArray.size(); ++e) {
+ const Edge& edge = edgeArray[e];
+ vertexArray[edge.vertexIndex[0]].edgeIndex.append(e);
+ vertexArray[edge.vertexIndex[1]].edgeIndex.append(~e);
+ }
+}
+
+
+void MeshAlg::weldBoundaryEdges(
+ Array<Face>& faceArray,
+ Array<Edge>& edgeArray,
+ Array<Vertex>& vertexArray) {
+
+ // Copy over the original edge array
+ Array<Edge> oldEdgeArray = edgeArray;
+
+ // newEdgeIndex[e] is the new index of the old edge with index e
+ // Note that newEdgeIndex[e] might be negative, indicating that
+ // the edge switched direction between the arrays.
+ Array<int> newEdgeIndex;
+ newEdgeIndex.resize(edgeArray.size());
+ edgeArray.resize(0);
+
+ // boundaryEdgeIndices[v_low] is an array of the indices of
+ // all boundary edges whose lower vertex is v_low.
+ Table<int, Array<int> > boundaryEdgeIndices;
+
+ // Copy over non-boundary edges to the new array
+ for (int e = 0; e < oldEdgeArray.size(); ++e) {
+ if (oldEdgeArray[e].boundary()) {
+
+ // Add to the boundary table
+ const int v_low = iMin(oldEdgeArray[e].vertexIndex[0], oldEdgeArray[e].vertexIndex[1]);
+ if (! boundaryEdgeIndices.containsKey(v_low)) {
+ boundaryEdgeIndices.set(v_low, Array<int>());
+ }
+ boundaryEdgeIndices[v_low].append(e);
+
+ // We'll fill out newEdgeIndex[e] later, when we find pairs
+
+ } else {
+
+ // Copy the edge to the new array
+ newEdgeIndex[e] = edgeArray.size();
+ edgeArray.append(oldEdgeArray[e]);
+
+ }
+ }
+
+
+ // Remove all edges from the table that have pairs.
+ Table<int, Array<int> >::Iterator cur = boundaryEdgeIndices.begin();
+ Table<int, Array<int> >::Iterator end = boundaryEdgeIndices.end();
+ while (cur != end) {
+ Array<int>& boundaryEdge = cur->value;
+
+ for (int i = 0; i < boundaryEdge.size(); ++i) {
+ int ei = boundaryEdge[i];
+ const Edge& edgei = oldEdgeArray[ei];
+
+ for (int j = i + 1; j < boundaryEdge.size(); ++j) {
+ int ej = boundaryEdge[j];
+ const Edge& edgej = oldEdgeArray[ej];
+
+ // See if edge ei is the reverse (match) of edge ej.
+
+ // True if the edges match
+ bool match = false;
+
+ // True if edgej's vertex indices are reversed from
+ // edgei's (usually true).
+ bool reversej = false;
+
+ int u = edgei.vertexIndex[0];
+ int v = edgei.vertexIndex[1];
+
+ if (edgei.faceIndex[0] != Face::NONE) {
+ // verts|faces
+ // edgei = [u v A /]
+
+ if (edgej.faceIndex[0] != Face::NONE) {
+ if ((edgej.vertexIndex[0] == v) && (edgej.vertexIndex[1] == u)) {
+ // This is the most common of the four cases
+
+ // edgej = [v u B /]
+ match = true;
+ reversej = true;
+ }
+ } else {
+ if ((edgej.vertexIndex[0] == u) && (edgej.vertexIndex[1] == v)) {
+ // edgej = [u v / B]
+ match = true;
+ }
+ }
+ } else {
+ // edgei = [u v / A]
+ if (edgej.faceIndex[0] != Face::NONE) {
+ if ((edgej.vertexIndex[0] == u) && (edgej.vertexIndex[1] == v)) {
+ // edgej = [u v B /]
+ match = true;
+ }
+ } else {
+ if ((edgej.vertexIndex[0] == v) && (edgej.vertexIndex[1] == u)) {
+ // edgej = [v u / B]
+ match = true;
+ reversej = true;
+ }
+ }
+ }
+
+ if (match) {
+ // ei and ej can be paired as a single edge
+ int e = edgeArray.size();
+ Edge& edge = edgeArray.next();
+
+ // Follow the direction of edgei.
+ edge = edgei;
+ newEdgeIndex[ei] = e;
+
+ // Insert the face index for edgej.
+ int fj = edgej.faceIndex[0];
+ if (fj == Face::NONE) {
+ fj = edgej.faceIndex[1];
+ }
+
+ if (edge.faceIndex[0] == Face::NONE) {
+ edge.faceIndex[0] = fj;
+ } else {
+ edge.faceIndex[1] = fj;
+ }
+
+ if (reversej) {
+ // The new edge is backwards of the old edge for ej
+ newEdgeIndex[ej] = ~e;
+ } else {
+ newEdgeIndex[ej] = e;
+ }
+
+ // Remove both ei and ej from being candidates for future pairing.
+ // Remove ej first since it comes later in the list (removing
+ // ei would decrease the index of ej to j - 1).
+ boundaryEdge.fastRemove(j);
+ boundaryEdge.fastRemove(i);
+
+ // Re-process element i, which is now a new edge index
+ --i;
+
+ // Jump out of the j for-loop
+ break;
+ }
+ }
+ }
+ ++cur;
+ }
+
+ // Anything remaining in the table is a real boundary edge; just copy it to
+ // the end of the array.
+ cur = boundaryEdgeIndices.begin();
+ end = boundaryEdgeIndices.end();
+ while (cur != end) {
+ Array<int>& boundaryEdge = cur->value;
+
+ for (int b = 0; b < boundaryEdge.size(); ++b) {
+ const int e = boundaryEdge[b];
+
+ newEdgeIndex[e] = edgeArray.size();
+ edgeArray.append(oldEdgeArray[e]);
+ }
+
+ ++cur;
+ }
+
+ // Finally, fix up edge indices in the face and vertex arrays
+ for (int f = 0; f < faceArray.size(); ++f) {
+ Face& face = faceArray[f];
+ for (int i = 0; i < 3; ++i) {
+ int e = face.edgeIndex[i];
+
+ if (e < 0) {
+ face.edgeIndex[i] = ~newEdgeIndex[~e];
+ } else {
+ face.edgeIndex[i] = newEdgeIndex[e];
+ }
+ }
+ }
+
+ for (int v = 0; v < vertexArray.size(); ++v) {
+ Vertex& vertex = vertexArray[v];
+ for (int i = 0; i < vertex.edgeIndex.size(); ++i) {
+ int e = vertex.edgeIndex[i];
+
+ if (e < 0) {
+ vertex.edgeIndex[i] = ~newEdgeIndex[~e];
+ } else {
+ vertex.edgeIndex[i] = newEdgeIndex[e];
+ }
+ }
+ }
+}
+
+
+void MeshAlg::weldAdjacency(
+ const Array<Vector3>& originalGeometry,
+ Array<Face>& faceArray,
+ Array<Edge>& edgeArray,
+ Array<Vertex>& vertexArray,
+ double radius) {
+
+ // Num vertices
+ const int n = originalGeometry.size();
+
+ // canonical[v] = first occurance of any vertex near oldVertexArray[v]
+ Array<int> canonical;
+ canonical.resize(n);
+
+ Array<int> toNew, toOld;
+ // Throw away the new vertex array
+ Array<Vector3> dummy;
+ computeWeld(originalGeometry, dummy, toNew, toOld, radius);
+
+ for (int v = 0; v < canonical.size(); ++v) {
+ // Round-trip through the toNew/toOld process. This will give
+ // us the original vertex.
+ canonical[v] = toOld[toNew[v]];
+ }
+
+ // Destroy vertexArray (we reconstruct it below)
+ vertexArray.clear();
+ vertexArray.resize(n);
+
+ bool hasBoundaryEdges = false;
+
+ // Fix edge vertex indices
+ for (int e = 0; e < edgeArray.size(); ++e) {
+ Edge& edge = edgeArray[e];
+
+ const int v0 = canonical[edge.vertexIndex[0]];
+ const int v1 = canonical[edge.vertexIndex[1]];
+
+ edge.vertexIndex[0] = v0;
+ edge.vertexIndex[1] = v1;
+
+ vertexArray[v0].edgeIndex.append(e);
+ vertexArray[v1].edgeIndex.append(~e);
+
+ hasBoundaryEdges = hasBoundaryEdges || edge.boundary();
+ }
+
+ // Fix face vertex indices
+ for (int f = 0; f < faceArray.size(); ++f) {
+ Face& face = faceArray[f];
+ for (int i = 0; i < 3; ++i) {
+ const int v = canonical[face.vertexIndex[i]];
+
+ face.vertexIndex[i] = v;
+
+ // Add the back pointer
+ vertexArray[v].faceIndex.append(f);
+ }
+ }
+
+ if (hasBoundaryEdges) {
+ // As a result of the welding, some of the boundary edges at
+ // the end of the array may now have mates and no longer be
+ // boundaries. Try to pair these up.
+
+ weldBoundaryEdges(faceArray, edgeArray, vertexArray);
+ }
+}
+
+
+void MeshAlg::debugCheckConsistency(
+ const Array<Face>& faceArray,
+ const Array<Edge>& edgeArray,
+ const Array<Vertex>& vertexArray) {
+
+#ifdef _DEBUG
+ for (int v = 0; v < vertexArray.size(); ++v) {
+ const MeshAlg::Vertex& vertex = vertexArray[v];
+
+ for (int i = 0; i < vertex.edgeIndex.size(); ++i) {
+ const int e = vertex.edgeIndex[i];
+ debugAssert(edgeArray[(e >= 0) ? e : ~e].containsVertex(v));
+ }
+
+ for (int i = 0; i < vertex.faceIndex.size(); ++i) {
+ const int f = vertex.faceIndex[i];
+ debugAssert(faceArray[f].containsVertex(v));
+ }
+
+ }
+
+ for (int e = 0; e < edgeArray.size(); ++e) {
+ const MeshAlg::Edge& edge = edgeArray[e];
+
+ for (int i = 0; i < 2; ++i) {
+ debugAssert((edge.faceIndex[i] == MeshAlg::Face::NONE) ||
+ faceArray[edge.faceIndex[i]].containsEdge(e));
+
+ debugAssert(vertexArray[edge.vertexIndex[i]].inEdge(e));
+ }
+ }
+
+ // Every face's edge must be on that face
+ for (int f = 0; f < faceArray.size(); ++f) {
+ const MeshAlg::Face& face = faceArray[f];
+ for (int i = 0; i < 3; ++i) {
+ int e = face.edgeIndex[i];
+ int ei = (e >= 0) ? e : ~e;
+ debugAssert(edgeArray[ei].inFace(f));
+
+ // Make sure the edge is oriented appropriately
+ if (e >= 0) {
+ debugAssert(edgeArray[ei].faceIndex[0] == (int)f);
+ } else {
+ debugAssert(edgeArray[ei].faceIndex[1] == (int)f);
+ }
+
+ debugAssert(vertexArray[face.vertexIndex[i]].inFace(f));
+ }
+ }
+#else
+ (void)faceArray;
+ (void)edgeArray;
+ (void)vertexArray;
+#endif // _DEBUG
+}
+
+} // G3D namespace
diff --git a/dep/src/g3dlite/MeshAlgWeld.cpp b/dep/src/g3dlite/MeshAlgWeld.cpp
new file mode 100644
index 00000000000..6067f17c2fb
--- /dev/null
+++ b/dep/src/g3dlite/MeshAlgWeld.cpp
@@ -0,0 +1,213 @@
+/**
+ @file MeshAlgWeld.cpp
+
+ The MeshAlg::computeWeld method.
+
+ @maintainer Morgan McGuire, http://graphics.cs.williams.edu
+ @created 2003-10-22
+ @edited 2005-02-24
+
+ Copyright 2000-2003, Morgan McGuire.
+ All rights reserved.
+
+ */
+
+#include "G3D/MeshAlg.h"
+#include "G3D/Table.h"
+#include "G3D/Set.h"
+
+namespace G3D {
+
+namespace _internal {
+
+class Welder {
+private:
+
+ // Intentionally illegal
+ Welder& operator=(const Welder& w);
+
+public:
+ /** Indices of newVertexArray elements in <B>or near</B> a grid cell. */
+ typedef Array<int> List;
+
+ enum {GRID_RES = 32};
+
+ List grid[GRID_RES][GRID_RES][GRID_RES];
+
+ const Array<Vector3>& oldVertexArray;
+ Array<Vector3>& newVertexArray;
+ Array<int>& toNew;
+ Array<int>& toOld;
+
+ /** Must be less than one grid cell, not checked */
+ const double radius;
+
+ /** (oldVertexArray[i] - offset) * scale is on the range [0, 1] */
+ Vector3 offset;
+ Vector3 scale;
+
+ Welder(
+ const Array<Vector3>& _oldVertexArray,
+ Array<Vector3>& _newVertexArray,
+ Array<int>& _toNew,
+ Array<int>& _toOld,
+ double _radius);
+
+ /**
+ Computes the grid index from an ordinate.
+ */
+ void toGridCoords(Vector3 v, int& x, int& y, int& z) const;
+
+ /** Gets the index of a vertex, adding it to
+ newVertexArray if necessary. */
+ int getIndex(const Vector3& vertex);
+
+ void weld();
+};
+
+} // namespace _internal
+
+} // namespace G3D
+
+template<> struct HashTrait<G3D::_internal::Welder::List*> {
+ static size_t hashCode(const G3D::_internal::Welder::List* key) { return reinterpret_cast<size_t>(key); }
+};
+
+namespace G3D {
+namespace _internal {
+
+Welder::Welder(
+ const Array<Vector3>& _oldVertexArray,
+ Array<Vector3>& _newVertexArray,
+ Array<int>& _toNew,
+ Array<int>& _toOld,
+ double _radius) :
+ oldVertexArray(_oldVertexArray),
+ newVertexArray(_newVertexArray),
+ toNew(_toNew),
+ toOld(_toOld),
+ radius(_radius) {
+
+ // Compute a scale factor that moves the range
+ // of all ordinates to [0, 1]
+ Vector3 minBound = Vector3::inf();
+ Vector3 maxBound = -minBound;
+
+ for (int i = 0; i < oldVertexArray.size(); ++i) {
+ minBound = minBound.min(oldVertexArray[i]);
+ maxBound = maxBound.max(oldVertexArray[i]);
+ }
+
+ offset = minBound;
+ scale = maxBound - minBound;
+ for (int i = 0; i < 3; ++i) {
+ // The model might have zero extent along some axis
+ if (fuzzyEq(scale[i], 0.0)) {
+ scale[i] = 1.0;
+ } else {
+ scale[i] = 1.0 / scale[i];
+ }
+ }
+}
+
+
+void Welder::toGridCoords(Vector3 v, int& x, int& y, int& z) const {
+ v = (v - offset) * scale;
+ x = iClamp(iFloor(v.x * GRID_RES), 0, GRID_RES - 1);
+ y = iClamp(iFloor(v.y * GRID_RES), 0, GRID_RES - 1);
+ z = iClamp(iFloor(v.z * GRID_RES), 0, GRID_RES - 1);
+}
+
+
+int Welder::getIndex(const Vector3& vertex) {
+
+ int closestIndex = -1;
+ double distanceSquared = inf();
+
+ int ix, iy, iz;
+ toGridCoords(vertex, ix, iy, iz);
+
+ // Check against all vertices within radius of this grid cube
+ const List& list = grid[ix][iy][iz];
+
+ for (int i = 0; i < list.size(); ++i) {
+ double d = (newVertexArray[list[i]] - vertex).squaredMagnitude();
+
+ if (d < distanceSquared) {
+ distanceSquared = d;
+ closestIndex = list[i];
+ }
+ }
+
+ if (distanceSquared <= radius * radius) {
+
+ return closestIndex;
+
+ } else {
+
+ // This is a new vertex
+ int newIndex = newVertexArray.size();
+ newVertexArray.append(vertex);
+
+ // Create a new vertex and store its index in the
+ // neighboring grid cells (usually, only 1 neighbor)
+
+ Set<List*> neighbors;
+
+ for (float dx = -1; dx <= +1; ++dx) {
+ for (float dy = -1; dy <= +1; ++dy) {
+ for (float dz = -1; dz <= +1; ++dz) {
+ int ix, iy, iz;
+ toGridCoords(vertex + Vector3(dx, dy, dz) * radius, ix, iy, iz);
+ neighbors.insert(&(grid[ix][iy][iz]));
+ }
+ }
+ }
+
+ Set<List*>::Iterator neighbor(neighbors.begin());
+ Set<List*>::Iterator none(neighbors.end());
+
+ while (neighbor != none) {
+ (*neighbor)->append(newIndex);
+ ++neighbor;
+ }
+
+ return newIndex;
+ }
+}
+
+
+void Welder::weld() {
+ newVertexArray.resize(0);
+
+ // Prime the vertex positions
+ for (int i = 0; i < oldVertexArray.size(); ++i) {
+ getIndex(oldVertexArray[i]);
+ }
+
+ // Now create the official remapping by snapping to
+ // nearby vertices.
+ toNew.resize(oldVertexArray.size());
+ toOld.resize(newVertexArray.size());
+
+ for (int oi = 0; oi < oldVertexArray.size(); ++oi) {
+ toNew[oi] = getIndex(oldVertexArray[oi]);
+ toOld[toNew[oi]] = oi;
+ }
+}
+
+} // internal namespace
+
+
+void MeshAlg::computeWeld(
+ const Array<Vector3>& oldVertexArray,
+ Array<Vector3>& newVertexArray,
+ Array<int>& toNew,
+ Array<int>& toOld,
+ double radius) {
+
+ _internal::Welder welder(oldVertexArray, newVertexArray, toNew, toOld, radius);
+ welder.weld();
+}
+
+} // G3D namespace
diff --git a/dep/src/g3dlite/MeshBuilder.cpp b/dep/src/g3dlite/MeshBuilder.cpp
new file mode 100644
index 00000000000..1bf2bab5d1c
--- /dev/null
+++ b/dep/src/g3dlite/MeshBuilder.cpp
@@ -0,0 +1,113 @@
+/**
+ @file MeshBuilder.cpp
+
+ @maintainer Morgan McGuire, http://graphics.cs.williams.edu
+
+ @created 2002-02-27
+ @edited 2005-02-24
+ */
+
+#include "G3D/MeshBuilder.h"
+#include "G3D/MeshAlg.h"
+
+namespace G3D {
+
+void MeshBuilder::setName(const std::string& n) {
+ name = n;
+}
+
+
+void MeshBuilder::commit(std::string& n, Array<int>& indexArray, Array<Vector3>& outvertexArray) {
+ n = name;
+
+ // Make the data fit in a unit cube
+ centerTriList();
+
+ Array<int> toNew, toOld;
+
+ if (close == MeshBuilder::AUTO_WELD) {
+ Array<int> index;
+ MeshAlg::createIndexArray(triList.size(), index);
+ double minEdgeLen, maxEdgeLen, meanEdgeLen, medianEdgeLen;
+ double minFaceArea, maxFaceArea, meanFaceArea, medianFaceArea;
+ MeshAlg::computeAreaStatistics(triList, index,
+ minEdgeLen, meanEdgeLen, medianEdgeLen, maxEdgeLen,
+ minFaceArea, meanFaceArea, medianFaceArea, maxFaceArea);
+ close = minEdgeLen * 0.1;
+ }
+
+ MeshAlg::computeWeld(triList, outvertexArray, toNew, toOld, close);
+
+ // Construct triangles
+ for (int t = 0; t < triList.size(); t += 3) {
+ int index[3];
+
+ for (int i = 0; i < 3; ++i) {
+ index[i] = toNew[t + i];
+ }
+
+ // Throw out zero size triangles
+ if ((index[0] != index[1]) &&
+ (index[1] != index[2]) &&
+ (index[2] != index[0])) {
+ indexArray.append(index[0], index[1], index[2]);
+ }
+ }
+}
+
+
+void MeshBuilder::centerTriList() {
+ // Compute the range of the vertices
+ Vector3 vmin, vmax;
+
+ computeBounds(vmin, vmax);
+
+ Vector3 diagonal = vmax - vmin;
+ double scale = max(max(diagonal.x, diagonal.y), diagonal.z) / 2;
+ debugAssert(scale > 0);
+
+ Vector3 translation = vmin + diagonal / 2;
+
+ // Center and scale all vertices in the input list
+ int v;
+
+ //Matrix3 rot90 = Matrix3::fromAxisAngle(Vector3::UNIT_Y, toRadians(180)) * Matrix3::fromAxisAngle(Vector3::UNIT_X, toRadians(90));
+ for (v = 0; v < triList.size(); ++v) {
+ triList[v] = (triList[v] - translation) / scale;
+ //triList[v] = rot90 * triList[v];
+ }
+}
+
+
+void MeshBuilder::computeBounds(Vector3& min, Vector3& max) {
+ min = Vector3::inf();
+ max = -min;
+
+ int v;
+ for (v = 0; v < triList.size(); ++v) {
+ min = min.min(triList[v]);
+ max = max.max(triList[v]);
+ }
+}
+
+
+void MeshBuilder::addTriangle(const Vector3& a, const Vector3& b, const Vector3& c) {
+ triList.append(a, b, c);
+
+ if (_twoSided) {
+ triList.append(c, b, a);
+ }
+}
+
+
+void MeshBuilder::addQuad(const Vector3& a, const Vector3& b, const Vector3& c, const Vector3& d) {
+ addTriangle(a, b, c);
+ addTriangle(a, c, d);
+}
+
+
+void MeshBuilder::addTriangle(const Triangle& t) {
+ addTriangle(t.vertex(0), t.vertex(1), t.vertex(2));
+}
+
+} // namespace
diff --git a/dep/src/g3dlite/NetAddress.cpp b/dep/src/g3dlite/NetAddress.cpp
new file mode 100644
index 00000000000..64d692d4763
--- /dev/null
+++ b/dep/src/g3dlite/NetAddress.cpp
@@ -0,0 +1,164 @@
+/**
+ @file NetMessage.cpp
+
+ @maintainer Morgan McGuire, morgan@cs.brown.edu
+ @created 2005-02-06
+ @edited 2005-02-06
+ */
+#include "G3D/platform.h"
+#include "G3D/NetAddress.h"
+#include "G3D/BinaryInput.h"
+#include "G3D/BinaryOutput.h"
+#include "G3D/Array.h"
+#include "G3D/stringutils.h"
+#include "G3D/System.h"
+#include "G3D/NetworkDevice.h"
+
+#if defined(G3D_LINUX) || defined(G3D_OSX)
+ #include <unistd.h>
+ #include <errno.h>
+ #include <sys/socket.h>
+ #include <netinet/in.h>
+ #include <arpa/inet.h>
+ #include <netdb.h>
+ #include <netinet/tcp.h>
+ #define _alloca alloca
+
+# ifndef SOCKADDR_IN
+# define SOCKADDR_IN struct sockaddr_in
+# endif
+# ifndef SOCKET
+# define SOCKET int
+# endif
+
+// SOCKADDR_IN is supposed to be defined in NetAddress.h
+#ifndef SOCKADDR_IN
+# error Network headers included in wrong order
+#endif
+#endif
+
+
+namespace G3D {
+
+NetAddress::NetAddress() {
+ System::memset(&addr, 0, sizeof(addr));
+}
+
+void NetAddress::init(uint32 host, uint16 port) {
+ if ((host != 0) || (port != 0)) {
+ addr.sin_family = AF_INET;
+ addr.sin_port = htons(port);
+ if (host == 0) {
+ host = INADDR_ANY;
+ }
+ addr.sin_addr.s_addr = htonl(host);
+ } else {
+ System::memset(&addr, 0, sizeof(addr));
+ }
+}
+
+
+NetAddress::NetAddress(
+ const std::string& hostname,
+ uint16 port) {
+ init(hostname, port);
+}
+
+
+void NetAddress::init(
+ const std::string& hostname,
+ uint16 port) {
+
+ uint32 addr;
+
+ if (hostname == "") {
+ addr = INADDR_NONE;
+ } else {
+ addr = inet_addr(hostname.c_str());
+ }
+
+ // The address wasn't in numeric form, resolve it
+ if (addr == INADDR_NONE) {
+ // Get the IP address of the server and store it in host
+ struct hostent* host = gethostbyname(hostname.c_str());
+
+ if (host == NULL) {
+ init(0, 0);
+ return;
+ }
+
+ System::memcpy(&addr, host->h_addr_list[0], host->h_length);
+ }
+
+ if (addr != INADDR_NONE) {
+ addr = ntohl(addr);
+ }
+ init(addr, port);
+}
+
+
+NetAddress::NetAddress(uint32 hostip, uint16 port) {
+ init(hostip, port);
+}
+
+
+NetAddress NetAddress::broadcastAddress(uint16 port) {
+ return NetAddress(NetworkDevice::instance()->broadcastAddressArray()[0], port);
+}
+
+
+NetAddress::NetAddress(const std::string& hostnameAndPort) {
+
+ Array<std::string> part = stringSplit(hostnameAndPort, ':');
+
+ debugAssert(part.length() == 2);
+ init(part[0], atoi(part[1].c_str()));
+}
+
+
+NetAddress::NetAddress(const SOCKADDR_IN& a) {
+ addr = a;
+}
+
+
+NetAddress::NetAddress(const struct in_addr& addr, uint16 port) {
+ #ifdef G3D_WIN32
+ init(ntohl(addr.S_un.S_addr), port);
+ #else
+ init(htonl(addr.s_addr), port);
+ #endif
+}
+
+
+void NetAddress::serialize(class BinaryOutput& b) const {
+ b.writeUInt32(ip());
+ b.writeUInt16(port());
+}
+
+
+void NetAddress::deserialize(class BinaryInput& b) {
+ uint32 i;
+ uint16 p;
+
+ i = b.readUInt32();
+ p = b.readUInt16();
+
+ init(i, p);
+}
+
+
+bool NetAddress::ok() const {
+ return addr.sin_family != 0;
+}
+
+
+std::string NetAddress::ipString() const {
+ return format("%s", inet_ntoa(*(in_addr*)&(addr.sin_addr)));
+}
+
+
+std::string NetAddress::toString() const {
+ return ipString() + format(":%d", ntohs(addr.sin_port));
+}
+
+}
diff --git a/dep/src/g3dlite/NetworkDevice.cpp b/dep/src/g3dlite/NetworkDevice.cpp
new file mode 100644
index 00000000000..246c97d4dbf
--- /dev/null
+++ b/dep/src/g3dlite/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/dep/src/g3dlite/PhysicsFrame.cpp b/dep/src/g3dlite/PhysicsFrame.cpp
new file mode 100644
index 00000000000..28ba8f8d477
--- /dev/null
+++ b/dep/src/g3dlite/PhysicsFrame.cpp
@@ -0,0 +1,77 @@
+/**
+ @file PhysicsFrame.cpp
+
+ @maintainer Morgan McGuire, http://graphics.cs.williams.edu
+
+ @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/dep/src/g3dlite/Plane.cpp b/dep/src/g3dlite/Plane.cpp
index 5ae60b0f762..9b7991c0333 100644
--- a/dep/src/g3dlite/Plane.cpp
+++ b/dep/src/g3dlite/Plane.cpp
@@ -1,33 +1,51 @@
/**
@file Plane.cpp
-
- @maintainer Morgan McGuire, matrix@graphics3d.com
-
+
+ @maintainer Morgan McGuire, http://graphics.cs.williams.edu
+
@created 2003-02-06
@edited 2006-01-29
*/
#include "G3D/platform.h"
-#include "G3D/format.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 ||
+ 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) &&
+ while ((point0.w == 0) &&
((point1.w == 0) || (point2.w != 0))) {
Vector4 temp = point0;
point0 = point1;
@@ -60,6 +78,7 @@ Plane::Plane(
_distance = _normal.dot(point0.xyz());
}
+
Plane::Plane(
const Vector3& point0,
const Vector3& point1,
@@ -69,14 +88,16 @@ Plane::Plane(
_distance = _normal.dot(point0);
}
+
Plane::Plane(
const Vector3& __normal,
const Vector3& point) {
- _normal = __normal.direction();
+ _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();
@@ -85,11 +106,13 @@ Plane Plane::fromEquation(float a, float b, float c, float d) {
return Plane(n, -d);
}
+
void Plane::flip() {
_normal = -_normal;
_distance = -_distance;
}
+
void Plane::getEquation(Vector3& n, float& d) const {
double _d;
getEquation(n, _d);
@@ -101,6 +124,7 @@ void Plane::getEquation(Vector3& n, double& d) const {
d = -_distance;
}
+
void Plane::getEquation(float& a, float& b, float& c, float& d) const {
double _a, _b, _c, _d;
getEquation(_a, _b, _c, _d);
@@ -117,9 +141,9 @@ void Plane::getEquation(double& a, double& b, double& c, double& d) const {
d = -_distance;
}
+
std::string Plane::toString() const {
return format("Plane(%g, %g, %g, %g)", _normal.x, _normal.y, _normal.z, _distance);
}
}
-
diff --git a/dep/src/g3dlite/PrecomputedRandom.cpp b/dep/src/g3dlite/PrecomputedRandom.cpp
new file mode 100644
index 00000000000..387ded35195
--- /dev/null
+++ b/dep/src/g3dlite/PrecomputedRandom.cpp
@@ -0,0 +1,125 @@
+/**
+ @file PrecomputedRandom.cpp
+
+ @maintainer Morgan McGuire, http://graphics.cs.williams.edu
+
+ @created 2009-03-31
+ @edited 2009-07-01
+
+ Copyright 2000-2009, Morgan McGuire.
+ All rights reserved.
+ */
+
+#include "G3D/PrecomputedRandom.h"
+#include "G3D/System.h"
+
+namespace G3D {
+
+PrecomputedRandom::PrecomputedRandom(int dataSize, uint32 seed) :
+ Random((void*)NULL),
+ m_hemiUniform(NULL),
+ m_sphereBits(NULL),
+ m_modMask(dataSize - 1),
+ m_freeData(true) {
+
+ alwaysAssertM(isPow2(dataSize), "dataSize must be a power of 2");
+ m_index = seed & m_modMask;
+
+ HemiUniformData* h;
+ SphereBitsData* s;
+ m_hemiUniform = h = (HemiUniformData*) System::malloc(sizeof(HemiUniformData) * dataSize);
+ m_sphereBits = s = (SphereBitsData*) System::malloc(sizeof(SphereBitsData) * dataSize);
+
+ Random r;
+
+ for (int i = 0; i < dataSize; ++i) {
+ h[i].uniform = r.uniform();
+ r.cosHemi(h[i].cosHemiX, h[i].cosHemiY, h[i].cosHemiZ);
+
+ s[i].bits = r.bits();
+ r.sphere(s[i].sphereX, s[i].sphereY, s[i].sphereZ);
+ }
+
+}
+
+
+PrecomputedRandom::PrecomputedRandom(const HemiUniformData* data1, const SphereBitsData* data2, int dataSize, uint32 seed) :
+ Random((void*)NULL),
+ m_hemiUniform(data1),
+ m_sphereBits(data2),
+ m_modMask(dataSize - 1),
+ m_freeData(false) {
+
+ m_index = seed & m_modMask;
+ alwaysAssertM(isPow2(dataSize), "dataSize must be a power of 2");
+}
+
+
+PrecomputedRandom::~PrecomputedRandom() {
+ if (m_freeData) {
+ System::free(const_cast<HemiUniformData*>(m_hemiUniform));
+ System::free(const_cast<SphereBitsData*>(m_sphereBits));
+ }
+}
+
+float PrecomputedRandom::uniform(float low, float high) {
+ m_index = (m_index + 1) & m_modMask;
+ return low + m_hemiUniform[m_index].uniform * (high - low);
+}
+
+
+float PrecomputedRandom::uniform() {
+ m_index = (m_index + 1) & m_modMask;
+ return m_hemiUniform[m_index].uniform;
+}
+
+
+void PrecomputedRandom::cosHemi(float& x, float& y, float& z) {
+ m_index = (m_index + 1) & m_modMask;
+ x = m_hemiUniform[m_index].cosHemiX;
+ y = m_hemiUniform[m_index].cosHemiY;
+ z = m_hemiUniform[m_index].cosHemiZ;
+}
+
+void PrecomputedRandom::cosPowHemi(const float k, float& x, float& y, float& z) {
+ // Computing a cosPowHemi costs 4 slow functions (pow, sqrt, sin,
+ // cos). We can do it with two, given a cosHemi sample, basically
+ // saving the cost of sin and cos and making a single 128-byte
+ // memory read (for a vector) instead of two (for adjacent uniform
+ // floats).
+
+ // cos^1 distribution sample
+ float cos1;
+ cosHemi(x, y, cos1);
+
+ // Fix the distribution by adjusting the cosine:
+ // rnd(cos^k t) = (rnd(cos(t))^2)^(1/k)
+
+ // produces cos^k distribution sample
+ z = pow(cos1, 2.0f / (1.0f + k));
+
+ // Rescale x and y by sqrt(1.0f - square(z)) / sqrt(x*x + y*y).
+ // Add a very tiny offset to handle the (almost impossibly unlikely) case where
+ // z = 1 and x^2+y^2 = 0.
+ static const float eps = 0.000001f;
+ const float s = sqrt((1.0f + eps - square(z)) / (square(x) + square(y) + eps));
+
+ x *= s;
+ y *= s;
+}
+
+
+uint32 PrecomputedRandom::bits() {
+ m_index = (m_index + 1) & m_modMask;
+ return m_sphereBits[m_index].bits;
+}
+
+
+void PrecomputedRandom::sphere(float& x, float& y, float& z) {
+ m_index = (m_index + 1) & m_modMask;
+ x = m_sphereBits[m_index].sphereX;
+ y = m_sphereBits[m_index].sphereY;
+ z = m_sphereBits[m_index].sphereZ;
+}
+
+}
diff --git a/dep/src/g3dlite/Quat.cpp b/dep/src/g3dlite/Quat.cpp
new file mode 100644
index 00000000000..225c5b51acc
--- /dev/null
+++ b/dep/src/g3dlite/Quat.cpp
@@ -0,0 +1,583 @@
+/**
+ @file Quat.cpp
+
+ Quaternion implementation based on Watt & Watt page 363
+
+ @author Morgan McGuire, graphics3d.com
+
+ @created 2002-01-23
+ @edited 2006-01-31
+ */
+
+#include "G3D/Quat.h"
+#include "G3D/BinaryInput.h"
+#include "G3D/BinaryOutput.h"
+
+namespace G3D {
+
+Quat Quat::fromAxisAngleRotation(
+ const Vector3& axis,
+ float angle) {
+
+ Quat q;
+
+ q.w = cos(angle / 2.0f);
+ q.imag() = axis.direction() * sin(angle / 2.0f);
+
+ return q;
+}
+
+
+Quat::Quat(
+ const Matrix3& rot) {
+
+ static const int plus1mod3[] = {1, 2, 0};
+
+ // Find the index of the largest diagonal component
+ // These ? operations hopefully compile to conditional
+ // move instructions instead of branches.
+ int i = (rot[1][1] > rot[0][0]) ? 1 : 0;
+ i = (rot[2][2] > rot[i][i]) ? 2 : i;
+
+ // Find the indices of the other elements
+ int j = plus1mod3[i];
+ int k = plus1mod3[j];
+
+ // Index the elements of the vector part of the quaternion as a float*
+ float* v = (float*)(this);
+
+ // If we attempted to pre-normalize and trusted the matrix to be
+ // perfectly orthonormal, the result would be:
+ //
+ // double c = sqrt((rot[i][i] - (rot[j][j] + rot[k][k])) + 1.0)
+ // v[i] = -c * 0.5
+ // v[j] = -(rot[i][j] + rot[j][i]) * 0.5 / c
+ // v[k] = -(rot[i][k] + rot[k][i]) * 0.5 / c
+ // w = (rot[j][k] - rot[k][j]) * 0.5 / c
+ //
+ // Since we're going to pay the sqrt anyway, we perform a post normalization, which also
+ // fixes any poorly normalized input. Multiply all elements by 2*c in the above, giving:
+
+ // nc2 = -c^2
+ double nc2 = ((rot[j][j] + rot[k][k]) - rot[i][i]) - 1.0;
+ v[i] = nc2;
+ w = (rot[j][k] - rot[k][j]);
+ v[j] = -(rot[i][j] + rot[j][i]);
+ v[k] = -(rot[i][k] + rot[k][i]);
+
+ // We now have the correct result with the wrong magnitude, so normalize it:
+ float s = sqrt(x*x + y*y + z*z + w*w);
+ if (s > 0.00001f) {
+ s = 1.0f / s;
+ x *= s;
+ y *= s;
+ z *= s;
+ w *= s;
+ } else {
+ // The quaternion is nearly zero. Make it 0 0 0 1
+ x = 0.0f;
+ y = 0.0f;
+ z = 0.0f;
+ w = 1.0f;
+ }
+}
+
+
+void Quat::toAxisAngleRotation(
+ Vector3& axis,
+ double& angle) const {
+
+ // Decompose the quaternion into an angle and an axis.
+
+ axis = Vector3(x, y, z);
+ angle = 2 * acos(w);
+
+ float len = sqrt(1.0f - w * w);
+
+ if (fuzzyGt(abs(len), 0.0f)) {
+ axis /= len;
+ }
+
+ // Reduce the range of the angle.
+
+ if (angle < 0) {
+ angle = -angle;
+ axis = -axis;
+ }
+
+ while (angle > twoPi()) {
+ angle -= twoPi();
+ }
+
+ if (abs(angle) > pi()) {
+ angle -= twoPi();
+ }
+
+ // Make the angle positive.
+
+ if (angle < 0.0f) {
+ angle = -angle;
+ axis = -axis;
+ }
+}
+
+
+Matrix3 Quat::toRotationMatrix() const {
+ Matrix3 out = Matrix3::zero();
+
+ toRotationMatrix(out);
+
+ return out;
+}
+
+
+void Quat::toRotationMatrix(
+ Matrix3& rot) const {
+
+ rot = Matrix3(*this);
+}
+
+
+Quat Quat::slerp(
+ const Quat& _quat1,
+ float alpha,
+ float threshold) const {
+
+ // From: Game Physics -- David Eberly pg 538-540
+ // Modified to include lerp for small angles, which
+ // is a common practice.
+
+ // See also:
+ // http://number-none.com/product/Understanding%20Slerp,%20Then%20Not%20Using%20It/index.html
+
+ const Quat& quat0 = *this;
+ Quat quat1 = _quat1;
+
+ // angle between quaternion rotations
+ float phi;
+ float cosphi = quat0.dot(quat1);
+
+
+ if (cosphi < 0) {
+ // Change the sign and fix the dot product; we need to
+ // loop the other way to get the shortest path
+ quat1 = -quat1;
+ cosphi = -cosphi;
+ }
+
+ // Using G3D::aCos will clamp the angle to 0 and pi
+ phi = static_cast<float>(G3D::aCos(cosphi));
+
+ if (phi >= threshold) {
+ // For large angles, slerp
+ float scale0, scale1;
+
+ scale0 = sin((1.0f - alpha) * phi);
+ scale1 = sin(alpha * phi);
+
+ return ( (quat0 * scale0) + (quat1 * scale1) ) / sin(phi);
+ } else {
+ // For small angles, linear interpolate
+ return quat0.nlerp(quat1, alpha);
+ }
+}
+
+
+Quat Quat::nlerp(
+ const Quat& quat1,
+ float alpha) const {
+
+ Quat result = (*this) * (1.0f - alpha) + quat1 * alpha;
+ return result / result.magnitude();
+}
+
+
+Quat Quat::operator*(const Quat& other) const {
+
+ // Following Watt & Watt, page 360
+ const Vector3& v1 = imag();
+ const Vector3& v2 = other.imag();
+ float s1 = w;
+ float s2 = other.w;
+
+ return Quat(s1*v2 + s2*v1 + v1.cross(v2), s1*s2 - v1.dot(v2));
+}
+
+
+// From "Uniform Random Rotations", Ken Shoemake, Graphics Gems III.
+Quat Quat::unitRandom() {
+ float x0 = uniformRandom();
+ float r1 = sqrtf(1 - x0),
+ r2 = sqrtf(x0);
+ float t1 = (float)G3D::twoPi() * uniformRandom();
+ float t2 = (float)G3D::twoPi() * uniformRandom();
+ float c1 = cosf(t1),
+ s1 = sinf(t1);
+ float c2 = cosf(t2),
+ s2 = sinf(t2);
+ return Quat(s1 * r1, c1 * r1, s2 * r2, c2 * r2);
+}
+
+
+void Quat::deserialize(class BinaryInput& b) {
+ x = b.readFloat32();
+ y = b.readFloat32();
+ z = b.readFloat32();
+ w = b.readFloat32();
+}
+
+
+void Quat::serialize(class BinaryOutput& b) const {
+ b.writeFloat32(x);
+ b.writeFloat32(y);
+ b.writeFloat32(z);
+ b.writeFloat32(w);
+}
+
+
+// 2-char swizzles
+
+Vector2 Quat::xx() const { return Vector2 (x, x); }
+Vector2 Quat::yx() const { return Vector2 (y, x); }
+Vector2 Quat::zx() const { return Vector2 (z, x); }
+Vector2 Quat::wx() const { return Vector2 (w, x); }
+Vector2 Quat::xy() const { return Vector2 (x, y); }
+Vector2 Quat::yy() const { return Vector2 (y, y); }
+Vector2 Quat::zy() const { return Vector2 (z, y); }
+Vector2 Quat::wy() const { return Vector2 (w, y); }
+Vector2 Quat::xz() const { return Vector2 (x, z); }
+Vector2 Quat::yz() const { return Vector2 (y, z); }
+Vector2 Quat::zz() const { return Vector2 (z, z); }
+Vector2 Quat::wz() const { return Vector2 (w, z); }
+Vector2 Quat::xw() const { return Vector2 (x, w); }
+Vector2 Quat::yw() const { return Vector2 (y, w); }
+Vector2 Quat::zw() const { return Vector2 (z, w); }
+Vector2 Quat::ww() const { return Vector2 (w, w); }
+
+// 3-char swizzles
+
+Vector3 Quat::xxx() const { return Vector3 (x, x, x); }
+Vector3 Quat::yxx() const { return Vector3 (y, x, x); }
+Vector3 Quat::zxx() const { return Vector3 (z, x, x); }
+Vector3 Quat::wxx() const { return Vector3 (w, x, x); }
+Vector3 Quat::xyx() const { return Vector3 (x, y, x); }
+Vector3 Quat::yyx() const { return Vector3 (y, y, x); }
+Vector3 Quat::zyx() const { return Vector3 (z, y, x); }
+Vector3 Quat::wyx() const { return Vector3 (w, y, x); }
+Vector3 Quat::xzx() const { return Vector3 (x, z, x); }
+Vector3 Quat::yzx() const { return Vector3 (y, z, x); }
+Vector3 Quat::zzx() const { return Vector3 (z, z, x); }
+Vector3 Quat::wzx() const { return Vector3 (w, z, x); }
+Vector3 Quat::xwx() const { return Vector3 (x, w, x); }
+Vector3 Quat::ywx() const { return Vector3 (y, w, x); }
+Vector3 Quat::zwx() const { return Vector3 (z, w, x); }
+Vector3 Quat::wwx() const { return Vector3 (w, w, x); }
+Vector3 Quat::xxy() const { return Vector3 (x, x, y); }
+Vector3 Quat::yxy() const { return Vector3 (y, x, y); }
+Vector3 Quat::zxy() const { return Vector3 (z, x, y); }
+Vector3 Quat::wxy() const { return Vector3 (w, x, y); }
+Vector3 Quat::xyy() const { return Vector3 (x, y, y); }
+Vector3 Quat::yyy() const { return Vector3 (y, y, y); }
+Vector3 Quat::zyy() const { return Vector3 (z, y, y); }
+Vector3 Quat::wyy() const { return Vector3 (w, y, y); }
+Vector3 Quat::xzy() const { return Vector3 (x, z, y); }
+Vector3 Quat::yzy() const { return Vector3 (y, z, y); }
+Vector3 Quat::zzy() const { return Vector3 (z, z, y); }
+Vector3 Quat::wzy() const { return Vector3 (w, z, y); }
+Vector3 Quat::xwy() const { return Vector3 (x, w, y); }
+Vector3 Quat::ywy() const { return Vector3 (y, w, y); }
+Vector3 Quat::zwy() const { return Vector3 (z, w, y); }
+Vector3 Quat::wwy() const { return Vector3 (w, w, y); }
+Vector3 Quat::xxz() const { return Vector3 (x, x, z); }
+Vector3 Quat::yxz() const { return Vector3 (y, x, z); }
+Vector3 Quat::zxz() const { return Vector3 (z, x, z); }
+Vector3 Quat::wxz() const { return Vector3 (w, x, z); }
+Vector3 Quat::xyz() const { return Vector3 (x, y, z); }
+Vector3 Quat::yyz() const { return Vector3 (y, y, z); }
+Vector3 Quat::zyz() const { return Vector3 (z, y, z); }
+Vector3 Quat::wyz() const { return Vector3 (w, y, z); }
+Vector3 Quat::xzz() const { return Vector3 (x, z, z); }
+Vector3 Quat::yzz() const { return Vector3 (y, z, z); }
+Vector3 Quat::zzz() const { return Vector3 (z, z, z); }
+Vector3 Quat::wzz() const { return Vector3 (w, z, z); }
+Vector3 Quat::xwz() const { return Vector3 (x, w, z); }
+Vector3 Quat::ywz() const { return Vector3 (y, w, z); }
+Vector3 Quat::zwz() const { return Vector3 (z, w, z); }
+Vector3 Quat::wwz() const { return Vector3 (w, w, z); }
+Vector3 Quat::xxw() const { return Vector3 (x, x, w); }
+Vector3 Quat::yxw() const { return Vector3 (y, x, w); }
+Vector3 Quat::zxw() const { return Vector3 (z, x, w); }
+Vector3 Quat::wxw() const { return Vector3 (w, x, w); }
+Vector3 Quat::xyw() const { return Vector3 (x, y, w); }
+Vector3 Quat::yyw() const { return Vector3 (y, y, w); }
+Vector3 Quat::zyw() const { return Vector3 (z, y, w); }
+Vector3 Quat::wyw() const { return Vector3 (w, y, w); }
+Vector3 Quat::xzw() const { return Vector3 (x, z, w); }
+Vector3 Quat::yzw() const { return Vector3 (y, z, w); }
+Vector3 Quat::zzw() const { return Vector3 (z, z, w); }
+Vector3 Quat::wzw() const { return Vector3 (w, z, w); }
+Vector3 Quat::xww() const { return Vector3 (x, w, w); }
+Vector3 Quat::yww() const { return Vector3 (y, w, w); }
+Vector3 Quat::zww() const { return Vector3 (z, w, w); }
+Vector3 Quat::www() const { return Vector3 (w, w, w); }
+
+// 4-char swizzles
+
+Vector4 Quat::xxxx() const { return Vector4 (x, x, x, x); }
+Vector4 Quat::yxxx() const { return Vector4 (y, x, x, x); }
+Vector4 Quat::zxxx() const { return Vector4 (z, x, x, x); }
+Vector4 Quat::wxxx() const { return Vector4 (w, x, x, x); }
+Vector4 Quat::xyxx() const { return Vector4 (x, y, x, x); }
+Vector4 Quat::yyxx() const { return Vector4 (y, y, x, x); }
+Vector4 Quat::zyxx() const { return Vector4 (z, y, x, x); }
+Vector4 Quat::wyxx() const { return Vector4 (w, y, x, x); }
+Vector4 Quat::xzxx() const { return Vector4 (x, z, x, x); }
+Vector4 Quat::yzxx() const { return Vector4 (y, z, x, x); }
+Vector4 Quat::zzxx() const { return Vector4 (z, z, x, x); }
+Vector4 Quat::wzxx() const { return Vector4 (w, z, x, x); }
+Vector4 Quat::xwxx() const { return Vector4 (x, w, x, x); }
+Vector4 Quat::ywxx() const { return Vector4 (y, w, x, x); }
+Vector4 Quat::zwxx() const { return Vector4 (z, w, x, x); }
+Vector4 Quat::wwxx() const { return Vector4 (w, w, x, x); }
+Vector4 Quat::xxyx() const { return Vector4 (x, x, y, x); }
+Vector4 Quat::yxyx() const { return Vector4 (y, x, y, x); }
+Vector4 Quat::zxyx() const { return Vector4 (z, x, y, x); }
+Vector4 Quat::wxyx() const { return Vector4 (w, x, y, x); }
+Vector4 Quat::xyyx() const { return Vector4 (x, y, y, x); }
+Vector4 Quat::yyyx() const { return Vector4 (y, y, y, x); }
+Vector4 Quat::zyyx() const { return Vector4 (z, y, y, x); }
+Vector4 Quat::wyyx() const { return Vector4 (w, y, y, x); }
+Vector4 Quat::xzyx() const { return Vector4 (x, z, y, x); }
+Vector4 Quat::yzyx() const { return Vector4 (y, z, y, x); }
+Vector4 Quat::zzyx() const { return Vector4 (z, z, y, x); }
+Vector4 Quat::wzyx() const { return Vector4 (w, z, y, x); }
+Vector4 Quat::xwyx() const { return Vector4 (x, w, y, x); }
+Vector4 Quat::ywyx() const { return Vector4 (y, w, y, x); }
+Vector4 Quat::zwyx() const { return Vector4 (z, w, y, x); }
+Vector4 Quat::wwyx() const { return Vector4 (w, w, y, x); }
+Vector4 Quat::xxzx() const { return Vector4 (x, x, z, x); }
+Vector4 Quat::yxzx() const { return Vector4 (y, x, z, x); }
+Vector4 Quat::zxzx() const { return Vector4 (z, x, z, x); }
+Vector4 Quat::wxzx() const { return Vector4 (w, x, z, x); }
+Vector4 Quat::xyzx() const { return Vector4 (x, y, z, x); }
+Vector4 Quat::yyzx() const { return Vector4 (y, y, z, x); }
+Vector4 Quat::zyzx() const { return Vector4 (z, y, z, x); }
+Vector4 Quat::wyzx() const { return Vector4 (w, y, z, x); }
+Vector4 Quat::xzzx() const { return Vector4 (x, z, z, x); }
+Vector4 Quat::yzzx() const { return Vector4 (y, z, z, x); }
+Vector4 Quat::zzzx() const { return Vector4 (z, z, z, x); }
+Vector4 Quat::wzzx() const { return Vector4 (w, z, z, x); }
+Vector4 Quat::xwzx() const { return Vector4 (x, w, z, x); }
+Vector4 Quat::ywzx() const { return Vector4 (y, w, z, x); }
+Vector4 Quat::zwzx() const { return Vector4 (z, w, z, x); }
+Vector4 Quat::wwzx() const { return Vector4 (w, w, z, x); }
+Vector4 Quat::xxwx() const { return Vector4 (x, x, w, x); }
+Vector4 Quat::yxwx() const { return Vector4 (y, x, w, x); }
+Vector4 Quat::zxwx() const { return Vector4 (z, x, w, x); }
+Vector4 Quat::wxwx() const { return Vector4 (w, x, w, x); }
+Vector4 Quat::xywx() const { return Vector4 (x, y, w, x); }
+Vector4 Quat::yywx() const { return Vector4 (y, y, w, x); }
+Vector4 Quat::zywx() const { return Vector4 (z, y, w, x); }
+Vector4 Quat::wywx() const { return Vector4 (w, y, w, x); }
+Vector4 Quat::xzwx() const { return Vector4 (x, z, w, x); }
+Vector4 Quat::yzwx() const { return Vector4 (y, z, w, x); }
+Vector4 Quat::zzwx() const { return Vector4 (z, z, w, x); }
+Vector4 Quat::wzwx() const { return Vector4 (w, z, w, x); }
+Vector4 Quat::xwwx() const { return Vector4 (x, w, w, x); }
+Vector4 Quat::ywwx() const { return Vector4 (y, w, w, x); }
+Vector4 Quat::zwwx() const { return Vector4 (z, w, w, x); }
+Vector4 Quat::wwwx() const { return Vector4 (w, w, w, x); }
+Vector4 Quat::xxxy() const { return Vector4 (x, x, x, y); }
+Vector4 Quat::yxxy() const { return Vector4 (y, x, x, y); }
+Vector4 Quat::zxxy() const { return Vector4 (z, x, x, y); }
+Vector4 Quat::wxxy() const { return Vector4 (w, x, x, y); }
+Vector4 Quat::xyxy() const { return Vector4 (x, y, x, y); }
+Vector4 Quat::yyxy() const { return Vector4 (y, y, x, y); }
+Vector4 Quat::zyxy() const { return Vector4 (z, y, x, y); }
+Vector4 Quat::wyxy() const { return Vector4 (w, y, x, y); }
+Vector4 Quat::xzxy() const { return Vector4 (x, z, x, y); }
+Vector4 Quat::yzxy() const { return Vector4 (y, z, x, y); }
+Vector4 Quat::zzxy() const { return Vector4 (z, z, x, y); }
+Vector4 Quat::wzxy() const { return Vector4 (w, z, x, y); }
+Vector4 Quat::xwxy() const { return Vector4 (x, w, x, y); }
+Vector4 Quat::ywxy() const { return Vector4 (y, w, x, y); }
+Vector4 Quat::zwxy() const { return Vector4 (z, w, x, y); }
+Vector4 Quat::wwxy() const { return Vector4 (w, w, x, y); }
+Vector4 Quat::xxyy() const { return Vector4 (x, x, y, y); }
+Vector4 Quat::yxyy() const { return Vector4 (y, x, y, y); }
+Vector4 Quat::zxyy() const { return Vector4 (z, x, y, y); }
+Vector4 Quat::wxyy() const { return Vector4 (w, x, y, y); }
+Vector4 Quat::xyyy() const { return Vector4 (x, y, y, y); }
+Vector4 Quat::yyyy() const { return Vector4 (y, y, y, y); }
+Vector4 Quat::zyyy() const { return Vector4 (z, y, y, y); }
+Vector4 Quat::wyyy() const { return Vector4 (w, y, y, y); }
+Vector4 Quat::xzyy() const { return Vector4 (x, z, y, y); }
+Vector4 Quat::yzyy() const { return Vector4 (y, z, y, y); }
+Vector4 Quat::zzyy() const { return Vector4 (z, z, y, y); }
+Vector4 Quat::wzyy() const { return Vector4 (w, z, y, y); }
+Vector4 Quat::xwyy() const { return Vector4 (x, w, y, y); }
+Vector4 Quat::ywyy() const { return Vector4 (y, w, y, y); }
+Vector4 Quat::zwyy() const { return Vector4 (z, w, y, y); }
+Vector4 Quat::wwyy() const { return Vector4 (w, w, y, y); }
+Vector4 Quat::xxzy() const { return Vector4 (x, x, z, y); }
+Vector4 Quat::yxzy() const { return Vector4 (y, x, z, y); }
+Vector4 Quat::zxzy() const { return Vector4 (z, x, z, y); }
+Vector4 Quat::wxzy() const { return Vector4 (w, x, z, y); }
+Vector4 Quat::xyzy() const { return Vector4 (x, y, z, y); }
+Vector4 Quat::yyzy() const { return Vector4 (y, y, z, y); }
+Vector4 Quat::zyzy() const { return Vector4 (z, y, z, y); }
+Vector4 Quat::wyzy() const { return Vector4 (w, y, z, y); }
+Vector4 Quat::xzzy() const { return Vector4 (x, z, z, y); }
+Vector4 Quat::yzzy() const { return Vector4 (y, z, z, y); }
+Vector4 Quat::zzzy() const { return Vector4 (z, z, z, y); }
+Vector4 Quat::wzzy() const { return Vector4 (w, z, z, y); }
+Vector4 Quat::xwzy() const { return Vector4 (x, w, z, y); }
+Vector4 Quat::ywzy() const { return Vector4 (y, w, z, y); }
+Vector4 Quat::zwzy() const { return Vector4 (z, w, z, y); }
+Vector4 Quat::wwzy() const { return Vector4 (w, w, z, y); }
+Vector4 Quat::xxwy() const { return Vector4 (x, x, w, y); }
+Vector4 Quat::yxwy() const { return Vector4 (y, x, w, y); }
+Vector4 Quat::zxwy() const { return Vector4 (z, x, w, y); }
+Vector4 Quat::wxwy() const { return Vector4 (w, x, w, y); }
+Vector4 Quat::xywy() const { return Vector4 (x, y, w, y); }
+Vector4 Quat::yywy() const { return Vector4 (y, y, w, y); }
+Vector4 Quat::zywy() const { return Vector4 (z, y, w, y); }
+Vector4 Quat::wywy() const { return Vector4 (w, y, w, y); }
+Vector4 Quat::xzwy() const { return Vector4 (x, z, w, y); }
+Vector4 Quat::yzwy() const { return Vector4 (y, z, w, y); }
+Vector4 Quat::zzwy() const { return Vector4 (z, z, w, y); }
+Vector4 Quat::wzwy() const { return Vector4 (w, z, w, y); }
+Vector4 Quat::xwwy() const { return Vector4 (x, w, w, y); }
+Vector4 Quat::ywwy() const { return Vector4 (y, w, w, y); }
+Vector4 Quat::zwwy() const { return Vector4 (z, w, w, y); }
+Vector4 Quat::wwwy() const { return Vector4 (w, w, w, y); }
+Vector4 Quat::xxxz() const { return Vector4 (x, x, x, z); }
+Vector4 Quat::yxxz() const { return Vector4 (y, x, x, z); }
+Vector4 Quat::zxxz() const { return Vector4 (z, x, x, z); }
+Vector4 Quat::wxxz() const { return Vector4 (w, x, x, z); }
+Vector4 Quat::xyxz() const { return Vector4 (x, y, x, z); }
+Vector4 Quat::yyxz() const { return Vector4 (y, y, x, z); }
+Vector4 Quat::zyxz() const { return Vector4 (z, y, x, z); }
+Vector4 Quat::wyxz() const { return Vector4 (w, y, x, z); }
+Vector4 Quat::xzxz() const { return Vector4 (x, z, x, z); }
+Vector4 Quat::yzxz() const { return Vector4 (y, z, x, z); }
+Vector4 Quat::zzxz() const { return Vector4 (z, z, x, z); }
+Vector4 Quat::wzxz() const { return Vector4 (w, z, x, z); }
+Vector4 Quat::xwxz() const { return Vector4 (x, w, x, z); }
+Vector4 Quat::ywxz() const { return Vector4 (y, w, x, z); }
+Vector4 Quat::zwxz() const { return Vector4 (z, w, x, z); }
+Vector4 Quat::wwxz() const { return Vector4 (w, w, x, z); }
+Vector4 Quat::xxyz() const { return Vector4 (x, x, y, z); }
+Vector4 Quat::yxyz() const { return Vector4 (y, x, y, z); }
+Vector4 Quat::zxyz() const { return Vector4 (z, x, y, z); }
+Vector4 Quat::wxyz() const { return Vector4 (w, x, y, z); }
+Vector4 Quat::xyyz() const { return Vector4 (x, y, y, z); }
+Vector4 Quat::yyyz() const { return Vector4 (y, y, y, z); }
+Vector4 Quat::zyyz() const { return Vector4 (z, y, y, z); }
+Vector4 Quat::wyyz() const { return Vector4 (w, y, y, z); }
+Vector4 Quat::xzyz() const { return Vector4 (x, z, y, z); }
+Vector4 Quat::yzyz() const { return Vector4 (y, z, y, z); }
+Vector4 Quat::zzyz() const { return Vector4 (z, z, y, z); }
+Vector4 Quat::wzyz() const { return Vector4 (w, z, y, z); }
+Vector4 Quat::xwyz() const { return Vector4 (x, w, y, z); }
+Vector4 Quat::ywyz() const { return Vector4 (y, w, y, z); }
+Vector4 Quat::zwyz() const { return Vector4 (z, w, y, z); }
+Vector4 Quat::wwyz() const { return Vector4 (w, w, y, z); }
+Vector4 Quat::xxzz() const { return Vector4 (x, x, z, z); }
+Vector4 Quat::yxzz() const { return Vector4 (y, x, z, z); }
+Vector4 Quat::zxzz() const { return Vector4 (z, x, z, z); }
+Vector4 Quat::wxzz() const { return Vector4 (w, x, z, z); }
+Vector4 Quat::xyzz() const { return Vector4 (x, y, z, z); }
+Vector4 Quat::yyzz() const { return Vector4 (y, y, z, z); }
+Vector4 Quat::zyzz() const { return Vector4 (z, y, z, z); }
+Vector4 Quat::wyzz() const { return Vector4 (w, y, z, z); }
+Vector4 Quat::xzzz() const { return Vector4 (x, z, z, z); }
+Vector4 Quat::yzzz() const { return Vector4 (y, z, z, z); }
+Vector4 Quat::zzzz() const { return Vector4 (z, z, z, z); }
+Vector4 Quat::wzzz() const { return Vector4 (w, z, z, z); }
+Vector4 Quat::xwzz() const { return Vector4 (x, w, z, z); }
+Vector4 Quat::ywzz() const { return Vector4 (y, w, z, z); }
+Vector4 Quat::zwzz() const { return Vector4 (z, w, z, z); }
+Vector4 Quat::wwzz() const { return Vector4 (w, w, z, z); }
+Vector4 Quat::xxwz() const { return Vector4 (x, x, w, z); }
+Vector4 Quat::yxwz() const { return Vector4 (y, x, w, z); }
+Vector4 Quat::zxwz() const { return Vector4 (z, x, w, z); }
+Vector4 Quat::wxwz() const { return Vector4 (w, x, w, z); }
+Vector4 Quat::xywz() const { return Vector4 (x, y, w, z); }
+Vector4 Quat::yywz() const { return Vector4 (y, y, w, z); }
+Vector4 Quat::zywz() const { return Vector4 (z, y, w, z); }
+Vector4 Quat::wywz() const { return Vector4 (w, y, w, z); }
+Vector4 Quat::xzwz() const { return Vector4 (x, z, w, z); }
+Vector4 Quat::yzwz() const { return Vector4 (y, z, w, z); }
+Vector4 Quat::zzwz() const { return Vector4 (z, z, w, z); }
+Vector4 Quat::wzwz() const { return Vector4 (w, z, w, z); }
+Vector4 Quat::xwwz() const { return Vector4 (x, w, w, z); }
+Vector4 Quat::ywwz() const { return Vector4 (y, w, w, z); }
+Vector4 Quat::zwwz() const { return Vector4 (z, w, w, z); }
+Vector4 Quat::wwwz() const { return Vector4 (w, w, w, z); }
+Vector4 Quat::xxxw() const { return Vector4 (x, x, x, w); }
+Vector4 Quat::yxxw() const { return Vector4 (y, x, x, w); }
+Vector4 Quat::zxxw() const { return Vector4 (z, x, x, w); }
+Vector4 Quat::wxxw() const { return Vector4 (w, x, x, w); }
+Vector4 Quat::xyxw() const { return Vector4 (x, y, x, w); }
+Vector4 Quat::yyxw() const { return Vector4 (y, y, x, w); }
+Vector4 Quat::zyxw() const { return Vector4 (z, y, x, w); }
+Vector4 Quat::wyxw() const { return Vector4 (w, y, x, w); }
+Vector4 Quat::xzxw() const { return Vector4 (x, z, x, w); }
+Vector4 Quat::yzxw() const { return Vector4 (y, z, x, w); }
+Vector4 Quat::zzxw() const { return Vector4 (z, z, x, w); }
+Vector4 Quat::wzxw() const { return Vector4 (w, z, x, w); }
+Vector4 Quat::xwxw() const { return Vector4 (x, w, x, w); }
+Vector4 Quat::ywxw() const { return Vector4 (y, w, x, w); }
+Vector4 Quat::zwxw() const { return Vector4 (z, w, x, w); }
+Vector4 Quat::wwxw() const { return Vector4 (w, w, x, w); }
+Vector4 Quat::xxyw() const { return Vector4 (x, x, y, w); }
+Vector4 Quat::yxyw() const { return Vector4 (y, x, y, w); }
+Vector4 Quat::zxyw() const { return Vector4 (z, x, y, w); }
+Vector4 Quat::wxyw() const { return Vector4 (w, x, y, w); }
+Vector4 Quat::xyyw() const { return Vector4 (x, y, y, w); }
+Vector4 Quat::yyyw() const { return Vector4 (y, y, y, w); }
+Vector4 Quat::zyyw() const { return Vector4 (z, y, y, w); }
+Vector4 Quat::wyyw() const { return Vector4 (w, y, y, w); }
+Vector4 Quat::xzyw() const { return Vector4 (x, z, y, w); }
+Vector4 Quat::yzyw() const { return Vector4 (y, z, y, w); }
+Vector4 Quat::zzyw() const { return Vector4 (z, z, y, w); }
+Vector4 Quat::wzyw() const { return Vector4 (w, z, y, w); }
+Vector4 Quat::xwyw() const { return Vector4 (x, w, y, w); }
+Vector4 Quat::ywyw() const { return Vector4 (y, w, y, w); }
+Vector4 Quat::zwyw() const { return Vector4 (z, w, y, w); }
+Vector4 Quat::wwyw() const { return Vector4 (w, w, y, w); }
+Vector4 Quat::xxzw() const { return Vector4 (x, x, z, w); }
+Vector4 Quat::yxzw() const { return Vector4 (y, x, z, w); }
+Vector4 Quat::zxzw() const { return Vector4 (z, x, z, w); }
+Vector4 Quat::wxzw() const { return Vector4 (w, x, z, w); }
+Vector4 Quat::xyzw() const { return Vector4 (x, y, z, w); }
+Vector4 Quat::yyzw() const { return Vector4 (y, y, z, w); }
+Vector4 Quat::zyzw() const { return Vector4 (z, y, z, w); }
+Vector4 Quat::wyzw() const { return Vector4 (w, y, z, w); }
+Vector4 Quat::xzzw() const { return Vector4 (x, z, z, w); }
+Vector4 Quat::yzzw() const { return Vector4 (y, z, z, w); }
+Vector4 Quat::zzzw() const { return Vector4 (z, z, z, w); }
+Vector4 Quat::wzzw() const { return Vector4 (w, z, z, w); }
+Vector4 Quat::xwzw() const { return Vector4 (x, w, z, w); }
+Vector4 Quat::ywzw() const { return Vector4 (y, w, z, w); }
+Vector4 Quat::zwzw() const { return Vector4 (z, w, z, w); }
+Vector4 Quat::wwzw() const { return Vector4 (w, w, z, w); }
+Vector4 Quat::xxww() const { return Vector4 (x, x, w, w); }
+Vector4 Quat::yxww() const { return Vector4 (y, x, w, w); }
+Vector4 Quat::zxww() const { return Vector4 (z, x, w, w); }
+Vector4 Quat::wxww() const { return Vector4 (w, x, w, w); }
+Vector4 Quat::xyww() const { return Vector4 (x, y, w, w); }
+Vector4 Quat::yyww() const { return Vector4 (y, y, w, w); }
+Vector4 Quat::zyww() const { return Vector4 (z, y, w, w); }
+Vector4 Quat::wyww() const { return Vector4 (w, y, w, w); }
+Vector4 Quat::xzww() const { return Vector4 (x, z, w, w); }
+Vector4 Quat::yzww() const { return Vector4 (y, z, w, w); }
+Vector4 Quat::zzww() const { return Vector4 (z, z, w, w); }
+Vector4 Quat::wzww() const { return Vector4 (w, z, w, w); }
+Vector4 Quat::xwww() const { return Vector4 (x, w, w, w); }
+Vector4 Quat::ywww() const { return Vector4 (y, w, w, w); }
+Vector4 Quat::zwww() const { return Vector4 (z, w, w, w); }
+Vector4 Quat::wwww() const { return Vector4 (w, w, w, w); }
+}
+
diff --git a/dep/src/g3dlite/Random.cpp b/dep/src/g3dlite/Random.cpp
new file mode 100644
index 00000000000..2dda744a1ac
--- /dev/null
+++ b/dep/src/g3dlite/Random.cpp
@@ -0,0 +1,212 @@
+/**
+ @file Random.cpp
+
+ @maintainer Morgan McGuire, http://graphics.cs.williams.edu
+
+ @created 2009-01-02
+ @edited 2009-03-29
+
+ Copyright 2000-2009, Morgan McGuire.
+ All rights reserved.
+ */
+#include "G3D/Random.h"
+
+namespace G3D {
+
+Random& Random::common() {
+ static Random r;
+ return r;
+}
+
+Random::Random(void* x) : state(NULL), m_threadsafe(false) {
+ (void)x;
+}
+
+
+Random::Random(uint32 seed, bool threadsafe) : m_threadsafe(threadsafe) {
+ const uint32 X = 1812433253UL;
+
+ state = new uint32[N];
+ state[0] = seed;
+ for (index = 1; index < (int)N; ++index) {
+ state[index] = X * (state[index - 1] ^ (state[index - 1] >> 30)) + index;
+ }
+}
+
+
+Random::~Random() {
+ delete[] state;
+ state = NULL;
+}
+
+
+uint32 Random::bits() {
+ // See http://en.wikipedia.org/wiki/Mersenne_twister
+
+ // Make a local copy of the index variable to ensure that it
+ // is not out of bounds
+ int localIndex = index;
+
+ // Automatically checks for index < 0 if corrupted
+ // by unsynchronized threads.
+ if ((unsigned int)localIndex >= (unsigned int)N) {
+ generate();
+ localIndex = 0;
+ }
+ // Increment the global index. It may go out of bounds on
+ // multiple threads, but the above check ensures that the
+ // array index actually used never goes out of bounds.
+ // It doesn't matter if we grab the same array index twice
+ // on two threads, since the distribution of random numbers
+ // will still be uniform.
+ ++index;
+ // Return the next random in the sequence
+ uint32 r = state[localIndex];
+
+ // Temper the result
+ r ^= r >> U;
+ r ^= (r << S) & B;
+ r ^= (r << T) & C;
+ r ^= r >> L;
+
+ return r;
+}
+
+
+/** Generate the next N ints, and store them for readback later */
+void Random::generate() {
+ // Lower R bits
+ static const uint32 LOWER_MASK = (1LU << R) - 1;
+
+ // Upper (32 - R) bits
+ static const uint32 UPPER_MASK = 0xFFFFFFFF << R;
+ static const uint32 mag01[2] = {0UL, (uint32)A};
+
+ if (m_threadsafe) {
+ bool contention = ! lock.lock();
+ if (contention) {
+ // Another thread just generated a set of numbers; no need for
+ // this thread to do it too
+ lock.unlock();
+ return;
+ }
+ }
+
+ // First N - M
+ for (unsigned int i = 0; i < N - M; ++i) {
+ uint32 x = (state[i] & UPPER_MASK) | (state[i + 1] & LOWER_MASK);
+ state[i] = state[i + M] ^ (x >> 1) ^ mag01[x & 1];
+ }
+
+ // Rest
+ for (unsigned int i = N - M + 1; i < N - 1; ++i) {
+ uint32 x = (state[i] & UPPER_MASK) | (state[i + 1] & LOWER_MASK);
+ state[i] = state[i + (M - N)] ^ (x >> 1) ^ mag01[x & 1];
+ }
+
+ uint32 y = (state[N - 1] & UPPER_MASK) | (state[0] & LOWER_MASK);
+ state[N - 1] = state[M - 1] ^ (y >> 1) ^ mag01[y & 1];
+ index = 0;
+
+ if (m_threadsafe) {
+ lock.unlock();
+ }
+}
+
+
+int Random::integer(int low, int high) {
+ int r = iFloor(low + (high - low + 1) * (double)bits() / 0xFFFFFFFFUL);
+
+ // There is a *very small* chance of generating
+ // a number larger than high.
+ if (r > high) {
+ return high;
+ } else {
+ return r;
+ }
+}
+
+
+float Random::gaussian(float mean, float stdev) {
+
+ // Using Box-Mueller method from http://www.taygeta.com/random/gaussian.html
+ // Modified to specify standard deviation and mean of distribution
+ float w, x1, x2;
+
+ // Loop until w is less than 1 so that log(w) is negative
+ do {
+ x1 = uniform(-1.0, 1.0);
+ x2 = uniform(-1.0, 1.0);
+
+ w = float(square(x1) + square(x2));
+ } while (w > 1.0f);
+
+ // Transform to gassian distribution
+ // Multiply by sigma (stdev ^ 2) and add mean.
+ return x2 * (float)square(stdev) * sqrtf((-2.0f * logf(w) ) / w) + mean;
+}
+
+
+void Random::cosHemi(float& x, float& y, float& z) {
+ const float e1 = uniform();
+ const float e2 = uniform();
+
+ // Jensen's method
+ const float sin_theta = sqrtf(1.0f - e1);
+ const float cos_theta = sqrtf(e1);
+ const float phi = 6.28318531f * e2;
+
+ x = cos(phi) * sin_theta;
+ y = sin(phi) * sin_theta;
+ z = cos_theta;
+
+ // We could also use Malley's method (pbrt p.657), since they are the same cost:
+ //
+ // r = sqrt(e1);
+ // t = 2*pi*e2;
+ // x = cos(t)*r;
+ // y = sin(t)*r;
+ // z = sqrt(1.0 - x*x + y*y);
+}
+
+
+void Random::cosPowHemi(const float k, float& x, float& y, float& z) {
+ const float e1 = uniform();
+ const float e2 = uniform();
+
+ const float cos_theta = pow(e1, 1.0f / (k + 1.0f));
+ const float sin_theta = sqrtf(1.0f - square(cos_theta));
+ const float phi = 6.28318531f * e2;
+
+ x = cos(phi) * sin_theta;
+ y = sin(phi) * sin_theta;
+ z = cos_theta;
+}
+
+
+void Random::hemi(float& x, float& y, float& z) {
+ sphere(x, y, z);
+ z = fabsf(z);
+}
+
+
+void Random::sphere(float& x, float& y, float& z) {
+ // Squared magnitude
+ float m2;
+
+ // Rejection sample
+ do {
+ x = uniform() * 2.0f - 1.0f,
+ y = uniform() * 2.0f - 1.0f,
+ z = uniform() * 2.0f - 1.0f;
+ m2 = x*x + y*y + z*z;
+ } while (m2 >= 1.0f);
+
+ // Divide by magnitude to produce a unit vector
+ float s = rsqrt(m2);
+ x *= s;
+ y *= s;
+ z *= s;
+}
+
+} // G3D
diff --git a/dep/src/g3dlite/Ray.cpp b/dep/src/g3dlite/Ray.cpp
new file mode 100644
index 00000000000..0436ef0b323
--- /dev/null
+++ b/dep/src/g3dlite/Ray.cpp
@@ -0,0 +1,218 @@
+/**
+ @file Ray.cpp
+
+ @maintainer Morgan McGuire, http://graphics.cs.williams.edu
+
+ @created 2002-07-12
+ @edited 2004-03-19
+ */
+
+#include "G3D/platform.h"
+#include "G3D/Ray.h"
+#include "G3D/Plane.h"
+#include "G3D/Sphere.h"
+#include "G3D/CollisionDetection.h"
+
+namespace G3D {
+
+void Ray::set(const Vector3& origin, const Vector3& direction) {
+ m_origin = origin;
+ m_direction = direction;
+ debugAssert(direction.isUnit());
+
+ m_invDirection = Vector3::one() / direction;
+
+ // ray slope
+ ibyj = m_direction.x * m_invDirection.y;
+ jbyi = m_direction.y * m_invDirection.x;
+ jbyk = m_direction.y * m_invDirection.z;
+ kbyj = m_direction.z * m_invDirection.y;
+ ibyk = m_direction.x * m_invDirection.z;
+ kbyi = m_direction.z * m_invDirection.x;
+
+ // precomputed terms
+ c_xy = m_origin.y - jbyi * m_origin.x;
+ c_xz = m_origin.z - kbyi * m_origin.x;
+ c_yx = m_origin.x - ibyj * m_origin.y;
+ c_yz = m_origin.z - kbyj * m_origin.y;
+ c_zx = m_origin.x - ibyk * m_origin.z;
+ c_zy = m_origin.y - jbyk * m_origin.z;
+
+ //ray slope classification
+ if (m_direction.x < 0) {
+ if (m_direction.y < 0) {
+ if (m_direction.z < 0) {
+ classification = MMM;
+ } else if (m_direction.z > 0) {
+ classification = MMP;
+ } else { //(m_direction.z >= 0)
+ classification = MMO;
+ }
+ } else { //(m_direction.y >= 0)
+ if (m_direction.z < 0) {
+ if (m_direction.y == 0) {
+ classification = MOM;
+ } else {
+ classification = MPM;
+ }
+ } else { //(m_direction.z >= 0)
+ if ((m_direction.y == 0) && (m_direction.z == 0)) {
+ classification = MOO;
+ } else if (m_direction.z == 0) {
+ classification = MPO;
+ } else if (m_direction.y == 0) {
+ classification = MOP;
+ } else {
+ classification = MPP;
+ }
+ }
+ }
+ } else { //(m_direction.x >= 0)
+ if (m_direction.y < 0) {
+ if (m_direction.z < 0) {
+ if (m_direction.x == 0) {
+ classification = OMM;
+ } else {
+ classification = PMM;
+ }
+ } else { //(m_direction.z >= 0)
+ if ((m_direction.x == 0) && (m_direction.z == 0)) {
+ classification = OMO;
+ } else if (m_direction.z == 0) {
+ classification = PMO;
+ } else if (m_direction.x == 0) {
+ classification = OMP;
+ } else {
+ classification = PMP;
+ }
+ }
+ } else { //(m_direction.y >= 0)
+ if (m_direction.z < 0) {
+ if ((m_direction.x == 0) && (m_direction.y == 0)) {
+ classification = OOM;
+ } else if (m_direction.x == 0) {
+ classification = OPM;
+ } else if (m_direction.y == 0) {
+ classification = POM;
+ } else {
+ classification = PPM;
+ }
+ } else { //(m_direction.z > 0)
+ if (m_direction.x == 0) {
+ if (m_direction.y == 0) {
+ classification = OOP;
+ } else if (m_direction.z == 0) {
+ classification = OPO;
+ } else {
+ classification = OPP;
+ }
+ } else {
+ if ((m_direction.y == 0) && (m_direction.z == 0)) {
+ classification = POO;
+ } else if (m_direction.y == 0) {
+ classification = POP;
+ } else if (m_direction.z == 0) {
+ classification = PPO;
+ } else {
+ classification = PPP;
+ }
+ }
+ }
+ }
+ }
+}
+
+Ray::Ray(class BinaryInput& b) {
+ deserialize(b);
+}
+
+
+void Ray::serialize(class BinaryOutput& b) const {
+ m_origin.serialize(b);
+ m_direction.serialize(b);
+}
+
+
+void Ray::deserialize(class BinaryInput& b) {
+ m_origin.deserialize(b);
+ m_direction.deserialize(b);
+ set(m_origin, m_direction);
+}
+
+
+Ray Ray::refract(
+ const Vector3& newOrigin,
+ const Vector3& normal,
+ float iInside,
+ float iOutside) const {
+
+ Vector3 D = m_direction.refractionDirection(normal, iInside, iOutside);
+ return Ray(newOrigin + (m_direction + normal * (float)sign(m_direction.dot(normal))) * 0.001f, D);
+}
+
+
+Ray Ray::reflect(
+ const Vector3& newOrigin,
+ const Vector3& normal) const {
+
+ Vector3 D = m_direction.reflectionDirection(normal);
+ return Ray(newOrigin + (D + normal) * 0.001f, D);
+}
+
+
+Vector3 Ray::intersection(const Plane& plane) const {
+ float d;
+ Vector3 normal = plane.normal();
+ plane.getEquation(normal, d);
+ float rate = m_direction.dot(normal);
+
+ if (rate >= 0.0f) {
+ return Vector3::inf();
+ } else {
+ float t = -(d + m_origin.dot(normal)) / rate;
+ return m_origin + m_direction * t;
+ }
+}
+
+
+float Ray::intersectionTime(const class Sphere& sphere, bool solid) const {
+ Vector3 dummy;
+ return CollisionDetection::collisionTimeForMovingPointFixedSphere(
+ m_origin, m_direction, sphere, dummy, dummy, solid);
+}
+
+
+float Ray::intersectionTime(const class Plane& plane) const {
+ Vector3 dummy;
+ return CollisionDetection::collisionTimeForMovingPointFixedPlane(
+ m_origin, m_direction, plane, dummy);
+}
+
+
+float Ray::intersectionTime(const class Box& box) const {
+ Vector3 dummy;
+ float time = CollisionDetection::collisionTimeForMovingPointFixedBox(
+ m_origin, m_direction, box, dummy);
+
+ if ((time == finf()) && (box.contains(m_origin))) {
+ return 0.0f;
+ } else {
+ return time;
+ }
+}
+
+
+float Ray::intersectionTime(const class AABox& box) const {
+ Vector3 dummy;
+ bool inside;
+ float time = CollisionDetection::collisionTimeForMovingPointFixedAABox(
+ m_origin, m_direction, box, dummy, inside);
+
+ if ((time == finf()) && inside) {
+ return 0.0f;
+ } else {
+ return time;
+ }
+}
+
+}
diff --git a/dep/src/g3dlite/Rect2D.cpp b/dep/src/g3dlite/Rect2D.cpp
new file mode 100644
index 00000000000..e4148315a58
--- /dev/null
+++ b/dep/src/g3dlite/Rect2D.cpp
@@ -0,0 +1,41 @@
+/**
+ @file Rect2D.cpp
+
+ @maintainer Morgan McGuire, http://graphics.cs.williams.edu
+
+ @created 2003-11-13
+ @created 2009-11-16
+
+ Copyright 2000-2009, Morgan McGuire.
+ All rights reserved.
+ */
+
+#include "G3D/platform.h"
+#include "G3D/Rect2D.h"
+#include "G3D/Any.h"
+#include "G3D/stringutils.h"
+
+namespace G3D {
+
+/** \param any Must either Rect2D::xywh(#, #, #, #) or Rect2D::xyxy(#, #, #, #)*/
+Rect2D::Rect2D(const Any& any) {
+ any.verifyName("Rect2D");
+ any.verifyType(Any::ARRAY);
+ any.verifySize(4);
+ if (toUpper(any.name()) == "RECT2D::XYWH") {
+ *this = Rect2D::xywh(any[0], any[1], any[2], any[3]);
+ } else {
+ any.verifyName("Rect2D::xyxy");
+ *this = Rect2D::xyxy(any[0], any[1], any[2], any[3]);
+ }
+}
+
+
+/** Converts the Rect2D to an Any. */
+Rect2D::operator Any() const {
+ Any any(Any::ARRAY, "Rect2D::xywh");
+ any.append(x0(), y0(), width(), height());
+ return any;
+}
+
+}
diff --git a/dep/src/g3dlite/ReferenceCount.cpp b/dep/src/g3dlite/ReferenceCount.cpp
new file mode 100644
index 00000000000..2e1f117e0d9
--- /dev/null
+++ b/dep/src/g3dlite/ReferenceCount.cpp
@@ -0,0 +1,61 @@
+/**
+ @file ReferenceCount.cpp
+
+ Reference Counting Garbage Collector for C++
+
+ @maintainer Morgan McGuire, http://graphics.cs.williams.edu
+ @cite Adapted and extended from Justin Miller's "RGC" class that appeared in BYTE magazine.
+ @cite See also http://www.jelovic.com/articles/cpp_without_memory_errors_slides.htm
+
+ @created 2001-10-23
+ @edited 2009-04-25
+*/
+#include "G3D/platform.h"
+#include "G3D/ReferenceCount.h"
+
+namespace G3D {
+
+ReferenceCountedObject::ReferenceCountedObject() :
+ ReferenceCountedObject_refCount(0),
+ ReferenceCountedObject_weakPointer(0) {
+
+ debugAssertM(isValidHeapPointer(this),
+ "Reference counted objects must be allocated on the heap.");
+}
+
+void ReferenceCountedObject::ReferenceCountedObject_zeroWeakPointers() {
+ // Tell all of my weak pointers that I'm gone.
+
+ _WeakPtrLinkedList* node = ReferenceCountedObject_weakPointer;
+
+ while (node != NULL) {
+ // Notify the weak pointer that it is going away
+ node->weakPtr->objectCollected();
+
+ // Free the node and advance
+ _WeakPtrLinkedList* tmp = node;
+ node = node->next;
+ delete tmp;
+ }
+}
+
+ReferenceCountedObject::~ReferenceCountedObject() {}
+
+
+ReferenceCountedObject::ReferenceCountedObject(const ReferenceCountedObject& notUsed) :
+ ReferenceCountedObject_refCount(0),
+ ReferenceCountedObject_weakPointer(0) {
+ (void)notUsed;
+ debugAssertM(G3D::isValidHeapPointer(this),
+ "Reference counted objects must be allocated on the heap.");
+}
+
+ReferenceCountedObject& ReferenceCountedObject::operator=(const ReferenceCountedObject& other) {
+ (void)other;
+ // Nothing changes when I am assigned; the reference count on
+ // both objects is the same (although my super-class probably
+ // changes).
+ return *this;
+}
+
+} // G3D
diff --git a/dep/src/g3dlite/RegistryUtil.cpp b/dep/src/g3dlite/RegistryUtil.cpp
new file mode 100644
index 00000000000..fc4cebc2ee5
--- /dev/null
+++ b/dep/src/g3dlite/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, size_t 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/dep/src/g3dlite/Sphere.cpp b/dep/src/g3dlite/Sphere.cpp
new file mode 100644
index 00000000000..4ed0811cb29
--- /dev/null
+++ b/dep/src/g3dlite/Sphere.cpp
@@ -0,0 +1,223 @@
+/**
+ @file Sphere.cpp
+
+ Sphere class
+
+ @maintainer Morgan McGuire, http://graphics.cs.williams.edu
+
+ @created 2001-04-17
+ @edited 2009-01-20
+ */
+
+#include "G3D/platform.h"
+#include "G3D/Sphere.h"
+#include "G3D/stringutils.h"
+#include "G3D/BinaryOutput.h"
+#include "G3D/BinaryInput.h"
+#include "G3D/AABox.h"
+#include "G3D/Plane.h"
+
+namespace G3D {
+
+int32 Sphere::dummy;
+
+Sphere::Sphere(class BinaryInput& b) {
+ deserialize(b);
+}
+
+
+void Sphere::serialize(class BinaryOutput& b) const {
+ center.serialize(b);
+ b.writeFloat64(radius);
+}
+
+
+void Sphere::deserialize(class BinaryInput& b) {
+ center.deserialize(b);
+ radius = (float)b.readFloat64();
+}
+
+
+std::string Sphere::toString() const {
+ return format("Sphere(<%g, %g, %g>, %g)",
+ center.x, center.y, center.z, radius);
+}
+
+
+bool Sphere::contains(const Vector3& point) const {
+ float distance = (center - point).squaredMagnitude();
+ return distance <= square(radius);
+}
+
+
+bool Sphere::contains(const Sphere& other) const {
+ float distance = (center - other.center).squaredMagnitude();
+ return (radius >= other.radius) && (distance <= square(radius - other.radius));
+}
+
+
+bool Sphere::intersects(const Sphere& other) const {
+ return (other.center - center).length() <= (radius + other.radius);
+}
+
+
+void Sphere::merge(const Sphere& other) {
+ if (other.contains(*this)) {
+ *this = other;
+ } else if (! contains(other)) {
+ // The farthest distance is along the axis between the centers, which
+ // must not be colocated since neither contains the other.
+ Vector3 toMe = center - other.center;
+ // Get a point on the axis from each
+ toMe = toMe.direction();
+ const Vector3& A = center + toMe * radius;
+ const Vector3& B = other.center - toMe * other.radius;
+
+ // Now just bound the A->B segment
+ center = (A + B) * 0.5f;
+ radius = (A - B).length();
+ }
+ // (if this contains other, we're done)
+}
+
+
+bool Sphere::culledBy(
+ const Array<Plane>& plane,
+ int& cullingPlaneIndex,
+ const uint32 inMask,
+ uint32& outMask) const {
+
+ return culledBy(plane.getCArray(), plane.size(), cullingPlaneIndex, inMask, outMask);
+}
+
+
+bool Sphere::culledBy(
+ const Array<Plane>& plane,
+ int& cullingPlaneIndex,
+ const uint32 inMask) const {
+
+ return culledBy(plane.getCArray(), plane.size(), cullingPlaneIndex, inMask);
+}
+
+
+bool Sphere::culledBy(
+ const class Plane* plane,
+ int numPlanes,
+ int& cullingPlane,
+ const uint32 _inMask,
+ uint32& childMask) const {
+
+ if (radius == finf()) {
+ // No plane can cull the infinite box
+ return false;
+ }
+
+ uint32 inMask = _inMask;
+ assert(numPlanes < 31);
+
+ childMask = 0;
+
+ // See if there is one plane for which all of the
+ // vertices are in the negative half space.
+ for (int p = 0; p < numPlanes; p++) {
+
+ // Only test planes that are not masked
+ if ((inMask & 1) != 0) {
+
+ bool culledLow = ! plane[p].halfSpaceContainsFinite(center + plane[p].normal() * radius);
+ bool culledHigh = ! plane[p].halfSpaceContainsFinite(center - plane[p].normal() * radius);
+
+ if (culledLow) {
+ // Plane p culled the sphere
+ cullingPlane = p;
+
+ // The caller should not recurse into the children,
+ // since the parent is culled. If they do recurse,
+ // make them only test against this one plane, which
+ // will immediately cull the volume.
+ childMask = 1 << p;
+ return true;
+
+ } else if (culledHigh) {
+ // The bounding volume straddled the plane; we have
+ // to keep testing against this plane
+ childMask |= (1 << p);
+ }
+ }
+
+ // Move on to the next bit.
+ inMask = inMask >> 1;
+ }
+
+ // None of the planes could cull this box
+ cullingPlane = -1;
+ return false;
+}
+
+
+bool Sphere::culledBy(
+ const class Plane* plane,
+ int numPlanes,
+ int& cullingPlane,
+ const uint32 _inMask) const {
+
+ uint32 inMask = _inMask;
+ assert(numPlanes < 31);
+
+ // See if there is one plane for which all of the
+ // vertices are in the negative half space.
+ for (int p = 0; p < numPlanes; p++) {
+
+ // Only test planes that are not masked
+ if ((inMask & 1) != 0) {
+ bool culled = ! plane[p].halfSpaceContains(center + plane[p].normal() * radius);
+ if (culled) {
+ // Plane p culled the sphere
+ cullingPlane = p;
+ return true;
+ }
+ }
+
+ // Move on to the next bit.
+ inMask = inMask >> 1;
+ }
+
+ // None of the planes could cull this box
+ cullingPlane = -1;
+ return false;
+}
+
+
+Vector3 Sphere::randomSurfacePoint() const {
+ return Vector3::random() * radius + center;
+}
+
+
+Vector3 Sphere::randomInteriorPoint() const {
+ Vector3 result;
+ do {
+ result = Vector3(uniformRandom(-1, 1),
+ uniformRandom(-1, 1),
+ uniformRandom(-1, 1));
+ } while (result.squaredMagnitude() >= 1.0f);
+
+ return result * radius + center;
+}
+
+
+float Sphere::volume() const {
+ return (float)pi() * (4.0f / 3.0f) * powf((float)radius, 3.0f);
+}
+
+
+float Sphere::area() const {
+ return (float)pi() * 4.0f * powf((float)radius, 2.0f);
+}
+
+
+void Sphere::getBounds(AABox& out) const {
+ Vector3 extent(radius, radius, radius);
+ out = AABox(center - extent, center + extent);
+}
+
+} // namespace
diff --git a/dep/src/g3dlite/SplineBase.cpp b/dep/src/g3dlite/SplineBase.cpp
new file mode 100644
index 00000000000..41221624b06
--- /dev/null
+++ b/dep/src/g3dlite/SplineBase.cpp
@@ -0,0 +1,162 @@
+#include "G3D/platform.h"
+#include "G3D/Spline.h"
+
+namespace G3D {
+
+float SplineBase::getFinalInterval() const {
+ if (! cyclic) {
+ return 0;
+ } else if (finalInterval <= 0) {
+ int N = time.size();
+ if (N >= 2) {
+ return (time[1] - time[0] + time[N - 1] - time[N - 2]) * 0.5f;
+ } else {
+ return 1.0f;
+ }
+ } else {
+ return finalInterval;
+ }
+}
+
+
+Matrix4 SplineBase::computeBasis() {
+ // The standard Catmull-Rom spline basis (e.g., Watt & Watt p108)
+ // is for [u^3 u^2 u^1 u^0] * B * [p[0] p[1] p[2] p[3]]^T.
+ // We need a basis formed for:
+ //
+ // U * C * [2*p'[1] p[1] p[2] 2*p'[2]]^T
+ //
+ // U * C * [p2-p0 p1 p2 p3-p1]^T
+ //
+ // To make this transformation, compute the differences of columns in C:
+ // For [p0 p1 p2 p3]
+ Matrix4 basis =
+ Matrix4( -1, 3, -3, 1,
+ 2, -5, 4, -1,
+ -1, 0, 1, 0,
+ 0, 2, 0, 0) * 0.5f;
+
+ // For [-p0 p1 p2 p3]^T
+ basis.setColumn(0, -basis.column(0));
+
+ // For [-p0 p1 p2 p3-p1]^T
+ basis.setColumn(1, basis.column(1) + basis.column(3));
+
+ // For [p2-p0 p1 p2 p3-p1]^T
+ basis.setColumn(2, basis.column(2) - basis.column(0));
+
+ return basis;
+}
+
+
+float SplineBase::duration() const {
+ if (time.size() == 0) {
+ return 0;
+ } else {
+ return time.last() - time[0] + getFinalInterval();
+ }
+}
+
+
+void SplineBase::computeIndexInBounds(float s, int& i, float& u) const {
+ int N = time.size();
+ float t0 = time[0];
+ float tn = time[N - 1];
+
+ i = iFloor((N - 1) * (s - t0) / (tn - t0));
+
+ // Inclusive bounds for binary search
+ int hi = N - 1;
+ int lo = 0;
+
+ while ((time[i] > s) || (time[i + 1] <= s)) {
+
+ if (time[i] > s) {
+ // too big
+ hi = i - 1;
+ } else if (time[i + 1] <= s) {
+ // too small
+ lo = i + 1;
+ }
+
+ i = (hi + lo) / 2;
+ }
+
+ // Having exited the above loop, i must be correct, so compute u.
+ u = (s - time[i]) / (time[i + 1] - time[i]);
+}
+
+
+void SplineBase::computeIndex(float s, int& i, float& u) const {
+ int N = time.size();
+ debugAssertM(N > 0, "No control points");
+ float t0 = time[0];
+ float tn = time[N - 1];
+
+ if (N < 2) {
+ // No control points to work with
+ i = 0;
+ u = 0.0;
+ } else if (cyclic) {
+ float fi = getFinalInterval();
+
+ // Cyclic spline
+ if ((s < t0) || (s >= tn + fi)) {
+ // Cyclic, off the bottom or top
+
+ // Compute offset and reduce to the in-bounds case
+
+ float d = duration();
+ // Number of times we wrapped around the cyclic array
+ int wraps = iFloor((s - t0) / d);
+
+ debugAssert(s - d * wraps >= t0);
+ debugAssert(s - d * wraps < tn + getFinalInterval());
+
+ computeIndex(s - d * wraps, i, u);
+ i += wraps * N;
+
+ } else if (s >= tn) {
+ debugAssert(s < tn + fi);
+ // Cyclic, off the top but before the end of the last interval
+ i = N - 1;
+ u = (s - tn) / fi;
+
+ } else {
+ // Cyclic, in bounds
+ computeIndexInBounds(s, i, u);
+ }
+
+ } else {
+ // Non-cyclic
+
+ if (s < t0) {
+ // Non-cyclic, off the bottom. Assume points are spaced
+ // following the first time interval.
+
+ float dt = time[1] - t0;
+ float x = (s - t0) / dt;
+ i = iFloor(x);
+ u = x - i;
+
+ } else if (s >= tn) {
+ // Non-cyclic, off the top. Assume points are spaced following
+ // the last time interval.
+
+ float dt = tn - time[N - 2];
+ float x = N - 1 + (s - tn) / dt;
+ i = iFloor(x);
+ u = x - i;
+
+ } else {
+ // In bounds, non-cyclic. Assume a regular
+ // distribution (which gives O(1) for uniform spacing)
+ // and then binary search to handle the general case
+ // efficiently.
+ computeIndexInBounds(s, i, u);
+
+ } // if in bounds
+ } // if cyclic
+}
+
+}
diff --git a/dep/src/g3dlite/Stopwatch.cpp b/dep/src/g3dlite/Stopwatch.cpp
new file mode 100644
index 00000000000..9b785d50295
--- /dev/null
+++ b/dep/src/g3dlite/Stopwatch.cpp
@@ -0,0 +1,119 @@
+/**
+ @file Stopwatch.cpp
+
+ @maintainer Morgan McGuire, http://graphics.cs.williams.edu
+
+ @created 2005-10-05
+ @edited 2009-03-14
+
+ Copyright 2000-2009, Morgan McGuire.
+ All rights reserved.
+ */
+
+#include "G3D/Stopwatch.h"
+#include "G3D/System.h"
+
+namespace G3D {
+
+Stopwatch::Stopwatch(const std::string& myName) :
+ myName(myName),
+ inBetween(false), lastTockTime(-1),
+ lastDuration(0), lastCycleCount(0), m_fps(0), emwaFPS(0),
+ m_smoothFPS(0), emwaDuration(0) {
+ computeOverhead();
+ reset();
+}
+
+
+void Stopwatch::computeOverhead() {
+ cycleOverhead = 0;
+ tick();
+ tock();
+ cycleOverhead = elapsedCycles();
+}
+
+
+void Stopwatch::tick() {
+ // This is 'alwaysAssert' instead of 'debugAssert'
+ // since people rarely profile in debug mode.
+ alwaysAssertM(! inBetween, "Stopwatch::tick() called twice in a row.");
+ inBetween = true;
+
+ // We read RDTSC twice here, but it is more abstract to implement this
+ // way and at least we're reading the cycle count last.
+ timeStart = System::time();
+ System::beginCycleCount(cycleStart);
+}
+
+
+void Stopwatch::tock() {
+ System::endCycleCount(cycleStart);
+ RealTime now = System::time();
+ lastDuration = now - timeStart;
+ if (abs(emwaDuration - lastDuration) > max(emwaDuration, lastDuration) * 0.50) {
+ // Off by more than 50%
+ emwaDuration = lastDuration;
+ } else {
+ emwaDuration = lastDuration * 0.05 + emwaDuration * 0.95;
+ }
+
+ lastCycleCount = cycleStart - cycleOverhead;
+ if (lastCycleCount < 0) {
+ lastCycleCount = 0;
+ }
+
+ if (lastTockTime != -1.0) {
+ m_fps = 1.0 / (now - lastTockTime);
+
+ const double blend = 0.01;
+ emwaFPS = m_fps * blend + emwaFPS * (1.0 - blend);
+
+ double maxDiscrepancyPercentage = 0.25;
+ if (abs(emwaFPS - m_fps) > max(emwaFPS, m_fps) * maxDiscrepancyPercentage) {
+ // The difference between emwa and m_fps is way off, so
+ // update emwa directly.
+ emwaFPS = m_fps * 0.20 + emwaFPS * 0.80;
+ }
+
+ // Update m_smoothFPS only when the value varies significantly.
+ // We round so as to not mislead the user as to the accuracy of
+ // the number.
+ if (m_smoothFPS == 0) {
+ m_smoothFPS = m_fps;
+ } else if (emwaFPS <= 20) {
+ if (::fabs(m_smoothFPS - emwaFPS) > 0.75) {
+ // Small number and display is off by more than 0.75; round to the nearest 0.1
+ m_smoothFPS = floor(emwaFPS * 10.0 + 0.5) / 10.0;
+ }
+ } else if (::fabs(m_smoothFPS - emwaFPS) > 1.25) {
+ // Large number and display is off by more than 1.25; round to the nearest 1.0
+ m_smoothFPS = floor(emwaFPS + 0.5);
+ }
+ }
+ lastTockTime = now;
+
+ alwaysAssertM(inBetween, "Stopwatch::tock() called without matching tick.");
+ inBetween = false;
+}
+
+
+void Stopwatch::reset() {
+ prevTime = startTime = System::time();
+ prevMark = "start";
+}
+
+
+void Stopwatch::after(const std::string& s) {
+ RealTime now = System::time();
+ debugPrintf("%s: %10s - %8fs since %s (%fs since start)\n",
+ myName.c_str(),
+ s.c_str(),
+ now - prevTime,
+ prevMark.c_str(),
+ now - startTime);
+ prevTime = now;
+ prevMark = s;
+}
+
+}
+
diff --git a/dep/src/g3dlite/System.cpp b/dep/src/g3dlite/System.cpp
index e55be13adc5..e03c4e8c6fa 100644
--- a/dep/src/g3dlite/System.cpp
+++ b/dep/src/g3dlite/System.cpp
@@ -1,7 +1,7 @@
-/**
+/**
@file System.cpp
-
- @maintainer Morgan McGuire, matrix@graphics3d.com
+
+ @maintainer Morgan McGuire, http://graphics.cs.williams.edu
Note: every routine must call init() first.
@@ -10,44 +10,56 @@
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 2006-05-17
+ @edited 2010-01-03
*/
#include "G3D/platform.h"
#include "G3D/System.h"
#include "G3D/debug.h"
-#include "G3D/format.h"
-
-#if defined(__OpenBSD__)
- #include <stdint.h>
+#include "G3D/fileutils.h"
+#include "G3D/TextOutput.h"
+#include "G3D/G3DGameUnits.h"
+#include "G3D/Crypto.h"
+#include "G3D/prompt.h"
+#include "G3D/stringutils.h"
+#include "G3D/Log.h"
+#include "G3D/Table.h"
+#include "G3D/GMutex.h"
+#include "G3D/units.h"
+#include <time.h>
+
+#include <cstring>
+#include <cstdio>
+
+// Uncomment the following line to turn off G3D::System memory
+// allocation and use the operating system's malloc.
+//#define NO_BUFFERPOOL
+
+#if defined(__i386__) || defined(__x86_64__) || defined(G3D_WIN32)
+# define G3D_NOT_OSX_PPC
#endif
-#ifdef G3D_WIN32
+#include <cstdlib>
- #include <conio.h>
- #include <sys/timeb.h>
- #include "G3D/RegistryUtil.h"
+#ifdef G3D_WIN32
-#elif defined(G3D_LINUX)
+# include <conio.h>
+# include <sys/timeb.h>
+# include "G3D/RegistryUtil.h"
- #include <stdlib.h>
- #include <stdio.h>
- #include <string.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_LINUX)
- // #include <assert.h>
+# 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)
@@ -67,50 +79,847 @@
#include <CoreServices/CoreServices.h>
#endif
-#if defined(SSE)
- #include <xmmintrin.h>
+// SIMM include
+#ifdef __SSE__
+#include <xmmintrin.h>
#endif
namespace G3D {
-static char versionCstr[1024];
-System::OutOfMemoryCallback System::outOfMemoryCallback = NULL;
-void System::init() {
- // Cannot use most G3D data structures or utility functions in here because
- // they are not initialized.
+/** Checks if the CPUID command is available on the processor (called from init) */
+static bool checkForCPUID();
- static bool initialized = false;
+/** Called from init */
+static void getG3DVersion(std::string& s);
+
+/** Called from init */
+static G3DEndian checkEndian();
- if (initialized) {
+
+System& System::instance() {
+ static System thesystem;
+ return thesystem;
+}
+
+
+System::System() :
+ m_initialized(false),
+ m_cpuSpeed(0),
+ m_hasCPUID(false),
+ m_hasRDTSC(false),
+ m_hasMMX(false),
+ m_hasSSE(false),
+ m_hasSSE2(false),
+ m_hasSSE3(false),
+ m_has3DNOW(false),
+ m_has3DNOW2(false),
+ m_hasAMDMMX(false),
+ m_cpuVendor("Uninitialized"),
+ m_numCores(1),
+ m_machineEndian(G3D_LITTLE_ENDIAN),
+ m_cpuArch("Uninitialized"),
+ m_operatingSystem("Uninitialized"),
+ m_version("Uninitialized"),
+ m_outOfMemoryCallback(NULL),
+ m_realWorldGetTickTime0(0),
+ m_highestCPUIDFunction(0) {
+
+ init();
+}
+
+
+void System::init() {
+ // NOTE: Cannot use most G3D data structures or utility functions
+ // in here because they are not initialized.
+
+ if (m_initialized) {
return;
+ } else {
+ m_initialized = true;
}
- initialized = true;
+ getG3DVersion(m_version);
+
+ m_machineEndian = checkEndian();
+
+ m_hasCPUID = checkForCPUID();
+ // Process the CPUID information
+ if (m_hasCPUID) {
+ // We read the standard CPUID level 0x00000000 which should
+ // be available on every x86 processor. This fills out
+ // a string with the processor vendor tag.
+ unsigned int eaxreg = 0, ebxreg = 0, ecxreg = 0, edxreg = 0;
+
+ cpuid(CPUID_VENDOR_ID, eaxreg, ebxreg, ecxreg, edxreg);
+
+ {
+ char c[100];
+ // Then we connect the single register values to the vendor string
+ *((unsigned int*) c) = ebxreg;
+ *((unsigned int*) (c + 4)) = edxreg;
+ *((unsigned int*) (c + 8)) = ecxreg;
+ c[12] = '\0';
+ m_cpuVendor = c;
+ }
+
+ switch (ebxreg) {
+ case 0x756E6547: // GenuineIntel
+ m_cpuArch = "Intel Processor";
+ break;
+
+ case 0x68747541: // AuthenticAMD
+ m_cpuArch = "AMD Processor";
+ break;
+
+ case 0x69727943: // CyrixInstead
+ m_cpuArch = "Cyrix Processor";
+ break;
+
+ default:
+ m_cpuArch = "Unknown Processor Vendor";
+ break;
+ }
+
+
+ unsigned int highestFunction = eaxreg;
+ if (highestFunction >= CPUID_NUM_CORES) {
+ cpuid(CPUID_NUM_CORES, eaxreg, ebxreg, ecxreg, edxreg);
+ // Number of cores is in (eax>>26) + 1
+ m_numCores = (eaxreg >> 26) + 1;
+ }
+ cpuid(CPUID_GET_HIGHEST_FUNCTION, m_highestCPUIDFunction, ebxreg, ecxreg, edxreg);
+ }
+
+
+ // Get the operating system name (also happens to read some other information)
+# ifdef G3D_WIN32
+ // Note that this overrides some of the values computed above
+ bool success = RegistryUtil::readInt32
+ ("HKEY_LOCAL_MACHINE\\HARDWARE\\DESCRIPTION\\System\\CentralProcessor\\0",
+ "~MHz", m_cpuSpeed);
+
+ SYSTEM_INFO systemInfo;
+ GetSystemInfo(&systemInfo);
+ const char* arch = NULL;
+ switch (systemInfo.wProcessorArchitecture) {
+ case PROCESSOR_ARCHITECTURE_INTEL:
+ arch = "Intel";
+ break;
+
+ case PROCESSOR_ARCHITECTURE_MIPS:
+ arch = "MIPS";
+ break;
+
+ case PROCESSOR_ARCHITECTURE_ALPHA:
+ arch = "Alpha";
+ break;
+
+ case PROCESSOR_ARCHITECTURE_PPC:
+ arch = "Power PC";
+ break;
+
+ default:
+ arch = "Unknown";
+ }
+
+ m_numCores = systemInfo.dwNumberOfProcessors;
+ uint32 maxAddr = (uint32)systemInfo.lpMaximumApplicationAddress;
+ {
+ char c[1024];
+ sprintf(c, "%d x %d-bit %s processor",
+ systemInfo.dwNumberOfProcessors,
+ (int)(::log((double)maxAddr) / ::log(2.0) + 2.0),
+ arch);
+ m_cpuArch = c;
+ }
+
+ OSVERSIONINFO osVersionInfo;
+ osVersionInfo.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
+ success = GetVersionEx(&osVersionInfo) != 0;
+
+ if (success) {
+ char c[1000];
+ sprintf(c, "Windows %d.%d build %d Platform %d %s",
+ osVersionInfo.dwMajorVersion,
+ osVersionInfo.dwMinorVersion,
+ osVersionInfo.dwBuildNumber,
+ osVersionInfo.dwPlatformId,
+ osVersionInfo.szCSDVersion);
+ m_operatingSystem = c;
+ } else {
+ m_operatingSystem = "Windows";
+ }
+
+# elif defined(G3D_LINUX) || defined(G3D_FREEBSD)
+
+ {
+ // Find the operating system using the 'uname' command
+ FILE* f = popen("uname -a", "r");
+
+ int len = 100;
+ char* r = (char*)::malloc(len * sizeof(char));
+ fgets(r, len, f);
+ // Remove trailing newline
+ if (r[strlen(r) - 1] == '\n') {
+ r[strlen(r) - 1] = '\0';
+ }
+ fclose(f);
+
+ m_operatingSystem = r;
+ ::free(r);
+ }
+
+# elif defined(G3D_OSX)
+
+ // Operating System:
+ SInt32 macVersion;
+ Gestalt(gestaltSystemVersion, &macVersion);
+
+ int major = (macVersion >> 8) & 0xFF;
+ int minor = (macVersion >> 4) & 0xF;
+ int revision = macVersion & 0xF;
+
+ {
+ char c[1000];
+ sprintf(c, "OS X %x.%x.%x", major, minor, revision);
+ m_operatingSystem = c;
+ }
+
+ // Clock Cycle Timing Information:
+ Gestalt('pclk', &m_OSXCPUSpeed);
+ m_cpuSpeed = iRound((double)m_OSXCPUSpeed / (1024 * 1024));
+ m_secondsPerNS = 1.0 / 1.0e9;
+
+ // System Architecture:
+ const NXArchInfo* pInfo = NXGetLocalArchInfo();
+
+ if (pInfo) {
+ m_cpuArch = pInfo->description;
+
+ switch (pInfo->cputype) {
+ case CPU_TYPE_POWERPC:
+ switch(pInfo->cpusubtype){
+ case CPU_SUBTYPE_POWERPC_750:
+ case CPU_SUBTYPE_POWERPC_7400:
+ case CPU_SUBTYPE_POWERPC_7450:
+ m_cpuVendor = "Motorola";
+ break;
+ case CPU_SUBTYPE_POWERPC_970:
+ m_cpuVendor = "IBM";
+ break;
+ }
+ break;
+
+ case CPU_TYPE_I386:
+ m_cpuVendor = "Intel";
+ break;
+ }
+ }
+# endif
+
+ initTime();
+
+ getStandardProcessorExtensions();
+}
+
+
+void getG3DVersion(std::string& s) {
+ char cstr[100];
if ((G3D_VER % 100) != 0) {
- sprintf(versionCstr, "G3D %d.%02d beta %d",
- G3D_VER / 10000,
- (G3D_VER / 100) % 100,
- G3D_VER % 100);
+ sprintf(cstr, "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);
+ sprintf(cstr, "G3D %d.%02d",
+ G3D_VER / 10000,
+ (G3D_VER / 100) % 100);
}
+ s = cstr;
+}
+
+#if 0 // TODO: delete
+struct Directory {
+ std::string path;
+ Array<std::string> contents;
+};
+static bool maybeAddDirectory(const std::string& newPath, Array<Directory>& directoryArray, bool recurse = true) {
+ if (fileExists(newPath)) {
+ Directory& d = directoryArray.next();
+ d.path = newPath;
+ getFiles(pathConcat(newPath, "*"), d.contents);
+ Array<std::string> dirs;
+ getDirs(pathConcat(newPath, "*"), dirs);
+ d.contents.append(dirs);
+
+ if (recurse) {
+ // Look for subdirectories
+ static const std::string subdirs[] =
+ {"font", "gui", "SuperShader", "cubemap", "icon", "material", "image", "md2", "md3", "ifs", "3ds", "sky", ""};
+
+ for (int j = 0; j < dirs.size(); ++j) {
+ for (int i = 0; ! subdirs[i].empty(); ++i) {
+ if (dirs[j] == subdirs[i]) {
+ maybeAddDirectory(pathConcat(newPath, dirs[j]), directoryArray, false);
+ }
+ }
+ }
+ }
+ return true;
+ } else {
+ return false;
+ }
}
+#endif
+
+std::string System::findDataFile
+(const std::string& full,
+ bool errorIfNotFound) {
+
+ // Places where specific files were most recently found. This is
+ // used to cache seeking of common files.
+ static Table<std::string, std::string> lastFound;
+
+ // First check if the file exists as requested. This will go
+ // through the FileSystemCache, so most calls do not touch disk.
+ if (fileExists(full)) {
+ return full;
+ }
+
+ // Now check where we previously found this file.
+ std::string* last = lastFound.getPointer(full);
+ if (last != NULL) {
+ if (fileExists(*last)) {
+ // Even if cwd has changed the file is still present.
+ // We won't notice if it has been deleted, however.
+ return *last;
+ } else {
+ // Remove this from the cache it is invalid
+ lastFound.remove(full);
+ }
+ }
+
+ // Places to look
+ static Array<std::string> directoryArray;
+
+ if (directoryArray.size() == 0) {
+ // Initialize the directory array
+ RealTime t0 = System::time();
+
+ Array<std::string> baseDirArray;
+
+ std::string initialAppDataDir(instance().m_appDataDir);
+
+ baseDirArray.append("");
+ if (! initialAppDataDir.empty()) {
+ baseDirArray.append(initialAppDataDir);
+ }
+
+ const char* g3dPath = getenv("G3DDATA");
+
+ if (g3dPath && (initialAppDataDir != g3dPath)) {
+ baseDirArray.append(g3dPath);
+ }
+
+ static const std::string subdirs[] =
+ {"font", "gui", "SuperShader", "cubemap", "icon", "material", "image", "md2", "md3", "ifs", "3ds", "sky", ""};
+ for (int j = 0; j < baseDirArray.size(); ++j) {
+ std::string d = baseDirArray[j];
+ if (fileExists(d)) {
+ directoryArray.append(d);
+ for (int i = 0; ! subdirs[i].empty(); ++i) {
+ const std::string& p = pathConcat(d, subdirs[i]);
+ if (fileExists(p)) {
+ directoryArray.append(p);
+ }
+ }
+ }
+ }
+
+ logLazyPrintf("Initializing System::findDataFile took %fs\n", System::time() - t0);
+ }
+
+ for (int i = 0; i < directoryArray.size(); ++i) {
+ const std::string& p = pathConcat(directoryArray[i], full);
+ if (fileExists(p)) {
+ lastFound.set(full, p);
+ return p;
+ }
+ }
+
+ if (errorIfNotFound) {
+ // Generate an error message
+ std::string locations;
+ for (int i = 0; i < directoryArray.size(); ++i) {
+ locations += pathConcat(directoryArray[i], full) + "\n";
+ }
+ alwaysAssertM(false, "Could not find '" + full + "' in:\n" + locations);
+ }
+
+ // Not found
+ return "";
+}
+
+
+void System::setAppDataDir(const std::string& path) {
+ instance().m_appDataDir = path;
+}
+
+
+std::string demoFindData(bool errorIfNotFound) {
+ static const char* g3dPath = getenv("G3DDATA");
+ if (g3dPath) {
+ return g3dPath;
+# ifdef G3D_WIN32
+ } else if (fileExists("../data")) {
+ // G3D install on Windows
+ return "../data";
+ } else if (fileExists("../data-files")) {
+ // G3D source on Windows
+ return "../data-files";
+# else
+ } else if (fileExists("../../../../data")) {
+ // G3D install on Unix
+ return "../../../../data";
+ } else if (fileExists("../../../../data-files")) {
+ // G3D source on Unix
+ return "../../../../data-files";
+# endif
+ } else {
+ return "";
+ }
+}
+
+
+const std::string& System::build() {
+ const static std::string b =
+# ifdef _DEBUG
+ "Debug";
+# else
+ "Release";
+# endif
+
+ return b;
+}
+
+
+static G3DEndian checkEndian() {
+ int32 a = 1;
+ if (*(uint8*)&a == 1) {
+ return G3D_LITTLE_ENDIAN;
+ } else {
+ return G3D_BIG_ENDIAN;
+ }
+}
+
+
+static bool checkForCPUID() {
+ // all known supported architectures have cpuid
+ // add cases for incompatible architectures if they are added
+ // e.g., if we ever support __powerpc__ being defined again
+
+ return true;
+}
+
+
+void System::getStandardProcessorExtensions() {
+#if ! defined(G3D_OSX) || defined(G3D_OSX_INTEL)
+ if (! m_hasCPUID) {
+ return;
+ }
+
+ uint32 eaxreg = 0, ebxreg = 0, ecxreg = 0, features = 0;
+
+ cpuid(CPUID_PROCESSOR_FEATURES, eaxreg, ebxreg, ecxreg, features);
+
+# define checkBit(var, bit) ((var & (1 << bit)) ? true : false)
+
+ m_hasRDTSC = checkBit(features, 4);
+ m_hasMMX = checkBit(features, 23);
+ m_hasSSE = checkBit(features, 25);
+ m_hasSSE2 = checkBit(features, 26);
+ // Bit 28 is HTT; not checked by G3D
+
+ m_hasSSE3 = checkBit(ecxreg, 0);
+
+ if (m_highestCPUIDFunction >= CPUID_EXTENDED_FEATURES) {
+ cpuid(CPUID_EXTENDED_FEATURES, eaxreg, ebxreg, ecxreg, features);
+ m_hasAMDMMX = checkBit(features, 22); // Only on AMD
+ m_has3DNOW = checkBit(features, 31); // Only on AMD
+ m_has3DNOW2 = checkBit(features, 30); // Only on AMD
+ } else {
+ m_hasAMDMMX = false;
+ m_has3DNOW = false;
+ m_has3DNOW2 = false;
+ }
+
+# undef checkBit
+#endif
+}
+
+#if defined(G3D_WIN32) && !defined(G3D_64BIT)
+ #pragma message("Port System::memcpy SIMD to all platforms")
+/** Michael Herf's fast memcpy */
+void memcpyMMX(void* dst, const void* src, int nbytes) {
+ int remainingBytes = nbytes;
+
+ if (nbytes > 64) {
+ _asm {
+ mov esi, src
+ mov edi, dst
+ mov ecx, nbytes
+ shr ecx, 6 // 64 bytes per iteration
+
+ loop1:
+ movq mm1, 0[ESI] // Read in source data
+ movq mm2, 8[ESI]
+ movq mm3, 16[ESI]
+ movq mm4, 24[ESI]
+ movq mm5, 32[ESI]
+ movq mm6, 40[ESI]
+ movq mm7, 48[ESI]
+ movq mm0, 56[ESI]
+
+ movntq 0[EDI], mm1 // Non-temporal stores
+ movntq 8[EDI], mm2
+ movntq 16[EDI], mm3
+ movntq 24[EDI], mm4
+ movntq 32[EDI], mm5
+ movntq 40[EDI], mm6
+ movntq 48[EDI], mm7
+ movntq 56[EDI], mm0
+
+ add esi, 64
+ add edi, 64
+ dec ecx
+ jnz loop1
+
+ emms
+ }
+ remainingBytes -= ((nbytes >> 6) << 6);
+ }
+
+ if (remainingBytes > 0) {
+ // Memcpy the rest
+ memcpy((uint8*)dst + (nbytes - remainingBytes),
+ (const uint8*)src + (nbytes - remainingBytes), remainingBytes);
+ }
+}
+#endif
void System::memcpy(void* dst, const void* src, size_t numBytes) {
- ::memcpy(dst, src, numBytes);
+#if defined(G3D_WIN32) && !defined(G3D_64BIT)
+ memcpyMMX(dst, src, numBytes);
+#else
+ ::memcpy(dst, src, numBytes);
+#endif
+}
+
+
+/** Michael Herf's fastest memset. n32 must be filled with the same
+ character repeated. */
+#if defined(G3D_WIN32) && !defined(G3D_64BIT)
+ #pragma message("Port System::memfill SIMD to all platforms")
+
+// On x86 processors, use MMX
+void memfill(void *dst, int n32, unsigned long i) {
+
+ int originalSize = i;
+ int bytesRemaining = i;
+
+ if (i > 16) {
+
+ bytesRemaining = i % 16;
+ i -= bytesRemaining;
+ __asm {
+ movq mm0, n32
+ punpckldq mm0, mm0
+ mov edi, dst
+
+ loopwrite:
+
+ movntq 0[edi], mm0
+ movntq 8[edi], mm0
+
+ add edi, 16
+ sub i, 16
+ jg loopwrite
+
+ emms
+ }
+ }
+
+ if (bytesRemaining > 0) {
+ ::memset((uint8*)dst + (originalSize - bytesRemaining), n32, bytesRemaining);
+ }
}
+#endif
+
void System::memset(void* dst, uint8 value, size_t numBytes) {
- ::memset(dst, value, numBytes);
+#if defined(G3D_WIN32) && !defined(G3D_64BIT)
+ uint32 v = value;
+ v = v + (v << 8) + (v << 16) + (v << 24);
+ G3D::memfill(dst, v, numBytes);
+#else
+ ::memset(dst, value, numBytes);
+#endif
+}
+
+
+/** Removes the 'd' that icompile / Morgan's VC convention appends. */
+static std::string computeAppName(const std::string& start) {
+ if (start.size() < 2) {
+ return start;
+ }
+
+ if (start[start.size() - 1] == 'd') {
+ // Maybe remove the 'd'; see if ../ or ../../ has the same name
+ char tmp[1024];
+ getcwd(tmp, sizeof(tmp));
+ std::string drive, base, ext;
+ Array<std::string> path;
+ parseFilename(tmp, drive, path, base, ext);
+
+ std::string shortName = start.substr(0, start.size() - 1);
+
+ if ((path.size() > 1) && (toLower(path.last()) == toLower(shortName))) {
+ return shortName;
+ }
+
+ if ((path.size() > 2) && (toLower(path[path.size() - 2]) == toLower(shortName))) {
+ return shortName;
+ }
+ }
+
+ return start;
+}
+
+
+std::string& System::appName() {
+ static std::string n = computeAppName(filenameBase(currentProgramFilename()));
+ return n;
+}
+
+
+std::string System::currentProgramFilename() {
+ char filename[2048];
+
+# ifdef G3D_WIN32
+ {
+ GetModuleFileNameA(NULL, filename, sizeof(filename));
+ }
+# elif defined(G3D_OSX)
+ {
+ // Run the 'ps' program to extract the program name
+ // from the process ID.
+ int pid;
+ FILE* fd;
+ char cmd[80];
+ pid = getpid();
+ sprintf(cmd, "ps -p %d -o comm=\"\"", pid);
+
+ fd = popen(cmd, "r");
+ int s = fread(filename, 1, sizeof(filename), fd);
+ // filename will contain a newline. Overwrite it:
+ filename[s - 1] = '\0';
+ }
+# else
+ {
+ int ret = readlink("/proc/self/exe", filename, sizeof(filename));
+
+ // In case of an error, leave the handling up to the caller
+ if (ret == -1) {
+ return "";
+ }
+
+ debugAssert((int)sizeof(filename) > ret);
+
+ // Ensure proper NULL termination
+ filename[ret] = 0;
+ }
+ #endif
+
+ return filename;
+}
+
+
+void System::sleep(RealTime t) {
+
+ // Overhead of calling this function, measured from a previous run.
+ static const RealTime OVERHEAD = 0.00006f;
+
+ RealTime now = time();
+ RealTime wakeupTime = now + t - OVERHEAD;
+
+ RealTime remainingTime = wakeupTime - now;
+ RealTime sleepTime = 0;
+
+ // On Windows, a "time slice" is measured in quanta of 3-5 ms (http://support.microsoft.com/kb/259025)
+ // Sleep(0) yields the remainder of the time slice, which could be a long time.
+ // A 1 ms minimum time experimentally kept the "Empty GApp" at nearly no CPU load at 100 fps,
+ // yet nailed the frame timing perfectly.
+ static RealTime minRealSleepTime = 3 * units::milliseconds();
+
+ while (remainingTime > 0) {
+
+ if (remainingTime > minRealSleepTime * 2.5) {
+ // Safe to use Sleep with a time... sleep for half the remaining time
+ sleepTime = max(remainingTime * 0.5, 0.0005);
+ } else if (remainingTime > minRealSleepTime) {
+ // Safe to use Sleep with a zero time;
+ // causes the program to yield only
+ // the current time slice, and then return.
+ sleepTime = 0;
+ } else {
+ // Not safe to use Sleep; busy wait
+ sleepTime = -1;
+ }
+
+ if (sleepTime >= 0) {
+ #ifdef G3D_WIN32
+ // Translate to milliseconds
+ Sleep((int)(sleepTime * 1e3));
+ #else
+ // Translate to microseconds
+ usleep((int)(sleepTime * 1e6));
+ #endif
+ }
+
+ now = time();
+ remainingTime = wakeupTime - now;
+ }
+}
+
+
+void System::consoleClearScreen() {
+# ifdef G3D_WIN32
+ system("cls");
+# else
+ system("clear");
+# endif
+}
+
+
+bool System::consoleKeyPressed() {
+ #ifdef G3D_WIN32
+
+ return _kbhit() != 0;
+
+ #else
+
+ static const int STDIN = 0;
+ static bool initialized = false;
+
+ if (! initialized) {
+ // Use termios to turn off line buffering
+ termios term;
+ tcgetattr(STDIN, &term);
+ term.c_lflag &= ~ICANON;
+ tcsetattr(STDIN, TCSANOW, &term);
+ setbuf(stdin, NULL);
+ initialized = true;
+ }
+
+ #ifdef G3D_LINUX
+
+ int bytesWaiting;
+ ioctl(STDIN, FIONREAD, &bytesWaiting);
+ return bytesWaiting;
+
+ #else
+
+ timeval timeout;
+ fd_set rdset;
+
+ FD_ZERO(&rdset);
+ FD_SET(STDIN, &rdset);
+ timeout.tv_sec = 0;
+ timeout.tv_usec = 0;
+
+ return select(STDIN + 1, &rdset, NULL, NULL, &timeout);
+ #endif
+ #endif
+}
+
+
+int System::consoleReadKey() {
+# ifdef G3D_WIN32
+ return _getch();
+# else
+ char c;
+ read(0, &c, 1);
+ return c;
+# endif
+}
+
+
+void System::initTime() {
+ #ifdef G3D_WIN32
+ if (QueryPerformanceFrequency(&m_counterFrequency)) {
+ QueryPerformanceCounter(&m_start);
+ }
+
+ struct _timeb t;
+ _ftime(&t);
+
+ m_realWorldGetTickTime0 = (RealTime)t.time - t.timezone * G3D::MINUTE + (t.dstflag ? G3D::HOUR : 0);
+
+ #else
+ gettimeofday(&m_start, NULL);
+ // "sse" = "seconds since epoch". The time
+ // function returns the seconds since the epoch
+ // GMT (perhaps more correctly called UTC).
+ time_t gmt = ::time(NULL);
+
+ // No call to free or delete is needed, but subsequent
+ // calls to asctime, ctime, mktime, etc. might overwrite
+ // local_time_vals.
+ tm* localTimeVals = localtime(&gmt);
+
+ time_t local = gmt;
+
+ if (localTimeVals) {
+ // tm_gmtoff is already corrected for daylight savings.
+ local = local + localTimeVals->tm_gmtoff;
+ }
+
+ m_realWorldGetTickTime0 = local;
+ #endif
+}
+
+
+RealTime System::time() {
+# ifdef G3D_WIN32
+ LARGE_INTEGER now;
+ QueryPerformanceCounter(&now);
+
+ return ((RealTime)(now.QuadPart - instance().m_start.QuadPart) /
+ instance().m_counterFrequency.QuadPart) + instance().m_realWorldGetTickTime0;
+# else
+ // Linux resolution defaults to 100Hz.
+ // There is no need to do a separate RDTSC call as gettimeofday
+ // actually uses RDTSC when on systems that support it, otherwise
+ // it uses the system clock.
+ struct timeval now;
+ gettimeofday(&now, NULL);
+
+ return (now.tv_sec - instance().m_start.tv_sec) +
+ (now.tv_usec - instance().m_start.tv_usec) / 1e6
+ + instance().m_realWorldGetTickTime0;
+# endif
}
////////////////////////////////////////////////////////////////
+
+#define REALPTR_TO_USERPTR(x) ((uint8*)(x) + sizeof (void *))
+#define USERPTR_TO_REALPTR(x) ((uint8*)(x) - sizeof (void *))
+#define REALBLOCK_SIZE(x) ((x) + sizeof (void *))
+
class BufferPool {
public:
@@ -119,16 +928,18 @@ public:
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.
- 64000 * 128 = 8 MB (preallocated)
- 1024 * 1024 = 1 MB (allocated on demand)
- 1024 * 4096 = 4 MB (allocated on demand)
+ 250000 * 128 = 32 MB (preallocated)
+ 10000 * 1024 = 10 MB (allocated on demand)
+ 1024 * 4096 = 4 MB (allocated on demand)
*/
- enum {maxTinyBuffers = 64000, maxSmallBuffers = 1024, maxMedBuffers = 1024};
+ enum {maxTinyBuffers = 250000, maxSmallBuffers = 10000, maxMedBuffers = 1024};
private:
@@ -157,6 +968,17 @@ private:
/** Pointer to the data in the tiny pool */
void* tinyHeap;
+ Spinlock m_lock;
+
+ void lock() {
+ m_lock.lock();
+ }
+
+ void unlock() {
+ m_lock.unlock();
+ }
+
+#if 0 //-----------------------------------------------old mutex
# ifdef G3D_WIN32
CRITICAL_SECTION mutex;
# else
@@ -179,22 +1001,35 @@ private:
pthread_mutex_unlock(&mutex);
# endif
}
+#endif //-------------------------------------------old mutex
- /**
- Malloc out of the tiny heap.
+ /**
+ 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;
- debugAssert(tinyBufferSize >= bytes);
+ assert(tinyBufferSize >= bytes);
void* ptr = NULL;
if (tinyPoolSize > 0) {
--tinyPoolSize;
- // Return the last one
+
+ // 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;
@@ -202,12 +1037,26 @@ private:
/** Returns true if this is a pointer into the tiny heap. */
bool inTinyHeap(void* ptr) {
- return (ptr >= tinyHeap) &&
- (ptr < (uint8*)tinyHeap + maxTinyBuffers * tinyBufferSize);
+ return
+ (ptr >= tinyHeap) &&
+ (ptr < (uint8*)tinyHeap + maxTinyBuffers * tinyBufferSize);
}
void tinyFree(void* ptr) {
- debugAssert(tinyPoolSize < maxTinyBuffers);
+ 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;
@@ -217,16 +1066,17 @@ private:
void flushPool(MemBlock* pool, int& poolSize) {
for (int i = 0; i < poolSize; ++i) {
- ::free(pool->ptr);
- pool->ptr = NULL;
- pool->bytes = 0;
+ ::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.
+ /** Allocate out of a specific pool-> Return NULL if no suitable
+ memory was found.
+
*/
void* malloc(MemBlock* pool, int& poolSize, size_t bytes) {
@@ -260,13 +1110,13 @@ public:
int mallocsFromSmallPool;
int mallocsFromMedPool;
- /** Amount of memory currently allocated (according to the application).
+ /** 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!
- int bytesAllocated;
+ volatile int bytesAllocated;
BufferPool() {
totalMallocs = 0;
@@ -284,6 +1134,7 @@ public:
medPoolSize = 0;
+
// Initialize the tiny heap as a bunch of pointers into one
// pre-allocated buffer.
tinyHeap = ::malloc(maxTinyBuffers * tinyBufferSize);
@@ -292,22 +1143,28 @@ public:
}
tinyPoolSize = maxTinyBuffers;
+#if 0 ///---------------------------------- old mutex
# ifdef G3D_WIN32
InitializeCriticalSection(&mutex);
# else
pthread_mutex_init(&mutex, NULL);
# endif
+#endif ///---------------------------------- old mutex
}
+
~BufferPool() {
::free(tinyHeap);
+#if 0 //-------------------------------- old mutex
# ifdef G3D_WIN32
DeleteCriticalSection(&mutex);
# else
// No destruction on pthreads
# endif
+#endif //--------------------------------old mutex
}
+
void* realloc(void* ptr, size_t bytes) {
if (ptr == NULL) {
return malloc(bytes);
@@ -319,7 +1176,7 @@ public:
return ptr;
} else {
// Free the old pointer and malloc
-
+
void* newPtr = malloc(bytes);
System::memcpy(newPtr, ptr, tinyBufferSize);
tinyFree(ptr);
@@ -330,7 +1187,7 @@ public:
// In one of our heaps.
// See how big the block really was
- size_t realSize = ((uint32*)ptr)[-1];
+ size_t realSize = *(uint32*)USERPTR_TO_REALPTR(ptr);
if (bytes <= realSize) {
// The old block was big enough.
return ptr;
@@ -344,6 +1201,7 @@ public:
}
}
+
void* malloc(size_t bytes) {
lock();
++totalMallocs;
@@ -358,12 +1216,12 @@ public:
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) {
@@ -372,7 +1230,7 @@ public:
return ptr;
}
- } else if (bytes <= medBufferSize) {
+ } 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.
@@ -382,54 +1240,63 @@ public:
if (ptr) {
++mallocsFromMedPool;
unlock();
+ debugAssertM(ptr != NULL, "BufferPool::malloc returned NULL");
return ptr;
}
}
- bytesAllocated += 4 + (int) bytes;
+ 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(bytes + 4);
+ 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(bytes + 4);
+ ptr = ::malloc(REALBLOCK_SIZE(bytes));
}
if (ptr == NULL) {
- if ((System::outOfMemoryCallback != NULL) &&
- (System::outOfMemoryCallback(bytes + 4, true) == true)) {
+ if ((System::outOfMemoryCallback() != NULL) &&
+ (System::outOfMemoryCallback()(REALBLOCK_SIZE(bytes), true) == true)) {
// Re-attempt the malloc
- ptr = ::malloc(bytes + 4);
+ ptr = ::malloc(REALBLOCK_SIZE(bytes));
}
}
if (ptr == NULL) {
- if (System::outOfMemoryCallback != NULL) {
+ if (System::outOfMemoryCallback() != NULL) {
// Notify the application
- System::outOfMemoryCallback(bytes + 4, false);
+ System::outOfMemoryCallback()(REALBLOCK_SIZE(bytes), false);
}
+# ifdef G3D_DEBUG
+ debugPrintf("::malloc(%d) returned NULL\n", (int)REALBLOCK_SIZE(bytes));
+# endif
+ debugAssertM(ptr != NULL,
+ "::malloc returned NULL. Either the "
+ "operating system is out of memory or the "
+ "heap is corrupt.");
return NULL;
}
- *(uint32*)ptr = (uint32)bytes;
+ *(uint32*)ptr = bytes;
- return (uint8*)ptr + 4;
+ return REALPTR_TO_USERPTR(ptr);
}
+
void free(void* ptr) {
if (ptr == NULL) {
// Free does nothing on null pointers
return;
}
- debugAssert(isValidPointer(ptr));
+ assert(isValidPointer(ptr));
if (inTinyHeap(ptr)) {
lock();
@@ -438,7 +1305,7 @@ public:
return;
}
- uint32 bytes = ((uint32*)ptr)[-1];
+ uint32 bytes = *(uint32*)USERPTR_TO_REALPTR(ptr);
lock();
if (bytes <= smallBufferSize) {
@@ -456,17 +1323,17 @@ public:
return;
}
}
- bytesAllocated -= bytes + 4;
+ bytesAllocated -= REALBLOCK_SIZE(bytes);
unlock();
// Free; the buffer pools are full or this is too big to store.
- ::free((uint8*)ptr - 4);
+ ::free(USERPTR_TO_REALPTR(ptr));
}
std::string performance() const {
if (totalMallocs > 0) {
int pooled = mallocsFromTinyPool +
- mallocsFromSmallPool +
+ mallocsFromSmallPool +
mallocsFromMedPool;
int total = totalMallocs;
@@ -493,11 +1360,11 @@ public:
};
// Dynamically allocated because we need to ensure that
-// the buffer pool is still around when the last global variable
+// the buffer pool is still around when the last global variable
// is deallocated.
static BufferPool* bufferpool = NULL;
-std::string System::mallocPerformance() {
+std::string System::mallocPerformance() {
#ifndef NO_BUFFERPOOL
return bufferpool->performance();
#else
@@ -505,7 +1372,7 @@ std::string System::mallocPerformance() {
#endif
}
-std::string System::mallocStatus() {
+std::string System::mallocStatus() {
#ifndef NO_BUFFERPOOL
return bufferpool->status();
#else
@@ -513,6 +1380,7 @@ std::string System::mallocStatus() {
#endif
}
+
void System::resetMallocPerformanceCounters() {
#ifndef NO_BUFFERPOOL
bufferpool->totalMallocs = 0;
@@ -522,6 +1390,7 @@ void System::resetMallocPerformanceCounters() {
#endif
}
+
#ifndef NO_BUFFERPOOL
inline void initMem() {
// Putting the test here ensures that the system is always
@@ -534,6 +1403,7 @@ inline void initMem() {
}
#endif
+
void* System::malloc(size_t bytes) {
#ifndef NO_BUFFERPOOL
initMem();
@@ -546,6 +1416,8 @@ void* System::malloc(size_t bytes) {
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
@@ -553,6 +1425,7 @@ void* System::calloc(size_t n, size_t x) {
#endif
}
+
void* System::realloc(void* block, size_t bytes) {
#ifndef NO_BUFFERPOOL
initMem();
@@ -562,6 +1435,7 @@ void* System::realloc(void* block, size_t bytes) {
#endif
}
+
void System::free(void* p) {
#ifndef NO_BUFFERPOOL
bufferpool->free(p);
@@ -570,80 +1444,303 @@ void System::free(void* 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((int)alignment, sizeof(void *));
+ 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(intptr_t);
+ size_t totalBytes = bytes + alignment + sizeof(void*);
- void* truePtr = System::malloc(totalBytes);
+ size_t truePtr = (size_t)System::malloc(totalBytes);
- if (!truePtr) {
+ if (truePtr == 0) {
// malloc returned NULL
return NULL;
}
- debugAssert(isValidHeapPointer(truePtr));
+ 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(truePtr, totalBytes, TRUE) );
+ // 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).
- char* alignedPtr = ((char*)truePtr)+ sizeof(intptr_t);
+ size_t alignedPtr = truePtr + sizeof(void*);
-#if 0
// 2^n - 1 has the form 1111... in binary.
uint32 bitMask = (alignment - 1);
// Advance forward until we reach an aligned location.
- while ((((intptr_t)alignedPtr) & bitMask) != 0) {
+ while ((alignedPtr & bitMask) != 0) {
alignedPtr += sizeof(void*);
}
-#else
- alignedPtr += alignment - (((intptr_t)alignedPtr) & (alignment - 1));
- // assert((alignedPtr - truePtr) + bytes <= totalBytes);
-#endif
- debugAssert((alignedPtr - truePtr) + bytes <= totalBytes);
+ debugAssert(alignedPtr - truePtr + bytes <= totalBytes);
// Immediately before the aligned location, write the true array location
// so that we can free it correctly.
- intptr_t* redirectPtr = (intptr_t*)(alignedPtr - sizeof(intptr_t));
- redirectPtr[0] = (intptr_t)truePtr;
+ size_t* redirectPtr = (size_t *)(alignedPtr - sizeof(void *));
+ redirectPtr[0] = truePtr;
- debugAssert(isValidHeapPointer(truePtr));
+ debugAssert(isValidHeapPointer((void*)truePtr));
#ifdef G3D_WIN32
- debugAssert( _CrtIsValidPointer(alignedPtr, bytes, TRUE) );
+ debugAssert( _CrtIsValidPointer((void*)alignedPtr, bytes, TRUE) );
#endif
- return (void*)alignedPtr;
+ return (void *)alignedPtr;
}
+
void System::alignedFree(void* _ptr) {
if (_ptr == NULL) {
return;
}
- char* alignedPtr = (char*)_ptr;
+ 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.
- intptr_t* redirectPtr = (intptr_t*)(alignedPtr - sizeof(intptr_t));
+ size_t* redirectPtr = (size_t*)(alignedPtr - sizeof(void *));
// Dereference that pointer so that ptr = true start
- void* truePtr = (void*)(redirectPtr[0]);
+ void* truePtr = (void*)redirectPtr[0];
- debugAssert(isValidHeapPointer(truePtr));
+ debugAssert(isValidHeapPointer((void*)truePtr));
System::free(truePtr);
}
-} // namespace
+void System::setEnv(const std::string& name, const std::string& value) {
+ std::string cmd = name + "=" + value;
+# ifdef G3D_WIN32
+ _putenv(cmd.c_str());
+# else
+ // Many linux implementations of putenv expect char*
+ putenv(const_cast<char*>(cmd.c_str()));
+# endif
+}
+
+
+const char* System::getEnv(const std::string& name) {
+ return getenv(name.c_str());
+}
+
+
+static void var(TextOutput& t, const std::string& name, const std::string& val) {
+ t.writeSymbols(name,"=");
+ t.writeString(val);
+ t.writeNewline();
+}
+
+
+static void var(TextOutput& t, const std::string& name, const bool val) {
+ t.writeSymbols(name, "=", val ? "Yes" : "No");
+ t.writeNewline();
+}
+
+
+static void var(TextOutput& t, const std::string& name, const int val) {
+ t.writeSymbols(name,"=");
+ t.writeNumber(val);
+ t.writeNewline();
+}
+
+
+void System::describeSystem(
+ std::string& s) {
+
+ TextOutput t;
+ describeSystem(t);
+ t.commitString(s);
+}
+
+void System::describeSystem(
+ TextOutput& t) {
+
+ t.writeSymbols("App", "{");
+ t.writeNewline();
+ t.pushIndent();
+ {
+ var(t, "Name", System::currentProgramFilename());
+ char cwd[1024];
+ getcwd(cwd, 1024);
+ var(t, "cwd", std::string(cwd));
+ }
+ t.popIndent();
+ t.writeSymbols("}");
+ t.writeNewline();
+ t.writeNewline();
+
+ t.writeSymbols("OS", "{");
+ t.writeNewline();
+ t.pushIndent();
+ {
+ var(t, "Name", System::operatingSystem());
+ }
+ t.popIndent();
+ t.writeSymbols("}");
+ t.writeNewline();
+ t.writeNewline();
+
+ t.writeSymbols("CPU", "{");
+ t.writeNewline();
+ t.pushIndent();
+ {
+ var(t, "Vendor", System::cpuVendor());
+ var(t, "Architecture", System::cpuArchitecture());
+ var(t, "hasCPUID", System::hasCPUID());
+ var(t, "hasMMX", System::hasMMX());
+ var(t, "hasSSE", System::hasSSE());
+ var(t, "hasSSE2", System::hasSSE2());
+ var(t, "hasSSE3", System::hasSSE3());
+ var(t, "has3DNow", System::has3DNow());
+ var(t, "hasRDTSC", System::hasRDTSC());
+ var(t, "numCores", System::numCores());
+ }
+ t.popIndent();
+ t.writeSymbols("}");
+ t.writeNewline();
+ t.writeNewline();
+
+ t.writeSymbols("G3D", "{");
+ t.writeNewline();
+ t.pushIndent();
+ {
+ var(t, "Link version", G3D_VER);
+ var(t, "Compile version", System::version());
+ }
+ t.popIndent();
+ t.writeSymbols("}");
+ t.writeNewline();
+ t.writeNewline();
+}
+
+
+void System::setClipboardText(const std::string& s) {
+# ifdef G3D_WIN32
+ if (OpenClipboard(NULL)) {
+ HGLOBAL hMem = GlobalAlloc(GHND | GMEM_DDESHARE, s.size() + 1);
+ if (hMem) {
+ char *pMem = (char*)GlobalLock(hMem);
+ strcpy(pMem, s.c_str());
+ GlobalUnlock(hMem);
+
+ EmptyClipboard();
+ SetClipboardData(CF_TEXT, hMem);
+ }
+
+ CloseClipboard();
+ GlobalFree(hMem);
+ }
+# endif
+}
+
+
+std::string System::getClipboardText() {
+ std::string s;
+
+# ifdef G3D_WIN32
+ if (OpenClipboard(NULL)) {
+ HANDLE h = GetClipboardData(CF_TEXT);
+
+ if (h) {
+ char* temp = (char*)GlobalLock(h);
+ if (temp) {
+ s = temp;
+ }
+ temp = NULL;
+ GlobalUnlock(h);
+ }
+ CloseClipboard();
+ }
+# endif
+ return s;
+}
+
+
+std::string System::currentDateString() {
+ time_t t1;
+ ::time(&t1);
+ tm* t = localtime(&t1);
+ return format("%d-%02d-%02d", t->tm_year + 1900, t->tm_mon + 1, t->tm_mday);
+}
+
+#ifdef _MSC_VER
+
+// VC on Intel
+void System::cpuid(CPUIDFunction func, uint32& areg, uint32& breg, uint32& creg, uint32& dreg) {
+#if !defined(G3D_64BIT)
+ // Can't copy from assembler direct to a function argument (which is on the stack) in VC.
+ uint32 a,b,c,d;
+
+ // Intel assembler syntax
+ __asm {
+ mov eax, func // eax <- func
+ mov ecx, 0
+ cpuid
+ mov a, eax
+ mov b, ebx
+ mov c, ecx
+ mov d, edx
+ }
+ areg = a;
+ breg = b;
+ creg = c;
+ dreg = d;
+#else
+ int CPUInfo[4];
+ __cpuid(CPUInfo, func);
+ memcpy(&areg, &CPUInfo[0], 4);
+ memcpy(&breg, &CPUInfo[1], 4);
+ memcpy(&creg, &CPUInfo[2], 4);
+ memcpy(&dreg, &CPUInfo[3], 4);
+#endif
+}
+
+#elif defined(G3D_OSX) && ! defined(G3D_OSX_INTEL)
+
+// non-intel OS X; no CPUID
+void System::cpuid(CPUIDFunction func, uint32& eax, uint32& ebx, uint32& ecx, uint32& edx) {
+ eax = 0;
+ ebx = 0;
+ ecx = 0;
+ edx = 0;
+}
+
+#else
+
+// See http://sam.zoy.org/blog/2007-04-13-shlib-with-non-pic-code-have-inline-assembly-and-pic-mix-well
+// for a discussion of why the second version saves ebx; it allows 32-bit code to compile with the -fPIC option.
+// On 64-bit x86, PIC code has a dedicated rip register for PIC so there is no ebx conflict.
+void System::cpuid(CPUIDFunction func, uint32& eax, uint32& ebx, uint32& ecx, uint32& edx) {
+#if ! defined(__PIC__) || defined(__x86_64__)
+ // AT&T assembler syntax
+ asm volatile(
+ "movl $0, %%ecx \n\n" /* Wipe ecx */
+ "cpuid \n\t"
+ : "=a"(eax), "=b"(ebx), "=c"(ecx), "=d"(edx)
+ : "a"(func));
+#else
+ // AT&T assembler syntax
+ asm volatile(
+ "pushl %%ebx \n\t" /* save ebx */
+ "movl $0, %%ecx \n\n" /* Wipe ecx */
+ "cpuid \n\t"
+ "movl %%ebx, %1 \n\t" /* save what cpuid just put in %ebx */
+ "popl %%ebx \n\t" /* restore the old ebx */
+ : "=a"(eax), "=r"(ebx), "=c"(ecx), "=d"(edx)
+ : "a"(func));
+#endif
+}
+
+#endif
+
+} // namespace
diff --git a/dep/src/g3dlite/TextInput.cpp b/dep/src/g3dlite/TextInput.cpp
new file mode 100644
index 00000000000..7276d8c66b2
--- /dev/null
+++ b/dep/src/g3dlite/TextInput.cpp
@@ -0,0 +1,1136 @@
+/**
+ @file TextInput.cpp
+
+ @author Morgan McGuire, graphics3d.com
+
+ @cite Based on a lexer written by Aaron Orenstein.
+
+ @created 2001-11-27
+ @edited 2008-07-14
+ */
+
+#include "G3D/fileutils.h"
+#include "G3D/TextInput.h"
+#include "G3D/BinaryInput.h"
+#include "G3D/stringutils.h"
+
+#ifdef _MSC_VER
+# pragma warning (push)
+// conversion from 'int' to 'char', possible loss of data (TODO: fix underlying problems)
+# pragma warning (disable: 4244)
+#endif
+
+namespace G3D {
+
+Token TextInput::readSignificant() {
+ Token t;
+ do {
+ t = read();
+ } while ((t.type() == Token::COMMENT) || (t.type() == Token::NEWLINE));
+ return t;
+}
+
+
+double Token::number() const {
+ if (_type == NUMBER) {
+ std::string s = toLower(_string);
+ if (s == "-1.#ind00") {
+ return nan();
+ }
+
+ if (s == "1.#inf00") {
+ return inf();
+ }
+
+ if (s == "-1.#inf00") {
+ return -inf();
+ }
+
+ double n;
+ if ((_string.length() > 2) &&
+ (_string[0] == '0') &&
+ (_string[1] == 'x')) {
+ // Hex
+ uint32 i;
+ sscanf(_string.c_str(), "%x", &i);
+ n = i;
+ } else {
+ sscanf(_string.c_str(), "%lg", &n);
+ }
+ return n;
+ } else {
+ return 0.0;
+ }
+}
+
+
+TextInput::Settings::Settings () :
+ cppBlockComments(true),
+ cppLineComments(true),
+ otherLineComments(true),
+ escapeSequencesInStrings(true),
+ otherCommentCharacter('\0'),
+ otherCommentCharacter2('\0'),
+ generateCommentTokens(false),
+ generateNewlineTokens(false),
+ signedNumbers(true),
+ singleQuotedStrings(true),
+ singleQuoteCharacter('\''),
+ sourceFileName(),
+ startingLineNumberOffset(0),
+ msvcSpecials(true),
+ proofSymbols(false),
+ caseSensitive(true)
+{
+ trueSymbols.insert("true");
+ falseSymbols.insert("false");
+}
+
+
+Token TextInput::peek() {
+ if (stack.size() == 0) {
+ Token t = nextToken();
+ push(t);
+ }
+
+ return stack.front();
+}
+
+
+int TextInput::peekLineNumber() {
+ return peek().line();
+}
+
+
+int TextInput::peekCharacterNumber() {
+ return peek().character();
+}
+
+
+Token TextInput::read() {
+ if (stack.size() > 0) {
+ Token t = stack.front();
+ stack.pop_front();
+ return t;
+ } else {
+ return nextToken();
+ }
+}
+
+static void toUpper(Set<std::string>& set) {
+ Array<std::string> symbols;
+ set.getMembers(symbols);
+ set.clear();
+ for (int i = 0; i < symbols.size(); ++i) {
+ set.insert(toUpper(symbols[i]));
+ }
+}
+
+void TextInput::init() {
+ currentCharOffset = 0;
+ charNumber = 1;
+ lineNumber = 1 + options.startingLineNumberOffset;
+
+ if (! options.caseSensitive) {
+ // Convert true and false symbols to all uppercase for fast comparisons
+ toUpper(options.trueSymbols);
+ toUpper(options.falseSymbols);
+ }
+}
+
+
+void TextInput::push(const Token& t) {
+ stack.push_front(t);
+}
+
+
+bool TextInput::hasMore() {
+ return (peek()._type != Token::END);
+}
+
+
+int TextInput::eatInputChar() {
+ // Don't go off the end
+ if (currentCharOffset >= buffer.length()) {
+ return EOF;
+ }
+
+ unsigned char c = buffer[currentCharOffset];
+ ++currentCharOffset;
+
+ // update lineNumber and charNumber to reflect the location of the *next*
+ // character which will be read.
+
+ // increment line number for \r, \n and \r\n which matches Token::NEWLINE parsing
+ if (c == '\r') {
+ ++lineNumber;
+ charNumber = 1;
+
+ // check for \r\n
+ if (currentCharOffset < buffer.length()) {
+ unsigned char c2 = buffer[currentCharOffset];
+ if (c2 == '\n') {
+ c = c2;
+ ++currentCharOffset;
+ }
+ }
+ } else if (c == '\n') {
+ ++lineNumber;
+ charNumber = 1;
+ } else {
+ ++charNumber;
+ }
+
+ return c;
+}
+
+int TextInput::peekInputChar(int distance) {
+ // Don't go off the end
+ if ((currentCharOffset + distance) >= buffer.length()) {
+ return EOF;
+ }
+
+ unsigned char c = buffer[currentCharOffset + distance];
+ return c;
+}
+
+
+Token TextInput::nextToken() {
+ Token t;
+
+ t._line = lineNumber;
+ t._character = charNumber;
+ t._type = Token::END;
+ t._extendedType = Token::END_TYPE;
+
+ int c = peekInputChar();
+ if (c == EOF) {
+ return t;
+ }
+
+ // loop through white space, newlines and comments
+ // found before other tokens
+ bool whitespaceDone = false;
+ while (! whitespaceDone) {
+ whitespaceDone = true;
+
+ // generate newlines tokens for '\n' and '\r' and '\r\n'
+ if (options.generateNewlineTokens && isNewline(c)) {
+ t._type = Token::NEWLINE;
+ t._extendedType = Token::NEWLINE_TYPE;
+ t._string = c;
+
+ int c2 = peekInputChar(1);
+ if (c == '\r' && c2 == '\n') {
+ t._string += c2;
+ }
+
+ eatInputChar();
+ return t;
+ } else {
+ // Consume whitespace
+ while (isWhiteSpace(c)) {
+ c = eatAndPeekInputChar();
+ }
+ }
+
+ // update line and character number to include discarded whitespace
+ t._line = lineNumber;
+ t._character = charNumber;
+
+ int c2 = peekInputChar(1);
+
+ // parse comments and generate tokens if enabled
+ std::string commentString;
+
+ // check for line comments first
+ bool isLineComment = false;
+ if (options.cppLineComments && (c == '/' && c2 == '/')) {
+ // set start of line comment and eat markers
+ isLineComment = true;
+ eatInputChar();
+ eatInputChar();
+ } else if ( options.otherCommentCharacter &&
+ (options.otherCommentCharacter != '\0' && c == options.otherCommentCharacter) ) {
+ // set start of line comment and eat markers
+ isLineComment = true;
+ eatInputChar();
+ } else if ( options.otherCommentCharacter &&
+ (options.otherCommentCharacter2 != '\0' && c == options.otherCommentCharacter2) ) {
+ // set start of line comment and eat markers
+ isLineComment = true;
+ eatInputChar();
+ }
+
+ if (isLineComment) {
+
+ // consume line comment to newline or EOF
+ c = peekInputChar();
+ while (! isNewline(c) && c != EOF) {
+ // build comment string for token
+ commentString += c;
+
+ c = eatAndPeekInputChar();
+ }
+
+ if (options.generateCommentTokens) {
+ t._type = Token::COMMENT;
+ t._extendedType = Token::LINE_COMMENT_TYPE;
+ t._string = commentString;
+ return t;
+ } else {
+ // There is whitespace after the comment (in particular, the
+ // newline that terminates the comment). There might also be
+ // whitespace at the start of the next line.
+ whitespaceDone = false;
+ }
+
+ } else if (options.cppBlockComments && (c == '/' && c2 == '*')) {
+ // consume block comment to end-marker or EOF
+
+ // consume both start-comment chars, can't let the trailing one
+ // help close the comment.
+ eatInputChar();
+ eatInputChar();
+
+ c = peekInputChar();
+ c2 = peekInputChar(1);
+ while (! ((c == '*') && (c2 == '/')) && (c != EOF)) {
+ commentString += c;
+
+ eatInputChar();
+ c = c2;
+ c2 = peekInputChar(1);
+ }
+ eatInputChar(); // eat closing '*'
+ eatInputChar(); // eat closing '/'
+
+ c = peekInputChar();
+
+ if (options.generateCommentTokens) {
+ t._type = Token::COMMENT;
+ t._extendedType = Token::BLOCK_COMMENT_TYPE;
+ t._string = commentString;
+ return t;
+ } else {
+ // There is whitespace after the comment (in particular, the
+ // newline that terminates the comment). There might also be
+ // whitespace at the start of the next line.
+ whitespaceDone = false;
+ }
+ }
+
+ } // while (! whitespaceDone)
+
+ t._line = lineNumber;
+ t._character = charNumber;
+
+ // handle EOF
+ if (c == EOF) {
+ return t;
+ }
+
+ // Extended ASCII parses as itself, except for EOF
+ if (c > 127 && c < 255) {
+ t._type = Token::SYMBOL;
+ t._extendedType = Token::SYMBOL_TYPE;
+ t._string = c;
+ c = eatAndPeekInputChar();
+ }
+
+
+ // Perform appropriate setup for a symbol (including setting up the token
+ // string to start with c), eat the input character, and overwrite
+ // 'c' with the peeked next input character.
+#define SETUP_SYMBOL(c) \
+ { \
+ t._type = Token::SYMBOL; \
+ t._extendedType = Token::SYMBOL_TYPE; \
+ t._string = c; \
+ c = eatAndPeekInputChar(); \
+ }
+
+ switch (c) {
+
+ case '@': // Simple symbols -> just themselves.
+ case '(':
+ case ')':
+ case ',':
+ case ';':
+ case '{':
+ case '}':
+ case '[':
+ case ']':
+ case '#':
+ case '$':
+ case '?':
+ case '%':
+ SETUP_SYMBOL(c);
+ return t;
+
+ case '-': // negative number, -, --, -=, or ->
+ SETUP_SYMBOL(c);
+
+ switch (c) {
+ case '>': // ->
+ case '-': // --
+ case '=': // -=
+ t._string += c;
+ eatInputChar();
+ return t;
+ }
+
+ if (options.signedNumbers
+ && (isDigit(c) || (c == '.' && isDigit(peekInputChar(1))))) {
+
+ // Negative number. 'c' is still the first digit, and is
+ // the next input char.
+
+ goto numLabel;
+ }
+
+ // plain -
+ return t;
+
+ case '+': // positive number, +, ++, or +=
+ SETUP_SYMBOL(c);
+
+ switch (c) {
+ case '+': // ++
+ case '=': // +=
+ t._string += c;
+ eatInputChar();
+ return t;
+ }
+
+ if (options.signedNumbers
+ && (isDigit(c) || (c == '.' && isDigit(peekInputChar(1))))) {
+
+ // Positive number. 'c' is still the first digit, and is
+ // the next input char.
+
+ goto numLabel;
+ }
+
+ return t;
+
+ case ':': // : or :: or ::> or ::= or := or :>
+ SETUP_SYMBOL(c);
+
+ if (c == ':') {
+ t._string += c;
+ eatInputChar();
+
+ if (options.proofSymbols) {
+ c = peekInputChar(0);
+
+ if ((c == '>') || (c == '=')) {
+ t._string += c;
+ eatInputChar();
+ }
+ }
+ }
+ else if (options.proofSymbols && (c == '=' || c == '>')) {
+ t._string += c;
+ eatInputChar();
+ }
+ return t;
+
+ case '=': // = or == or =>
+ SETUP_SYMBOL(c);
+
+ if (c == '=') {
+ t._string += c;
+ eatInputChar();
+ return t;
+ } else if (options.proofSymbols && (c == '>')) {
+ t._string += c;
+ eatInputChar();
+ return t;
+ }
+ return t;
+
+ case '*': // * or *=
+ case '/': // / or /=
+ case '!': // ! or !=
+ case '~': // ~ or ~=
+ case '^': // ^ or ^=
+ SETUP_SYMBOL(c);
+
+ if (c == '=') {
+ t._string += c;
+ eatInputChar();
+ return t;
+ }
+ return t;
+
+ case '>': // >, >>,or >=
+ case '<': // <<, <<, or <= or <- or <:
+ case '|': // ||, ||, or |= or |-
+ case '&': // &, &&, or &=
+ {
+ int orig_c = c;
+ SETUP_SYMBOL(c);
+
+ if ((c == '=') || (orig_c == c)) {
+ t._string += c;
+ eatInputChar();
+ return t;
+ } else if (options.proofSymbols) {
+ if ((orig_c == '<') && (c == '-')) {
+ t._string += c;
+ eatInputChar();
+ } else if ((orig_c == '|') && (c == '-')) {
+ t._string += c;
+ eatInputChar();
+ } else if ((orig_c == '<') && (c == ':')) {
+ t._string += c;
+
+ c = eatAndPeekInputChar();
+
+ if (c == ':') {
+ t._string += c;
+ eatInputChar();
+ }
+ }
+ }
+ }
+ return t;
+
+ case '\\': // backslash or escaped comment char.
+ SETUP_SYMBOL(c);
+
+ if ((options.otherCommentCharacter != '\0'
+ && c == options.otherCommentCharacter)
+ || (options.otherCommentCharacter2 != '\0'
+ && c == options.otherCommentCharacter2)) {
+
+ // escaped comment character. Return the raw comment
+ // char (no backslash).
+
+ t._string = c;
+ eatInputChar();
+ return t;
+ }
+ return t;
+
+ case '.': // number, ., .., or ...
+ if (isDigit(peekInputChar(1))) {
+ // We're parsing a float that began without a leading zero
+ goto numLabel;
+ }
+
+ SETUP_SYMBOL(c);
+
+ if (c == '.') { // .. or ...
+ t._string += c;
+ c = eatAndPeekInputChar();
+
+ if (c == '.') { // ...
+ t._string += c;
+ eatInputChar();
+ }
+ return t;
+ }
+
+ return t;
+
+ } // switch (c)
+
+#undef SETUP_SYMBOL
+
+numLabel:
+ if (isDigit(c) || (c == '.')) {
+
+ // A number. Note-- single dots have been
+ // parsed already, so a . indicates a number
+ // less than 1 in floating point form.
+
+ // [0-9]*(\.[0-9][f]) or [0-9]+ or 0x[0-9,A-F]+
+
+ if (t._string != "-") {
+ // If we picked up a leading "-" sign above, keep it,
+ // otherwise drop the string parsed thus far
+ t._string = "";
+ }
+ t._type = Token::NUMBER;
+ if (c == '.') {
+ t._extendedType = Token::FLOATING_POINT_TYPE;
+ } else {
+ t._extendedType = Token::INTEGER_TYPE;
+ }
+
+ if ((c == '0') && (peekInputChar(1) == 'x')) {
+ // Hex number
+ t._string += "0x";
+
+ // skip the 0x
+ eatInputChar();
+ eatInputChar();
+
+ c = peekInputChar();
+ while (isDigit(c) || ((c >= 'A') && (c <= 'F')) || ((c >= 'a') && (c <= 'f'))) {
+ t._string += c;
+ c = eatAndPeekInputChar();
+ }
+
+ } else {
+ // Non-hex number
+
+ // Read the part before the decimal.
+ while (isDigit(c)) {
+ t._string += c;
+ c = eatAndPeekInputChar();
+ }
+
+ // True if we are reading a floating-point special type
+ bool isSpecial = false;
+
+ // Read the decimal, if one exists
+ if (c == '.') {
+ t._extendedType = Token::FLOATING_POINT_TYPE;
+
+ // The '.' character was a decimal point, not the start of a
+ // method or range operator
+ t._string += c;
+ c = eatAndPeekInputChar();
+
+ // Floating point specials (msvc format only)
+ if (options.msvcSpecials && (c == '#')) {
+ isSpecial = true;
+ // We are reading a floating point special value
+ // of the form -1.#IND00, -1.#INF00, or 1.#INF00
+ c = eatAndPeekInputChar();
+ char test = c;
+ if (! options.caseSensitive) {
+ test = toupper(c);
+ }
+ if (test != 'I') {
+ throw BadMSVCSpecial
+ (
+ "Incorrect floating-point special (inf or nan) "
+ "format.",
+ t.line(), charNumber);
+ }
+ c = eatAndPeekInputChar();
+ test = c;
+ if (! options.caseSensitive) {
+ test = toupper(c);
+ }
+ if (test != 'N') {
+ throw BadMSVCSpecial
+ (
+ "Incorrect floating-point special (inf or nan) "
+ "format.",
+ t.line(), charNumber);
+ }
+ t._string += "#IN";
+ c = eatAndPeekInputChar();
+ test = c;
+ if (! options.caseSensitive) {
+ test = toupper(c);
+ }
+ if ((test != 'F') && (test != 'D')) {
+ throw BadMSVCSpecial
+ (
+ "Incorrect floating-point special (inf or nan) "
+ "format.",
+ t.line(), charNumber);
+ }
+ t._string += c;
+ for (int j = 0; j < 2; ++j) {
+ c = eatAndPeekInputChar();
+ if (c != '0') {
+ throw BadMSVCSpecial
+ (
+ "Incorrect floating-point special (inf or"
+ "nan) format.",
+ t.line(), charNumber);
+ }
+ t._string += (char)c;
+ }
+
+ } else {
+
+ // Read the part after the decimal
+ while (isDigit((char)c)) {
+ t._string += (char)c;
+ c = eatAndPeekInputChar();
+ }
+ }
+ }
+
+ if (! isSpecial && ((c == 'e') || (c == 'E'))) {
+ // Read exponent
+ t._extendedType = Token::FLOATING_POINT_TYPE;
+ t._string += c;
+
+ c = eatAndPeekInputChar();
+ if ((c == '-') || (c == '+')) {
+ t._string += c;
+ c = eatAndPeekInputChar();
+ }
+
+ while (isDigit(c)) {
+ t._string += c;
+ c = eatAndPeekInputChar();
+ }
+ }
+
+ if (! isSpecial && (t._extendedType == Token::FLOATING_POINT_TYPE) && (c == 'f')) {
+ // Trailing f on a float
+ t._string += c;
+ c = eatAndPeekInputChar();
+ }
+ }
+ return t;
+
+ } else if (isLetter(c) || (c == '_')) {
+ // Identifier or keyword
+ // [A-Za-z_][A-Za-z_0-9]*
+
+ t._type = Token::SYMBOL;
+ t._extendedType = Token::SYMBOL_TYPE;
+ t._string = "";
+ do {
+ t._string += c;
+ c = eatAndPeekInputChar();
+ } while (isLetter(c) || isDigit(c) || (c == '_'));
+
+ // See if this symbol is actually a boolean
+ if ((options.trueSymbols.size() > 0) || (options.falseSymbols.size() > 0)) {
+ std::string str = t._string;
+ if (! options.caseSensitive) {
+ str = toUpper(str);
+ }
+ if (options.trueSymbols.contains(str)) {
+ t._type = Token::BOOLEAN;
+ t._extendedType = Token::BOOLEAN_TYPE;
+ t._bool = true;
+ } else if (options.falseSymbols.contains(str)) {
+ t._type = Token::BOOLEAN;
+ t._extendedType = Token::BOOLEAN_TYPE;
+ t._bool = false;
+ }
+ }
+
+ return t;
+
+ } else if (c == '\"') {
+
+ // Discard the double-quote.
+ eatInputChar();
+
+ // Double quoted string
+ parseQuotedString('\"', t);
+ return t;
+
+ } else if (c == options.singleQuoteCharacter) {
+
+ // Discard the single-quote.
+ eatInputChar();
+
+ if (options.singleQuotedStrings) {
+ // Single quoted string
+ parseQuotedString(options.singleQuoteCharacter, t);
+ } else {
+ t._string = c;
+ t._type = Token::SYMBOL;
+ t._extendedType = Token::SYMBOL_TYPE;
+ }
+ return t;
+
+ } // end of special case tokens
+
+ if (c == EOF) {
+ t._type = Token::END;
+ t._extendedType = Token::END_TYPE;
+ t._string = "";
+ return t;
+ }
+
+ // Some unknown token
+ debugAssertM(false,
+ format("Unrecognized token type beginning with character '%c' (ASCII %d)",
+ c, c));
+ return t;
+}
+
+
+void TextInput::parseQuotedString(unsigned char delimiter, Token& t) {
+
+ t._type = Token::STRING;
+
+ if (delimiter == options.singleQuoteCharacter) {
+ t._extendedType = Token::SINGLE_QUOTED_TYPE;
+ } else {
+ t._extendedType = Token::DOUBLE_QUOTED_TYPE;
+ }
+
+ while (true) {
+ // We're definitely going to consume the next input char, so we get
+ // it right now. This makes the condition handling below a bit easier.
+ int c = eatInputChar();
+
+ if (c == EOF) {
+ // END inside a quoted string. (We finish the string.)
+ break;
+ }
+
+ if (options.escapeSequencesInStrings && (c == '\\')) {
+ // An escaped character. We're definitely going to consume it,
+ // so we get it (and consume it) now.
+
+ c = eatInputChar();
+
+ switch (c) {
+ case 'r':
+ t._string += '\r';
+ break;
+ case 'n':
+ t._string += '\n';
+ break;
+ case 't':
+ t._string += '\t';
+ break;
+ case '0':
+ t._string += '\0';
+ break;
+
+ case '\\':
+ case '\"':
+ t._string += (char)c;
+ break;
+
+ default:
+ if (c == options.singleQuoteCharacter) {
+ t._string += (char)c;
+ break;
+ }
+
+ if (((c == options.otherCommentCharacter) &&
+ (options.otherCommentCharacter != '\0')) ||
+ ((c == options.otherCommentCharacter2) &&
+ (options.otherCommentCharacter2 != '\0'))) {
+ t._string += c;
+ }
+ // otherwise, some illegal escape sequence; skip it.
+ break;
+
+ } // switch
+
+ } else if (c == delimiter) {
+ // End of the string. Already consumed the character.
+ break;
+ } else {
+ // All other chars, go on to the string. Already consumed the
+ // character.
+ t._string += (char)c;
+ }
+
+ }
+}
+
+bool TextInput::readBoolean() {
+ Token t(read());
+
+ if (t._type == Token::BOOLEAN) {
+ return t.boolean();
+ }
+
+ // Push initial token back, and throw an error. We intentionally
+ // indicate that the wrong type is the type of the initial token.
+ // Logically, the number started there.
+ push(t);
+ throw WrongTokenType(options.sourceFileName, t.line(), t.character(),
+ Token::BOOLEAN, t._type);
+}
+
+double TextInput::readNumber() {
+ Token t(read());
+
+ if (t._type == Token::NUMBER) {
+ return t.number();
+ }
+
+ // Even if signedNumbers is disabled, readNumber attempts to
+ // read a signed number, so we handle that case here.
+ if (! options.signedNumbers
+ && (t._type == Token::SYMBOL)
+ && ((t._string == "-")
+ || (t._string == "+"))) {
+
+ Token t2(read());
+
+ if ((t2._type == Token::NUMBER)
+ && (t2._character == t._character + 1)) {
+
+ if (t._string == "-") {
+ return -t2.number();
+ } else {
+ return t2.number();
+ }
+ }
+
+ // push back the second token.
+ push(t2);
+ }
+
+ // Push initial token back, and throw an error. We intentionally
+ // indicate that the wrong type is the type of the initial token.
+ // Logically, the number started there.
+ push(t);
+ throw WrongTokenType(options.sourceFileName, t.line(), t.character(),
+ Token::NUMBER, t._type);
+}
+
+
+Token TextInput::readStringToken() {
+ Token t(read());
+
+ if (t._type == Token::STRING) { // fast path
+ return t;
+ }
+
+ push(t);
+ throw WrongTokenType(options.sourceFileName, t.line(), t.character(),
+ Token::STRING, t._type);
+}
+
+std::string TextInput::readString() {
+ return readStringToken()._string;
+}
+
+void TextInput::readString(const std::string& s) {
+ Token t(readStringToken());
+
+ if (t._string == s) { // fast path
+ return;
+ }
+
+ push(t);
+ throw WrongString(options.sourceFileName, t.line(), t.character(),
+ s, t._string);
+}
+
+Token TextInput::readCommentToken() {
+ Token t(read());
+
+ if (t._type == Token::COMMENT) { // fast path
+ return t;
+ }
+
+ push(t);
+ throw WrongTokenType(options.sourceFileName, t.line(), t.character(),
+ Token::COMMENT, t._type);
+}
+
+std::string TextInput::readComment() {
+ return readCommentToken()._string;
+}
+
+void TextInput::readComment(const std::string& s) {
+ Token t(readCommentToken());
+
+ if (t._string == s) { // fast path
+ return;
+ }
+
+ push(t);
+ throw WrongString(options.sourceFileName, t.line(), t.character(),
+ s, t._string);
+}
+
+Token TextInput::readNewlineToken() {
+ Token t(read());
+
+ if (t._type == Token::NEWLINE) { // fast path
+ return t;
+ }
+
+ push(t);
+ throw WrongTokenType(options.sourceFileName, t.line(), t.character(),
+ Token::NEWLINE, t._type);
+}
+
+std::string TextInput::readNewline() {
+ return readNewlineToken()._string;
+}
+
+void TextInput::readNewline(const std::string& s) {
+ Token t(readNewlineToken());
+
+ if (t._string == s) { // fast path
+ return;
+ }
+
+ push(t);
+ throw WrongString(options.sourceFileName, t.line(), t.character(),
+ s, t._string);
+}
+
+Token TextInput::readSymbolToken() {
+ Token t(read());
+
+ if (t._type == Token::SYMBOL) { // fast path
+ return t;
+ }
+
+ push(t);
+ throw WrongTokenType(options.sourceFileName, t.line(), t.character(),
+ Token::SYMBOL, t._type);
+}
+
+
+std::string TextInput::readSymbol() {
+ return readSymbolToken()._string;
+}
+
+void TextInput::readSymbol(const std::string& symbol) {
+ Token t(readSymbolToken());
+
+ if (t._string == symbol) { // fast path
+ return;
+ }
+
+ push(t);
+ throw WrongSymbol(options.sourceFileName, t.line(), t.character(),
+ symbol, t._string);
+}
+
+
+TextInput::TextInput(const std::string& filename, const Settings& opt) : options(opt) {
+ init();
+ std::string input = readWholeFile(filename);
+
+ if (options.sourceFileName.empty()) {
+ options.sourceFileName = filename;
+ }
+ int n = input.size();
+ buffer.resize(n);
+ System::memcpy(buffer.getCArray(), input.c_str(), n);
+}
+
+
+TextInput::TextInput(FS fs, const std::string& str, const Settings& opt) : options(opt) {
+ (void)fs;
+ init();
+ if (options.sourceFileName.empty()) {
+ if (str.length() < 14) {
+ options.sourceFileName = std::string("\"") + str + "\"";
+ } else {
+ options.sourceFileName = std::string("\"") + str.substr(0, 10) + "...\"";
+ }
+ }
+ buffer.resize(str.length()); // we don't bother copying trailing NUL.
+ System::memcpy(buffer.getCArray(), str.c_str(), buffer.size());
+}
+
+
+const std::string& TextInput::filename() const {
+ return options.sourceFileName;
+}
+
+///////////////////////////////////////////////////////////////////////////////////
+
+TextInput::TokenException::TokenException(
+ const std::string& src,
+ int ln,
+ int ch) : ParseError(src, ln, ch, format("%s(%d) : ", src.c_str(), ln)),
+ sourceFile(src) {
+}
+
+///////////////////////////////////////////////////////////////////////////////////
+
+static const char* tokenTypeToString(Token::Type t) {
+ switch (t) {
+ case Token::SYMBOL:
+ return "Token::SYMBOL";
+ case Token::STRING:
+ return "Token::STRING";
+ case Token::NUMBER:
+ return "Token::NUMBER";
+ case Token::END:
+ return "Token::END";
+ default:
+ debugAssertM(false, "Fell through switch");
+ return "?";
+ }
+}
+
+TextInput::WrongTokenType::WrongTokenType(
+ const std::string& src,
+ int ln,
+ int ch,
+ Token::Type e,
+ Token::Type a) :
+ TokenException(src, ln, ch), expected(e), actual(a) {
+
+ message += format("Expected token of type %s, found type %s.",
+ tokenTypeToString(e), tokenTypeToString(a));
+}
+
+
+TextInput::BadMSVCSpecial::BadMSVCSpecial(
+ const std::string& src,
+ int ln,
+ int ch) :
+ TokenException(src, ln, ch) {
+}
+
+
+TextInput::WrongSymbol::WrongSymbol(
+ const std::string& src,
+ int ln,
+ int ch,
+ const std::string& e,
+ const std::string& a) :
+ TokenException(src, ln, ch), expected(e), actual(a) {
+
+ message += format("Expected symbol '%s', found symbol '%s'.",
+ e.c_str(), a.c_str());
+}
+
+
+TextInput::WrongString::WrongString(
+ const std::string& src,
+ int ln,
+ int ch,
+ const std::string& e,
+ const std::string& a) :
+ TokenException(src, ln, ch), expected(e), actual(a) {
+
+ message += format("Expected string '%s', found string '%s'.",
+ e.c_str(), a.c_str());
+}
+
+
+void deserialize(bool& b, TextInput& ti) {
+ b = ti.readSymbol() == "true";
+}
+
+
+void deserialize(int& b, TextInput& ti) {
+ b = iRound(ti.readNumber());
+}
+
+
+void deserialize(uint8& b, TextInput& ti) {
+ b = (uint8)iRound(ti.readNumber());
+}
+
+
+void deserialize(double& b, TextInput& ti) {
+ b = ti.readNumber();
+}
+
+
+void deserialize(float& b, TextInput& ti) {
+ b = (float)ti.readNumber();
+}
+
+} // namespace
+
+#ifdef _MSC_VER
+# pragma warning (pop)
+#endif
diff --git a/dep/src/g3dlite/TextOutput.cpp b/dep/src/g3dlite/TextOutput.cpp
new file mode 100644
index 00000000000..11347252eba
--- /dev/null
+++ b/dep/src/g3dlite/TextOutput.cpp
@@ -0,0 +1,452 @@
+/**
+ @file TextOutput.cpp
+
+ @maintainer Morgan McGuire, http://graphics.cs.williams.edu
+ @created 2004-06-21
+ @edited 2006-08-14
+
+ Copyright 2000-2006, Morgan McGuire.
+ All rights reserved.
+ */
+
+#include "G3D/TextOutput.h"
+#include "G3D/Log.h"
+#include "G3D/fileutils.h"
+
+namespace G3D {
+
+TextOutput::TextOutput(const TextOutput::Settings& opt) :
+ startingNewLine(true),
+ currentColumn(0),
+ inDQuote(false),
+ filename(""),
+ indentLevel(0)
+{
+ setOptions(opt);
+}
+
+
+TextOutput::TextOutput(const std::string& fil, const TextOutput::Settings& opt) :
+ startingNewLine(true),
+ currentColumn(0),
+ inDQuote(false),
+ filename(fil),
+ indentLevel(0)
+{
+
+ setOptions(opt);
+}
+
+
+void TextOutput::setIndentLevel(int i) {
+ indentLevel = i;
+
+ // If there were more pops than pushes, don't let that take us below 0 indent.
+ // Don't ever indent more than the number of columns.
+ indentSpaces =
+ iClamp(option.spacesPerIndent * indentLevel,
+ 0,
+ option.numColumns - 1);
+}
+
+
+void TextOutput::setOptions(const Settings& _opt) {
+ option = _opt;
+
+ debugAssert(option.numColumns > 1);
+
+ setIndentLevel(indentLevel);
+
+ newline = (option.newlineStyle == Settings::NEWLINE_WINDOWS) ? "\r\n" : "\n";
+}
+
+
+void TextOutput::pushIndent() {
+ setIndentLevel(indentLevel + 1);
+}
+
+
+void TextOutput::popIndent() {
+ setIndentLevel(indentLevel - 1);
+}
+
+
+static std::string escape(const std::string& string) {
+ std::string result = "";
+
+ for (std::string::size_type i = 0; i < string.length(); ++i) {
+ char c = string.at(i);
+ switch (c) {
+ case '\0':
+ result += "\\0";
+ break;
+
+ case '\r':
+ result += "\\r";
+ break;
+
+ case '\n':
+ result += "\\n";
+ break;
+
+ case '\t':
+ result += "\\t";
+ break;
+
+ case '\\':
+ result += "\\\\";
+ break;
+
+ default:
+ result += c;
+ }
+ }
+
+ return result;
+}
+
+void TextOutput::writeString(const std::string& string) {
+ // Convert special characters to escape sequences
+ this->printf("\"%s\"", escape(string).c_str());
+}
+
+
+void TextOutput::writeBoolean(bool b) {
+ this->printf("%s ", b ? option.trueSymbol.c_str() : option.falseSymbol.c_str());
+}
+
+void TextOutput::writeNumber(double n) {
+ this->printf("%f ", n);
+}
+
+
+void TextOutput::writeNumber(int n) {
+ this->printf("%d ", n);
+}
+
+
+void TextOutput::writeSymbol(const std::string& string) {
+ if (string.size() > 0) {
+ // TODO: check for legal symbols?
+ this->printf("%s ", string.c_str());
+ }
+}
+
+void TextOutput::writeSymbols(
+ const std::string& a,
+ const std::string& b,
+ const std::string& c,
+ const std::string& d,
+ const std::string& e,
+ const std::string& f) {
+
+ writeSymbol(a);
+ writeSymbol(b);
+ writeSymbol(c);
+ writeSymbol(d);
+ writeSymbol(e);
+ writeSymbol(f);
+}
+
+
+void TextOutput::printf(const std::string formatString, ...) {
+ va_list argList;
+ va_start(argList, formatString);
+ this->vprintf(formatString.c_str(), argList);
+ va_end(argList);
+}
+
+
+void TextOutput::printf(const char* formatString, ...) {
+ va_list argList;
+ va_start(argList, formatString);
+ this->vprintf(formatString, argList);
+ va_end(argList);
+}
+
+
+void TextOutput::convertNewlines(const std::string& in, std::string& out) {
+ // TODO: can be significantly optimized in cases where
+ // single characters are copied in order by walking through
+ // the array and copying substrings as needed.
+
+ if (option.convertNewlines) {
+ out = "";
+ for (uint32 i = 0; i < in.size(); ++i) {
+ if (in[i] == '\n') {
+ // Unix newline
+ out += newline;
+ } else if ((in[i] == '\r') && (i + 1 < in.size()) && (in[i + 1] == '\n')) {
+ // Windows newline
+ out += newline;
+ ++i;
+ } else {
+ out += in[i];
+ }
+ }
+ } else {
+ out = in;
+ }
+}
+
+
+void TextOutput::writeNewline() {
+ for (uint32 i = 0; i < newline.size(); ++i) {
+ indentAppend(newline[i]);
+ }
+}
+
+
+void TextOutput::writeNewlines(int numLines) {
+ for (int i = 0; i < numLines; ++i) {
+ writeNewline();
+ }
+}
+
+
+void TextOutput::wordWrapIndentAppend(const std::string& str) {
+ // TODO: keep track of the last space character we saw so we don't
+ // have to always search.
+
+ if ((option.wordWrap == Settings::WRAP_NONE) ||
+ (currentColumn + (int)str.size() <= option.numColumns)) {
+ // No word-wrapping is needed
+
+ // Add one character at a time.
+ // TODO: optimize for strings without newlines to add multiple
+ // characters.
+ for (uint32 i = 0; i < str.size(); ++i) {
+ indentAppend(str[i]);
+ }
+ return;
+ }
+
+ // Number of columns to wrap against
+ int cols = option.numColumns - indentSpaces;
+
+ // Copy forward until we exceed the column size,
+ // and then back up and try to insert newlines as needed.
+ for (uint32 i = 0; i < str.size(); ++i) {
+
+ indentAppend(str[i]);
+ if ((str[i] == '\r') && (i + 1 < str.size()) && (str[i + 1] == '\n')) {
+ // \r\n, we need to hit the \n to enter word wrapping.
+ ++i;
+ indentAppend(str[i]);
+ }
+
+ if (currentColumn >= cols) {
+ debugAssertM(str[i] != '\n' && str[i] != '\r',
+ "Should never enter word-wrapping on a newline character");
+
+ // True when we're allowed to treat a space as a space.
+ bool unquotedSpace = option.allowWordWrapInsideDoubleQuotes || ! inDQuote;
+
+ // Cases:
+ //
+ // 1. Currently in a series of spaces that ends with a newline
+ // strip all spaces and let the newline
+ // flow through.
+ //
+ // 2. Currently in a series of spaces that does not end with a newline
+ // strip all spaces and replace them with single newline
+ //
+ // 3. Not in a series of spaces
+ // search backwards for a space, then execute case 2.
+
+ // Index of most recent space
+ uint32 lastSpace = data.size() - 1;
+
+ // How far back we had to look for a space
+ uint32 k = 0;
+ uint32 maxLookBackward = currentColumn - indentSpaces;
+
+ // Search backwards (from current character), looking for a space.
+ while ((k < maxLookBackward) &&
+ (lastSpace > 0) &&
+ (! ((data[lastSpace] == ' ') && unquotedSpace))) {
+ --lastSpace;
+ ++k;
+
+ if ((data[lastSpace] == '\"') && !option.allowWordWrapInsideDoubleQuotes) {
+ unquotedSpace = ! unquotedSpace;
+ }
+ }
+
+ if (k == maxLookBackward) {
+ // We couldn't find a series of spaces
+
+ if (option.wordWrap == Settings::WRAP_ALWAYS) {
+ // Strip the last character we wrote, force a newline,
+ // and replace the last character;
+ data.pop();
+ writeNewline();
+ indentAppend(str[i]);
+ } else {
+ // Must be Settings::WRAP_WITHOUT_BREAKING
+ //
+ // Don't write the newline; we'll come back to
+ // the word wrap code after writing another character
+ }
+ } else {
+ // We found a series of spaces. If they continue
+ // to the new string, strip spaces off both. Otherwise
+ // strip spaces from data only and insert a newline.
+
+ // Find the start of the spaces. firstSpace is the index of the
+ // first non-space, looking backwards from lastSpace.
+ uint32 firstSpace = lastSpace;
+ while ((k < maxLookBackward) &&
+ (firstSpace > 0) &&
+ (data[firstSpace] == ' ')) {
+ --firstSpace;
+ ++k;
+ }
+
+ if (k == maxLookBackward) {
+ ++firstSpace;
+ }
+
+ if (lastSpace == (uint32)data.size() - 1) {
+ // Spaces continued up to the new string
+ data.resize(firstSpace + 1);
+ writeNewline();
+
+ // Delete the spaces from the new string
+ while ((i < str.size() - 1) && (str[i + 1] == ' ')) {
+ ++i;
+ }
+ } else {
+ // Spaces were somewhere in the middle of the old string.
+ // replace them with a newline.
+
+ // Copy over the characters that should be saved
+ Array<char> temp;
+ for (uint32 j = lastSpace + 1; j < (uint32)data.size(); ++j) {
+ char c = data[j];
+
+ if (c == '\"') {
+ // Undo changes to quoting (they will be re-done
+ // when we paste these characters back on).
+ inDQuote = !inDQuote;
+ }
+ temp.append(c);
+ }
+
+ // Remove those characters and replace with a newline.
+ data.resize(firstSpace + 1);
+ writeNewline();
+
+ // Write them back
+ for (uint32 j = 0; j < (uint32)temp.size(); ++j) {
+ indentAppend(temp[j]);
+ }
+
+ // We are now free to continue adding from the
+ // new string, which may or may not begin with spaces.
+
+ } // if spaces included new string
+ } // if hit indent
+ } // if line exceeded
+ } // iterate over str
+}
+
+
+void TextOutput::indentAppend(char c) {
+
+ if (startingNewLine) {
+ for (int j = 0; j < indentSpaces; ++j) {
+ data.push(' ');
+ }
+ startingNewLine = false;
+ currentColumn = indentSpaces;
+ }
+
+ data.push(c);
+
+ // Don't increment the column count on return character
+ // newline is taken care of below.
+ if (c != '\r') {
+ ++currentColumn;
+ }
+
+ if (c == '\"') {
+ inDQuote = ! inDQuote;
+ }
+
+ startingNewLine = (c == '\n');
+ if (startingNewLine) {
+ currentColumn = 0;
+ }
+}
+
+
+void TextOutput::vprintf(const char* formatString, va_list argPtr) {
+ std::string str = vformat(formatString, argPtr);
+
+ std::string clean;
+ convertNewlines(str, clean);
+ wordWrapIndentAppend(clean);
+}
+
+
+void TextOutput::commit(bool flush) {
+ std::string p = filenamePath(filename);
+ if (! fileExists(p, false)) {
+ createDirectory(p);
+ }
+
+ FILE* f = fopen(filename.c_str(), "wb");
+ debugAssertM(f, "Could not open \"" + filename + "\"");
+ fwrite(data.getCArray(), 1, data.size(), f);
+ if (flush) {
+ fflush(f);
+ }
+ fclose(f);
+}
+
+
+void TextOutput::commitString(std::string& out) {
+ // Null terminate
+ data.push('\0');
+ out = data.getCArray();
+ data.pop();
+}
+
+
+std::string TextOutput::commitString() {
+ std::string str;
+ commitString(str);
+ return str;
+}
+
+
+
+/////////////////////////////////////////////////////////////////////
+
+void serialize(const float& b, TextOutput& to) {
+ to.writeNumber(b);
+}
+
+
+void serialize(const bool& b, TextOutput& to) {
+ to.writeSymbol(b ? "true" : "false");
+}
+
+
+void serialize(const int& b, TextOutput& to) {
+ to.writeNumber(b);
+}
+
+
+void serialize(const uint8& b, TextOutput& to) {
+ to.writeNumber(b);
+}
+
+
+void serialize(const double& b, TextOutput& to) {
+ to.writeNumber(b);
+}
+
+
+}
diff --git a/dep/src/g3dlite/ThreadSet.cpp b/dep/src/g3dlite/ThreadSet.cpp
new file mode 100644
index 00000000000..ee3895fe9de
--- /dev/null
+++ b/dep/src/g3dlite/ThreadSet.cpp
@@ -0,0 +1,166 @@
+#include "G3D/ThreadSet.h"
+
+namespace G3D {
+
+int ThreadSet::size() const {
+ ThreadSet* me = const_cast<ThreadSet*>(this);
+ me->m_lock.lock();
+ int s = m_thread.size();
+ me->m_lock.unlock();
+ return s;
+}
+
+
+int ThreadSet::numStarted() const {
+ ThreadSet* me = const_cast<ThreadSet*>(this);
+ me->m_lock.lock();
+ int count = 0;
+ for (int i = 0; i < m_thread.size(); ++i) {
+ if (m_thread[i]->started()) {
+ ++count;
+ }
+ }
+ me->m_lock.unlock();
+ return count;
+}
+
+
+void ThreadSet::start(GThread::SpawnBehavior lastBehavior) const {
+ ThreadSet* me = const_cast<ThreadSet*>(this);
+
+ Array<GThreadRef> unstarted;
+ me->m_lock.lock();
+ // Find the unstarted threads
+ for (int i = 0; i < m_thread.size(); ++i) {
+ if (! m_thread[i]->started()) {
+ unstarted.append(m_thread[i]);
+ }
+ }
+
+ int last = unstarted.size();
+ if (lastBehavior == GThread::USE_CURRENT_THREAD) {
+ // Save the last unstarted for the current thread
+ --last;
+ }
+
+ for (int i = 0; i < last; ++i) {
+ unstarted[i]->start(GThread::USE_NEW_THREAD);
+ }
+
+ me->m_lock.unlock();
+
+ // Start the last one on my thread
+ if ((unstarted.size() > 0) && (lastBehavior == GThread::USE_CURRENT_THREAD)) {
+ unstarted.last()->start(GThread::USE_CURRENT_THREAD);
+ }
+}
+
+
+void ThreadSet::terminate() const {
+ ThreadSet* me = const_cast<ThreadSet*>(this);
+ me->m_lock.lock();
+ for (int i = 0; i < m_thread.size(); ++i) {
+ if (m_thread[i]->started()) {
+ m_thread[i]->terminate();
+ }
+ }
+ me->m_lock.unlock();
+}
+
+
+void ThreadSet::waitForCompletion() const {
+ ThreadSet* me = const_cast<ThreadSet*>(this);
+ me->m_lock.lock();
+ for (int i = 0; i < m_thread.size(); ++i) {
+ if (m_thread[i]->started()) {
+ m_thread[i]->waitForCompletion();
+ }
+ }
+ me->m_lock.unlock();
+}
+
+
+int ThreadSet::removeCompleted() {
+ m_lock.lock();
+ for (int i = 0; i < m_thread.size(); ++i) {
+ if (m_thread[i]->completed()) {
+ m_thread.fastRemove(i);
+ --i;
+ }
+ }
+
+ int s = m_thread.size();
+ m_lock.unlock();
+ return s;
+}
+
+
+void ThreadSet::clear() {
+ m_lock.lock();
+ m_thread.clear();
+ m_lock.unlock();
+}
+
+
+int ThreadSet::insert(const ThreadRef& t) {
+ m_lock.lock();
+ bool found = false;
+ for (int i = 0; i < m_thread.size() && ! found; ++i) {
+ found = (m_thread[i] == t);
+ }
+ if (! found) {
+ m_thread.append(t);
+ }
+ int s = m_thread.size();
+ m_lock.unlock();
+ return s;
+}
+
+
+bool ThreadSet::remove(const ThreadRef& t) {
+ m_lock.lock();
+ bool found = false;
+ for (int i = 0; i < m_thread.size() && ! found; ++i) {
+ found = (m_thread[i] == t);
+ if (found) {
+ m_thread.fastRemove(i);
+ }
+ }
+ m_lock.unlock();
+ return found;
+}
+
+
+bool ThreadSet::contains(const ThreadRef& t) const {
+ ThreadSet* me = const_cast<ThreadSet*>(this);
+ me->m_lock.lock();
+ bool found = false;
+ for (int i = 0; i < m_thread.size() && ! found; ++i) {
+ found = (m_thread[i] == t);
+ }
+ me->m_lock.unlock();
+ return found;
+}
+
+
+ThreadSet::Iterator ThreadSet::begin() {
+ return m_thread.begin();
+}
+
+
+ThreadSet::Iterator ThreadSet::end() {
+ return m_thread.end();
+}
+
+
+ThreadSet::ConstIterator ThreadSet::begin() const {
+ return m_thread.begin();
+}
+
+
+ThreadSet::ConstIterator ThreadSet::end() const {
+ return m_thread.end();
+}
+
+
+} // namespace G3D
diff --git a/dep/src/g3dlite/Triangle.cpp b/dep/src/g3dlite/Triangle.cpp
index 2e221b12f1d..253438ad5fb 100644
--- a/dep/src/g3dlite/Triangle.cpp
+++ b/dep/src/g3dlite/Triangle.cpp
@@ -1,22 +1,27 @@
/**
@file Triangle.cpp
-
- @maintainer Morgan McGuire, graphics3d.com
-
+
+ @maintainer Morgan McGuire, http://graphics.cs.williams.edu
+
@created 2001-04-06
- @edited 2006-01-20
+ @edited 2008-12-28
- Copyright 2000-2006, Morgan McGuire.
+ Copyright 2000-2009, Morgan McGuire.
All rights reserved.
*/
#include "G3D/platform.h"
#include "G3D/Triangle.h"
#include "G3D/Plane.h"
+#include "G3D/BinaryInput.h"
+#include "G3D/BinaryOutput.h"
+#include "G3D/debugAssert.h"
#include "G3D/AABox.h"
+#include "G3D/Ray.h"
namespace G3D {
+
void Triangle::init(const Vector3& v0, const Vector3& v1, const Vector3& v2) {
_plane = Plane(v0, v1, v2);
@@ -27,8 +32,8 @@ void Triangle::init(const Vector3& v0, const Vector3& v1, const Vector3& 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();
+ const Vector3& e = _vertex[next[i]] - _vertex[i];
+ edgeMagnitude[i] = e.magnitude();
if (edgeMagnitude[i] == 0) {
edgeDirection[i] = Vector3::zero();
@@ -37,37 +42,64 @@ void Triangle::init(const Vector3& v0, const Vector3& v1, const Vector3& v2) {
}
}
- edge01 = _vertex[1] - _vertex[0];
- edge02 = _vertex[2] - _vertex[0];
+ _edge01 = _vertex[1] - _vertex[0];
+ _edge02 = _vertex[2] - _vertex[0];
_primaryAxis = _plane.normal().primaryAxis();
- _area = (float)edgeDirection[0].cross(edgeDirection[2]).magnitude() * (edgeMagnitude[0] * edgeMagnitude[2]);
-
+ _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() {
}
-double Triangle::area() const {
+
+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;
}
@@ -85,9 +117,10 @@ Vector3 Triangle::randomPoint() const {
s = 1.0f - s;
}
- return edge01 * s + edge02 * t + _vertex[0];
+ return _edge01 * s + _edge02 * t + _vertex[0];
}
+
void Triangle::getBounds(AABox& out) const {
Vector3 lo = _vertex[0];
Vector3 hi = lo;
@@ -100,5 +133,54 @@ void Triangle::getBounds(AABox& out) const {
out = AABox(lo, hi);
}
-} // G3D
+bool Triangle::intersect(const Ray& ray, float& distance, float baryCoord[3]) const {
+ static const float EPS = 1e-5f;
+
+ // See RTR2 ch. 13.7 for the algorithm.
+
+ const Vector3& e1 = edge01();
+ const Vector3& e2 = edge02();
+ const Vector3 p(ray.direction().cross(e2));
+ const float a = e1.dot(p);
+
+ if (abs(a) < EPS) {
+ // Determinant is ill-conditioned; abort early
+ return false;
+ }
+
+ const float f = 1.0f / a;
+ const Vector3 s(ray.origin() - vertex(0));
+ const float u = f * s.dot(p);
+
+ if ((u < 0.0f) || (u > 1.0f)) {
+ // We hit the plane of the m_geometry, but outside the m_geometry
+ return false;
+ }
+
+ const Vector3 q(s.cross(e1));
+ const float v = f * ray.direction().dot(q);
+
+ if ((v < 0.0f) || ((u + v) > 1.0f)) {
+ // We hit the plane of the triangle, but outside the triangle
+ return false;
+ }
+
+ const float t = f * e2.dot(q);
+
+ if ((t > 0.0f) && (t < distance)) {
+ // This is a new hit, closer than the previous one
+ distance = t;
+
+ baryCoord[0] = 1.0 - u - v;
+ baryCoord[1] = u;
+ baryCoord[2] = v;
+
+ return true;
+ } else {
+ // This hit is after the previous hit, so ignore it
+ return false;
+ }
+}
+
+} // G3D
diff --git a/dep/src/g3dlite/UprightFrame.cpp b/dep/src/g3dlite/UprightFrame.cpp
new file mode 100644
index 00000000000..c80264bf4e8
--- /dev/null
+++ b/dep/src/g3dlite/UprightFrame.cpp
@@ -0,0 +1,132 @@
+/**
+ @file UprightFrame.cpp
+ Box class
+
+ @maintainer Morgan McGuire, http://graphics.cs.williams.edu
+
+ @created 2007-05-02
+ @edited 2007-05-05
+*/
+
+#include "G3D/UprightFrame.h"
+#include "G3D/BinaryInput.h"
+#include "G3D/BinaryOutput.h"
+
+namespace G3D {
+
+UprightFrame::UprightFrame(const CoordinateFrame& cframe) {
+ Vector3 look = cframe.lookVector();
+
+ yaw = G3D::pi() + atan2(look.x, look.z);
+ pitch = asin(look.y);
+
+ translation = cframe.translation;
+}
+
+
+CoordinateFrame UprightFrame::toCoordinateFrame() const {
+ CoordinateFrame cframe;
+
+ Matrix3 P(Matrix3::fromAxisAngle(Vector3::unitX(), pitch));
+ Matrix3 Y(Matrix3::fromAxisAngle(Vector3::unitY(), yaw));
+
+ cframe.rotation = Y * P;
+ cframe.translation = translation;
+
+ return cframe;
+}
+
+
+UprightFrame UprightFrame::operator+(const UprightFrame& other) const {
+ return UprightFrame(translation + other.translation, pitch + other.pitch, yaw + other.yaw);
+}
+
+
+UprightFrame UprightFrame::operator*(const float k) const {
+ return UprightFrame(translation * k, pitch * k, yaw * k);
+}
+
+
+void UprightFrame::unwrapYaw(UprightFrame* a, int N) {
+ // Use the first point to establish the wrapping convention
+ for (int i = 1; i < N; ++i) {
+ const float prev = a[i - 1].yaw;
+ float& cur = a[i].yaw;
+
+ // No two angles should be more than pi (i.e., 180-degrees) apart.
+ if (abs(cur - prev) > G3D::pi()) {
+ // These angles must have wrapped at zero, causing them
+ // to be interpolated the long way.
+
+ // Find canonical [0, 2pi] versions of these numbers
+ float p = wrap(prev, twoPi());
+ float c = wrap(cur, twoPi());
+
+ // Find the difference -pi < diff < pi between the current and previous values
+ float diff = c - p;
+ if (diff < -G3D::pi()) {
+ diff += twoPi();
+ } else if (diff > G3D::pi()) {
+ diff -= twoPi();
+ }
+
+ // Offset the current from the previous by the difference
+ // between them.
+ cur = prev + diff;
+ }
+ }
+}
+
+
+void UprightFrame::serialize(class BinaryOutput& b) const {
+ translation.serialize(b);
+ b.writeFloat32(pitch);
+ b.writeFloat32(yaw);
+}
+
+
+void UprightFrame::deserialize(class BinaryInput& b) {
+ translation.deserialize(b);
+ pitch = b.readFloat32();
+ yaw = b.readFloat32();
+}
+
+
+void UprightSpline::serialize(class BinaryOutput& b) const {
+ b.writeBool8(cyclic);
+
+ b.writeInt32(control.size());
+ for (int i = 0; i < control.size(); ++i) {
+ control[i].serialize(b);
+ }
+ b.writeInt32(time.size());
+ for (int i = 0; i < time.size(); ++i) {
+ b.writeFloat32(time[i]);
+ }
+}
+
+
+void UprightSpline::deserialize(class BinaryInput& b) {
+ cyclic = b.readBool8();
+
+ control.resize(b.readInt32());
+ for (int i = 0; i < control.size(); ++i) {
+ control[i].deserialize(b);
+ }
+
+ if (b.hasMore()) {
+ time.resize(b.readInt32());
+ for (int i = 0; i < time.size(); ++i) {
+ time[i] = b.readFloat32();
+ }
+ debugAssert(time.size() == control.size());
+ } else {
+ // Import legacy path
+ time.resize(control.size());
+ for (int i = 0; i < time.size(); ++i) {
+ time[i] = i;
+ }
+ }
+}
+
+}
diff --git a/dep/src/g3dlite/Vector2.cpp b/dep/src/g3dlite/Vector2.cpp
new file mode 100644
index 00000000000..ec0737c3755
--- /dev/null
+++ b/dep/src/g3dlite/Vector2.cpp
@@ -0,0 +1,224 @@
+/**
+ @file Vector2.cpp
+
+ 2D vector class, used for texture coordinates primarily.
+
+ @maintainer Morgan McGuire, http://graphics.cs.williams.edu
+
+ @cite Portions based on Dave Eberly'x Magic Software Library
+ at http://www.magic-software.com
+
+ @created 2001-06-02
+ @edited 2009-11-16
+ */
+
+#include "G3D/platform.h"
+#include <stdlib.h>
+#include "G3D/Vector2.h"
+#include "G3D/g3dmath.h"
+#include "G3D/format.h"
+#include "G3D/BinaryInput.h"
+#include "G3D/BinaryOutput.h"
+#include "G3D/TextInput.h"
+#include "G3D/TextOutput.h"
+#include "G3D/Any.h"
+
+namespace G3D {
+
+
+Vector2::Vector2(const Any& any) {
+ any.verifyName("Vector2");
+ any.verifyType(Any::TABLE, Any::ARRAY);
+ any.verifySize(2);
+
+ if (any.type() == Any::ARRAY) {
+ x = any[0];
+ y = any[1];
+ } else {
+ // Table
+ x = any["x"];
+ y = any["y"];
+ }
+}
+
+
+Vector2::operator Any() const {
+ Any any(Any::ARRAY, "Vector2");
+ any.append(x, y);
+ return any;
+}
+
+
+const Vector2& Vector2::one() {
+ static const Vector2 v(1, 1); return v;
+}
+
+
+const Vector2& Vector2::zero() {
+ static Vector2 v(0, 0);
+ return v;
+}
+
+const Vector2& Vector2::unitX() {
+ static Vector2 v(1, 0);
+ return v;
+}
+
+const Vector2& Vector2::unitY() {
+ static Vector2 v(0, 1);
+ return v;
+}
+
+const Vector2& Vector2::inf() {
+ static Vector2 v((float)G3D::finf(), (float)G3D::finf());
+ return v;
+}
+
+
+const Vector2& Vector2::nan() {
+ static Vector2 v((float)G3D::fnan(), (float)G3D::fnan());
+ return v;
+}
+
+
+const Vector2& Vector2::minFinite() {
+ static Vector2 v(-FLT_MAX, -FLT_MAX);
+ return v;
+}
+
+
+const Vector2& Vector2::maxFinite() {
+ static Vector2 v(FLT_MAX, FLT_MAX);
+ return v;
+}
+
+
+size_t Vector2::hashCode() const {
+ unsigned int xhash = (*(int*)(void*)(&x));
+ unsigned int yhash = (*(int*)(void*)(&y));
+
+ return xhash + (yhash * 37);
+}
+
+
+Vector2::Vector2(BinaryInput& b) {
+ deserialize(b);
+}
+
+
+void Vector2::deserialize(BinaryInput& b) {
+ x = b.readFloat32();
+ y = b.readFloat32();
+}
+
+
+void Vector2::serialize(BinaryOutput& b) const {
+ b.writeFloat32(x);
+ b.writeFloat32(y);
+}
+
+
+void Vector2::deserialize(TextInput& t) {
+ t.readSymbol("(");
+ x = (float)t.readNumber();
+ t.readSymbol(",");
+ y = (float)t.readNumber();
+ t.readSymbol(")");
+}
+
+
+void Vector2::serialize(TextOutput& t) const {
+ t.writeSymbol("(");
+ t.writeNumber(x);
+ t.writeSymbol(",");
+ t.writeNumber(y);
+ t.writeSymbol(")");
+}
+
+//----------------------------------------------------------------------------
+
+Vector2 Vector2::random(G3D::Random& r) {
+ Vector2 result;
+
+ do {
+ result = Vector2(r.uniform(-1, 1), r.uniform(-1, 1));
+
+ } while (result.squaredLength() >= 1.0f);
+
+ result.unitize();
+
+ return result;
+}
+
+
+Vector2 Vector2::operator/ (float k) const {
+ return *this * (1.0f / k);
+}
+
+Vector2& Vector2::operator/= (float k) {
+ this->x /= k;
+ this->y /= k;
+ return *this;
+}
+
+//----------------------------------------------------------------------------
+float Vector2::unitize (float fTolerance) {
+ float fLength = length();
+
+ if (fLength > fTolerance) {
+ float fInvLength = 1.0f / fLength;
+ x *= fInvLength;
+ y *= fInvLength;
+ } else {
+ fLength = 0.0;
+ }
+
+ return fLength;
+}
+
+//----------------------------------------------------------------------------
+
+std::string Vector2::toString() const {
+ return G3D::format("(%g, %g)", x, y);
+}
+
+// 2-char swizzles
+
+Vector2 Vector2::xx() const { return Vector2 (x, x); }
+Vector2 Vector2::yx() const { return Vector2 (y, x); }
+Vector2 Vector2::xy() const { return Vector2 (x, y); }
+Vector2 Vector2::yy() const { return Vector2 (y, y); }
+
+// 3-char swizzles
+
+Vector3 Vector2::xxx() const { return Vector3 (x, x, x); }
+Vector3 Vector2::yxx() const { return Vector3 (y, x, x); }
+Vector3 Vector2::xyx() const { return Vector3 (x, y, x); }
+Vector3 Vector2::yyx() const { return Vector3 (y, y, x); }
+Vector3 Vector2::xxy() const { return Vector3 (x, x, y); }
+Vector3 Vector2::yxy() const { return Vector3 (y, x, y); }
+Vector3 Vector2::xyy() const { return Vector3 (x, y, y); }
+Vector3 Vector2::yyy() const { return Vector3 (y, y, y); }
+
+// 4-char swizzles
+
+Vector4 Vector2::xxxx() const { return Vector4 (x, x, x, x); }
+Vector4 Vector2::yxxx() const { return Vector4 (y, x, x, x); }
+Vector4 Vector2::xyxx() const { return Vector4 (x, y, x, x); }
+Vector4 Vector2::yyxx() const { return Vector4 (y, y, x, x); }
+Vector4 Vector2::xxyx() const { return Vector4 (x, x, y, x); }
+Vector4 Vector2::yxyx() const { return Vector4 (y, x, y, x); }
+Vector4 Vector2::xyyx() const { return Vector4 (x, y, y, x); }
+Vector4 Vector2::yyyx() const { return Vector4 (y, y, y, x); }
+Vector4 Vector2::xxxy() const { return Vector4 (x, x, x, y); }
+Vector4 Vector2::yxxy() const { return Vector4 (y, x, x, y); }
+Vector4 Vector2::xyxy() const { return Vector4 (x, y, x, y); }
+Vector4 Vector2::yyxy() const { return Vector4 (y, y, x, y); }
+Vector4 Vector2::xxyy() const { return Vector4 (x, x, y, y); }
+Vector4 Vector2::yxyy() const { return Vector4 (y, x, y, y); }
+Vector4 Vector2::xyyy() const { return Vector4 (x, y, y, y); }
+Vector4 Vector2::yyyy() const { return Vector4 (y, y, y, y); }
+
+
+
+} // namespace
diff --git a/dep/src/g3dlite/Vector2int16.cpp b/dep/src/g3dlite/Vector2int16.cpp
new file mode 100644
index 00000000000..2a4035a4d09
--- /dev/null
+++ b/dep/src/g3dlite/Vector2int16.cpp
@@ -0,0 +1,47 @@
+/**
+ @file Vector2int16.cpp
+
+ @author Morgan McGuire, http://graphics.cs.williams.edu
+
+ @created 2003-08-09
+ @edited 2006-01-29
+ */
+
+#include "G3D/platform.h"
+#include "G3D/g3dmath.h"
+#include "G3D/Vector2int16.h"
+#include "G3D/Vector2.h"
+#include "G3D/BinaryInput.h"
+#include "G3D/BinaryOutput.h"
+
+namespace G3D {
+
+Vector2int16::Vector2int16(const class Vector2& v) {
+ x = (int16)iFloor(v.x + 0.5);
+ y = (int16)iFloor(v.y + 0.5);
+}
+
+
+Vector2int16::Vector2int16(class BinaryInput& bi) {
+ deserialize(bi);
+}
+
+
+void Vector2int16::serialize(class BinaryOutput& bo) const {
+ bo.writeInt16(x);
+ bo.writeInt16(y);
+}
+
+
+void Vector2int16::deserialize(class BinaryInput& bi) {
+ x = bi.readInt16();
+ y = bi.readInt16();
+}
+
+
+Vector2int16 Vector2int16::clamp(const Vector2int16& lo, const Vector2int16& hi) {
+ return Vector2int16(iClamp(x, lo.x, hi.x), iClamp(y, lo.y, hi.y));
+}
+
+
+}
diff --git a/dep/src/g3dlite/Vector3.cpp b/dep/src/g3dlite/Vector3.cpp
index b0ca1990a3f..a53fa8269b7 100644
--- a/dep/src/g3dlite/Vector3.cpp
+++ b/dep/src/g3dlite/Vector3.cpp
@@ -1,44 +1,84 @@
/**
@file Vector3.cpp
-
+
3D vector class
-
- @maintainer Morgan McGuire, matrix@graphics3d.com
-
+
+ @maintainer Morgan McGuire, http://graphics.cs.williams.edu
+
@cite Portions based on Dave Eberly's Magic Software Library at http://www.magic-software.com
-
+
@created 2001-06-02
- @edited 2006-01-30
+ @edited 2009-11-27
*/
#include <limits>
#include <stdlib.h>
#include "G3D/Vector3.h"
#include "G3D/g3dmath.h"
-#include "G3D/format.h"
#include "G3D/stringutils.h"
+#include "G3D/BinaryInput.h"
+#include "G3D/BinaryOutput.h"
+#include "G3D/TextInput.h"
+#include "G3D/TextOutput.h"
#include "G3D/Vector3int16.h"
#include "G3D/Matrix3.h"
#include "G3D/Vector2.h"
-
+#include "G3D/Color3.h"
+#include "G3D/Vector4int8.h"
+#include "G3D/Vector3int32.h"
+#include "G3D/Any.h"
+
namespace G3D {
-Vector3 Vector3::dummy;
+Vector3::Vector3(const Any& any) {
+ any.verifyName("Vector3");
+ any.verifyType(Any::TABLE, Any::ARRAY);
+ any.verifySize(3);
-// Deprecated.
-const Vector3 Vector3::ZERO(0, 0, 0);
-const Vector3 Vector3::ZERO3(0, 0, 0);
-const Vector3 Vector3::UNIT_X(1, 0, 0);
-const Vector3 Vector3::UNIT_Y(0, 1, 0);
-const Vector3 Vector3::UNIT_Z(0, 0, 1);
-const Vector3 Vector3::INF3((float)G3D::inf(), (float)G3D::inf(), (float)G3D::inf());
-const Vector3 Vector3::NAN3((float)G3D::nan(), (float)G3D::nan(), (float)G3D::nan());
+ if (any.type() == Any::ARRAY) {
+ x = any[0];
+ y = any[1];
+ z = any[2];
+ } else {
+ // Table
+ x = any["x"];
+ y = any["y"];
+ z = any["z"];
+ }
+}
+
+Vector3::operator Any() const {
+ Any any(Any::ARRAY, "Vector3");
+ any.append(x, y, z);
+ return any;
+}
+
+Vector3::Vector3(const class Color3& v) : x(v.r), y(v.g), z(v.b) {}
+
+Vector3::Vector3(const class Vector3int32& v) : x((float)v.x), y((float)v.y), z((float)v.z) {}
+
+Vector3::Vector3(const Vector4int8& v) : x(v.x / 127.0f), y(v.y / 127.0f), z(v.z / 127.0f) {}
Vector3::Vector3(const class Vector2& v, float _z) : x(v.x), y(v.y), z(_z) {
}
-Vector3::Axis Vector3::primaryAxis() const {
+Vector3& Vector3::ignore() {
+ static Vector3 v;
+ return v;
+}
+const Vector3& Vector3::zero() { static const Vector3 v(0, 0, 0); return v; }
+const Vector3& Vector3::one() { static const Vector3 v(1, 1, 1); return v; }
+const Vector3& Vector3::unitX() { static const Vector3 v(1, 0, 0); return v; }
+const Vector3& Vector3::unitY() { static const Vector3 v(0, 1, 0); return v; }
+const Vector3& Vector3::unitZ() { static const Vector3 v(0, 0, 1); return v; }
+const Vector3& Vector3::inf() { static const Vector3 v((float)G3D::finf(), (float)G3D::finf(), (float)G3D::finf()); return v; }
+const Vector3& Vector3::nan() { static const Vector3 v((float)G3D::fnan(), (float)G3D::fnan(), (float)G3D::fnan()); return v; }
+const Vector3& Vector3::minFinite(){ static const Vector3 v(-FLT_MAX, -FLT_MAX, -FLT_MAX); return v; }
+const Vector3& Vector3::maxFinite(){ static const Vector3 v(FLT_MAX, FLT_MAX, FLT_MAX); return v; }
+
+Vector3::Axis Vector3::primaryAxis() const {
+
Axis a = X_AXIS;
double nx = abs(x);
@@ -62,7 +102,8 @@ Vector3::Axis Vector3::primaryAxis() const {
return a;
}
-unsigned int Vector3::hashCode() const {
+
+size_t Vector3::hashCode() const {
unsigned int xhash = (*(int*)(void*)(&x));
unsigned int yhash = (*(int*)(void*)(&y));
unsigned int zhash = (*(int*)(void*)(&z));
@@ -74,65 +115,73 @@ 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;
}
-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);
+void Vector3::deserialize(BinaryInput& b) {
+ x = b.readFloat32();
+ y = b.readFloat32();
+ z = b.readFloat32();
+}
- result.unitize();
- return result;
+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(")");
}
-//----------------------------------------------------------------------------
-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();
- }
+
+void Vector3::serialize(TextOutput& t) const {
+ t.writeSymbol("(");
+ t.writeNumber(x);
+ t.writeSymbol(",");
+ t.writeNumber(y);
+ t.writeSymbol(",");
+ t.writeNumber(z);
+ t.writeSymbol(")");
}
-//----------------------------------------------------------------------------
-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;
+void Vector3::serialize(BinaryOutput& b) const {
+ b.writeFloat32(x);
+ b.writeFloat32(y);
+ b.writeFloat32(z);
}
-//----------------------------------------------------------------------------
-float Vector3::unitize (float fTolerance) {
+
+Vector3 Vector3::random(Random& r) {
+ Vector3 result;
+ r.sphere(result.x, result.y, result.z);
+ return result;
+}
+
+
+float Vector3::unitize(float fTolerance) {
float fMagnitude = magnitude();
if (fMagnitude > fTolerance) {
@@ -147,10 +196,8 @@ float Vector3::unitize (float fTolerance) {
return fMagnitude;
}
-//----------------------------------------------------------------------------
Vector3 Vector3::reflectAbout(const Vector3& normal) const {
-
Vector3 out;
Vector3 N = normal.direction();
@@ -159,36 +206,49 @@ Vector3 Vector3::reflectAbout(const Vector3& normal) const {
return N * 2 * this->dot(N) - *this;
}
-//----------------------------------------------------------------------------
-#if 0
-Vector3 Vector3::cosRandom(const Vector3& normal) {
- double e1 = G3D::random(0, 1);
- double e2 = G3D::random(0, 1);
- // Angle from normal
- double theta = acos(sqrt(e1));
+Vector3 Vector3::cosHemiRandom(const Vector3& normal, Random& r) {
+ debugAssertM(G3D::fuzzyEq(normal.length(), 1.0f),
+ "cosHemiRandom requires its argument to have unit length");
- // Angle about normal
- double phi = 2 * G3D_PI * e2;
+ float x, y, z;
+ r.cosHemi(x, y, z);
// Make a coordinate system
- Vector3 U = normal.direction();
- Vector3 V = Vector3::unitX();
+ const Vector3& Z = normal;
- if (abs(U.dot(V)) > .9) {
- V = Vector3::unitY();
- }
+ Vector3 X, Y;
+ normal.getTangents(X, Y);
+
+ return
+ x * X +
+ y * Y +
+ z * Z;
+}
+
+
+Vector3 Vector3::cosPowHemiRandom(const Vector3& normal, const float k, Random& r) {
+ debugAssertM(G3D::fuzzyEq(normal.length(), 1.0f),
+ "cosPowHemiRandom requires its argument to have unit length");
- Vector3 W = U.cross(V).direction();
- V = W.cross(U);
+ float x, y, z;
+ r.cosPowHemi(k, x, y, z);
- // Convert to rectangular form
- return cos(theta) * U + sin(theta) * (cos(phi) * V + sin(phi) * W);
+ // Make a coordinate system
+ const Vector3& Z = normal;
+
+ Vector3 X, Y;
+ normal.getTangents(X, Y);
+
+ return
+ x * X +
+ y * Y +
+ z * Z;
}
-//----------------------------------------------------------------------------
-Vector3 Vector3::hemiRandom(const Vector3& normal) {
- Vector3 V = Vector3::random();
+
+Vector3 Vector3::hemiRandom(const Vector3& normal, Random& r) {
+ const Vector3& V = Vector3::random(r);
if (V.dot(normal) < 0) {
return -V;
@@ -196,7 +256,7 @@ Vector3 Vector3::hemiRandom(const Vector3& normal) {
return V;
}
}
-#endif
+
//----------------------------------------------------------------------------
Vector3 Vector3::reflectionDirection(const Vector3& normal) const {
@@ -256,12 +316,12 @@ void Vector3::orthonormalize (Vector3 akVector[3]) {
akVector[0].unitize();
// compute u1
- float fDot0 = akVector[0].dot(akVector[1]);
+ float fDot0 = akVector[0].dot(akVector[1]);
akVector[1] -= akVector[0] * fDot0;
akVector[1].unitize();
// compute u2
- float fDot1 = akVector[1].dot(akVector[2]);
+ float fDot1 = akVector[1].dot(akVector[2]);
fDot0 = akVector[0].dot(akVector[2]);
akVector[2] -= akVector[0] * fDot0 + akVector[1] * fDot1;
akVector[2].unitize();
@@ -294,6 +354,7 @@ std::string Vector3::toString() const {
return G3D::format("(%g, %g, %g)", x, y, z);
}
+
//----------------------------------------------------------------------------
Matrix3 Vector3::cross() const {
@@ -302,6 +363,15 @@ Matrix3 Vector3::cross() const {
-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
@@ -431,5 +501,7 @@ Vector4 Vector3::zzzz() const { return Vector4 (z, z, z, z); }
-} // namespace
+
+
+} // namespace
diff --git a/dep/src/g3dlite/Vector3int16.cpp b/dep/src/g3dlite/Vector3int16.cpp
new file mode 100644
index 00000000000..44069b85d8c
--- /dev/null
+++ b/dep/src/g3dlite/Vector3int16.cpp
@@ -0,0 +1,49 @@
+/**
+ @file Vector3int16.cpp
+
+ @author Morgan McGuire, http://graphics.cs.williams.edu
+
+ @created 2003-04-07
+ @edited 2006-01-17
+ */
+
+#include "G3D/platform.h"
+#include "G3D/g3dmath.h"
+#include "G3D/Vector3int16.h"
+#include "G3D/Vector3.h"
+#include "G3D/BinaryInput.h"
+#include "G3D/BinaryOutput.h"
+#include "G3D/format.h"
+
+namespace G3D {
+
+Vector3int16::Vector3int16(const class Vector3& v) {
+ x = (int16)iFloor(v.x + 0.5);
+ y = (int16)iFloor(v.y + 0.5);
+ z = (int16)iFloor(v.z + 0.5);
+}
+
+
+Vector3int16::Vector3int16(class BinaryInput& bi) {
+ deserialize(bi);
+}
+
+
+void Vector3int16::serialize(class BinaryOutput& bo) const {
+ bo.writeInt16(x);
+ bo.writeInt16(y);
+ bo.writeInt16(z);
+}
+
+
+void Vector3int16::deserialize(class BinaryInput& bi) {
+ x = bi.readInt16();
+ y = bi.readInt16();
+ z = bi.readInt16();
+}
+
+std::string Vector3int16::toString() const {
+ return G3D::format("(%d, %d, %d)", x, y, z);
+}
+
+}
diff --git a/dep/src/g3dlite/Vector3int32.cpp b/dep/src/g3dlite/Vector3int32.cpp
new file mode 100644
index 00000000000..3bd8e9f2bc2
--- /dev/null
+++ b/dep/src/g3dlite/Vector3int32.cpp
@@ -0,0 +1,57 @@
+/**
+ @file Vector3int32.cpp
+
+ @author Morgan McGuire, http://graphics.cs.williams.edu
+
+ @created 2008-07-01
+ @edited 2008-07-01
+ */
+
+#include "G3D/platform.h"
+#include "G3D/g3dmath.h"
+#include "G3D/Vector3int32.h"
+#include "G3D/Vector3int16.h"
+#include "G3D/Vector3.h"
+#include "G3D/BinaryInput.h"
+#include "G3D/BinaryOutput.h"
+#include "G3D/format.h"
+
+namespace G3D {
+
+Vector3int32::Vector3int32(const class Vector3& v) {
+ x = (int32)iFloor(v.x + 0.5);
+ y = (int32)iFloor(v.y + 0.5);
+ z = (int32)iFloor(v.z + 0.5);
+}
+
+
+Vector3int32::Vector3int32(const class Vector3int16& v) {
+ x = v.x;
+ y = v.y;
+ z = v.z;
+}
+
+
+Vector3int32::Vector3int32(class BinaryInput& bi) {
+ deserialize(bi);
+}
+
+
+void Vector3int32::serialize(class BinaryOutput& bo) const {
+ bo.writeInt32(x);
+ bo.writeInt32(y);
+ bo.writeInt32(z);
+}
+
+
+void Vector3int32::deserialize(class BinaryInput& bi) {
+ x = bi.readInt32();
+ y = bi.readInt32();
+ z = bi.readInt32();
+}
+
+std::string Vector3int32::toString() const {
+ return G3D::format("(%d, %d, %d)", x, y, z);
+}
+
+}
diff --git a/dep/src/g3dlite/Vector4.cpp b/dep/src/g3dlite/Vector4.cpp
index b38e6f7cb57..f6abc1a6e0c 100644
--- a/dep/src/g3dlite/Vector4.cpp
+++ b/dep/src/g3dlite/Vector4.cpp
@@ -1,23 +1,74 @@
/**
@file Vector4.cpp
-
- @maintainer Morgan McGuire, matrix@graphics3d.com
-
+
+ @maintainer Morgan McGuire, http://graphics.cs.williams.edu
+
@created 2001-07-09
- @edited 2003-09-29
+ @edited 2009-11-29
*/
#include <stdlib.h>
#include <limits>
#include "G3D/Vector4.h"
-//#include "G3D/Color4.h"
+#include "G3D/Color4.h"
#include "G3D/g3dmath.h"
-#include "G3D/format.h"
#include "G3D/stringutils.h"
+#include "G3D/BinaryInput.h"
+#include "G3D/BinaryOutput.h"
+#include "G3D/Vector4int8.h"
+#include "G3D/Matrix4.h"
+#include "G3D/Any.h"
namespace G3D {
-unsigned int Vector4::hashCode() const {
+Vector4::Vector4(const Any& any) {
+ any.verifyName("Vector4");
+ any.verifyType(Any::TABLE, Any::ARRAY);
+ any.verifySize(4);
+
+ if (any.type() == Any::ARRAY) {
+ x = any[0];
+ y = any[1];
+ z = any[2];
+ w = any[3];
+ } else {
+ // Table
+ x = any["x"];
+ y = any["y"];
+ z = any["z"];
+ w = any["w"];
+ }
+}
+
+Vector4::operator Any() const {
+ Any any(Any::ARRAY, "Vector4");
+ any.append(x, y, z, w);
+ return any;
+}
+
+
+Vector4::Vector4(const Vector4int8& v) : x(v.x / 127.0f), y(v.y / 127.0f), z(v.z / 127.0f), w(v.w / 127.0f) {
+}
+
+
+const Vector4& Vector4::inf() {
+ static const Vector4 v((float)G3D::finf(), (float)G3D::finf(), (float)G3D::finf(), (float)G3D::finf());
+ return v;
+}
+
+
+const Vector4& Vector4::zero() {
+ static const Vector4 v(0,0,0,0);
+ return v;
+}
+
+const Vector4& Vector4::nan() {
+ static Vector4 v((float)G3D::fnan(), (float)G3D::fnan(), (float)G3D::fnan(), (float)G3D::fnan());
+ return v;
+}
+
+
+size_t Vector4::hashCode() const {
unsigned int xhash = (*(int*)(void*)(&x));
unsigned int yhash = (*(int*)(void*)(&y));
unsigned int zhash = (*(int*)(void*)(&z));
@@ -26,14 +77,14 @@ unsigned int Vector4::hashCode() const {
return xhash + (yhash * 37) + (zhash * 101) + (whash * 241);
}
-#if 0
+
Vector4::Vector4(const class Color4& c) {
x = c.r;
y = c.g;
z = c.b;
w = c.a;
}
-#endif
+
Vector4::Vector4(const Vector2& v1, const Vector2& v2) {
x = v1.x;
@@ -42,6 +93,7 @@ Vector4::Vector4(const Vector2& v1, const Vector2& v2) {
w = v2.y;
}
+
Vector4::Vector4(const Vector2& v1, float fz, float fw) {
x = v1.x;
y = v1.y;
@@ -49,13 +101,45 @@ Vector4::Vector4(const Vector2& v1, float fz, float fw) {
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;
+ float fInvScalar = 1.0f / fScalar;
kQuot.x = fInvScalar * x;
kQuot.y = fInvScalar * y;
kQuot.z = fInvScalar * z;
@@ -69,18 +153,19 @@ Vector4 Vector4::operator/ (float fScalar) const {
//----------------------------------------------------------------------------
Vector4& Vector4::operator/= (float fScalar) {
if (fScalar != 0.0f) {
- float fInvScalar = 1.0f / fScalar;
+ float fInvScalar = 1.0f / fScalar;
x *= fInvScalar;
y *= fInvScalar;
z *= fInvScalar;
w *= fInvScalar;
} else {
- *this = Vector4::inf();
+ *this = Vector4::inf();
}
return *this;
}
+
//----------------------------------------------------------------------------
std::string Vector4::toString() const {
@@ -431,5 +516,5 @@ 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
+}; // namespace
diff --git a/dep/src/g3dlite/Vector4int8.cpp b/dep/src/g3dlite/Vector4int8.cpp
new file mode 100644
index 00000000000..70bd143e01d
--- /dev/null
+++ b/dep/src/g3dlite/Vector4int8.cpp
@@ -0,0 +1,58 @@
+/**
+ @file Vector4int8.cpp
+
+ Homogeneous vector class.
+
+ @maintainer Morgan McGuire, http://graphics.cs.williams.edu
+
+ @created 2007-02-09
+ @edited 2007-02-09
+
+ Copyright 2000-2007, Morgan McGuire.
+ All rights reserved.
+ */
+
+#include "G3D/platform.h"
+#include "G3D/Vector4int8.h"
+#include "G3D/Vector3.h"
+#include "G3D/Vector4.h"
+#include "G3D/BinaryInput.h"
+#include "G3D/BinaryOutput.h"
+#include <string>
+
+namespace G3D {
+
+Vector4int8::Vector4int8(const Vector4& source) {
+ x = iClamp(iRound(source.x), -128, 127);
+ y = iClamp(iRound(source.y), -128, 127);
+ z = iClamp(iRound(source.z), -128, 127);
+ w = iClamp(iRound(source.w), -128, 127);
+}
+
+Vector4int8::Vector4int8(const Vector3& source, int8 w) : w(w) {
+ x = iClamp(iRound(source.x), -128, 127);
+ y = iClamp(iRound(source.y), -128, 127);
+ z = iClamp(iRound(source.z), -128, 127);
+}
+
+Vector4int8::Vector4int8(class BinaryInput& b) {
+ deserialize(b);
+}
+
+void Vector4int8::serialize(class BinaryOutput& b) const {
+ // Intentionally write individual bytes to avoid endian issues
+ b.writeInt8(x);
+ b.writeInt8(y);
+ b.writeInt8(z);
+ b.writeInt8(w);
+}
+
+void Vector4int8::deserialize(class BinaryInput& b) {
+ x = b.readInt8();
+ y = b.readInt8();
+ z = b.readInt8();
+ w = b.readInt8();
+}
+
+} // namespace G3D
+
diff --git a/dep/src/g3dlite/Welder.cpp b/dep/src/g3dlite/Welder.cpp
new file mode 100644
index 00000000000..b4f752f38bd
--- /dev/null
+++ b/dep/src/g3dlite/Welder.cpp
@@ -0,0 +1,416 @@
+/**
+ @file Welder.cpp
+
+ @author Morgan McGuire, Kyle Whitson, Corey Taylor
+
+ @created 2008-07-30
+ @edited 2009-11-29
+ */
+
+#include "G3D/platform.h"
+#include "G3D/Vector2.h"
+#include "G3D/Vector3.h"
+#include "G3D/Sphere.h"
+#include "G3D/PointHashGrid.h"
+#include "G3D/Welder.h"
+#include "G3D/Stopwatch.h" // for profiling
+#include "G3D/AreaMemoryManager.h"
+#include "G3D/Any.h"
+#include "G3D/stringutils.h"
+
+namespace G3D { namespace _internal{
+
+
+/** Used by WeldHelper2::smoothNormals. */
+class VN {
+public:
+ Vector3 vertex;
+ Vector3 normal;
+
+ VN() {}
+ VN(const Vector3& v, const Vector3& n) : vertex(v), normal(n) {}
+};
+
+/** Used by WeldHelper::getIndex to maintain a list of vertices by location. */
+class VNTi {
+public:
+ Vector3 vertex;
+ Vector3 normal;
+ Vector2 texCoord;
+ int index;
+
+ VNTi() : index(0) {}
+
+ VNTi(const Vector3& v, const Vector3& n, const Vector2& t, int i) :
+ vertex(v), normal(n), texCoord(t), index(i) {}
+};
+
+
+}} // G3D
+
+template <> struct HashTrait <G3D::_internal::VN> {
+ static size_t hashCode(const G3D::_internal::VN& k) { return static_cast<size_t>(k.vertex.hashCode()); }
+};
+template <> struct HashTrait <G3D::_internal::VNTi> {
+ static size_t hashCode(const G3D::_internal::VNTi& k) { return static_cast<size_t>(k.vertex.hashCode()); }
+};
+
+
+template<> struct EqualsTrait <G3D::_internal::VN> {
+ static bool equals(const G3D::_internal::VN& a, const G3D::_internal::VN& b) { return a.vertex == b.vertex; }
+};
+template<> struct EqualsTrait <G3D::_internal::VNTi> {
+ static bool equals(const G3D::_internal::VNTi& a, const G3D::_internal::VNTi& b) { return a.vertex == b.vertex; }
+};
+
+template<> struct PositionTrait<G3D::_internal::VN> {
+ static void getPosition(const G3D::_internal::VN& v, G3D::Vector3& p) { p = v.vertex; }
+};
+template<> struct PositionTrait<G3D::_internal::VNTi> {
+ static void getPosition(const G3D::_internal::VNTi& v, G3D::Vector3& p) { p = v.vertex; }
+};
+
+namespace G3D { namespace _internal {
+
+class WeldHelper {
+private:
+ /** Used by getIndex and updateTriLists */
+ 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) {
+
+ // Create an area memory manager for fast deallocation
+ MemoryManager::Ref mm = AreaMemoryManager::create(iRound(sizeof(VN) * normalArray.size() * 1.5));
+
+ if (normalSmoothingAngle <= 0) {
+ smoothNormalArray = normalArray;
+ return;
+ }
+
+ const float cosThresholdAngle = (float)cos(normalSmoothingAngle);
+
+ debugAssert(vertexArray.size() == normalArray.size());
+ smoothNormalArray.resize(normalArray.size());
+
+ // Compute a hash grid so that we can find neighbors quickly.
+ PointHashGrid<VN> grid(vertexWeldRadius, mm);
+ for (int v = 0; v < normalArray.size(); ++v) {
+ grid.insert(VN(vertexArray[v], normalArray[v]));
+ }
+
+ 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.
+ outputVertexArray = &vertexArray;
+ outputNormalArray = &normalArray;
+ outputTexCoordArray = &texCoordArray;
+ outputVertexArray->fastClear();
+ outputNormalArray->fastClear();
+ outputTexCoordArray->fastClear();
+
+ // For every three vertices, generate their face normal and store it at
+ // each vertex. The output array has the same length as the input.
+ computeFaceNormals(unrolledVertexArray, unrolledFaceNormalArray);
+
+ // Compute smooth normals at vertices.
+ if (unrolledFaceNormalArray.size() > 0) {
+ smoothNormals(unrolledVertexArray, unrolledFaceNormalArray, unrolledSmoothNormalArray);
+ unrolledFaceNormalArray.clear();
+ }
+
+ // Regenerate the triangle lists
+ updateTriLists(indexArrayArray, unrolledVertexArray, unrolledSmoothNormalArray, unrolledTexCoordArray);
+
+ if (! hasTexCoords) {
+ // Throw away the generated texCoords
+ texCoordArray.resize(0);
+ }
+ }
+
+ WeldHelper(float vertRadius) :
+ weldGrid(vertRadius),
+ vertexWeldRadius(vertRadius) {}
+
+};
+} // Internal
+
+void Welder::weld(
+ Array<Vector3>& vertexArray,
+ Array<Vector2>& texCoordArray,
+ Array<Vector3>& normalArray,
+ Array<Array<int>*>& indexArrayArray,
+ const Welder::Settings& settings) {
+
+ _internal::WeldHelper(settings.vertexWeldRadius).process(
+ vertexArray, texCoordArray, normalArray, indexArrayArray,
+ settings.normalSmoothingAngle, settings.textureWeldRadius, settings.normalWeldRadius);
+}
+
+
+Welder::Settings::Settings(const Any& any) {
+ *this = Settings();
+ any.verifyName("Welder::Settings");
+ for (Any::AnyTable::Iterator it = any.table().begin(); it.hasMore(); ++it) {
+ const std::string& key = toLower(it->key);
+ if (key == "normalsmoothingangle") {
+ normalSmoothingAngle = it->value;
+ } else if (key == "vertexweldradius") {
+ vertexWeldRadius = it->value;
+ } else if (key == "textureweldradius") {
+ textureWeldRadius = it->value;
+ } else if (key == "normalweldradius") {
+ normalWeldRadius = it->value;
+ } else {
+ any.verify(false, "Illegal key: " + it->key);
+ }
+ }
+}
+
+Welder::Settings::operator Any() const {
+ Any a(Any::TABLE, "Welder::Settings");
+ a.set("normalSmoothingAngle", normalSmoothingAngle);
+ a.set("vertexWeldRadius", vertexWeldRadius);
+ a.set("textureWeldRadius", textureWeldRadius);
+ a.set("normalWeldRadius", normalWeldRadius);
+ return a;
+}
+
+} // G3D
diff --git a/dep/src/g3dlite/WinMain.cpp b/dep/src/g3dlite/WinMain.cpp
new file mode 100644
index 00000000000..3cee71084e4
--- /dev/null
+++ b/dep/src/g3dlite/WinMain.cpp
@@ -0,0 +1,159 @@
+/*
+ Dervied from SDL_main.c, which was placed in the public domain by Sam Lantinga 4/13/98
+
+ The WinMain function -- calls your program's main() function
+*/
+
+#include "G3D/platform.h"
+
+#ifdef G3D_WIN32
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <cctype>
+
+#ifdef main
+# ifndef _WIN32_WCE_EMULATION
+# undef main
+# endif /* _WIN32_WCE_EMULATION */
+#endif /* main */
+
+#if defined(_WIN32_WCE) && _WIN32_WCE < 300
+/* seems to be undefined in Win CE although in online help */
+#define isspace(a) (((CHAR)a == ' ') || ((CHAR)a == '\t'))
+#endif /* _WIN32_WCE < 300 */
+
+// Turn off the G3D for loop scoping for C++
+#ifdef for
+# undef for
+#endif
+
+extern int main(int argc, const char** argv);
+
+/* Parse a command line buffer into arguments */
+static int ParseCommandLine(char *cmdline, char **argv) {
+ char *bufp;
+ int argc;
+
+ argc = 0;
+ for (bufp = cmdline; *bufp;) {
+ /* Skip leading whitespace */
+ while (isspace(*bufp)) {
+ ++bufp;
+ }
+ /* Skip over argument */
+ if (*bufp == '"') {
+ ++bufp;
+ if (*bufp) {
+ if (argv) {
+ argv[argc] = bufp;
+ }
+ ++argc;
+ }
+ /* Skip over word */
+ while (*bufp && (*bufp != '"')) {
+ ++bufp;
+ }
+ } else {
+ if (*bufp) {
+ if (argv) {
+ argv[argc] = bufp;
+ }
+ ++argc;
+ }
+ /* Skip over word */
+ while (*bufp && !isspace(*bufp)) {
+ ++bufp;
+ }
+ }
+ if (*bufp) {
+ if (argv) {
+ *bufp = '\0';
+ }
+ ++bufp;
+ }
+ }
+ if (argv) {
+ argv[argc] = NULL;
+ }
+ return (argc);
+}
+
+/* Show an error message */
+static void ShowError(const char *title, const char *message) {
+/* If USE_MESSAGEBOX is defined, you need to link with user32.lib */
+#ifdef USE_MESSAGEBOX
+ MessageBox(NULL, message, title, MB_ICONEXCLAMATION | MB_OK);
+#else
+ fprintf(stderr, "%s: %s\n", title, message);
+#endif
+}
+
+/* Pop up an out of memory message, returns to Windows */
+static BOOL OutOfMemory(void) {
+ ShowError("Fatal Error", "Out of memory - aborting");
+ return FALSE;
+}
+
+
+int WINAPI G3D_WinMain(HINSTANCE hInst, HINSTANCE hPrev, LPSTR szCmdLine, int sw) {
+ char **argv;
+ int argc;
+ int status;
+ char *cmdline;
+# ifdef _WIN32_WCE
+ wchar_t *bufp;
+ int nLen;
+# else
+ char *bufp;
+ size_t nLen;
+# endif
+ (void)sw;
+ (void)szCmdLine;
+ (void)hInst;
+ (void)hPrev;
+
+#ifdef _WIN32_WCE
+#error WinCE not supported
+ /*
+ nLen = wcslen(szCmdLine) + 128 + 1;
+ bufp = SDL_stack_alloc(wchar_t, nLen * 2);
+ wcscpy(bufp, TEXT("\""));
+ GetModuleFileName(NULL, bufp + 1, 128 - 3);
+ wcscpy(bufp + wcslen(bufp), TEXT("\" "));
+ wcsncpy(bufp + wcslen(bufp), szCmdLine, nLen - wcslen(bufp));
+ nLen = wcslen(bufp) + 1;
+ cmdline = SDL_stack_alloc(char, nLen);
+ if (cmdline == NULL) {
+ return OutOfMemory();
+ }
+ WideCharToMultiByte(CP_ACP, 0, bufp, -1, cmdline, nLen, NULL, NULL);
+ */
+#else
+ /* Grab the command line */
+ bufp = GetCommandLineA();
+ nLen = strlen(bufp) + 1;
+ cmdline = (char*)malloc(sizeof(char) * nLen);
+ if (cmdline == NULL) {
+ return OutOfMemory();
+ }
+ strncpy(cmdline, bufp, nLen);
+#endif
+
+ /* Parse it into argv and argc */
+ argc = ParseCommandLine(cmdline, NULL);
+ argv = (char**)malloc(sizeof(char*) * (argc + 1));
+ if (argv == NULL) {
+ return OutOfMemory();
+ }
+ ParseCommandLine(cmdline, argv);
+
+ /* Run the main program */
+ status = main(argc, (const char**)argv);
+ free(argv);
+ free(cmdline);
+
+ return status;
+}
+
+#endif // if Win32
diff --git a/dep/src/g3dlite/constants.cpp b/dep/src/g3dlite/constants.cpp
new file mode 100644
index 00000000000..52cad3cd90b
--- /dev/null
+++ b/dep/src/g3dlite/constants.cpp
@@ -0,0 +1,82 @@
+/**
+ @file constants.cpp
+
+ @maintainer Morgan McGuire, http://graphics.cs.williams.edu
+ @created 2009-05-20
+ @edited 2010-01-29
+*/
+#include "G3D/constants.h"
+#include "G3D/Any.h"
+#include "G3D/stringutils.h"
+
+namespace G3D {
+
+const std::string MirrorQuality::str[] = {"NONE", "STATIC_ENV", "DYNAMIC_PLANAR", "DYNAMIC_ENV", "BEST"};
+const MirrorQuality::Value MirrorQuality::enm[] = {MirrorQuality::NONE, MirrorQuality::STATIC_ENV,
+ MirrorQuality::DYNAMIC_PLANAR, MirrorQuality::DYNAMIC_ENV, MirrorQuality::BEST};
+
+MirrorQuality::MirrorQuality(const class Any& any) {
+ *this = any;
+}
+
+
+MirrorQuality& MirrorQuality::operator=(const Any& any) {
+ const std::string& s = toUpper(any.string());
+
+ for (int i = 0; ! str[i].empty(); ++i) {
+ if (s == str[i]) {
+ value = enm[i];
+ return *this;
+ }
+ }
+
+ any.verify(false, "Unrecognized MirrorQuality constant");
+ return *this;
+}
+
+
+MirrorQuality::operator Any() const {
+ return toString();
+}
+
+
+const std::string& MirrorQuality::toString() const {
+ return str[value];
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+const std::string RefractionQuality::str[] = {"NONE", "STATIC_ENV", "DYNAMIC_FLAT", "DYNAMIC_FLAT_MULTILAYER", "DYNAMIC_ENV", "BEST"};
+const RefractionQuality::Value RefractionQuality::enm[] = {RefractionQuality::NONE, RefractionQuality::STATIC_ENV,
+ RefractionQuality::DYNAMIC_FLAT, RefractionQuality::DYNAMIC_FLAT_MULTILAYER, RefractionQuality::DYNAMIC_ENV, RefractionQuality::BEST};
+
+RefractionQuality::RefractionQuality(const class Any& any) {
+ *this = any;
+}
+
+
+RefractionQuality& RefractionQuality::operator=(const Any& any) {
+ const std::string& s = toUpper(any.string());
+
+ for (int i = 0; ! str[i].empty(); ++i) {
+ if (s == str[i]) {
+ value = enm[i];
+ return *this;
+ }
+ }
+
+ any.verify(false, "Unrecognized RefractionQuality constant");
+ return *this;
+}
+
+
+RefractionQuality::operator Any() const {
+ return toString();
+}
+
+
+const std::string& RefractionQuality::toString() const {
+ return str[value];
+}
+
+} // G3D
diff --git a/dep/src/g3dlite/debugAssert.cpp b/dep/src/g3dlite/debugAssert.cpp
new file mode 100644
index 00000000000..a87161b261f
--- /dev/null
+++ b/dep/src/g3dlite/debugAssert.cpp
@@ -0,0 +1,389 @@
+/**
+ @file debugAssert.cpp
+
+ Windows implementation of assertion routines.
+
+ @maintainer Morgan McGuire, graphics3d.com
+
+ @created 2001-08-26
+ @edited 2009-06-02
+ */
+
+#include "G3D/debugAssert.h"
+#include "G3D/platform.h"
+#ifdef G3D_WIN32
+ #include <tchar.h>
+#endif
+#include "G3D/format.h"
+#include "G3D/prompt.h"
+#include <string>
+#include "G3D/debugPrintf.h"
+#include "G3D/Log.h"
+
+#include <cstdlib>
+
+#ifdef _MSC_VER
+ // disable: "C++ exception handler used"
+# pragma warning (push)
+# pragma warning (disable : 4530)
+#endif
+
+using namespace std;
+
+namespace G3D { namespace _internal {
+
+ConsolePrintHook _consolePrintHook;
+AssertionHook _debugHook = _handleDebugAssert_;
+AssertionHook _failureHook = _handleErrorCheck_;
+
+#ifdef G3D_LINUX
+#if SOMEONE_MADE_THIS_USEFUL
+ Display* x11Display = NULL;
+ Window x11Window = 0;
+#endif
+#endif
+
+
+#ifdef G3D_WIN32
+static void postToClipboard(const char *text) {
+ if (OpenClipboard(NULL)) {
+ HGLOBAL hMem = GlobalAlloc(GHND | GMEM_DDESHARE, strlen(text) + 1);
+ if (hMem) {
+ char *pMem = (char*)GlobalLock(hMem);
+ strcpy(pMem, text);
+ GlobalUnlock(hMem);
+
+ EmptyClipboard();
+ SetClipboardData(CF_TEXT, hMem);
+ }
+
+ CloseClipboard();
+ GlobalFree(hMem);
+ }
+}
+#endif
+
+/**
+ outTitle should be set before the call
+ */
+static void createErrorMessage(
+ const char* expression,
+ const std::string& message,
+ const char* filename,
+ int lineNumber,
+ std::string& outTitle,
+ std::string& outMessage) {
+
+ std::string le = "";
+ const char* newline = "\n";
+
+ #ifdef G3D_WIN32
+ newline = "\r\n";
+
+ // The last error value. (Which is preserved across the call).
+ DWORD lastErr = GetLastError();
+
+ // The decoded message from FormatMessage
+ LPTSTR formatMsg = NULL;
+
+ if (NULL == formatMsg) {
+ FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER |
+ FORMAT_MESSAGE_IGNORE_INSERTS |
+ FORMAT_MESSAGE_FROM_SYSTEM,
+ NULL,
+ lastErr,
+ 0,
+ (LPTSTR)&formatMsg,
+ 0,
+ NULL);
+ }
+
+ // Make sure the message got translated into something.
+ LPTSTR realLastErr;
+ if (NULL != formatMsg) {
+ realLastErr = formatMsg;
+ } else {
+ realLastErr = _T("Last error code does not exist.");
+ }
+
+ if (lastErr != 0) {
+ le = G3D::format("Last Error (0x%08X): %s\r\n\r\n", lastErr, (LPCSTR)realLastErr);
+ }
+
+ // Get rid of the allocated memory from FormatMessage.
+ if (NULL != formatMsg) {
+ LocalFree((LPVOID)formatMsg);
+ }
+
+ char modulePath[MAX_PATH];
+ GetModuleFileNameA(NULL, modulePath, MAX_PATH);
+
+ const char* moduleName = strrchr(modulePath, '\\');
+ outTitle = outTitle + string(" - ") + string(moduleName ? (moduleName + 1) : modulePath);
+
+ #endif
+
+ // Build the message.
+ outMessage =
+ G3D::format("%s%s%sExpression: %s%s%s:%d%s%s%s",
+ message.c_str(), newline, newline, expression, newline,
+ filename, lineNumber, newline, newline, le.c_str());
+}
+
+
+bool _handleDebugAssert_(
+ const char* expression,
+ const std::string& message,
+ const char* filename,
+ int lineNumber,
+ bool useGuiPrompt) {
+
+ std::string dialogTitle = "Assertion Failure";
+ std::string dialogText = "";
+ createErrorMessage(expression, message, filename, lineNumber, dialogTitle, dialogText);
+
+ #ifdef G3D_WIN32
+ DWORD lastErr = GetLastError();
+ postToClipboard(dialogText.c_str());
+ debugPrintf("\n%s\n", dialogText.c_str());
+ #endif
+
+ const int cBreak = 0;
+ const int cIgnore = 1;
+ const int cAbort = 2;
+
+ static const char* choices[] = {"Debug", "Ignore", "Exit"};
+
+ // Log the error
+ Log::common()->print(std::string("\n**************************\n\n") + dialogTitle + "\n" + dialogText);
+
+ int result = G3D::prompt(dialogTitle.c_str(), dialogText.c_str(), (const char**)choices, 3, useGuiPrompt);
+
+# ifdef G3D_WIN32
+ // Put the incoming last error back.
+ SetLastError(lastErr);
+# endif
+
+ switch (result) {
+ // -1 shouldn't actually occur because it means
+ // that we're in release mode.
+ case -1:
+ case cBreak:
+ return true;
+ break;
+
+ case cIgnore:
+ return false;
+ break;
+
+ case cAbort:
+ exit(-1);
+ break;
+ }
+
+ // Should never get here
+ return false;
+}
+
+
+bool _handleErrorCheck_(
+ const char* expression,
+ const std::string& message,
+ const char* filename,
+ int lineNumber,
+ bool useGuiPrompt) {
+
+ std::string dialogTitle = "Critical Error";
+ std::string dialogText = "";
+
+ createErrorMessage(expression, message, filename, lineNumber, dialogTitle, dialogText);
+
+ // Log the error
+ Log::common()->print(std::string("\n**************************\n\n") + dialogTitle + "\n" + dialogText);
+ #ifdef G3D_WIN32
+ DWORD lastErr = GetLastError();
+ (void)lastErr;
+ postToClipboard(dialogText.c_str());
+ debugPrintf("\n%s\n", dialogText.c_str());
+ #endif
+
+ static const char* choices[] = {"Ok"};
+
+ const std::string& m =
+ std::string("An internal error has occured in this program and it will now close. "
+ "The specific error is below. More information has been saved in \"") +
+ Log::getCommonLogFilename() + "\".\n" + dialogText;
+
+ int result = G3D::prompt("Error", m.c_str(), (const char**)choices, 1, useGuiPrompt);
+ (void)result;
+
+ return true;
+}
+
+
+#ifdef G3D_WIN32
+static HCURSOR oldCursor;
+static RECT oldCursorRect;
+static POINT oldCursorPos;
+static int oldShowCursorCount;
+#endif
+
+void _releaseInputGrab_() {
+ #ifdef G3D_WIN32
+
+ GetCursorPos(&oldCursorPos);
+
+ // Stop hiding the cursor if the application hid it.
+ oldShowCursorCount = ShowCursor(true) - 1;
+
+ if (oldShowCursorCount < -1) {
+ for (int c = oldShowCursorCount; c < -1; ++c) {
+ ShowCursor(true);
+ }
+ }
+
+ // Set the default cursor in case the application
+ // set the cursor to NULL.
+ oldCursor = GetCursor();
+ SetCursor(LoadCursor(NULL, IDC_ARROW));
+
+ // Allow the cursor full access to the screen
+ GetClipCursor(&oldCursorRect);
+ ClipCursor(NULL);
+
+ #elif defined(G3D_LINUX)
+#if SOMEONE_MADE_THIS_USEFUL
+ if (x11Display != NULL) {
+ XUngrabPointer(x11Display, CurrentTime);
+ XUngrabKeyboard(x11Display, CurrentTime);
+ if (x11Window != 0) {
+ //XUndefineCursor(x11Display, x11Window);
+ // TODO: Note that we leak this cursor; it should be
+ // freed in the restore code.
+ Cursor c = XCreateFontCursor(x11Display, 68);
+ XDefineCursor(x11Display, x11Window, c);
+ }
+ XSync(x11Display, false);
+ XAllowEvents(x11Display, AsyncPointer, CurrentTime);
+ XFlush(x11Display);
+ }
+#endif
+ #elif defined(G3D_OSX)
+ // TODO: OS X
+ #endif
+}
+
+
+void _restoreInputGrab_() {
+ #ifdef G3D_WIN32
+
+ // Restore the old clipping region
+ ClipCursor(&oldCursorRect);
+
+ SetCursorPos(oldCursorPos.x, oldCursorPos.y);
+
+ // Restore the old cursor
+ SetCursor(oldCursor);
+
+ // Restore old visibility count
+ if (oldShowCursorCount < 0) {
+ for (int c = 0; c > oldShowCursorCount; --c) {
+ ShowCursor(false);
+ }
+ }
+
+ #elif defined(G3D_LINUX)
+ // TODO: Linux
+ #elif defined(G3D_OSX)
+ // TODO: OS X
+ #endif
+}
+
+
+}; // internal namespace
+
+void setAssertionHook(AssertionHook hook) {
+ G3D::_internal::_debugHook = hook;
+}
+
+AssertionHook assertionHook() {
+ return G3D::_internal::_debugHook;
+}
+
+void setFailureHook(AssertionHook hook) {
+ G3D::_internal::_failureHook = hook;
+}
+
+AssertionHook failureHook() {
+ return G3D::_internal::_failureHook;
+}
+
+
+void setConsolePrintHook(ConsolePrintHook h) {
+ G3D::_internal::_consolePrintHook = h;
+}
+
+ConsolePrintHook consolePrintHook() {
+ return G3D::_internal::_consolePrintHook;
+}
+
+
+std::string __cdecl debugPrint(const std::string& s) {
+# ifdef G3D_WIN32
+ const int MAX_STRING_LEN = 1024;
+
+ // Windows can't handle really long strings sent to
+ // the console, so we break the string.
+ if (s.size() < MAX_STRING_LEN) {
+ OutputDebugStringA(s.c_str());
+ } else {
+ for (unsigned int i = 0; i < s.size(); i += MAX_STRING_LEN) {
+ std::string sub = s.substr(i, MAX_STRING_LEN);
+ OutputDebugStringA(sub.c_str());
+ }
+ }
+# else
+ fprintf(stderr, "%s", s.c_str());
+ fflush(stderr);
+# endif
+
+ return s;
+}
+
+std::string __cdecl debugPrintf(const char* fmt ...) {
+ va_list argList;
+ va_start(argList, fmt);
+ std::string s = G3D::vformat(fmt, argList);
+ va_end(argList);
+
+ return debugPrint(s);
+// return debugPrint(consolePrint(s));
+}
+
+std::string consolePrint(const std::string& s) {
+ FILE* L = Log::common()->getFile();
+ fprintf(L, "%s", s.c_str());
+
+ if (consolePrintHook()) {
+ consolePrintHook()(s);
+ }
+
+ fflush(L);
+ return s;
+}
+
+
+std::string __cdecl consolePrintf(const char* fmt ...) {
+ va_list argList;
+ va_start(argList, fmt);
+ std::string s = G3D::vformat(fmt, argList);
+ va_end(argList);
+
+ return consolePrint(s);
+}
+
+} // namespace
+
+#ifdef _MSC_VER
+# pragma warning (pop)
+#endif
diff --git a/dep/src/g3dlite/fileutils.cpp b/dep/src/g3dlite/fileutils.cpp
new file mode 100644
index 00000000000..3f5eb579ba9
--- /dev/null
+++ b/dep/src/g3dlite/fileutils.cpp
@@ -0,0 +1,1165 @@
+/**
+ @file fileutils.cpp
+
+ @author Morgan McGuire, graphics3d.com
+
+ @author 2002-06-06
+ @edited 2010-02-05
+ */
+
+#include <cstring>
+#include <cstdio>
+#include "G3D/platform.h"
+#include "G3D/fileutils.h"
+#include "G3D/BinaryInput.h"
+#include "G3D/BinaryOutput.h"
+#include "G3D/g3dmath.h"
+#include "G3D/stringutils.h"
+#include "G3D/Set.h"
+#include "G3D/g3dfnmatch.h"
+
+#include <sys/stat.h>
+#include <sys/types.h>
+#if _HAVE_ZIP
+ #include "zip.h"
+#endif
+
+#ifdef G3D_WIN32
+ // Needed for _getcwd
+ #include <direct.h>
+ #include <io.h>
+#else
+ #include <dirent.h>
+ #include <fnmatch.h>
+ #include <unistd.h>
+ #define _getcwd getcwd
+ #define _stat stat
+#endif
+
+
+namespace G3D {
+
+namespace _internal {
+ Set<std::string> currentFilesUsed;
+}
+
+std::string pathConcat(const std::string& dirname, const std::string& file) {
+ // Ensure that the directory ends in a slash
+ if ((dirname.size() != 0) &&
+ (dirname[dirname.size() - 1] != '/') &&
+ (dirname[dirname.size() - 1] != '\\') &&
+ (dirname[dirname.size() - 1] != ':')) {
+ return dirname + '/' + file;
+ } else {
+ return dirname + file;
+ }
+}
+
+std::string resolveFilename(const std::string& filename) {
+ if (filename.size() >= 1) {
+ if ((filename[0] == '/') || (filename[0] == '\\')) {
+ // Already resolved
+ return filename;
+ } else {
+
+ #ifdef G3D_WIN32
+ if ((filename.size() >= 2) && (filename[1] == ':')) {
+ // There is a drive spec on the front.
+ if ((filename.size() >= 3) && ((filename[2] == '\\') ||
+ (filename[2] == '/'))) {
+ // Already fully qualified
+ return filename;
+ } else {
+ // The drive spec is relative to the
+ // working directory on that drive.
+ debugAssertM(false, "Files of the form d:path are"
+ " not supported (use a fully qualified"
+ " name).");
+ return filename;
+ }
+ }
+ #endif
+ }
+ }
+
+ char buffer[1024];
+
+ // Prepend the working directory.
+ _getcwd(buffer, 1024);
+
+ return format("%s/%s", buffer, filename.c_str());
+}
+
+bool zipfileExists(const std::string& filename) {
+ std::string outZipfile;
+ std::string outInternalFile;
+ return zipfileExists(filename, outZipfile, outInternalFile);
+}
+
+std::string readWholeFile(
+ const std::string& filename) {
+
+ _internal::currentFilesUsed.insert(filename);
+
+ std::string s;
+
+ debugAssert(filename != "");
+ if (fileExists(filename, false)) {
+
+ int64 length = fileLength(filename);
+
+ char* buffer = (char*)System::alignedMalloc(length + 1, 16);
+ debugAssert(buffer);
+ FILE* f = fopen(filename.c_str(), "rb");
+ debugAssert(f);
+ int ret = fread(buffer, 1, length, f);
+ debugAssert(ret == length);(void)ret;
+ fclose(f);
+
+ buffer[length] = '\0';
+ s = std::string(buffer);
+
+ System::alignedFree(buffer);
+
+ } else if (zipfileExists(filename)) {
+
+ void* zipBuffer;
+ size_t length;
+ zipRead(filename, zipBuffer, length);
+
+ char* buffer = (char*)System::alignedMalloc(length + 1, 16);
+ System::memcpy(buffer,zipBuffer, length + 1);
+ zipClose(zipBuffer);
+
+ buffer[length] = '\0';
+ s = std::string(buffer);
+ System::alignedFree(buffer);
+ } else {
+ debugAssertM(false, filename + " not found");
+ }
+
+ return s;
+}
+
+
+void zipRead(const std::string& file,
+ void*& data,
+ size_t& length) {
+ std::string zip, desiredFile;
+#if _HAVE_ZIP
+ if (zipfileExists(file, zip, desiredFile)) {
+ struct zip *z = zip_open( zip.c_str(), ZIP_CHECKCONS, NULL );
+ {
+ struct zip_stat info;
+ zip_stat_init( &info ); // TODO: Docs unclear if zip_stat_init is required.
+ zip_stat( z, desiredFile.c_str(), ZIP_FL_NOCASE, &info );
+ length = info.size;
+ // sets machines up to use MMX, if they want
+ data = System::alignedMalloc(length, 16);
+ struct zip_file *zf = zip_fopen( z, desiredFile.c_str(), ZIP_FL_NOCASE );
+ {
+ int test = zip_fread( zf, data, length );
+ debugAssertM((size_t)test == length,
+ desiredFile + " was corrupt because it unzipped to the wrong size.");
+ (void)test;
+ }
+ zip_fclose( zf );
+ }
+ zip_close( z );
+ } else {
+ data = NULL;
+ }
+#else
+ data = NULL;
+#endif
+}
+
+
+void zipClose(void* data) {
+ System::alignedFree(data);
+}
+
+
+int64 fileLength(const std::string& filename) {
+ struct _stat st;
+ int result = _stat(filename.c_str(), &st);
+
+ if (result == -1) {
+#if _HAVE_ZIP
+ std::string zip, contents;
+ if(zipfileExists(filename, zip, contents)){
+ int64 requiredMem;
+
+ struct zip *z = zip_open( zip.c_str(), ZIP_CHECKCONS, NULL );
+ debugAssertM(z != NULL, zip + ": zip open failed.");
+ {
+ struct zip_stat info;
+ zip_stat_init( &info ); // TODO: Docs unclear if zip_stat_init is required.
+ int success = zip_stat( z, contents.c_str(), ZIP_FL_NOCASE, &info );
+ debugAssertM(success == 0, zip + ": " + contents + ": zip stat failed.");
+ requiredMem = info.size;
+ }
+ zip_close( z );
+ return requiredMem;
+ } else {
+ return -1;
+ }
+#else
+ return -1;
+#endif
+ }
+
+ return st.st_size;
+}
+
+/** Used by robustTmpfile. Returns nonzero if fread, fwrite, and fseek all
+succeed on the file.
+ @author Morgan McGuire, http://graphics.cs.williams.edu */
+static int isFileGood(FILE* f) {
+
+ int x, n, result;
+
+ /* Must be a valid file handle */
+ if (f == NULL) {
+ return 0;
+ }
+
+ /* Try to write */
+ x = 1234;
+ n = fwrite(&x, sizeof(int), 1, f);
+
+ if (n != 1) {
+ return 0;
+ }
+
+ /* Seek back to the beginning */
+ result = fseek(f, 0, SEEK_SET);
+ if (result != 0) {
+ return 0;
+ }
+
+ /* Read */
+ n = fread(&x, sizeof(int), 1, f);
+ if (n != 1) {
+ return 0;
+ }
+
+ /* Seek back to the beginning again */
+ fseek(f, 0, SEEK_SET);
+
+ return 1;
+}
+
+FILE* createTempFile() {
+ FILE* t = NULL;
+
+//# ifdef G3D_WIN32
+ t = tmpfile();
+//# else
+// // On Unix, tmpfile generates a warning for any code that links against it.
+// const char* tempfilename = "/tmp/g3dtemp.XXXXXXXX";
+// mktemp(tempfilename);
+// t = fopen(tempfilename, "w");
+//# endif
+
+# ifdef _WIN32
+ char* n = NULL;
+# endif
+ char name[256];
+
+ if (isFileGood(t)) {
+ return t;
+ }
+
+# ifdef G3D_WIN32
+ /* tmpfile failed; try the tmpnam routine */
+ t = fopen(tmpnam(NULL), "w+");
+ if (isFileGood(t)) {
+ return t;
+ }
+
+ n = _tempnam("c:/tmp/", "t");
+ /* Try to create something in C:\tmp */
+ t = fopen(n, "w+");
+ if (isFileGood(t)) {
+ return t;
+ }
+
+ /* Try c:\temp */
+ n = _tempnam("c:/temp/", "t");
+ t = fopen(n, "w+");
+ if (isFileGood(t)) {
+ return t;
+ }
+
+ /* try the current directory */
+ n = _tempnam("./", "t");
+ t = fopen(n, "w+");
+ if (isFileGood(t)) {
+ return t;
+ }
+
+ sprintf(name, "%s/tmp%d", "c:/temp", rand());
+ t = fopen(name, "w+");
+ if (isFileGood(t)) {
+ return t;
+ }
+
+ /* Try some hardcoded paths */
+ sprintf(name, "%s/tmp%d", "c:/tmp", rand());
+ t = fopen(name, "w+");
+ if (isFileGood(t)) {
+ return t;
+ }
+# else
+ sprintf(name, "%s/tmp%d", "/tmp", rand());
+ t = fopen(name, "w+");
+ if (isFileGood(t)) {
+ return t;
+ }
+#endif
+
+ sprintf(name, "tmp%d", rand());
+ t = fopen(name, "w+");
+ if (isFileGood(t)) {
+ return t;
+ }
+
+ fprintf(stderr, "Unable to create a temporary file; robustTmpfile returning NULL\n");
+
+ return NULL;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+void writeWholeFile(
+ const std::string& filename,
+ const std::string& str,
+ bool flush) {
+
+ // Make sure the directory exists.
+ std::string root, base, ext, path;
+ Array<std::string> pathArray;
+ parseFilename(filename, root, pathArray, base, ext);
+
+ path = root + stringJoin(pathArray, '/');
+ if (! fileExists(path, false)) {
+ createDirectory(path);
+ }
+
+ FILE* file = fopen(filename.c_str(), "wb");
+
+ debugAssert(file);
+
+ fwrite(str.c_str(), str.size(), 1, file);
+
+ if (flush) {
+ fflush(file);
+ }
+ fclose(file);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+/**
+ Creates the directory (which may optionally end in a /)
+ and any parents needed to reach it.
+ */
+void createDirectory(
+ const std::string& dir) {
+
+ if (dir == "") {
+ return;
+ }
+
+ std::string d;
+
+ // Add a trailing / if there isn't one.
+ switch (dir[dir.size() - 1]) {
+ case '/':
+ case '\\':
+ d = dir;
+ break;
+
+ default:
+ d = dir + "/";
+ }
+
+ // If it already exists, do nothing
+ if (fileExists(d.substr(0, d.size() - 1)), false) {
+ return;
+ }
+
+ // Parse the name apart
+ std::string root, base, ext;
+ Array<std::string> path;
+
+ std::string lead;
+ parseFilename(d, root, path, base, ext);
+ debugAssert(base == "");
+ debugAssert(ext == "");
+
+ // Begin with an extra period so "c:\" becomes "c:\.\" after
+ // appending a path and "c:" becomes "c:.\", not root: "c:\"
+ std::string p = root + ".";
+
+ // Create any intermediate that doesn't exist
+ for (int i = 0; i < path.size(); ++i) {
+ p += "/" + path[i];
+ if (! fileExists(p, false)) {
+ // Windows only requires one argument to mkdir,
+ // where as unix also requires the permissions.
+# ifndef G3D_WIN32
+ mkdir(p.c_str(), 0777);
+# else
+ _mkdir(p.c_str());
+# endif
+ }
+ }
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+class FileSystemCache {
+private:
+
+ Table<std::string, Array<std::string> > m_files;
+
+public:
+
+ bool fileExists(const std::string& filename) {
+ const std::string& path = resolveFilename(filenamePath(filename));
+ const std::string& name = filenameBaseExt(filename);
+
+ bool neverBeforeSeen = false;
+ Array<std::string>& fileList = m_files.getCreate(path, neverBeforeSeen);
+ if (neverBeforeSeen) {
+ if (! G3D::fileExists(path, true, false)) {
+ // The path itself doesn't exist... back out our insertion (which makes fileList& invalid)
+ m_files.remove(path);
+ return false;
+ }
+
+ std::string spec = pathConcat(path, "*");
+
+ // Will automatically recurse into zipfiles
+ getFiles(spec, fileList);
+ getDirs(spec, fileList);
+
+# ifdef G3D_WIN32
+ {
+ // Case insensitive
+ for (int i = 0; i < fileList.size(); ++i) {
+ fileList[i] = toLower(fileList[i]);
+ }
+ }
+# endif
+ }
+
+ if (filenameContainsWildcards(name)) {
+ // See if anything matches
+ for (int i = 0; i < fileList.size(); ++i) {
+ if (g3dfnmatch(name.c_str(), fileList[i].c_str(), 0) == 0) {
+ return true;
+ }
+ }
+ return false;
+ } else {
+ // On windows, this is a lower-lower comparison, so it is case insensitive
+ return fileList.contains(name);
+ }
+ }
+
+ void clear() {
+ m_files.clear();
+ }
+
+ static FileSystemCache& instance() {
+ static FileSystemCache i;
+ return i;
+ }
+};
+
+
+void clearFileSystemCache() {
+ FileSystemCache::instance().clear();
+}
+
+bool fileExists
+(const std::string& _filename,
+ bool lookInZipfiles,
+ bool trustCache) {
+
+ if (_filename.empty()) {
+ return false;
+ }
+
+ // Remove trailing slash from directories
+ const std::string& filename = (endsWith(_filename, "/") || endsWith(_filename, "\\")) ? _filename.substr(0, _filename.length() - 1) : _filename;
+
+ if (trustCache && lookInZipfiles) {
+# ifdef G3D_WIN32
+ // Case insensitive
+ return FileSystemCache::instance().fileExists(toLower(filename));
+# else
+ return FileSystemCache::instance().fileExists(filename);
+# endif
+ }
+
+ // Useful for debugging
+ //char curdir[1024]; _getcwd(curdir, 1024);
+
+ struct _stat st;
+ int ret = _stat(filename.c_str(), &st);
+
+ // _stat returns zero on success
+ bool exists = (ret == 0);
+
+ if (! exists && lookInZipfiles) {
+ // Does not exist standalone, but might exist in a zipfile
+
+ // These output arguments will be ignored
+ std::string zipDir, internalPath;
+ return zipfileExists(filename, zipDir, internalPath);
+ } else {
+ return exists;
+ }
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+#if _HAVE_ZIP
+/* Helper methods for zipfileExists()*/
+// Given a string (the drive) and an array (the path), computes the directory
+static void _zip_resolveDirectory(std::string& completeDir, const std::string& drive, const Array<std::string>& path, const int length){
+ completeDir = drive;
+ int tempLength;
+ // if the given length is longer than the array, we correct it
+ if(length > path.length()){
+ tempLength = path.length();
+ } else{
+ tempLength = length;
+ }
+
+ for(int t = 0; t < tempLength; ++t){
+ if(t > 0){
+ completeDir += "/";
+ }
+ completeDir += path[t];
+ }
+}
+
+
+// assumes that zipDir references a .zip file
+static bool _zip_zipContains(const std::string& zipDir, const std::string& desiredFile){
+ struct zip *z = zip_open( zipDir.c_str(), ZIP_CHECKCONS, NULL );
+ //the last parameter, an int, determines case sensitivity:
+ //1 is sensitive, 2 is not, 0 is default
+ int test = zip_name_locate( z, desiredFile.c_str(), ZIP_FL_NOCASE );
+ zip_close( z );
+ if(test == -1){
+ return false;
+ }
+ return true;
+}
+#endif
+
+// If no zipfile exists, outZipfile and outInternalFile are unchanged
+bool zipfileExists(const std::string& filename, std::string& outZipfile,
+ std::string& outInternalFile){
+#if _HAVE_ZIP
+ Array<std::string> path;
+ std::string drive, base, ext, zipfile, infile;
+ parseFilename(filename, drive, path, base, ext);
+
+ // Put the filename back together
+ if ((base != "") && (ext != "")) {
+ infile = base + "." + ext;
+ } else {
+ infile = base + ext;
+ }
+
+ // Remove "." from path
+ for (int i = 0; i < path.length(); ++i) {
+ if (path[i] == ".") {
+ path.remove(i);
+ --i;
+ }
+ }
+
+ // Remove ".." from path
+ for (int i = 1; i < path.length(); ++i) {
+ if ((path[i] == "..") && (i > 0) && (path[i - 1] != "..")) {
+ // Remove both i and i - 1
+ path.remove(i - 1, 2);
+ i -= 2;
+ }
+ }
+
+ // Walk the path backwards, accumulating pieces onto the infile until
+ // we find a zipfile that contains it
+ for (int t = 0; t < path.length(); ++t){
+ _zip_resolveDirectory(zipfile, drive, path, path.length() - t);
+ if (t > 0) {
+ infile = path[path.length() - t] + "/" + infile;
+ }
+
+ if (endsWith(zipfile, "..")) {
+ return false;
+ }
+
+ if (fileExists(zipfile, false)) {
+ // test if it actually is a zipfile
+ // if not, return false, a bad
+ // directory structure has been given,
+ // not a .zip
+ if (isZipfile(zipfile)){
+
+ if (_zip_zipContains(zipfile, infile)){
+ outZipfile = zipfile;
+ outInternalFile = infile;
+ return true;
+ } else {
+ return false;
+ }
+ } else {
+ // the directory structure was valid but did not point to a .zip
+ return false;
+ }
+ }
+
+ }
+#endif
+ // not a valid directory structure ever,
+ // obviously no .zip was found within the path
+ return false;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+std::string generateFilenameBase(const std::string& prefix, const std::string& suffix) {
+ Array<std::string> exist;
+
+ // Note "template" is a reserved word in C++
+ std::string templat = prefix + System::currentDateString() + "_";
+ getFiles(templat + "*", exist);
+
+ // Remove extensions
+ for (int i = 0; i < exist.size(); ++i) {
+ exist[i] = filenameBase(exist[i]);
+ }
+
+ int num = 0;
+ std::string result;
+ templat += "%03d" + suffix;
+ do {
+ result = format(templat.c_str(), num);
+ ++num;
+ } while (exist.contains(result));
+
+ return result;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+void copyFile(
+ const std::string& source,
+ const std::string& dest) {
+
+ #ifdef G3D_WIN32
+ CopyFileA(source.c_str(), dest.c_str(), FALSE);
+ #else
+ // TODO: don't use BinaryInput and BinaryOutput
+ // Read it all in, then dump it out
+ BinaryInput in(source, G3D_LITTLE_ENDIAN);
+ BinaryOutput out(dest, G3D_LITTLE_ENDIAN);
+ out.writeBytes(in.getCArray(), in.size());
+ out.commit(false);
+ #endif
+}
+
+//////////////////////////////////////////////////////////////////////////////
+
+void parseFilename(
+ const std::string& filename,
+ std::string& root,
+ Array<std::string>& path,
+ std::string& base,
+ std::string& ext) {
+
+ std::string f = filename;
+
+ root = "";
+ path.clear();
+ base = "";
+ ext = "";
+
+ if (f == "") {
+ // Empty filename
+ return;
+ }
+
+ // See if there is a root/drive spec.
+ if ((f.size() >= 2) && (f[1] == ':')) {
+
+ if ((f.size() > 2) && isSlash(f[2])) {
+
+ // e.g. c:\foo
+ root = f.substr(0, 3);
+ f = f.substr(3, f.size() - 3);
+
+ } else {
+
+ // e.g. c:foo
+ root = f.substr(2);
+ f = f.substr(2, f.size() - 2);
+
+ }
+
+ } else if ((f.size() >= 2) & isSlash(f[0]) && isSlash(f[1])) {
+
+ // e.g. //foo
+ root = f.substr(0, 2);
+ f = f.substr(2, f.size() - 2);
+
+ } else if (isSlash(f[0])) {
+
+ root = f.substr(0, 1);
+ f = f.substr(1, f.size() - 1);
+
+ }
+
+ // Pull the extension off
+ {
+ // Find the period
+ size_t i = f.rfind('.');
+
+ if (i != std::string::npos) {
+ // Make sure it is after the last slash!
+ size_t j = iMax(f.rfind('/'), f.rfind('\\'));
+ if ((j == std::string::npos) || (i > j)) {
+ ext = f.substr(i + 1, f.size() - i - 1);
+ f = f.substr(0, i);
+ }
+ }
+ }
+
+ // Pull the basename off
+ {
+ // Find the last slash
+ size_t i = iMax(f.rfind('/'), f.rfind('\\'));
+
+ if (i == std::string::npos) {
+
+ // There is no slash; the basename is the whole thing
+ base = f;
+ f = "";
+
+ } else if ((i != std::string::npos) && (i < f.size() - 1)) {
+
+ base = f.substr(i + 1, f.size() - i - 1);
+ f = f.substr(0, i);
+
+ }
+ }
+
+ // Parse what remains into path.
+ size_t prev, cur = 0;
+
+ while (cur < f.size()) {
+ prev = cur;
+
+ // Allow either slash
+ size_t i = f.find('/', prev + 1);
+ size_t j = f.find('\\', prev + 1);
+ if (i == std::string::npos) {
+ i = f.size();
+ }
+
+ if (j == std::string::npos) {
+ j = f.size();
+ }
+
+ cur = iMin(i, j);
+
+ if (cur == std::string::npos) {
+ cur = f.size();
+ }
+
+ path.append(f.substr(prev, cur - prev));
+ ++cur;
+ }
+}
+
+
+/**
+ Helper for getFileList and getDirectoryList.
+
+ @param wantFiles If false, returns the directories, otherwise
+ returns the files.
+ @param includePath If true, the names include paths
+ */
+static void getFileOrDirListNormal
+(const std::string& filespec,
+ Array<std::string>& files,
+ bool wantFiles,
+ bool includePath) {
+
+ bool test = wantFiles ? true : false;
+
+ std::string path = "";
+
+ // Find the place where the path ends and the file-spec begins
+ size_t i = filespec.rfind('/');
+ size_t j = filespec.rfind('\\');
+
+ // Drive letters on Windows can separate a path
+ size_t k = filespec.rfind(':');
+
+ if (((j != std::string::npos) && (j > i)) ||
+ (i == std::string::npos)) {
+ i = j;
+ }
+
+ if (((k != std::string::npos) && (k > i)) ||
+ (i == std::string::npos)) {
+ i = k;
+ }
+
+ // If there is a path, pull it off
+ if (i != std::string::npos) {
+ path = filespec.substr(0, i + 1);
+ }
+
+ std::string prefix = path;
+
+ if (path.size() > 0) {
+ // Strip the trailing character
+ path = path.substr(0, path.size() - 1);
+ }
+
+# ifdef G3D_WIN32
+ {
+ struct _finddata_t fileinfo;
+
+ long handle = _findfirst(filespec.c_str(), &fileinfo);
+ int result = handle;
+
+ while (result != -1) {
+ if ((((fileinfo.attrib & _A_SUBDIR) == 0) == test) &&
+ strcmp(fileinfo.name, ".") &&
+ strcmp(fileinfo.name, "..")) {
+
+ if (includePath) {
+ files.append(prefix + fileinfo.name);
+ } else {
+ files.append(fileinfo.name);
+ }
+ }
+
+ result = _findnext(handle, &fileinfo);
+ }
+ }
+# else
+ {
+ if (path == "") {
+ // Empty paths don't work on Unix
+ path = ".";
+ }
+
+ // Unix implementation
+ DIR* dir = opendir(path.c_str());
+
+ if (dir != NULL) {
+ struct dirent* entry = readdir(dir);
+
+ while (entry != NULL) {
+
+ // Exclude '.' and '..'
+ if ((strcmp(entry->d_name, ".") != 0) &&
+ (strcmp(entry->d_name, "..") != 0)) {
+
+ // Form a name with a path
+ std::string filename = prefix + entry->d_name;
+ // See if this is a file or a directory
+ struct _stat st;
+ bool exists = _stat(filename.c_str(), &st) != -1;
+
+ if (exists &&
+
+ // Make sure it has the correct type
+ (((st.st_mode & S_IFDIR) == 0) == test) &&
+
+ // Make sure it matches the wildcard
+ (fnmatch(filespec.c_str(),
+ filename.c_str(),
+ FNM_PATHNAME) == 0)) {
+
+ if (includePath) {
+ files.append(filename);
+ } else {
+ files.append(entry->d_name);
+ }
+ }
+ }
+
+ entry = readdir(dir);
+ }
+ closedir(dir);
+ }
+ }
+# endif
+}
+
+#if _HAVE_ZIP
+/**
+ @param path The zipfile name (no trailing slash)
+ @param prefix Directory inside the zipfile. No leading slash, must have trailing slash if non-empty.
+ @param file Name inside the zipfile that we are testing to see if it matches prefix + "*"
+ */
+static void _zip_addEntry(const std::string& path,
+ const std::string& prefix,
+ const std::string& file,
+ Set<std::string>& files,
+ bool wantFiles,
+ bool includePath) {
+
+ // Make certain we are within the desired parent folder (prefix)
+ if (beginsWith(file, prefix)) {
+ // validityTest was prefix/file
+
+ // Extract everything to the right of the prefix
+ std::string s = file.substr(prefix.length());
+
+ if (s == "") {
+ // This was the name of the prefix
+ return;
+ }
+
+ // See if there are any slashes
+ size_t slashPos = s.find('/');
+
+ bool add = false;
+
+ if (slashPos == std::string::npos) {
+ // No slashes, so s must be a file
+ add = wantFiles;
+ } else if (! wantFiles) {
+ // Not all zipfiles list directories as explicit entries.
+ // Because of this, if we're looking for directories and see
+ // any path longer than prefix, we must add the subdirectory.
+ // The Set will fix duplicates for us.
+ s = s.substr(0, slashPos);
+ add = true;
+ }
+
+ if (add) {
+ if (includePath) {
+ files.insert(path + "/" + prefix + s);
+ } else {
+ files.insert(s);
+ }
+ }
+ }
+}
+#endif
+
+static void getFileOrDirListZip(const std::string& path,
+ const std::string& prefix,
+ Array<std::string>& files,
+ bool wantFiles,
+ bool includePath){
+#if _HAVE_ZIP
+ struct zip *z = zip_open( path.c_str(), ZIP_CHECKCONS, NULL );
+
+ Set<std::string> fileSet;
+
+ int count = zip_get_num_files( z );
+ for( int i = 0; i < count; ++i ) {
+ struct zip_stat info;
+ zip_stat_init( &info ); // TODO: Docs unclear if zip_stat_init is required.
+ zip_stat_index( z, i, ZIP_FL_NOCASE, &info );
+ _zip_addEntry(path, prefix, info.name, fileSet, wantFiles, includePath);
+ }
+
+ zip_close( z );
+
+ fileSet.getMembers(files);
+#endif
+}
+
+
+static void determineFileOrDirList(
+ const std::string& filespec,
+ Array<std::string>& files,
+ bool wantFiles,
+ bool includePath) {
+
+ // if it is a .zip, prefix will specify the folder within
+ // whose contents we want to see
+ std::string prefix = "";
+ std::string path = filenamePath(filespec);
+
+ if ((path.size() > 0) && isSlash(path[path.size() - 1])) {
+ // Strip the trailing slash
+ path = path.substr(0, path.length() -1);
+ }
+
+ if ((path == "") || fileExists(path, false)) {
+ if ((path != "") && isZipfile(path)) {
+ // .zip should only work if * is specified as the Base + Ext
+ // Here, we have been asked for the root's contents
+ debugAssertM(filenameBaseExt(filespec) == "*", "Can only call getFiles/getDirs on zipfiles using '*' wildcard");
+ getFileOrDirListZip(path, prefix, files, wantFiles, includePath);
+ } else {
+ // It is a normal directory
+ getFileOrDirListNormal(filespec, files, wantFiles, includePath);
+ }
+ } else if (zipfileExists(filenamePath(filespec), path, prefix)) {
+ // .zip should only work if * is specified as the Base + Ext
+ // Here, we have been asked for the contents of a folder within the .zip
+ debugAssertM(filenameBaseExt(filespec) == "*", "Can only call getFiles/getDirs on zipfiles using '*' wildcard");
+ getFileOrDirListZip(path, prefix, files, wantFiles, includePath);
+ }
+}
+
+
+void getFiles(const std::string& filespec,
+ Array<std::string>& files,
+ bool includePath) {
+
+ determineFileOrDirList(filespec, files, true, includePath);
+}
+
+
+void getDirs(
+ const std::string& filespec,
+ Array<std::string>& files,
+ bool includePath) {
+
+ determineFileOrDirList(filespec, files, false, includePath);
+}
+
+
+std::string filenameBaseExt(const std::string& filename) {
+ int i = filename.rfind("/");
+ int j = filename.rfind("\\");
+
+ if ((j > i) && (j >= 0)) {
+ i = j;
+ }
+
+# ifdef G3D_WIN32
+ j = filename.rfind(":");
+ if ((i == -1) && (j >= 0)) {
+ i = j;
+ }
+# endif
+
+ if (i == -1) {
+ return filename;
+ } else {
+ return filename.substr(i + 1, filename.length() - i);
+ }
+}
+
+
+std::string filenameBase(const std::string& s) {
+ std::string drive;
+ std::string base;
+ std::string ext;
+ Array<std::string> path;
+
+ parseFilename(s, drive, path, base, ext);
+ return base;
+}
+
+
+std::string filenameExt(const std::string& filename) {
+ int i = filename.rfind(".");
+ if (i >= 0) {
+ return filename.substr(i + 1, filename.length() - i);
+ } else {
+ return "";
+ }
+}
+
+
+std::string filenamePath(const std::string& filename) {
+ int i = filename.rfind("/");
+ int j = filename.rfind("\\");
+
+ if ((j > i) && (j >= 0)) {
+ i = j;
+ }
+
+# ifdef G3D_WIN32
+ j = filename.rfind(":");
+ if ((i == -1) && (j >= 0)) {
+ i = j;
+ }
+# endif
+
+ if (i == -1) {
+ return "";
+ } else {
+ return filename.substr(0, i+1);
+ }
+}
+
+
+bool isZipfile(const std::string& filename) {
+
+ FILE* f = fopen(filename.c_str(), "r");
+ if (f == NULL) {
+ return false;
+ }
+ uint8 header[4];
+ fread(header, 4, 1, f);
+
+ const uint8 zipHeader[4] = {0x50, 0x4b, 0x03, 0x04};
+ for (int i = 0; i < 4; ++i) {
+ if (header[i] != zipHeader[i]) {
+ fclose(f);
+ return false;
+ }
+ }
+
+ fclose(f);
+ return true;
+}
+
+
+bool isDirectory(const std::string& filename) {
+ struct _stat st;
+ bool exists = _stat(filename.c_str(), &st) != -1;
+ return exists && ((st.st_mode & S_IFDIR) != 0);
+}
+
+
+bool filenameContainsWildcards(const std::string& filename) {
+ return (filename.find('*') != std::string::npos) || (filename.find('?') != std::string::npos);
+}
+
+
+bool fileIsNewer(const std::string& src, const std::string& dst) {
+ struct _stat sts;
+ bool sexists = _stat(src.c_str(), &sts) != -1;
+
+ struct _stat dts;
+ bool dexists = _stat(dst.c_str(), &dts) != -1;
+
+ return sexists && ((! dexists) || (sts.st_mtime > dts.st_mtime));
+}
+
+
+Array<std::string> filesUsed() {
+ Array<std::string> f;
+ _internal::currentFilesUsed.getMembers(f);
+ return f;
+}
+
+}
+
+#ifndef G3D_WIN32
+ #undef _stat
+#endif
diff --git a/dep/src/g3dlite/filter.cpp b/dep/src/g3dlite/filter.cpp
new file mode 100644
index 00000000000..72d6f0e05a7
--- /dev/null
+++ b/dep/src/g3dlite/filter.cpp
@@ -0,0 +1,32 @@
+/**
+ @file filter.cpp
+
+ @author Morgan McGuire, http://graphics.cs.williams.edu
+ @created 2007-03-01
+ @edited 2007-03-01
+
+ Copyright 2000-2007, Morgan McGuire.
+ All rights reserved.
+ */
+#include "G3D/filter.h"
+
+namespace G3D {
+
+void gaussian1D(Array<float>& coeff, int N, float std) {
+ coeff.resize(N);
+ float sum = 0.0f;
+ for (int i = 0; i < N; ++i) {
+ float x = i - (N - 1) / 2.0f;
+ float p = -square(x / std) / 2.0f;
+ float y = exp(p);
+ coeff[i] = y;
+ sum += y;
+ }
+
+ for (int i = 0; i < N; ++i) {
+ coeff[i] /= sum;
+ }
+}
+
+
+} // namespace
diff --git a/dep/src/g3dlite/format.cpp b/dep/src/g3dlite/format.cpp
index 13f01e26e6b..d9d1b516393 100644
--- a/dep/src/g3dlite/format.cpp
+++ b/dep/src/g3dlite/format.cpp
@@ -4,22 +4,13 @@
@author Morgan McGuire, graphics3d.com
@created 2000-09-09
- @edited 2006-04-30
+ @edited 2006-08-14
*/
#include "G3D/format.h"
#include "G3D/platform.h"
#include "G3D/System.h"
-#ifdef G3D_WIN32
- #include <math.h>
- #define vsnprintf _vsnprintf
- #define NEWLINE "\r\n"
-#else
- #include <stdarg.h>
- #define NEWLINE "\n"
-#endif
-
#ifdef _MSC_VER
// disable: "C++ exception handler used"
# pragma warning (push)
@@ -31,7 +22,7 @@
namespace G3D {
-std::string format(const char* fmt,...) {
+std::string __cdecl format(const char* fmt,...) {
va_list argList;
va_start(argList,fmt);
std::string result = vformat(fmt, argList);
@@ -40,10 +31,10 @@ std::string format(const char* fmt,...) {
return result;
}
-#if defined(G3D_WIN32) && (_MSC_VER >= 1300)
-// Both MSVC6 and 7 seem to use the non-standard vsnprintf
+#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 headers include vscprintf for some reason.
+// 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;
@@ -52,8 +43,9 @@ std::string vformat(const char *fmt, va_list argPtr) {
// allocate it on the stack because this saves
// the malloc/free time.
const int bufSize = 161;
- char stackBuffer[bufSize];
+ char stackBuffer[bufSize];
+ // MSVC does not support va_copy
int actualSize = _vscprintf(fmt, argPtr) + 1;
if (actualSize > bufSize) {
@@ -64,11 +56,11 @@ std::string vformat(const char *fmt, va_list argPtr) {
if (actualSize < maxSize) {
heapBuffer = (char*)System::malloc(maxSize + 1);
- vsnprintf(heapBuffer, maxSize, fmt, argPtr);
+ _vsnprintf(heapBuffer, maxSize, fmt, argPtr);
heapBuffer[maxSize] = '\0';
} else {
heapBuffer = (char*)System::malloc(actualSize);
- vsprintf(heapBuffer, fmt, argPtr);
+ vsprintf(heapBuffer, fmt, argPtr);
}
std::string formattedString(heapBuffer);
@@ -81,7 +73,7 @@ std::string vformat(const char *fmt, va_list argPtr) {
}
}
-#elif defined(G3D_WIN32) && (_MSC_VER < 1300)
+#elif defined(_MSC_VER) && (_MSC_VER < 1300)
std::string vformat(const char *fmt, va_list argPtr) {
// We draw the line at a 1MB string.
@@ -91,9 +83,12 @@ std::string vformat(const char *fmt, va_list argPtr) {
// allocate it on the stack because this saves
// the malloc/free time.
const int bufSize = 161;
- char stackBuffer[bufSize];
+ char stackBuffer[bufSize];
- int actualWritten = vsnprintf(stackBuffer, bufSize, fmt, argPtr);
+ // 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) {
@@ -101,8 +96,8 @@ std::string vformat(const char *fmt, va_list argPtr) {
int heapSize = 512;
double powSize = 1.0;
char* heapBuffer = (char*)System::malloc(heapSize);
-
- while ((vsnprintf(heapBuffer, heapSize, fmt, argPtr) == -1) &&
+
+ while ((_vsnprintf(heapBuffer, heapSize, fmt, argPtr) == -1) &&
(heapSize < maxSize)) {
heapSize = iCeil(heapSize * ::pow((double)2.0, powSize++));
@@ -133,18 +128,22 @@ std::string vformat(const char* fmt, va_list argPtr) {
const int bufSize = 161;
char stackBuffer[bufSize];
- int numChars = vsnprintf(stackBuffer, bufSize, fmt, argPtr);
+ 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));
- assert(heapBuffer);
+ debugAssert(heapBuffer);
int numChars2 = vsnprintf(heapBuffer, numChars + 1, fmt, argPtr);
- assert(numChars2 == numChars);
+ debugAssert(numChars2 == numChars);
+ (void)numChars2;
std::string result(heapBuffer);
-
+
System::free(heapBuffer);
return result;
@@ -160,13 +159,6 @@ std::string vformat(const char* fmt, va_list argPtr) {
} // namespace
-#ifdef G3D_WIN32
-# undef vsnprintf
-#endif
-
#ifdef _MSC_VER
# pragma warning (pop)
#endif
-
-#undef NEWLINE
-
diff --git a/dep/src/g3dlite/g3dfnmatch.cpp b/dep/src/g3dlite/g3dfnmatch.cpp
new file mode 100644
index 00000000000..39ef7b31914
--- /dev/null
+++ b/dep/src/g3dlite/g3dfnmatch.cpp
@@ -0,0 +1,204 @@
+/*-
+* Copyright (c) 1992, 1993
+*The Regents of the University of California. All rights reserved.
+*
+* Redistribution and use in source and binary forms, with or without
+* modification, are permitted provided that the following conditions
+* are met:
+* 1. Redistributions of source code must retain the above copyright
+* notice, this list of conditions and the following disclaimer.
+* 2. Redistributions in binary form must reproduce the above copyright
+* notice, this list of conditions and the following disclaimer in the
+* documentation and/or other materials provided with the distribution.
+* 3. All advertising materials mentioning features or use of this software
+* must display the following acknowledgement:
+*This product includes software developed by the University of
+*California, Berkeley and its contributors.
+* 4. Neither the name of the University nor the names of its contributors
+* may be used to endorse or promote products derived from this software
+* without specific prior written permission.
+*
+* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+* SUCH DAMAGE.
+*
+*@(#)fnmatch.h8.1 (Berkeley) 6/2/93
+*
+* From FreeBSD fnmatch.h 1.7
+* $Id: g3dfnmatch.cpp,v 1.2 2010/02/06 10:03:24 corey_taylor Exp $
+*/
+#include "G3D/g3dfnmatch.h"
+
+#ifdef G3D_WIN32
+
+#include <ctype.h>
+#include <string.h>
+#include <stdio.h>
+
+namespace G3D {
+
+#define EOS '\0'
+
+static const char *rangematch(const char *, char, int);
+
+int g3dfnmatch(const char *pattern, const char *string, int flags)
+{
+ const char *stringstart;
+ char c, test;
+
+ for (stringstart = string;;)
+ switch (c = *pattern++) {
+ case EOS:
+ if ((flags & FNM_LEADING_DIR) && *string == '/')
+ return (0);
+ return (*string == EOS ? 0 : FNM_NOMATCH);
+ case '?':
+ if (*string == EOS)
+ return (FNM_NOMATCH);
+ if (*string == '/' && (flags & FNM_PATHNAME))
+ return (FNM_NOMATCH);
+ if (*string == '.' && (flags & FNM_PERIOD) &&
+ (string == stringstart ||
+ ((flags & FNM_PATHNAME) && *(string - 1) == '/')))
+ return (FNM_NOMATCH);
+ ++string;
+ break;
+ case '*':
+ c = *pattern;
+ /* Collapse multiple stars. */
+ while (c == '*')
+ c = *++pattern;
+
+ if (*string == '.' && (flags & FNM_PERIOD) &&
+ (string == stringstart ||
+ ((flags & FNM_PATHNAME) && *(string - 1) == '/')))
+ return (FNM_NOMATCH);
+
+ /* Optimize for pattern with * at end or before /. */
+ if (c == EOS)
+ if (flags & FNM_PATHNAME)
+ return ((flags & FNM_LEADING_DIR) ||
+ strchr(string, '/') == NULL ?
+ 0 : FNM_NOMATCH);
+ else
+ return (0);
+ else if (c == '/' && flags & FNM_PATHNAME) {
+ if ((string = strchr(string, '/')) == NULL)
+ return (FNM_NOMATCH);
+ break;
+ }
+
+ /* General case, use recursion. */
+ while ((test = *string) != EOS) {
+ if (!rangematch(pattern, *string, flags & ~FNM_PERIOD))
+ return (0);
+ if (test == '/' && flags & FNM_PATHNAME)
+ break;
+ ++string;
+ }
+ return (FNM_NOMATCH);
+ case '[':
+ if (*string == EOS)
+ return (FNM_NOMATCH);
+ if (*string == '/' && flags & FNM_PATHNAME)
+ return (FNM_NOMATCH);
+ if ((pattern =
+ rangematch(pattern, *string, flags)) == NULL)
+ return (FNM_NOMATCH);
+ ++string;
+ break;
+ case '\\':
+ if (!(flags & FNM_NOESCAPE)) {
+ if ((c = *pattern++) == EOS) {
+ c = '\\';
+ --pattern;
+ }
+ }
+ /* FALLTHROUGH */
+ default:
+ if (c == *string)
+ ;
+ else if ((flags & FNM_CASEFOLD) &&
+ (tolower((unsigned char)c) ==
+ tolower((unsigned char)*string)))
+ ;
+ else if ((flags & FNM_PREFIX_DIRS) && *string == EOS &&
+ ((c == '/' && string != stringstart) ||
+ (string == stringstart+1 && *stringstart == '/')))
+ return (0);
+ else
+ return (FNM_NOMATCH);
+ string++;
+ break;
+ }
+ /* NOTREACHED */
+}
+
+static const char *
+rangematch(const char *pattern, char test, int flags)
+{
+ int negate, ok;
+ char c, c2;
+
+ /*
+ * A bracket expression starting with an unquoted circumflex
+ * character produces unspecified results (IEEE 1003.2-1992,
+ * 3.13.2). This implementation treats it like '!', for
+ * consistency with the regular expression syntax.
+ * J.T. Conklin (conklin@ngai.kaleida.com)
+ */
+ if ( (negate = (*pattern == '!' || *pattern == '^')) )
+ ++pattern;
+
+ if (flags & FNM_CASEFOLD)
+ test = tolower((unsigned char)test);
+
+ for (ok = 0; (c = *pattern++) != ']';) {
+ if (c == '\\' && !(flags & FNM_NOESCAPE))
+ c = *pattern++;
+ if (c == EOS)
+ return (NULL);
+
+ if (flags & FNM_CASEFOLD)
+ c = tolower((unsigned char)c);
+
+ if (*pattern == '-'
+ && (c2 = *(pattern+1)) != EOS && c2 != ']') {
+ pattern += 2;
+ if (c2 == '\\' && !(flags & FNM_NOESCAPE))
+ c2 = *pattern++;
+ if (c2 == EOS)
+ return (NULL);
+
+ if (flags & FNM_CASEFOLD)
+ c2 = tolower((unsigned char)c2);
+
+ if ((unsigned char)c <= (unsigned char)test &&
+ (unsigned char)test <= (unsigned char)c2)
+ ok = 1;
+ } else if (c == test)
+ ok = 1;
+ }
+ return (ok == negate ? NULL : pattern);
+}
+
+}
+
+#else
+
+namespace G3D {
+int g3dfnmatch(const char * a, const char *b, int c) {
+ return fnmatch(a, b, c);
+}
+}
+
+#endif
+
diff --git a/dep/src/g3dlite/g3dmath.cpp b/dep/src/g3dlite/g3dmath.cpp
new file mode 100644
index 00000000000..ad85e9efb9b
--- /dev/null
+++ b/dep/src/g3dlite/g3dmath.cpp
@@ -0,0 +1,108 @@
+/**
+ @file g3dmath.cpp
+
+ @author Morgan McGuire, graphics3d.com
+
+ @created 2001-06-02
+ @edited 2004-02-24
+ */
+
+#include "G3D/g3dmath.h"
+#include <cstdlib>
+#include <cstring>
+
+namespace G3D {
+
+float gaussRandom(float mean, float stdev) {
+
+ // Using Box-Mueller method from http://www.taygeta.com/random/gaussian.html
+ // Modified to specify standard deviation and mean of distribution
+ float w, x1, x2;
+
+ // Loop until w is less than 1 so that log(w) is negative
+ do {
+ x1 = uniformRandom(-1.0, 1.0);
+ x2 = uniformRandom(-1.0, 1.0);
+
+ w = float(square(x1) + square(x2));
+ } while (w > 1.0f);
+
+ // Transform to gassian distribution
+ // Multiply by sigma (stdev ^ 2) and add mean.
+ return x2 * (float)square(stdev) * sqrtf((-2.0f * logf(w) ) / w) + mean;
+}
+
+/**
+ This value should not be tested against directly, instead
+ G3D::isNan() and G3D::isFinite() will return reliable results. */
+double inf() {
+ return std::numeric_limits<double>::infinity();
+}
+
+bool isNaN(float x) {
+ static const float n = nan();
+ return memcmp(&x, &n, sizeof(float)) == 0;
+}
+
+bool isNaN(double x) {
+ static const double n = nan();
+ return memcmp(&x, &n, sizeof(double)) == 0;
+}
+
+
+/**
+ This value should not be tested against directly, instead
+ G3D::isNan() and G3D::isFinite() will return reliable results. */
+float finf() {
+ return std::numeric_limits<float>::infinity();
+}
+
+/** This value should not be tested against directly, instead
+ G3D::isNan() and G3D::isFinite() will return reliable results. */
+double nan() {
+ // double is a standard type and should have quiet NaN
+ return std::numeric_limits<double>::quiet_NaN();
+}
+
+float fnan() {
+ // double is a standard type and should have quiet NaN
+ return std::numeric_limits<float>::quiet_NaN();
+}
+
+
+int highestBit(uint32 x) {
+ // Binary search.
+ int base = 0;
+
+ if (x & 0xffff0000) {
+ base = 16;
+ x >>= 16;
+ }
+ if (x & 0x0000ff00) {
+ base += 8;
+ x >>= 8;
+ }
+ if (x & 0x000000f0) {
+ base += 4;
+ x >>= 4;
+ }
+
+ static const int lut[] = {-1,0,1,1,2,2,2,2,3,3,3,3,3,3,3,3};
+ return base + lut[x];
+}
+
+
+int iRandom(int low, int high) {
+ int r = iFloor(low + (high - low + 1) * (double)rand() / RAND_MAX);
+
+ // There is a *very small* chance of generating
+ // a number larger than high.
+ if (r > high) {
+ return high;
+ } else {
+ return r;
+ }
+}
+
+
+}
diff --git a/dep/src/g3dlite/license.cpp b/dep/src/g3dlite/license.cpp
new file mode 100644
index 00000000000..5049184cf9b
--- /dev/null
+++ b/dep/src/g3dlite/license.cpp
@@ -0,0 +1,73 @@
+/**
+ @file license.cpp
+
+ @author Morgan McGuire, graphics3d.com
+
+ @created 2004-04-15
+ @edited 2004-04-15
+*/
+
+#include "G3D/format.h"
+#include <string>
+
+namespace G3D {
+
+std::string license() {
+ return format(
+
+"This software is based in part on the PNG Reference Library which is\n"
+"Copyright (c) 2004 Glenn Randers-Pehrson\n\n"
+"This software is based in part on the work of the Independent JPEG Group.\n\n"
+"This software is based on part on the FFmpeg libavformat and libavcodec libraries\n"
+"(\"FFmpeg\", http://ffmpeg.mplayerhq.hu), which are included under the terms of the\n"
+"GNU Lesser General Public License (LGPL), (http://www.gnu.org/copyleft/lesser.html).\n\n"
+"%s"
+"This program uses the G3D Library (http://g3d.sf.net), which\n"
+"is licensed under the \"Modified BSD\" Open Source license. The G3D library\n"
+"source code is Copyright © 2000-2010, Morgan McGuire, All rights reserved.\n"
+"This program uses The OpenGL Extension Wrangler Library, which \n"
+"is licensed under the \"Modified BSD\" Open Source license. \n"
+"The OpenGL Extension Wrangler Library source code is\n"
+"Copyright (C) 2002-2008, Milan Ikits <milan ikits[]ieee org>\n"
+"Copyright (C) 2002-2008, Marcelo E. Magallon <mmagallo[]debian org>\n"
+"Copyright (C) 2002, Lev Povalahev\n"
+"All rights reserved.\n\n"
+"The Modified BSD license is below, and requires the following statement:\n"
+"\n"
+"Redistribution and use in source and binary forms, with or without \n"
+"modification, are permitted provided that the following conditions are met:\n"
+"\n"
+"* Redistributions of source code must retain the above copyright notice, \n"
+" this list of conditions and the following disclaimer.\n"
+"* Redistributions in binary form must reproduce the above copyright notice, \n"
+" this list of conditions and the following disclaimer in the documentation \n"
+" and/or other materials provided with the distribution.\n"
+"* The name of the author may be used to endorse or promote products \n"
+" derived from this software without specific prior written permission.\n"
+"\n"
+"THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\" \n"
+"AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE \n"
+"IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE\n"
+"ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE \n"
+"LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR \n"
+"CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF \n"
+"SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS\n"
+"INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN\n"
+"CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)\n"
+"ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF\n"
+"THE POSSIBILITY OF SUCH DAMAGE.\n"
+"\n\n"
+"G3D VERSION %d\n",
+
+#ifdef G3D_WIN32
+ "" // Win32 doesn't use SDL
+#else
+ "This software uses the Simple DirectMedia Layer library (\"SDL\",\n"
+ "http://www.libsdl.org), which is included under the terms of the\n"
+ "GNU Lesser General Public License, (http://www.gnu.org/copyleft/lesser.html).\n\n"
+#endif
+,
+G3D_VER);
+}
+
+}
diff --git a/dep/src/g3dlite/license.html b/dep/src/g3dlite/license.html
index 9bbb2ad5f9a..11c33882248 100644
--- a/dep/src/g3dlite/license.html
+++ b/dep/src/g3dlite/license.html
@@ -3,107 +3,113 @@
<head>
<meta http-equiv="Content-Type" content="text/html;charset=iso-8859-1">
- <title>G3D: License</title>
+ <title>G3D Innovation Engine: License</title>
<link href="stylesheet.css" rel="stylesheet" type="text/css">
- <style type="text/css">
- span.menu_tab a {
- background: #FFE183;
- border-width: 1px;
- border-style: solid;
- border-color: #FFB143;
- padding: 5px;
- margin: 0px;
- margin-left: 2px;
- font-size: 11px;
- font-weight: bold;
- font-family: arial;
- color: #000000;
- position: relative;
- display: inline;
- }
-
- span.menu_tab a:link, a:visited {
- text-decoration: none;
- }
- span.menu_tab a:hover {
- background: #FDEFA0;
- color: #000000;
- text-decoration: none;
- }
- .widthadjust {
- min-width: 800px;
- width: 80%;
- background: #FFFFFF;
- padding-left: 5px;
- padding-right: 5px;
- }
- * html .widthadjust {
- width: expression(document.body.clientWidth < 1000? "1000px": "auto";
- }
- </style>
</head>
<body style="width: 100%; background: #aaaaaa">
<table class="widthadjust" align=center><tr><td>
<table style="height: 44px; border-bottom-width: 1px; border-bottom-style: solid; border-bottom-color: #FFc161;" width=100% cellspacing=0 cellpadding=0>
+
+ <tr>
+ <td rowspan="2" valign="bottom" style="width: 51;">
+ <a href="index.html">
+ <img src="G3D-small-shadow.jpg" border="0" align="left">
+ </a>
+ </td>
+ <td style="width: 65%;" nowrap="true" valign="bottom">
+ <font size="2">
+ <center>
+ <a href="http://groups.google.com/group/g3d-users/topics">Support Forum</a> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
+ <a href="http://g3d.cvs.sourceforge.net/g3d/G3D/">Library Source</a> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
+ <a href="http://sourceforge.net/projects/g3d/">SourceForge Page</a> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
+ <a href="http://g3d.sf.net/">Web Page</a>
+ </center>
+ </font>
+ </td>
+ </tr>
<tr cellspacing=0 cellpadding=0>
- <td style="width: 70%;" nowrap=true>
- <img src="G3D-small-shadow.jpg">
- <span class="menu_tab"><a href="index.html">Contents</a></span>
- <span class="menu_tab"><a href="globals_func.html">Functions</a></span>
- <span class="menu_tab"><a href="classes.html">Classes</a></span>
- <span class="menu_tab"><a href="indexedbytopic.html">Topics</a></span>
- <span class="menu_tab"><a href="http://sourceforge.net/forum/forum.php?forum_id=262426">User Forum</a></span>
- <span class="menu_tab"><a href="http://cvs.sourceforge.net/viewcvs.py/g3d-cpp/cpp/">CVS</a></span>
- </td>
+<td style="width: 65%;" nowrap=true valign=bottom>
+
+ <span class="menu_tab">
+ <a href="http://sourceforge.net/apps/mediawiki/g3d/index.php?title=Main_Page">Wiki Doc</a>
+ </span>
+ <span class="menu_tab">
+ <a href="dataindex.html">Data</a>
+ </span>
+ <span class="menu_tab">
+ <a href="annotated.html">API Index</a>
+ </span>
+ <span class="menu_tab">
+ <a href="apiindex.html">APIs by Level</a>
+ </span>
+ <span class="menu_tab">
+ <a href="topicindex.html">APIs by Task</a>
+ </span>
+</td>
</tr>
</table>
-
+<br>
+<br>
<table cellspacing=0 cellpadding=0 width=100% >
<tr><td>
<!-- code goes here -->
-<!-- Generated by Doxygen 1.4.6-NO -->
-<h1><a class="anchor" name="license">License</a></h1>
-<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>
-<h2><a class="anchor" name="intent">
+
+<!-- Generated by Doxygen 1.6.1 -->
+<div class="contents">
+
+
+<h1><a class="anchor" id="license">License </a></h1><h2><a class="anchor" id="intent">
Intent of License</a></h2>
-(This section is informal and not legally binding.)<p>
-<br>
- This library is free code-- you can use it without charge and it is minimally legally encumbered. Unlike some other free libraries, we &lt;u&gt;do not&lt;/u&gt; 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 <a class="el" href="classG3D_1_1GImage.html">G3D::GImage</a> 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, <a class="el" href="namespaceG3D.html#2d6bccd0c2fa5b44882b7d0c732e2712">G3D::license</a> is a function that returns the license string you must put in your documentation. <a class="el" href="classG3D_1_1GApp.html">G3D::GApp</a> 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 <a class="el" href="namespaceG3D.html">G3D</a>. The <a class="el" href="namespaceG3D.html">G3D</a> 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 <a class="el" href="namespaceG3D.html">G3D</a> 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;<em><a href="mailto:matrix@graphics3d.com">matrix@graphics3d.com</a></em>&gt;<p>
-<hr>
-<h2><a class="anchor" name="reallicense">
+<p>(This section is informal and not legally binding.)</p>
+<p><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>
+<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>
+<p>The license for <a class="el" href="namespace_g3_d.html">G3D</a> itself and the libaries included in the <a class="el" href="namespace_g3_d.html">G3D</a> distribution create certain documentation obligations for you. For convenience, <a class="el" href="namespace_g3_d.html#a2d6bccd0c2fa5b44882b7d0c732e2712" title="G3D, SDL, and IJG libraries require license documentation to be distributed with...">G3D::license</a> is a function that returns the license string you must put in your documentation. <a class="el" href="class_g3_d_1_1_g_app.html" title="For each frame, the GApp has several tasks that can be implemented by overriding...">G3D::GApp</a> will automatically write a file (g3d-license.txt) to disk with the contents of this license unless you tell it not to, thus automatically satisfying your documentation requirement after the first time you run a <a class="el" href="namespace_g3_d.html">G3D</a> program.</p>
+<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 <a class="el" href="namespace_g3_d.html">G3D</a>. The <a class="el" href="namespace_g3_d.html">G3D</a> 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>
+<p>You are required by the BSD license to acknowledge <a class="el" href="namespace_g3_d.html">G3D</a> 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>
+<p>Likewise, you are encouraged but not required to submit patches to improve the library for the benefit of all. Post bugs, patches, and questions to the g3d-users forum linked at the top of this page. </p>
+<p>-Morgan McGuire &lt;<em><a href="mailto:morgan@cs.williams.edu">morgan@cs.williams.edu</a></em>&gt;</p>
+<hr/>
+<h2><a class="anchor" id="reallicense">
License</a></h2>
-<em><a class="el" href="namespaceG3D.html">G3D</a> 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> and <a href="libpng-LICENSE.txt">PNG Reference Library license</a></em><p>
-<code> <div align="center">
-<img src="http://opensource.org/trademarks/osi-certified/web/osi-certified-120x100.gif" alt="osi-certified-120x100.gif">
+<p><em><a class="el" href="namespace_g3_d.html">G3D</a> 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>.</em></p>
+<p><code> </p>
+<div align="center">
+<img src="http://opensource.org/trademarks/osi-certified/web/osi-certified-120x100.gif" alt="osi-certified-120x100.gif"/>
</div>
- </code><p>
-<code>This product uses software from the <a class="el" href="namespaceG3D.html">G3D</a> project (<a href="http://g3d-cpp.sf.net">http://g3d-cpp.sf.net</a>) </code><p>
-<code>Copyright &copy; 2000-2006, Morgan McGuire </code><p>
-<code>All rights reserved. </code><p>
-<code> Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: </code><p>
-<code> Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. </code><p>
-<code> 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. </code><p>
-<code> 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. </code><p>
-<code> 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 <a class="el" href="classG3D_1_1GImage.html">G3D::GImage</a> 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.</A>
+<p> </code></p>
+<p><code>This product uses software from the <a class="el" href="namespace_g3_d.html">G3D</a> project (<a href="http://g3d.sf.net">http://g3d.sf.net</a>) </code></p>
+<p><code>Copyright &copy; 2000-2010, Morgan McGuire </code></p>
+<p><code>All rights reserved. </code></p>
+<p><code> Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: </code></p>
+<p><code> Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. </code></p>
+<p><code> 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. </code></p>
+<p><code> 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. </code></p>
+<p><code> 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>
+</div>
+</A>
<hr><address style="align: right;"><small>
-Generated on Tue Jul 18 12:05:54 2006 for G3D by <a href="http://www.doxygen.org/index.html">
+G3D Innovation Engine documentation generated on Thu Mar 25 14:54:30 2010 using <a href="http://www.doxygen.org/index.html">
<img src="doxygen.png" alt="doxygen" align="middle" border=0 width=55 height=26>
-</a> 1.4.6-NO</small></address>
+</a> 1.6.1</small></address>
+ <!-- Removed to make page loading faster
Hosted by <A href="http://sourceforge.net"> <IMG src="http://sourceforge.net/sflogo.php?group_id=76879&amp;type=5" width="210" height="62" border="0" alt="SourceForge.net Logo"></A>
+-->
+
+<!-- Piwik -->
+<script type="text/javascript">
+var pkBaseURL = (("https:" == document.location.protocol) ? "https://apps.sourceforge.net/piwik/g3d/" : "http://apps.sourceforge.net/piwik/g3d/");
+document.write(unescape("%3Cscript src='" + pkBaseURL + "piwik.js' type='text/javascript'%3E%3C/script%3E"));
+</script><script type="text/javascript">
+piwik_action_name = '';
+piwik_idsite = 2;
+piwik_url = pkBaseURL + "piwik.php";
+piwik_log(piwik_action_name, piwik_idsite, piwik_url);
+</script>
+<object><noscript><p><img src="http://apps.sourceforge.net/piwik/g3d/piwik.php?idsite=2" alt="piwik"/></p></noscript></object>
+<!-- End Piwik Tag -->
+
</body>
</html>
diff --git a/dep/src/g3dlite/prompt.cpp b/dep/src/g3dlite/prompt.cpp
new file mode 100644
index 00000000000..6a28e6462b4
--- /dev/null
+++ b/dep/src/g3dlite/prompt.cpp
@@ -0,0 +1,729 @@
+/**
+ @file prompt.cpp
+
+ @author Morgan McGuire, http://graphics.cs.williams.edu
+ @cite Windows dialog interface by Max McGuire, mmcguire@ironlore.com
+ @cite Font setting code by Kurt Miller, kurt@flipcode.com
+
+ @created 2000-08-26
+ @edited 2005-01-14
+ */
+
+#include "G3D/prompt.h"
+#include "G3D/platform.h"
+
+#include <stdio.h>
+
+#ifdef G3D_WIN32
+# include <sstream>
+# include <conio.h>
+#else
+# define _getch getchar
+#endif
+
+#ifdef G3D_OSX
+
+/*#ifdef __LP64__
+# undef __LP64__
+#endif
+*/
+
+# include <Carbon/Carbon.h>
+
+/*
+#ifdef G3D_64BIT
+# define __LP64__
+#endif
+*/
+
+#endif
+
+namespace G3D {
+
+#ifdef G3D_WIN32
+
+namespace _internal {
+/**
+ Generic Win32 dialog facility.
+ @author Max McGuire
+ */
+class DialogTemplate {
+public:
+
+ DialogTemplate(LPCSTR caption, DWORD style,
+ int x, int y, int w, int h,
+ LPCSTR font = NULL, WORD fontSize = 8) {
+
+ usedBufferLength = sizeof(DLGTEMPLATE);
+ totalBufferLength = usedBufferLength;
+
+ dialogTemplate = (DLGTEMPLATE*)malloc(totalBufferLength);
+
+ dialogTemplate->style = style;
+
+ if (font != NULL) {
+ dialogTemplate->style |= DS_SETFONT;
+ }
+
+ dialogTemplate->x = (short)x;
+ dialogTemplate->y = (short)y;
+ dialogTemplate->cx = (short)w;
+ dialogTemplate->cy = (short)h;
+ dialogTemplate->cdit = 0;
+
+ dialogTemplate->dwExtendedStyle = 0;
+
+ // The dialog box doesn't have a menu or a special class
+ AppendData("\0", 2);
+ AppendData("\0", 2);
+
+ // Add the dialog's caption to the template
+
+ AppendString(caption);
+
+ if (font != NULL) {
+ AppendData(&fontSize, sizeof(WORD));
+ AppendString(font);
+ }
+ }
+
+ void AddComponent(LPCSTR type, LPCSTR caption, DWORD style, DWORD exStyle, int x, int y, int w, int h, WORD id) {
+
+ DLGITEMTEMPLATE item;
+
+ item.style = style;
+ item.x = (short)x;
+ item.y = (short)y;
+ item.cx = (short)w;
+ item.cy = (short)h;
+ item.id = id;
+
+ item.dwExtendedStyle = exStyle;
+
+ AppendData(&item, sizeof(DLGITEMTEMPLATE));
+
+ AppendString(type);
+ AppendString(caption);
+
+ WORD creationDataLength = 0;
+ AppendData(&creationDataLength, sizeof(WORD));
+
+ // Increment the component count
+ dialogTemplate->cdit++;
+
+ }
+
+
+ void AddButton(LPCSTR caption, DWORD style, DWORD exStyle, int x, int y, int w, int h, WORD id) {
+
+ AddStandardComponent(0x0080, caption, style, exStyle, x, y, w, h, id);
+
+ WORD creationDataLength = 0;
+ AppendData(&creationDataLength, sizeof(WORD));
+
+ }
+
+
+ void AddEditBox(LPCSTR caption, DWORD style, DWORD exStyle, int x, int y, int w, int h, WORD id) {
+
+ AddStandardComponent(0x0081, caption, style, exStyle, x, y, w, h, id);
+
+ WORD creationDataLength = 0;
+ AppendData(&creationDataLength, sizeof(WORD));
+
+ }
+
+
+ void AddStatic(LPCSTR caption, DWORD style, DWORD exStyle, int x, int y, int w, int h, WORD id) {
+
+ AddStandardComponent(0x0082, caption, style, exStyle, x, y, w, h, id);
+
+ WORD creationDataLength = 0;
+ AppendData(&creationDataLength, sizeof(WORD));
+
+ }
+
+
+ void AddListBox(LPCSTR caption, DWORD style, DWORD exStyle, int x, int y, int w, int h, WORD id) {
+
+ AddStandardComponent(0x0083, caption, style, exStyle, x, y, w, h, id);
+
+ WORD creationDataLength = sizeof(WORD) + 5 * sizeof(WCHAR);
+ AppendData(&creationDataLength, sizeof(WORD));
+
+ AppendString("TEST");
+
+ }
+
+
+ void AddScrollBar(LPCSTR caption, DWORD style, DWORD exStyle, int x, int y, int w, int h, WORD id) {
+
+ AddStandardComponent(0x0084, caption, style, exStyle, x, y, w, h, id);
+
+ WORD creationDataLength = 0;
+ AppendData(&creationDataLength, sizeof(WORD));
+
+ }
+
+
+ void AddComboBox(LPCSTR caption, DWORD style, DWORD exStyle, int x, int y, int w, int h, WORD id) {
+
+ AddStandardComponent(0x0085, caption, style, exStyle, x, y, w, h, id);
+
+ WORD creationDataLength = 0;
+ AppendData(&creationDataLength, sizeof(WORD));
+
+ }
+
+
+ /**
+ *
+ * Returns a pointer to the Win32 dialog template which the object
+ * represents. This pointer may become invalid if additional components
+ * are added to the template.
+ *
+ */
+ operator const DLGTEMPLATE*() const {
+ return dialogTemplate;
+ }
+
+ virtual ~DialogTemplate() {
+ free(dialogTemplate);
+ }
+
+protected:
+
+ void AddStandardComponent(WORD type, LPCSTR caption, DWORD style, DWORD exStyle,
+ int x, int y, int w, int h, WORD id, LPSTR font = NULL, WORD fontSize = 8) {
+
+ DLGITEMTEMPLATE item;
+
+ // DWORD align the beginning of the component data
+
+ AlignData(sizeof(DWORD));
+
+ item.style = style;
+ if (font != NULL) {
+ item.style |= DS_SETFONT;
+ }
+ item.x = (short)x;
+ item.y = (short)y;
+ item.cx = (short)w;
+ item.cy = (short)h;
+ item.id = id;
+
+ item.dwExtendedStyle = exStyle;
+
+ AppendData(&item, sizeof(DLGITEMTEMPLATE));
+
+ WORD preType = 0xFFFF;
+
+ AppendData(&preType, sizeof(WORD));
+ AppendData(&type, sizeof(WORD));
+
+ AppendString(caption);
+
+ if (font != NULL) {
+ AppendData(&fontSize, sizeof(WORD));
+ AppendString(font);
+ }
+
+ // Increment the component count
+ dialogTemplate->cdit++;
+ }
+
+
+ void AlignData(int size) {
+
+ int paddingSize = usedBufferLength % size;
+
+ if (paddingSize != 0) {
+ EnsureSpace(paddingSize);
+ usedBufferLength += paddingSize;
+ }
+
+ }
+
+ void AppendString(LPCSTR string) {
+
+ int length = MultiByteToWideChar(CP_ACP, 0, string, -1, NULL, 0);
+
+ WCHAR* wideString = (WCHAR*)malloc(sizeof(WCHAR) * length);
+ MultiByteToWideChar(CP_ACP, 0, string, -1, wideString, length);
+
+ AppendData(wideString, length * sizeof(WCHAR));
+ free(wideString);
+
+ }
+
+ void AppendData(const void* data, int dataLength) {
+
+ EnsureSpace(dataLength);
+
+ memcpy((char*)dialogTemplate + usedBufferLength, data, dataLength);
+ usedBufferLength += dataLength;
+
+ }
+
+ void EnsureSpace(int length) {
+ if (length + usedBufferLength > totalBufferLength) {
+ totalBufferLength += length * 2;
+
+ void* newBuffer = malloc(totalBufferLength);
+ memcpy(newBuffer, dialogTemplate, usedBufferLength);
+
+ free(dialogTemplate);
+ dialogTemplate = (DLGTEMPLATE*)newBuffer;
+ }
+ }
+
+private:
+
+ DLGTEMPLATE* dialogTemplate;
+
+ int totalBufferLength;
+ int usedBufferLength;
+
+};
+
+
+struct PromptParams {
+ const char* message;
+ const char* title;
+};
+
+/**
+ * Constants for controls.
+ */
+#define IDC_MESSAGE 1000
+#define IDC_BUTTON0 2000
+
+INT_PTR CALLBACK PromptDlgProc(HWND hDlg, UINT msg,
+ WPARAM wParam, LPARAM lParam) {
+ switch(msg) {
+ case WM_INITDIALOG:
+ {
+ PromptParams *params = (PromptParams*)lParam;
+ ::SetWindowTextA(::GetDlgItem(hDlg, IDC_MESSAGE), params->message);
+
+ ::SetFocus(::GetDlgItem(hDlg, IDC_BUTTON0));
+
+ SetWindowTextA(hDlg, params->title);
+
+ HFONT hfont =
+ CreateFontA(16, 0, 0, 0, FW_NORMAL,
+ FALSE, FALSE, FALSE,
+ ANSI_CHARSET,OUT_DEFAULT_PRECIS,CLIP_DEFAULT_PRECIS,
+ PROOF_QUALITY, FIXED_PITCH | FF_MODERN, "Courier New");
+
+ SendDlgItemMessage(hDlg, IDC_MESSAGE, WM_SETFONT, (WPARAM)hfont, MAKELPARAM(TRUE,0));
+
+
+ break;
+ }
+ case WM_COMMAND:
+ {
+ int choiceNumber = LOWORD(wParam) - IDC_BUTTON0;
+ if ((choiceNumber >= 0) && (choiceNumber < 10)) {
+ EndDialog(hDlg, choiceNumber);
+ return TRUE;
+ }
+ }
+
+ break;
+
+ case WM_NCDESTROY:
+ // Under SDL 1.2.6 we get a NCDESTROY message for no reason and the
+ // window is immediately closed. This is here to debug the problem.
+ (void)0;
+ break;
+
+ }
+
+ return FALSE;
+}
+
+}; // namespace _internal
+
+
+using namespace _internal;
+
+/**
+ * Show a dialog prompt.
+ */
+static int guiPrompt(
+ const char* windowTitle,
+ const char* prompt,
+ const char** choice,
+ int numChoices) {
+
+ int width = 280;
+ int height = 128;
+
+ const int buttonSpacing = 2;
+ const int buttonWidth =
+ (width - buttonSpacing * 2 -
+ buttonSpacing * (numChoices - 1)) / numChoices;
+ const int buttonHeight = 13;
+
+
+ DialogTemplate dialogTemplate(
+ windowTitle,
+ WS_CAPTION | DS_CENTER | WS_SYSMENU,
+ 10, 10, width, height,
+ "Tahoma");
+
+ dialogTemplate.AddEditBox(
+ "Edit", WS_VISIBLE | ES_READONLY | ES_OEMCONVERT | ES_MULTILINE | WS_TABSTOP, WS_EX_STATICEDGE,
+ 2, 2, width - 4, height - buttonHeight - 7, IDC_MESSAGE);
+
+ int i;
+ for (i = 0; i < numChoices; i++) {
+
+ int x = buttonSpacing + i * (buttonWidth + buttonSpacing);
+ int y = height - buttonHeight - buttonSpacing;
+
+ dialogTemplate.AddButton(choice[i], WS_VISIBLE | WS_TABSTOP, 0,
+ x, y, buttonWidth, buttonHeight, IDC_BUTTON0 + (WORD)i);
+
+ }
+
+ // Convert all single \n characters to \r\n for proper printing
+ int strLen = 0;
+ const char* pStr = prompt;
+
+ while (*pStr != '\0') {
+ if ((*pStr == '\n') && (pStr != prompt)) {
+ if (*(pStr - 1) != '\r') {
+ ++strLen;
+ }
+ }
+ ++strLen;
+ ++pStr;
+ }
+
+ char* newStr = (char*)malloc(strLen + 1);
+
+ const char* pStr2 = prompt;
+ char* pNew = newStr;
+
+ while (*pStr2 != '\0') {
+ if ((*pStr2 == '\n') && (pStr2 != prompt)) {
+ if (*(pStr2 - 1) != '\r') {
+ *pNew = '\r';
+ ++pNew;
+ }
+ }
+ *pNew = *pStr2;
+ ++pNew;
+ ++pStr2;
+ }
+
+ *pNew = '\0';
+
+ PromptParams params;
+ params.message = newStr;;
+ params.title = windowTitle;
+
+ HMODULE module = GetModuleHandle(0);
+ int ret = DialogBoxIndirectParam(module, dialogTemplate, NULL, (DLGPROC) PromptDlgProc, (DWORD)&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/dep/src/g3dlite/stringutils.cpp b/dep/src/g3dlite/stringutils.cpp
new file mode 100644
index 00000000000..c3876ebb6a4
--- /dev/null
+++ b/dep/src/g3dlite/stringutils.cpp
@@ -0,0 +1,275 @@
+/**
+ @file stringutils.cpp
+
+ @maintainer Morgan McGuire, http://graphics.cs.williams.edu
+
+ @created 2000-09-09
+ @edited 2008-01-10
+*/
+
+#include "G3D/platform.h"
+#include "G3D/stringutils.h"
+#include "G3D/BinaryInput.h"
+#include <algorithm>
+
+namespace G3D {
+
+#ifdef _MSC_VER
+ // disable: "C++ exception handler used"
+# pragma warning (push)
+# pragma warning (disable : 4530)
+#endif
+#ifdef G3D_WIN32
+ const char* NEWLINE = "\r\n";
+#else
+ const char* NEWLINE = "\n";
+ static bool iswspace(int ch) { return (ch==' ' || ch=='\t' || ch=='\n' || ch=='\r'); }
+#endif
+
+void parseCommaSeparated(const std::string s, Array<std::string>& array, bool stripQuotes) {
+ array.fastClear();
+ if (s == "") {
+ return;
+ }
+
+ size_t begin = 0;
+ const char delimiter = ',';
+ const char quote = '\"';
+ do {
+ size_t end = begin;
+ // Find the next comma, or the end of the string
+ bool inQuotes = false;
+ while ((end < s.length()) && (inQuotes || (s[end] != delimiter))) {
+ if (s[end] == quote) {
+ if ((end < s.length() - 2) && (s[end + 1] == quote) && (s[end + 2]) == quote) {
+ // Skip over the superquote
+ end += 2;
+ }
+ inQuotes = ! inQuotes;
+ }
+ ++end;
+ }
+ array.append(s.substr(begin, end - begin));
+ begin = end + 1;
+ } while (begin < s.length());
+
+ if (stripQuotes) {
+ for (int i = 0; i < array.length(); ++i) {
+ std::string& t = array[i];
+ int L = t.length();
+ if ((L > 1) && (t[0] == quote) && (t[L - 1] == quote)) {
+ if ((L > 6) && (t[1] == quote) && (t[2] == quote) && (t[L - 3] == quote) && (t[L - 2] == quote)) {
+ // Triple-quote
+ t = t.substr(3, L - 6);
+ } else {
+ // Double-quote
+ t = t.substr(1, L - 2);
+ }
+ }
+ }
+ }
+}
+
+bool beginsWith(
+ const std::string& test,
+ const std::string& pattern) {
+
+ if (test.size() >= pattern.size()) {
+ for (int i = 0; i < (int)pattern.size(); ++i) {
+ if (pattern[i] != test[i]) {
+ return false;
+ }
+ }
+ return true;
+ } else {
+ return false;
+ }
+}
+
+
+bool endsWith(
+ const std::string& test,
+ const std::string& pattern) {
+
+ if (test.size() >= pattern.size()) {
+ int te = test.size() - 1;
+ int pe = pattern.size() - 1;
+ for (int i = pattern.size() - 1; i >= 0; --i) {
+ if (pattern[pe - i] != test[te - i]) {
+ return false;
+ }
+ }
+ return true;
+ } else {
+ return false;
+ }
+}
+
+
+std::string wordWrap(
+ const std::string& input,
+ int numCols) {
+
+ std::string output;
+ size_t c = 0;
+ int len;
+
+ // Don't make lines less than this length
+ int minLength = numCols / 4;
+ size_t inLen = input.size();
+
+ bool first = true;
+ while (c < inLen) {
+ if (first) {
+ first = false;
+ } else {
+ output += NEWLINE;
+ }
+
+ if ((int)inLen - (int)c - 1 < numCols) {
+ // The end
+ output += input.substr(c, inLen - c);
+ break;
+ }
+
+ len = numCols;
+
+ // Look at character c + numCols, see if it is a space.
+ while ((len > minLength) &&
+ (input[c + len] != ' ')) {
+ len--;
+ }
+
+ if (len == minLength) {
+ // Just crop
+ len = numCols;
+
+ }
+
+ output += input.substr(c, len);
+ c += len;
+ if (c < input.size()) {
+ // Collapse multiple spaces.
+ while ((input[c] == ' ') && (c < input.size())) {
+ c++;
+ }
+ }
+ }
+
+ return output;
+}
+
+
+int stringCompare(
+ const std::string& s1,
+ const std::string& s2) {
+
+ return stringPtrCompare(&s1, &s2);
+}
+
+
+int stringPtrCompare(
+ const std::string* s1,
+ const std::string* s2) {
+
+ return s1->compare(*s2);
+}
+
+
+std::string toUpper(const std::string& x) {
+ std::string result = x;
+ std::transform(result.begin(), result.end(), result.begin(), toupper);
+ return result;
+}
+
+
+std::string toLower(const std::string& x) {
+ std::string result = x;
+ std::transform(result.begin(), result.end(), result.begin(), tolower);
+ return result;
+}
+
+
+Array<std::string> stringSplit(
+ const std::string& x,
+ char splitChar) {
+
+ Array<std::string> out;
+
+ // Pointers to the beginning and end of the substring
+ const char* start = x.c_str();
+ const char* stop = start;
+
+ while ((stop = strchr(start, splitChar))) {
+ out.append(std::string(start, stop - start));
+ start = stop + 1;
+ }
+
+ // Append the last one
+ out.append(std::string(start));
+
+ return out;
+}
+
+
+std::string stringJoin(
+ const Array<std::string>& a,
+ char joinChar) {
+
+ std::string out;
+
+ for (int i = 0; i < (int)a.size() - 1; ++i) {
+ out += a[i] + joinChar;
+ }
+
+ if (a.size() > 0) {
+ return out + a.last();
+ } else {
+ return out;
+ }
+}
+
+
+std::string stringJoin(
+ const Array<std::string>& a,
+ const std::string& joinStr) {
+
+ std::string out;
+
+ for (int i = 0; i < (int)a.size() - 1; ++i) {
+ out += a[i] + joinStr;
+ }
+
+ if (a.size() > 0) {
+ return out + a.last();
+ } else {
+ return out;
+ }
+}
+
+
+std::string trimWhitespace(
+ const std::string& s) {
+
+ size_t left = 0;
+
+ // Trim from left
+ while ((left < s.length()) && iswspace(s[left])) {
+ ++left;
+ }
+
+ int right = s.length() - 1;
+ // Trim from right
+ while ((right > (int)left) && iswspace(s[right])) {
+ --right;
+ }
+
+ return s.substr(left, right - left + 1);
+}
+
+}; // namespace
+
+#undef NEWLINE
+#ifdef _MSC_VER
+# pragma warning (pop)
+#endif
diff --git a/dep/src/g3dlite/uint128.cpp b/dep/src/g3dlite/uint128.cpp
new file mode 100644
index 00000000000..1f596fc3e51
--- /dev/null
+++ b/dep/src/g3dlite/uint128.cpp
@@ -0,0 +1,155 @@
+/**
+ @file uint128.cpp
+
+ @maintainer Morgan McGuire, http://graphics.cs.williams.edu
+ @author Kyle Whitson
+
+ @created 2008-07-17
+ @edited 2008-07-17
+ */
+
+#include "G3D/uint128.h"
+
+namespace G3D {
+
+/** Adds two 64-bit integers, placing the result and the overflow into 64-bit integers.*/
+static void addAndCarry(const uint64& _a, const uint64& _b, uint64& carry, uint64& result) {
+
+ // Break each number into 4 32-bit chunks. Since we are using uints, right-shifting will fill with zeros.
+ // This eliminates the need to and with 0xFFFFFFFF.
+ uint32 a [2] = {_a & 0xFFFFFFFF, _a >> 32};
+ uint32 b [2] = {_b & 0xFFFFFFFF, _b >> 32};
+
+ uint64 tmp = uint64(a[0]) + b[0];
+
+ result = tmp & 0xFFFFFFFF;
+ uint32 c = tmp >> 32;
+
+ tmp = uint64(c) + a[1] + b[1];
+ result += tmp << 32;
+ carry = (tmp >> 32);
+}
+
+/** Multiplies two unsigned 64-bit integers, placing the result into one 64-bit int and the overflow into another.*/
+void multiplyAndCarry(const uint64& _a, const uint64& _b, uint64& carry, uint64& result) {
+
+ // Break each number into 4 32-bit chunks. Since we are using uints, right-shifting will fill with zeros.
+ // This eliminates the need to and with 0xFFFFFFFF.
+ uint32 a [2] = {_a & 0xFFFFFFFF, _a >> 32};
+ uint32 b [2] = {_b & 0xFFFFFFFF, _b >> 32};
+
+ uint64 prod [2][2];
+ for(int i = 0; i < 2; ++i) {
+ for(int j = 0; j < 2; ++j) {
+ prod[i][j] = uint64(a[i]) * b[j];
+ }
+ }
+
+ // The product of the low bits of a and b will always fit into the result
+ result = prod[0][0];
+
+ // The product of the high bits of a and b will never fit into the result
+ carry = prod[1][1];
+
+ // The high 32 bits of prod[0][1] and prod[1][0] will never fit into the result
+ carry += prod[0][1] >> 32;
+ carry += prod[1][0] >> 32;
+
+ uint64 tmp;
+ addAndCarry(result, (prod[0][1] << 32), tmp, result);
+ carry += tmp;
+ addAndCarry(result, (prod[1][0] << 32), tmp, result);
+ carry += tmp;
+}
+
+
+uint128::uint128(const uint64& hi, const uint64& lo) : hi(hi), lo(lo) {
+}
+
+uint128::uint128(const uint64& lo) : hi(0), lo(lo) {
+}
+
+uint128& uint128::operator+=(const uint128& x) {
+
+ G3D::uint64 carry;
+ addAndCarry(lo, x.lo, carry, lo);
+
+ // Adding the carry will change hi. Save the old hi bits in case this == x.
+ const uint64 xHi = x.hi;
+ hi += carry;
+ hi += xHi;
+ return *this;
+}
+
+uint128& uint128::operator*=(const uint128& x) {
+
+ // The low bits will get overwritten when doing the multiply, so back up both (in case &x == this)
+ const uint64 oldLo = lo;
+ const uint64 oldXLo = x.lo;
+
+ G3D::uint64 carry;
+ multiplyAndCarry(oldLo, oldXLo, carry, lo);
+
+ // Overflow doesn't matter here because the result is going into hi - any overflow will exceed the capacity of a 128-bit number
+ // Note: hi * x.hi will always overflow, since (x * 2^64) * (y * 2^64) = x*y*(2^128). The largest number expressable in 128 bits is
+ // 2^128 - 1.
+ hi = carry + (oldLo * x.hi) + (hi * oldXLo);
+
+ return *this;
+}
+
+uint128& uint128::operator^=(const uint128& x) {
+ hi ^= x.hi;
+ lo ^= x.lo;
+ return *this;
+}
+
+uint128& uint128::operator&=(const uint128& x) {
+ hi &= x.hi;
+ lo &= x.lo;
+ return *this;
+}
+
+uint128& uint128::operator|=(const uint128& x) {
+ hi |= x.hi;
+ lo |= x.lo;
+ return *this;
+}
+
+bool uint128::operator==(const uint128& x) {
+ return (hi == x.hi) && (lo == x.lo);
+}
+
+uint128& uint128::operator>>=(const int x) {
+
+ //Before shifting, mask out the bits that will be shifted out of hi.
+ //Put a 1 in the first bit that will not be lost in the shift, then subtract 1 to get the mask.
+ uint64 mask = ((uint64)1L << x) - 1;
+ uint64 tmp = hi & mask;
+ hi >>= x;
+
+ //Shift lo and add the bits shifted down from hi
+ lo = (lo >> x) + (tmp << (64 - x));
+
+ return *this;
+}
+
+uint128& uint128::operator<<=(const int x) {
+
+ //Before shifting, mask out the bits that will be shifted out of lo.
+ //Put a 1 in the last bit that will be lost in the shift, then subtract 1 to get the logical inverse of the mask.
+ //A bitwise NOT will then produce the correct mask.
+ uint64 mask = ~((((uint64)1L) << (64 - x)) - 1);
+ uint64 tmp = lo & mask;
+ lo <<= x;
+
+ //Shift hi and add the bits shifted up from lo
+ hi = (hi << x) + (tmp >> (64 - x));
+
+ return *this;
+}
+
+uint128 uint128::operator&(const uint128& x) {
+ return uint128(hi & x.hi, lo & x.lo);
+}
+}